Хорошо, у меня все работает. Во-первых, пока я искал вокруг, я наткнулся на:
10 основных инструментов Erlang для Erlang Разработчики
и в этом заключается единственный величайший «инструмент» erlang, который я использовал на сегодняшний день:
История команд, которая не стирается при выходе из оболочки erlang.
Вы можете установить/включить его, следуя инструкциям здесь: https://github.com/ferd/erlang-history
Вернемся к проблеме:
1) Я обнаружил, что порядок модулей httpd важен. mod_esi
должно стоять перед mod_get
:
сервер.конф:
[
{modules, [
mod_alias,
mod_actions,
mod_esi,
mod_cgi,
mod_get,
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, {"/erl", [mymod]} },
{erl_script_nocache, true},
{error_log, "./errors.log"},
{transfer_log, "./requests.log"}
].
После правильного порядка модулей я перестал получать ошибки «Файл не найден» из-за странных преобразованных путей.
2) Код модуля esi должен находиться в каталоге server_root
. Имя моего модуля esi mymod.erl
:
~/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
├── mylog.log
├── mymod.beam
├── mymod.erl
├── requests.log
├── s.beam
├── s.erl
├── server.conf
├── xhttpd_example.beam
└── xhttpd_example.erl
3) Потому что я указал:
{erl_script_alias, {"/erl", [mymod]} }
URL-адрес, который мне нужно использовать:
http://localhost:57867/erl/mymod:get_data
Порт должен совпадать с портом сервера. Правильный путь — это любой путь, указанный вами в свойстве erl_script_alias, плюс /modulename:funcname
или /modulename/funcname
.
Вот mymod.erl:
-module(mymod).
-export([get_data/3]).
log(Data) ->
{ok, IoDevice} = file:open(
"/Users/7stud/erlang_programs/inets_proj/mylog.log",
[append]
),
file:write(IoDevice, Data),
file:close(IoDevice).
get_data(SessionID, Env, Input) ->
Headers = "Content-Type: text/html\r\n\r\n",
Data = [
<<"Hello, ">>,
"esi!\n"
],
log(io_lib:format(
"Inside mymod:get_data()\nSessionId=~p\nEnv=~p\nInput=~p\n",
[SessionID, Env, Input]
)),
mod_esi:deliver(SessionID, Headers), %Headers must be a string.
mod_esi:deliver(SessionID, Data). %Data can be an iolist.
Согласно документации mod_esi:
mod_esi:deliver/2 должен использоваться для генерации ответа клиенту, а SessionID — это идентификатор, который должен использоваться при вызове этой функции, не предполагая ничего о типе данных. Эту функцию можно вызывать несколько раз, чтобы разбить данные ответа на части. Обратите внимание, что первый блок данных, отправляемый клиенту, должен как минимум содержать все поля заголовка HTTP, которые будут сгенерированы в ответе. Если первый фрагмент не содержит конец заголовка HTTP, то есть "\r\n\r\n", сервер предполагает, что поля заголовка HTTP не будут созданы.
4) Скомпилируйте mymod.erl:
~/erlang_programs/inets_proj$ erlc mymod.erl
~/erlang_programs/inets_proj$
Вы должны перекомпилировать после каждого изменения, которое вы делаете в mymod.erl, а затем перезапускать сервер. Было бы проще, если бы это работало:
5> httpd:reload_config("server.conf", disturbing).
{error,{missing_property,server_name}}
но даже если в моем конфигурационном файле указано свойство server_name, я получаю эту ошибку.
5) Я предлагаю вам сделать логирование ошибок, включив mod_log
в список модулей и указав свойство:
{error_log, "./errors.log"}
Затем проверьте этот файл на наличие каких-либо отзывов о том, что произошло при сбое запроса.
6) Когда я вызвал свой пользовательский метод log() (чтобы записать некоторую информацию в файл), без моего ведома метод log() вызывал исключение, из-за которого сервер отклонял запрос и вводил сообщение module traverse failed
по ошибке. .бревно:
[Timestamp], module traverse failed: mod_esi:do =>
Error Type: exit
Error: {mod_esi_linked_process_died,<0.97.0>,normal}
Stack trace: [{mod_esi,receive_headers,1,[{file,"mod_esi.erl"},{line,428}]},
{mod_esi,deliver_webpage_chunk,3,
[{file,"mod_esi.erl"},{line,389}]},
{mod_esi,erl_scheme_webpage_chunk,5,
[{file,"mod_esi.erl"},{line,380}]},
{mod_esi,generate_webpage,7,
[{file,"mod_esi.erl"},{line,314}]},
{httpd_response,traverse_modules,2,
[{file,"httpd_response.erl"},{line,77}]},
{httpd_response,generate_and_send_response,1,
[{file,"httpd_response.erl"},{line,44}]},
{httpd_request_handler,handle_response,1,
[{file,"httpd_request_handler.erl"},{line,655}]},
{gen_server,try_dispatch,4,
[{file,"gen_server.erl"},{line,616}]}]
7) Вот модуль, который я использовал для запуска сервера:
-module(s).
-compile(export_all).
%Need to look up port with httpd:info(Server)
ensure_inets_start() ->
case inets:start() of
ok -> ok;
{error,{already_started,inets}} -> ok
end.
start() ->
ok = s:ensure_inets_start(),
{ok, Server} = inets:start(httpd,
[{proplist_file, "./server.conf"}]
),
Server.
stop(Server) ->
ok = inets:stop(httpd, Server).
8) Вот несколько примеров запросов с использованием curl...
Информация о сервере в оболочке:
~/erlang_programs/inets_proj$ 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_esi,mod_cgi,mod_get,
mod_log]},
{server_root,"/Users/7stud/erlang_programs/inets_proj"},
{erl_script_alias,{"/erl",[mymod]}},
{port,59202},
{transfer_log,<0.93.0>},
{error_log,<0.92.0>},
{document_root,"./htdocs"}]
4>
Запрос 1 (запрос esi get):
~$ curl -v "http://localhost:57867/erl/mymod:get_data"
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 57867 (#0)
> GET /erl/mymod:get_data HTTP/1.1
> Host: localhost:57867
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 28 Feb 2018 13:28:09 GMT
< Server: inets/6.4.5
< Cache-Control: no-cache
< Pragma: no-cache
< Expires: Wed, 28 Feb 2018 13:28:09 GMT
< Transfer-Encoding: chunked
< Content-Type: text/html
<
Hello, esi!
* Connection #0 to host localhost left intact
~$
мой лог.лог:
~/erlang_programs/inets_proj$ cat mylog.log
...
...
Inside mymod:get_data()
SessionId=<0.99.0>
Env=[{server_software,"inets/6.4.5"},
{server_name,"httpd_test"},
{host_name,"ChristophersMBP"},
{gateway_interface,"CGI/1.1"},
{server_protocol,"HTTP/1.1"},
{server_port,59202},
{request_method,"GET"},
{remote_addr,"127.0.0.1"},
{peer_cert,undefined},
{script_name,"/erl/mymod:get_data"},
{http_host,"localhost:59202"},
{http_user_agent,"curl/7.58.0"},
{http_accept,"*/*"}]
Input=[]
-------
Запрос 2 (esi получить запрос со строкой запроса):
~$ curl -v "http://localhost:59202/erl/mymod:get_data?a=1&b=2"
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 59202 (#0)
> GET /erl/mymod:get_data?a=1&b=2 HTTP/1.1
> Host: localhost:59202
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 28 Feb 2018 13:47:41 GMT
< Server: inets/6.4.5
< Cache-Control: no-cache
< Pragma: no-cache
< Expires: Wed, 28 Feb 2018 13:47:41 GMT
< Transfer-Encoding: chunked
< Content-Type: text/html
<
Hello, esi!
* Connection #0 to host localhost left intact
мой лог.лог:
~/erlang_programs/inets_proj$ cat mylog.log
...
...
Inside mymod:get_data()
SessionId=<0.105.0>
Env=[{server_software,"inets/6.4.5"},
{server_name,"httpd_test"},
{host_name,"ChristophersMBP"},
{gateway_interface,"CGI/1.1"},
{server_protocol,"HTTP/1.1"},
{server_port,59202},
{request_method,"GET"},
{remote_addr,"127.0.0.1"},
{peer_cert,undefined},
{script_name,"/erl/mymod:get_data?a=1&b=2"},
{http_host,"localhost:59202"},
{http_user_agent,"curl/7.58.0"},
{http_accept,"*/*"},
{query_string,"a=1&b=2"}]
Input="a=1&b=2"
Запрос 3 (почтовый запрос esi):
~$ curl -v --data "a=1&b=2" "http://localhost:59202/erl/mymod:get_data"
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 59202 (#0)
> POST /erl/mymod:get_data HTTP/1.1
> Host: localhost:59202
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 7
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 7 out of 7 bytes
< HTTP/1.1 200 OK
< Date: Wed, 28 Feb 2018 13:51:44 GMT
< Server: inets/6.4.5
< Cache-Control: no-cache
< Pragma: no-cache
< Expires: Wed, 28 Feb 2018 13:51:44 GMT
< Transfer-Encoding: chunked
< Content-Type: text/html
<
Hello, esi!
* Connection #0 to host localhost left intact
мой лог.лог:
Inside mymod:get_data()
SessionId=<0.108.0>
Env=[{server_software,"inets/6.4.5"},
{server_name,"httpd_test"},
{host_name,"ChristophersMBP"},
{gateway_interface,"CGI/1.1"},
{server_protocol,"HTTP/1.1"},
{server_port,59202},
{request_method,"POST"},
{remote_addr,"127.0.0.1"},
{peer_cert,undefined},
{script_name,"/erl/mymod:get_data"},
{http_host,"localhost:59202"},
{http_user_agent,"curl/7.58.0"},
{http_accept,"*/*"},
{http_content_length,"7"},
{http_content_type,"application/x-www-form-urlencoded"}]
Input="a=1&b=2"
-------
Запрос 4 (запрос на получение cgi):
~$ curl -v "http://localhost:59202/cgi-bin/1.pl"
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 59202 (#0)
> GET /cgi-bin/1.pl HTTP/1.1
> Host: localhost:59202
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 28 Feb 2018 13:41:43 GMT
< Server: inets/6.4.5
< Transfer-Encoding: chunked
< Content-Type: text/html
<
Hello, Perl.
* Connection #0 to host localhost left intact
Запрос 5 (получить запрос на обычный файл из каталога document_root):
~$ curl -v "http://localhost:59202/file1.txt"
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 59202 (#0)
> GET /file1.txt HTTP/1.1
> Host: localhost:59202
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 28 Feb 2018 13:42:15 GMT
< Server: inets/6.4.5
< Content-Type: text/plain
< Etag: nCZT0114
< Content-Length: 14
< Last-Modified: Mon, 26 Feb 2018 02:51:52 GMT
<
Hello, world!
* Connection #0 to host localhost left intact
person
7stud
schedule
28.02.2018