erlang functional

Erlang и HTTP сервер

Dmitry Vasiliev 23:49, 2009 6 23

В последнее время я стал достаточно активно заниматься изучением Erlang и по мере возможностей буду делиться своими открытиями и в этой области. Обычно, когда речь заходит об HTTP сервере написанном на Erlang, вспоминают MochiWeb, или Yaws. Но в стандартных модулях Erlang уже есть HTTP сервер как один из сервисов приложения Inets. Рассмотрим подробнее как с ним работать.

HTTP сервер можно запускать различными способам, но здесь я рассмотрю, на мой взгляд, наиболее гибкий. Нам понадобится следующая структура:

log/
www/
    index.html
test.config
httpd.conf
scripts.erl
start

В каталог log/ будут записываться лог файлы. В каталоге www/ хранятся статические файлы, в данном случае index.html:

<h1>Hello, World!</h1>

test.config - это основной файл конфигурации для приложения Inets, через который мы будем запускать HTTP сервер:

[
    {inets, [
        {services, [
            {httpd, [{proplist_file, httpd.conf}]}]}
    ]}
].

Здесь httpd добавлен как сервис inets с конфигурацией в файле httpd.conf:

[
    {modules, [
        mod_alias,
        mod_auth,
        mod_esi,
        mod_actions,
        mod_cgi,
        mod_get,
        mod_head,
        mod_dir,
        mod_log,
        mod_disk_log
    ]},

    {port, 8080},
    {bind_address, any},
    {server_name, "Test server"},

    {server_root, "log/"},
    {document_root, "www/"},
    {directory_index, ["index.html"]},

    {erl_script_alias, {"/scripts", [scripts]}},

    {error_log, "error.log"},
    {security_log, "security.log"},
    {transfer_log, "transfer.log"},

    {mime_types, [
        {"html", "text/html"},
        {"css", "text/css"},
        {"js", "application/x-javascript"}
    ]},
    {mime_type, "application/octet-stream"}
].

Рассмотрим кратко опции:

  • modules - это список подключаемых модулей. В данном случае подключены все модули, но по желанию список можно сократить. Внимание: порядок модулей важен, т.к. они вызываются по порядку пока запрос не будет обработан.
  • port и bind_address описывают порт и адрес на которых сервер будет принимать соединения. В качестве адреса указан атом any, но может быть указан и конкретный адрес в виде строки.
  • server_name указывает имя сервера.
  • server_root и document_root - соотв. корневые директории сервера и документов.
  • directory_index - список индексных файлов для директорий.
  • erl_script_alias описывает путь и список динамических модулей которые будут доступны через Web.
  • Параметры *_log описывают имена различных лог-файлов.
  • mime_types и mime_type - краткий список типов MIME и тип для неизвестных файлов. По документации mime_types можно указать в виде файла в стандартном формате, но пока у меня с этим есть сложности.

Файл конфигурации может быть также и в формате похожем на конфигурацию Apache, но мне больше нравится формат Erlang. Дополнительные опции конфигурации можно посмотреть в документации httpd.

В опции erl_script_alias в качестве доступных модулей был указан только модуль scripts. Исходный файл scripts.erl:

-module(scripts).
-export([test/3]).


test(SessionID, Env, _Input) ->
    mod_esi:deliver(SessionID,
        ["Content-Type: text/html\r\n\r\n" | format(Env)]).


format([]) ->
    "";
format([{Key, Value} | Env]) ->
    [io_lib:format("<b>~p:</b> ~p<br />\~n", [Key, Value]) | format(Env)].

Функция test/3 описывает нашу тестовую точку входа:

  • SessionID - это идентификатор запроса, который используется в последующих вызовах mod_esi:deliver/2. Рекомендуется не делать никаких предположений о типе идентификатора, но, по секрету, в текущей реализации - это pid процесса.
  • Env - список переменных среды в виде [{Ключ, Значение}].
  • Input - данные POST запроса. В данном случае мы их не используем.

Функция mod_esi:deliver/2 служит для посылки данных клиенту. Первым параметром должен передаваться полученный на входе идентификатор запроса и затем список строк для передачи. Функция может вызываться несколько раз, но если нужно передать заголовки, то они должны быть возвращены в первом вызове deliver, как в нашем примере.

В примере так же используется вспомогательная функция format/1 для форматирования переменных среды.

Перед запуском нам нужно откомпилировать наш модуль:

$ erlc -Wall scripts.erl

И затем мы можем запустить файл start со следующим содержимым:

#! /bin/sh

erl -noshell -config test -s inets

Здесь мы запускаем приложение inets с файлом конфигурации test.config. После запуска по адресу http://127.0.0.1:8080 можно увидеть вывод странички index.html и по адресу http://127.0.0.1:8080/scripts/scripts/test/ вывод получаемый при вызове функции test в модуле scripts.

Остановить тестовый сервер можно стандартным Ctrl+C и a.

Comments All comments

Comment by Andrey Popp on 00:53, 2009 6 24

Andrey Popp's Gravatar

А почему не по OTP-шному?

Comment by Dmitry Vasiliev on 08:56, 2009 6 24

Dmitry Vasiliev's Gravatar

По OTP-шному, если ты посмотришь что такое inets:start/0

Comment by Andrey Popp on 18:08, 2009 6 24

Andrey Popp's Gravatar

Не нравится мне inets, это как django в python - магия сплошная. Другое дело MochiWeb - всё просто и понятно ;)

Comment by Dmitry Vasiliev on 18:12, 2009 6 24

Dmitry Vasiliev's Gravatar

:-))) Где магия? Это все стандартные возможности Erlang. А вот MochiWeb как-раз использует недокументированные возможности. ;-)

Comment by zabivator on 18:12, 2010 1 11

zabivator's Gravatar

Можно узнать, какие именно недокументированные возможности использует Erlang?
А то мы его используем, вот и хочется знать какой "подземный стук" может вылезти.

P.S. уведомление об ответе мне придёт на e-mail, или самому проверять эту страницу периодически?

Comment by Dmitry Vasiliev on 18:51, 2010 1 11

Dmitry Vasiliev's Gravatar

Сам Erlang не может использовать недокументированных возможности, точнее - все, что внутри, все его. :-)

Приложения написанные на Erlang могут использовать недокументированные возможности Erlang и основная проблема с этим, что теоретически эти возможности могут убрать, или поменять до неузнаваемости. Поэтому я лично стараюсь избегать их использовать.

Видимо самая известная из недокументированных возможностей (которая и упомянута выше) - parameterized modules (ftp://ftp.sunet.se/pub/lang/erlang/workshop/2003/paper/p29-carlsson.pdf). Как минимум MochiWeb и Riak их используют.

Еще можно упомянуть packages (http://erlang.org/doc/man/packages.html), которые документированы, но не рекомендуются к использованию.

Из мелочей не стоит использовать, например, устаревшую форму вызова через кортеж: {string, to_lower}("TEST").

В целом, конечно, ничего страшного, тем более, если вы сами пишете эти приложения. Обычно сложнее (и менее приятно) разбираться с чужими приложениями, которые уже не поддерживаются и вдруг перестали работать.

P.S. Нужно проверять, или подписаться на RSS/Atom с комментариями.

Add comment

Name:
Email: (Never will be published.)
Web site:
Comment: (Paragraphs divided by empty lines, line breaks and links will be automatically formatted.)