Erlang gen_tcp:recv http_request abs_path

Я пишу код на Erlang, который принимает HTTP-запросы. У меня есть рабочий код, который показан ниже.

У меня проблема в том, что я не уверен в возвращаемом результате gen_tcp:recv.

Я создаю прослушивающий сокет и принимаю сокеты, используя

{ok, ListenSock}=gen_tcp:listen(Port, [list,{active, false},{packet,http}])
{ok, Sock}=gen_tcp:accept(ListenSock),

Я принимаю запрос GET (или любой другой), используя

{ok, {http_request, Method, Path, Version}} = gen_tcp:recv(Sock, 0),
handle_get(Sock, Path);

Затем, чтобы получить параметры URL (параметры CGI, например, ?foo=1&bar=2), я должен сопоставить Path со структурой {abs_path, RelativePath}.

handle_get(Sock, ReqPath) ->
    {abs_path, RelPath} = ReqPath,
    Parameters = string:substr(RelPath, string:str(RelPath, "?") + 1),

Когда я читал документы Erlang о gen_tcp и, в частности, о методе recv, я нашел страница с описанием HttpPacket.

Грамматика на странице ясно показывает, что Path в HttpPacket, а в данном случае тип HttpRequest, может иметь несколько типов HttpUri.

HttpRequest = {http_request, HttpMethod, HttpUri, HttpVersion}
HttpUri = '*'
        | {absoluteURI,
           http | https,
           Host :: HttpString,
           Port :: inet:port_number() | undefined,
           Path :: HttpString}
        | {scheme, Scheme :: HttpString, HttpString}
        | {abs_path, HttpString}
        | HttpString

Я понимаю, что должен поддерживать каждый из этих возможных случаев, однако я не уверен. Мне также интересно, как я могу проверить эти случаи. Я пытался использовать curl и RESTClient в Firefox, и оба они заставляют gen_tcp:recv возвращать abs_path.

Итак, чтобы было ясно, как определяется, содержит ли запрос {abs_path, HttpString}, {scheme, Scheme :: HttpString, HttpString} или {absoluteURI,...}, и нужно ли мне поддерживать их все?

Полный список

start(Port)->
    {ok, ListenSock}=gen_tcp:listen(Port, [list,{active, false},{packet,http}]),
    loop(ListenSock).


loop(ListenSock) -> 
    {ok, Sock}=gen_tcp:accept(ListenSock),
    spawn(?MODULE, handle_request, [Sock]),
    loop(ListenSock).

%% Takes a TCP socket and receives 
%% http://erlang.org/doc/man/erlang.html#decode_packet-3
handle_request(Sock) ->
    {ok, {http_request, Method, Path, _Version}} = gen_tcp:recv(Sock, 0),

    case (Method) of
        'GET' ->
            handle_get(Sock, Path);
        _ -> 
            send_unsupported_error(Sock)
    end.

handle_get(Sock, ReqPath) ->
    {abs_path, RelPath} = ReqPath,
    Parameters = string:substr(RelPath, string:str(RelPath, "?") + 1),
    %% Debugging
    ParsedParms = httpd:parse_query(Parameters),
    io:fwrite("Full Path: ~p~nParameters: ~p~n", [RelPath, ParsedParms]),
    %% End Debugging
    send_accept(Sock).

person Christophe De Troyer    schedule 13.05.2015    source источник
comment
Почему вы пишете свой собственный веб-сервер? Это больше работы, чем вы думаете.   -  person Steve Vinoski    schedule 14.05.2015
comment
Я не пишу свой собственный веб-сервер. Все, что нужно сделать моему коду, это принять запросы на получение с параметрами в URL-адресе. Я не собираюсь заниматься чем-то еще.   -  person Christophe De Troyer    schedule 14.05.2015
comment
Как насчет заголовков HTTP, сопровождающих запрос GET?   -  person Steve Vinoski    schedule 14.05.2015
comment
Соответствующие заголовки обрабатываются. Я не указал этот код, поскольку он не имел отношения к моему вопросу.   -  person Christophe De Troyer    schedule 14.05.2015


Ответы (1)


Вы можете использовать простой клиент с поддержкой сети, такой как netcat (/usr/bin/nc в моей системе), чтобы отправить запрос в любой форме. Например, следующий код подключается к веб-серверу, прослушивающему localhost порт 8000, и отправляет запрос GET, где путь является URL-адресом (обратите внимание, что $ обозначает приглашение оболочки):

$ nc localhost 8000
GET http://stackoverflow.com HTTP/1.1

$

Программа nc читает со своего стандартного ввода. Обязательно дважды нажмите Enter после строки GET, чтобы правильно указать конец заголовков HTTP. Это приводит к тому, что вызов gen_tcp:recv на сервере возвращает:

{absoluteURI,http,"stackoverflow.com",undefined,"/"}

Точно так же следующее вернет путь из gen_tcp:recv, который не является кортежем {abs_path, ...}, а просто "../foo":

$ nc localhost 8000
GET ../foo HTTP/1.1

$

Вы можете легко настроить тестовые варианты, подобные этим, в текстовых файлах и передать их в nc с помощью перенаправления стандартного ввода.

person Steve Vinoski    schedule 14.05.2015
comment
Ага, это отвечает на все, что я хотел знать! Благодарю вас! - person Christophe De Troyer; 14.05.2015