Here's the proplist_file that I use to configure the httpd server:
[
{modules, [
mod_alias,
mod_actions,
mod_cgi,
mod_get,
mod_esi,
mod_log
]},
{bind_address, "localhost"},
{port,0},
{server_name,"httpd_test"},
{server_root,"/Users/7stud/erlang_programs/inets_proj"},
{document_root,"./htdocs"},
{script_alias, {"/cgi-bin/", "/Users/7stud/erlang_programs/inets_proj/cgi-bin/"} },
{erl_script_alias, {"/cgi-bin/example", [httpd_example]} },
{erl_script_nocache, true},
{error_log, "./errors.log"},
{transfer_log, "./requests.log"}
].
For esi scripts the key property is:
{erl_script_alias, {"/cgi-bin/example", [httpd_example]} }
According to the inets docs:
ESI Properties - Requires mod_esi
{erl_script_alias, {URLPath, [AllowedModule]}}
URLPath = string() and AllowedModule = atom(). erl_script_alias marks all URLs matching url-path as erl scheme scripts. A matching URL is mapped into a specific module and function, for example:{erl_script_alias, {"/cgi-bin/example", [httpd_example]}}A request to
http://your.server.org/cgi-bin/example/httpd_example:yahoowould refer tohttpd_example:yahoo/3or, if that does not exist, httpd_example:yahoo/2 andhttp://your.server.org/cgi-bin/example/other:yahoowould not be allowed to execute.
My directory structure is:
~/erlang_programs$ tree inets_proj/
inets_proj/
├── cgi-bin
│ ├── 1.pl
│ ├── example
│ │ ├── httpd_example.beam
│ │ └── httpd_example.erl
│ ├── httpd_example.beam
│ └── httpd_example.erl
├── cl.beam
├── cl.erl
├── errors.log
├── htdocs
│ └── file1.txt
├── httpd_example.beam
├── httpd_example.erl
├── mylog.log
├── requests.log
├── s.beam
├── s.erl
└── server.conf
I wasn't sure where to put the httpd_example module, so I put it in several places.
esi_mod.erl:
-module(esi_mod).
-compile(export_all).
log(Data) ->
{ok, IoDevice} = file:open(
"/Users/7stud/erlang_programs/inets_proj/mylog.log",
[append]
),
ok = file:write(IoDevice, Data),
file:close(IoDevice).
get_data(SessionID, _Env, _Input) ->
Headers = "Content-Type: text/html\r\n\r\n",
Data = "Hello, esi",
log(["--Inside esi_mod:get_data() ~n"]),
ok = mod_esi:deliver(SessionID, Headers), %Headers must be a string.
ok = mod_esi:deliver(SessionID, Data). %Data can be an iolist.
Here is the server info as reported in the shell:
$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1> c(s).
s.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,s}
2> S = s:start().
<0.86.0>
3> httpd:info(S).
[{mime_types,[{"htm","text/html"},{"html","text/html"}]},
{server_name,"httpd_test"},
{erl_script_nocache,true},
{script_alias,{"/cgi-bin/",
"/Users/7stud/erlang_programs/inets_proj/cgi-bin/"}},
{bind_address,{127,0,0,1}},
{modules,[mod_alias,mod_actions,mod_cgi,mod_get,mod_esi,
mod_log]},
{server_root,"/Users/7stud/erlang_programs/inets_proj"},
{erl_script_alias,{"/cgi-bin/example",[httpd_example]}},
{port,64470},
{transfer_log,<0.93.0>},
{error_log,<0.92.0>},
{document_root,"./htdocs"}]
4>
Here's my request:
~$ curl -vv "http://localhost:64470/cgi-bin/example/httpd_example:get_data"
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 64470 (#0)
> GET /cgi-bin/example/httpd_example:get_data HTTP/1.1
> Host: localhost:64470
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 404 Object Not Found
< Date: Wed, 28 Feb 2018 10:22:38 GMT
< Server: inets/6.4.5
< Content-Type: text/html
< Content-Length: 245
<
<HTML>
<HEAD>
<TITLE>Object Not Found</TITLE>
</HEAD>
<BODY>
<H1>Object Not Found</H1>
The requested URL /cgi-bin/example/httpd_example:get_data was not found on this server.
</BODY>
</HTML>
* Connection #0 to host localhost left intact
~$
errors.log:
[Timestamp] access to /cgi-bin/example/httpd_example:get_data failed for 127.0.0.1, reason: "httpd_file: Can't open ./htdocs/cgi-bin/example/httpd_example:get_data: File not found"
According to the error message, the request path:
/cgi-bin/example/httpd_example:get_data
got converted to:
./htdocs/cgi-bin/example/httpd_example:get_data
which means the request path was tacked onto ./htdocs. Huh??
Okay, I got things working. First, while I was searching around I happened across:
10 Essential Erlang Tools for Erlang Developers
and therein lies the single greatest erlang "tool" that I've used to date:
You can install/enable it by following the instructions here: https://github.com/ferd/erlang-history
Back to the problem at hand:
1) I found that the order of the httpd modules is important.
mod_esineeds to be beforemod_get:server.conf:
After getting the module order correct, I stopped getting the "File not found" errors due to the strange converted paths.
2) The code for the esi module has to be in the
server_rootdirectory. My esi module's name ismymod.erl:3) Because I specified:
the url I need to use is:
The port has to match the server's port. The proper path is whatever path you specify in the erl_script_alias property plus
/modulename:funcnameor/modulename/funcname.Here's mymod.erl:
According to the mod_esi docs:
4) Compile mymod.erl:
You have to recompile after every change you make to mymod.erl, then restart the server. It would be simpler if this would work:
but even though my config file does specify a server_name property I get that error.
5) I suggest you do error logging by including
mod_login the list of modules and specifying the property:Then check that file for any feedback on what happened when a request fails.
6) When I called my custom log() method (in order to write some info to a file) unbeknownst to me the log() method was causing an exception, which caused the server to reject the request and enter a
module traverse failedmessage in error.log:7) Here's the module I used to start the server:
8) Here are a few sample requests using curl...
Server info in the shell:
Request 1 (esi get request):
mylog.log:
Request 2 (esi get request with query string):
mylog.log:
Request 3 (esi post request):
mylog.log:
Request 4 (cgi get request):
Request 5 (get request for regular file from document_root directory):