一个HTTP请求所需的最基本要素是什么?

86

我正在尝试使用 netcat 向我的本地服务器发出 GET 命令,具体操作如下:

echo -e "GET / HTTP/1.1\nHost: localhost" | nc localhost 80

不幸的是,我得到了一个HTTP/1.1 400 Bad Request的响应。那么,至少需要哪些内容才能进行HTTP请求?


1
不确定,我在 Bash、Apache、Ubuntu 上添加 \n\n 到末尾后可行。但我认为 HTTP 对行结尾的特性比较敏感,所以最好再检查一下。 - Kerrek SB
Echo在处理\r\n字符时存在一些问题,因此相同的命令使用printf可以工作,但是echo无法。http://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo - harry
请提供一个 HTTP 请求头必须包含的强制信息是什么? - Ciro Santilli OurBigBook.com
9个回答

93

如果请求为:"GET / HTTP/1.0\r\n\r\n",则响应包含头和正文,响应后连接将关闭。

如果请求为:"GET / HTTP/1.1\r\nHost: host:port\r\nConnection: close\r\n\r\n",则响应包含头和正文,响应后连接将关闭。

如果请求为:"GET / HTTP/1.1\r\nHost: host:port\r\n\r\n",则响应包含头和正文,响应后连接不会关闭。

如果请求为:"GET /\r\n\r\n",则响应仅包含正文而没有头,响应后连接将关闭。

如果请求为:"HEAD / HTTP/1.0\r\n\r\n",则响应仅包含头而没有正文,响应后连接将关闭。

如果请求为:"HEAD / HTTP/1.1\r\nHost: host:port\r\nConnection: close\r\n\r\n",则响应仅包含头而没有正文,响应后连接将关闭。

如果请求为:"HEAD / HTTP/1.1\r\nHost: host:port\r\n\r\n",则响应仅包含头而没有正文,响应后连接不会关闭。


1
它是特定于某个HTTP服务器还是由HTTP RFCs规定的? - jfs
2
对于HTTP 1.1连接:http://tools.ietf.org/html/rfc2616#section-8, 对于HTTP 1.1主机:http://tools.ietf.org/html/rfc2616#section-14.23, 对于HTTP 1.0:http://tools.ietf.org/html/rfc1945, 对于HTTP 0.9:http://www.w3.org/Protocols/HTTP/AsImplemented.html, HTTP持久连接:http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Persistent_connections(关于持久连接的解释,我提供维基百科链接,因为它有更好的解释)。 - Abhishek Oza
4
RFC 2616已经过时。请不要再引用它,除非是出于历史目的。 - Julian Reschke
鉴于@JulianReschke的评论,我在这里提供另一个链接:http://www.w3.org/Protocols/#rfc723x,用于HTTP规范。请在该页面下找到“RFC 723X”链接,以阅读有关HTTP规范的最新RFC。 - Abhishek Oza
@Kenneth "GET /\r\n\r\n" 是 HTTP/0.9 请求。HTTP/1.1 服务器通常提供向后兼容性。 - Abhishek Oza
显示剩余4条评论

73

它必须使用CRLF行结束符,并且必须以\r\n\r\n结尾,即空白行。这是我使用的代码:

printf 'GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n' |
  nc www.example.com 80

另外,我更喜欢使用printf而不是echo,并添加一个额外的头信息以关闭服务器连接,但这些都不是必需的。


1
请记住,在标准的HTTP中,换行符始终为\r\n - Matti Virkkunen
1
很好,对于我的目的,我只需要执行 printf 'GET / HTTP/1.1\r\nHost: localhost 80\r\n\r\n' | nc localhost 80。谢谢! - Naftuli Kay
3
主机应该只写成 localhost,而不是 localhost 80 - slebetman
1
@NaftuliTzviKay Host 格式定义为 Host = "Host" ":" host [ ":" port ] ; Section 3.2.2,因此它应该是 localhostlocalhost:80 - Pijusn

13

请查看Wiki: HTTP客户端请求(示例)

注意以下内容:

客户端请求(在本例中仅由请求行和一个标头组成)后跟着一个空行,因此请求以双重换行符结束,每个都是回车符后跟随一个换行符的形式。 "Host"标头区分了共享单个IP地址的各种DNS名称,允许基于名称的虚拟主机。虽然在HTTP / 1.0中是可选的,但在HTTP / 1.1中是强制要求的

最小的请求(如果允许删除Host)是 GET / HTTP/1.0\r\n\r\n

愉快地编码!


这正是我在寻找的。谢谢。(同时也回答了上面的HTTP/1.1请求出了什么问题) - Wyatt Ward

10

我成功从我的Apache服务器获取了响应,仅仅是请求的文档,没有响应头,只用了以下代码:

GET /\r\n

如果你想要响应头,包括状态码,你需要参考其他答案。

7
有趣。这个使用了上世纪90年代早期的原始HTTP/0.9协议。我很惊讶Apache仍然能够响应它。 - Ben Russell
Nginx 也仍然支持这一点。从我所看到的情况来看,每当有人开发新的 HTTP 服务器时,他们只是复制其他现有实现的所有修补程序,这就是为什么这些东西会持续数十年的原因。 - Tronic

7

400 Bad Request错误本身并不意味着您的请求违反了HTTP协议。服务器完全可以因为其他原因而返回此响应。

据我所知,最小的有效HTTP请求是:

GET / HTTP/1.0\r\n\r\n

7
实际上,绝对的最小请求是 "GET /\r\n"。如果没有指定版本,服务器应该假定为HTTP/0.9。在HTTP/0.9中,请求头不允许存在,所以你不需要空行来终止它们。然而,我不会期望这在所有地方都被支持,因为HTTP/0.9客户端实际上非常少见,因此服务器可能没有经过与它们的测试。 - Jules
3
HTTP/1.0不接受头部信息,HTTP/1.1则有一个必需的头部信息:Host。 - Pedro

6
请不要在未阅读相关规范之前实现自己的HTTP客户端。请先阅读并确保您已经完全理解至少RFC 2616(如果您雄心勃勃,还可以阅读RFC 7230到7235)。
虽然HTTP看起来是一个简单的协议,但实际上有许多微妙的问题。任何编写过HTTP服务器的人都会告诉你,他必须实现一些变通措施来处理不正确但广泛部署的客户端。除非您熟悉规范,否则请使用成熟的客户端库;Curl是一个不错的选择,但我相信还有其他选择。
如果您要实现自己的HTTP客户端:
  • 不要使用HTTP/0.9;
  • HTTP/1.0需要查询行和空行;
  • 在HTTP/1.1中,除了上述内容,Host:头是强制性的。
在HTTP/1.1中省略Host:头是导致400错误最常见的原因。

在2019年,HTTP/1.0似乎终于消失了,你只需要支持HTTP/1.1(如果你愿意,还可以支持HTTP/2和HTTP/3)。 - Tronic

0

真正的绝对最低限度,不是使用netcat,而是使用bash本身:

user@localhost:~$ exec 3<>/dev/tcp/127.0.0.1/80
user@localhost:~$ echo -e "GET / HTTP/1.1\n" >&3
user@localhost:~$ cat <&3
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.7.6
Date: Mon, 13 Oct 2014 17:55:55 GMT
Content-type: text/html; charset=UTF-8
Content-Length: 514

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
</ul>
<hr>
</body>
</html>
user@localhost:~$ 

1
实际上,这是一个无效的HTTP/1.1请求。1. 它应该使用CRLF作为行终止符。2. 它应该包括“Host”头。 - Pijusn
4
这并不意味着请求符合HTTP/1.1标准。生产环境中的Web服务器会接受一些不完全符合HTTP/1.1标准的请求。原因可能各不相同,但是如果您发送无效请求,您不能指望服务器理解您的意图。例如,缺少“Host”头信息是400错误非常常见的原因,因为没有它,服务器就不知道您想要什么(例如Apache将其用于路由)。 - Pijusn
1
有位好心人为我解答了这个问题。我真的不明白为什么我在这里得到了负分。我回答得很严谨。 - Marcel
就我所知,最小化是指实际请求的最小化,而不是生成此类请求的代码。 - jfklein
如果您使用 GET / HTTP/1.0\r\n\r\n 请求,它可以正常工作,但无法与 Tomcat 一起使用。 - shikida
显示剩余2条评论


0
对于HTTP 1.1来说,最低要求似乎是
const std::string host = "example.com";
std::stringstream http;
http << "GET /index.html HTTP/1.1\r\n";
http << "Host: " << host << "\r\n";
http << "Connection: close\r\n";
//end
http << "\r\n";

没有'Host'头部,会返回HTTP 400 Bad Request错误(在Apache服务器上进行测试) 没有'Connection: close',连接将不会关闭(因此没有响应)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接