连接到 erlang.org 的 erlang gen_tcp 声明 404

erlang gen_tcp connecting to erlang.org claims a 404

上下文:JA 的 "Programming Erlang" 2ed,关于文件的第 16 章,第 256 页,关于从二进制文件解析 urls 的示例。

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

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

那(巧妙地)失败了——列表 L 最终为空。 运行 第一步,观察到一个 st运行ge 东西 - 它 return 一个二进制文件,但它不是我正在寻找的二进制文件:

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 进行 CURLing,将其转储到文件中,然后是文件: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 的代码没有任何明显的错误! =) 然而,带有注释 (3)、(4) 和 (5) 的行并不是我完全理解的内容。

所以,Erlanger 的同胞们,有什么想法吗? 非常感谢!

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

$ 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 之后也适用于主机 header:

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>}