【问题标题】:erlang gen_tcp connecting to erlang.org claims a 404连接到 erlang.org 的 erlang gen_tcp 要求 404
【发布时间】:2018-05-02 13:28:33
【问题描述】:

上下文:JA 的“Programming Erlang”第 2 版,第 16 章文件,第 256 页,使用从二进制文件解析 url 的示例。

建议的步骤(在为 scavenge_urls 模块编写代码之后)如下:

B = socket_examples:nano_get_url("www.erlang.org"),
L = scavenge_urls:bin2urls(B),
scavenge_urls:urls2htmlFile(L,"gathered.html").

这(微妙地)失败了 - 列表 L 最终为空。自行运行第一步,观察到一个奇怪的事情 - 它确实返回了一个二进制文件,但它不是我正在寻找的二进制文件:

9> B.
<<"HTTP/1.1 404 Not Found\r\nServer: nginx\r\nDate: Sun, 19 Nov 2017 01:57:07 GMT\r\nContent-Type: text/html; charset=UTF-8\r\n"...>>
 shows that this is where the problem lies.

但在浏览器中,母舰一切正常!我能够通过替换对 socket_examples:nano_get_urls/1 的调用来完成练习,首先,对相同的 url 进行 CURL,将其转储到文件中,然后是 file:read_file/1。接下来的步骤都运行良好。

偷看 socket_examples 模块,我看到了这个:

nano_get_url(Host) ->
    {ok,Socket} = gen_tcp:connect(Host,80,[binary, {packet, 0}]), %% (1)
    ok = gen_tcp:send(Socket, "GET / HTTP/1.0\r\n\r\n"),  %% (2)
    receive_data(Socket, []).

receive_data(Socket, SoFar) ->
    receive
        {tcp,Socket,Bin} ->    %% (3)
            receive_data(Socket, [Bin|SoFar]);
        {tcp_closed,Socket} -> %% (4)
            list_to_binary(reverse(SoFar)) %% (5)
    end.

看起来没什么可疑的。首先它建立连接,然后触发 GET,然后接收响应。我以前从未必须先显式连接,然后再触发 GET,我的 http 客户端库对我隐藏了这一点。所以也许我不知道要寻找什么……而且我确信 Joe 的代码没有任何明显的错误! =) 然而,带有 cmets (3)、(4) 和 (5) 的行并不是我完全理解的。

那么,有什么想法吗,厄兰格兄弟们? 非常感谢!

【问题讨论】:

    标签: erlang httpclient erlang-ports


    【解决方案1】:

    问题不在于 Erlang。看起来运行 erlang.org 的服务器也需要 Host 标头:

    $ nc www.erlang.org 80
    GET / HTTP/1.0
    
    HTTP/1.1 404 Not Found
    Server: nginx
    Date: Sun, 19 Nov 2017 05:51:39 GMT
    Content-Type: text/html; charset=UTF-8
    Content-Length: 162
    Connection: close
    Vary: Accept-Encoding
    
    <html>
    <head><title>404 Not Found</title></head>
    <body bgcolor="white">
    <center><h1>404 Not Found</h1></center>
    <hr><center>nginx</center>
    </body>
    </html>
    $ nc www.erlang.org 80
    GET / HTTP/1.0
    Host: www.erlang.org
    
    HTTP/1.1 200 OK
    Server: nginx
    Date: Sun, 19 Nov 2017 05:51:50 GMT
    Content-Type: text/html; charset=UTF-8
    Content-Length: 12728
    Connection: close
    Vary: Accept-Encoding
    
    <!DOCTYPE html>
    <html>
    ...
    

    您的 Erlang 代码也适用于 GET HTTP/1.0\r\n 之后的 Host 标头:

    1> Host = "www.erlang.org".
    "www.erlang.org"
    2> {ok, Socket} = gen_tcp:connect(Host, 80, [binary, {packet, 0}]).
    {ok,#Port<0.469>}
    3> ok = gen_tcp:send(Socket, "GET / HTTP/1.0\r\nHost: www.erlang.org\r\n\r\n").
    ok
    4> flush().
    Shell got {tcp,#Port<0.469>,
                   <<"HTTP/1.1 200 OK\r\nServer: nginx\r\n...>>
    Shell got {tcp_closed,#Port<0.469>}
    

    【讨论】:

    • 我预计这与 Erlang 代码无关,但不确定它是关于什么的,也不知道为什么会这样,本书的勘误表中也没有。
    • 这是一个后续问题,@Dogbert - 您是如何确定服务器需要该特定标头的?因为 404 中似乎没有任何内容表明缺少某些标头是请求的问题。这是我缺少的 HTTP 标准的一些细节吗?这似乎很奇怪..
    • 我的线索是 404 响应包含 HTTP/1.1,即使我们的请求是 HTTP/1.0HTTP/1.1 请求必须有一个 Host 标头:w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23。当我尝试使用 Host 标头的请求时,它起作用了。 :)
    • 呵呵,很有意思。这也是:所有基于 Internet 的 HTTP/1.1 服务器必须以 400(错误请求)状态代码响应任何缺少 Host 标头字段的 HTTP/1.1 请求消息。
    • 如果你这样做GET / HTTP/1.1,它确实会这样做。对于HTTP/1.0 请求,它可能也应该这样做。
    猜你喜欢
    • 2011-08-12
    • 2014-12-21
    • 2018-06-01
    • 2014-06-26
    • 2020-05-23
    • 2012-04-02
    • 2012-09-21
    • 1970-01-01
    • 2014-07-10
    相关资源
    最近更新 更多