二郎 gen_tcp:recv http_request abs_path

Erlang gen_tcp:recv http_request abs_path

我正在用接受 HTTP 请求的 Erlang 编写代码。我有如下所示的工作代码。

我遇到的问题是我不确定 gen_tcp:recv 的 returned 结果。

我创建了一个侦听套接字并使用

接受套接字
{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),

当我阅读有关 gen_tcp 的 Erlang 文档时,更具体地说是 recv 方法,我发现了 the page describing HttpPacket.

页面上的语法清楚地表明PathHttpPacket中,而在本例中是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

我知道我必须支持每一种可能的情况,但我不确定。我也想知道如何测试这些案例。我试过在 Firefox 中使用 curlRESTClient,它们都使 gen_tcp:recv return 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).

您可以使用一个简单的 network-capable 客户端,例如 netcat(在我的系统上是 /usr/bin/nc)来发送您喜欢的任何形式的请求。例如,以下连接到侦听 localhost 端口 8000 的 Web 服务器并发送 GET 请求,其中路径为 URL(注意 $ 表示 shell提示):

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

$

nc 程序从其标准输入读取。请务必在 GET 行后按两次 Enter 以正确指示 HTTP headers 的结尾。这导致服务器上的 gen_tcp:recv 调用 returning:

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

类似地,以下将 return 来自 gen_tcp:recv 的路径不是 {abs_path, ...} 元组,而只是 "../foo":

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

$

您可以轻松地在文本文件中设置测试变体,并使用标准输入重定向将它们输入 nc