协议

当你在浏览器地址栏中输入eloquentjavascript.net/18_http.html时,浏览器会首先找到和eloquentjavascript.net相关的服务器的地址,然后尝试通过 80 端口建立 TCP 连接,其中 80 端口是 HTTP 的默认通信端口。如果该服务器存在并且接受了该连接,浏览器可能发送如下内容。

  1. GET /18_http.html HTTP/1.1
  2. Host: eloquentjavascript.net
  3. User-Agent: Your browser's name

然后服务器会通过同一个链接返回如下内容。

  1. HTTP/1.1 200 OK
  2. Content-Length: 65585
  3. Content-Type: text/html
  4. Last-Modified: Mon, 08 Jan 2018 10:29:45 GMT
  5. <!doctype html>
  6. ... the rest of the document

浏览器会选取空行之后的响应部分,也就是正文(不要与 HTML <body>标签混淆),并将其显示为 HTML 文档。

由客户端发出的信息叫作请求。请求的第一行如下。

  1. GET /17_http.html HTTP/1.1

请求中的第一个单词是请求方法。GET表示我们希望得到一个我们指定的资源。其他常用方式还有DELETE,用于删除一个资源;PUT用于替换资源;POST用于发送消息。需要注意的是服务器并不需要处理所有收到的请求。如果你随机访问一个网站并请求删除主页,服务器很有可能会拒绝你的请求。

方法名后的请求部分是所请求的资源的路径。在最简单的情况下,一个资源只是服务器中的一个文件。不过,协议并没有要求资源一定是实际文件。一个资源可以是任何可以像文件一样传输的东西。很多服务器会实时地生成这些资源。例如,如果你打开github.com/marijnh,服务器会在数据库中寻找名为marijnjh的用户,如果找到了则会为该用户的生成介绍页面。

请求的第一行中位于资源路径后面的HTTP/1.1用来表明所使用的 HTTP 协议的版本。

在实践中,许多网站使用 HTTP v2,它支持与版本 1.1 相同的概念,但是要复杂得多,因此速度更快。 浏览器在与给定服务器通信时,会自动切换到适当的协议版本,并且无论使用哪个版本,请求的结果都是相同的。 由于 1.1 版更直接,更易于使用,因此我们将专注于此。

服务器的响应也是以版本号开始的。版本号后面是响应状态,首先是一个三位的状态码,然后是一个可读的字符串。

  1. HTTP/1.1 200 OK

以 2 开头的状态码表示请求成功。以 4 开头的状态码表示请求中有错误。404 是最著名的 HTTP 状态码了,表示找不到资源。以 5 开头的状态码表示服务器端出现了问题,而请求没有问题。

请求或响应的第一行后可能会有任意个协议头,多个形如name: value的行表明了和请求或响应相关的更多信息。这些是示例响应中的头信息。

  1. Content-Length: 65585
  2. Content-Type: text/html
  3. Last-Modified: Thu, 04 Jan 2018 14:05:30 GMT

这些信息说明了响应文档的大小和类型。在这个例子中,响应是一个 65585 字节的 HTML 文档,同时也说明了该文档最后的更改时间。

多数大多数协议头,客户端或服务器可以自由决定需要在请求或响应中包含的协议头,不过也有一些协议头是必需的。例如,指明主机名的Host头在请求中是必须的,因为一个服务器可能在一个 IP 地址下有多个主机名服务,如果没有Host头,服务器则无法判断客户端尝试请求哪个主机。

请求和响应可能都会在协议头后包含一个空行,后面则是消息体,包含所发送的数据。GETDELETE请求不单独发送任何数据,但PUTPOST请求则会。同样地,一些响应类型(如错误响应)不需要有消息体。