# 11.5.3 HTTP 事务

因为 HTTP 是基于在因特网连接上传送的文本行的,我们可以使用 Linux 的 TELNET 程序来和因特网上的任何 Web 服务器执行事务。对于调试在连接上通过文本行来与客户端对话的服务器来说,TELNET 程序是非常便利的。例如,图 11-24 使用 TELNET 向 AOL Web 服务器请求主页。

 1  linux> telnet www.aol.com 80              Client: open connection to server
 2  Trying 205.188.146.23...                  Telnet prints 3 lines to the terminal
 3  Connected to aol.com.
 4  Escape character is '^]'.
 5  GET / HTTP/1.1                            Client: request line
 6  Host: www.aol.com                         Client: required HTTP/1.1 header
 7                                             Client: empty line terminates headers
 8  HTTP/1.0 200 OK                           Server: response line
 9  MIME-Version: 1.0                         Server: followed by five response headers
10  Date: Mon, 8 Jan 2010 4:59:42 GMT
11  Server: Apache-Coyote/1.1
12  Content-Type: text/html                   Server: expect HTML in the response body
13  Content-Length: 42092                     Server: expect 42,092 bytes in the response body
14                                             Server: empty line terminates response headers
15  <html>                                    Server: first HTML line in response body
16  ...                                       Server: 766 lines of HTML not shown
17  </html>                                   Server: last HTML line in response body
18  Connection closed by foreign host.        Server: closes connection
19  linux>                                    Client: closes connection and terminates

图 11-24 一个服务静态内容的 HTTP 事务

在第 1 行,我们从 Linux shell 运行 TELNET,要求它打开一个到 AOL Web 服务器的连接。TELNET 向终端打印三行输出,打开连接,然后等待我们输入文本(第 5 行)。每次输入一个文本行,并键入回车键,TELNET 会读取该行,在后面加上回车和换行符号(在 C 的表示中为“\r\n”),并且将这一行发送到服务器。这是和 HTTP 标准相符的,HTTP 标准要求每个文本行都由一对回车和换行符来结束。为了发起事务,我们输入一个 HTTP 请求(第 5~7 行)。服务器返回 HTTP 响应(第 8~17 行),然后关闭连接(第 18 行)。

# 1. HTTP 请求

一个 HTTP 请求的组成是这样的:一个请求行(request line)(第 5 行),后面跟随零个或更多个请求报头(request header)(第 6 行),再跟随一个空的文本行来终止报头列表(第 7 行)。一个请求行的形式是

method URI version

HTTP 支持许多不同的方法,包括 GET、POST、OPTIONS、HEAD、PUT、DELETE 和 TRACE。我们将只讨论广为应用的 GET 方法,大多数 HTTP 请求都是这种类型的。

GET 方法指导服务器生成和返回 URI(Uniform Resource Identifier,统一资源标识符)标识的内容。URI 是相应的 URL 的后缀,包括文件名和可选的参数。①

请求行中的 version 字段表明了该请求遵循的 HTTP 版本。最新的 HTTP 版本是 HTTP/1.1 [37]。HTTP/1.0 是从 1996 年沿用至今的老版本 [6]。HTTP/1.1 定义了一些附加的报头,为诸如缓冲和安全等高级特性提供支持,它还支持一种机制,允许客户端和服务器在同一条持久连接(persistent connection)上执行多个事务。在实际中,两个版本是互相兼容的,因为 HTTP/1.0 的客户端和服务器会简单地忽略 HTTP/1.1 的报头。

总的来说,第 5 行的请求行要求服务器取出并返回 HTML 文件 /index.html。它也告知服务器请求剩下的部分是 HTTP/1.1 格式的。

请求报头为服务器提供了额外的信息,例如浏览器的商标名,或者浏览器理解的 MIME 类型。请求报头的格式为

header-name: header-data

针对我们的目的,唯一需要关注的报头是 Host 报头(第 6 行),这个报头在 HTTP/1.1 请求中是需要的,而在 HTTP/1.0 请求中是不需要的。代理缓存(proxy cache)会使用 Host 报头,这个代理缓存有时作为浏览器和管理被请求文件的原始服务器(origin server)的中介。客户端和原始服务器之间,可以有多个代理,即所谓的代理链(proxy chain)。Host 报头中的数据指示了原始服务器的域名,使得代理链中的代理能够判断它是否可以在本地缓存中拥有一个被请求内容的副本。

继续图 11-24 中的示例,第 7 行的空文本行(通过在键盘上键入回车键生成的)终止了报头,并指示服务器发送被请求的 HTML 文件。

# 2. HTTP 响应

HTTP 响应和 HTTP 请求是相似的。一个 HTTP 响应的组成是这样的:一个响应行(response line)(第 8 行),后面跟随着零个或更多的响应报头(response header)(第 9~13 行),再跟随一个终止报头的空行(第 14 行),再跟随一个响应主体(response body)(第 15~17 行)。一个响应行的格式是

version status-code status-message

version 字段描述的是响应所遵循的 HTTP 版本。状态码(status-code)是一个 3 位的正整数,指明对请求的处理。状态消息(status message)给出与错误代码等价的英文描述。图 11-25 列出了一些常见的状态码,以及它们相应的消息。

状态代码 状态消息 描述
200 成功 处理请求无误
301 永久移动 内容已移动到 location 头中指明的主机上
400 错误请求 服务器不能理解请求
403 禁止 服务器无权访问所请求的文件
404 未发现 服务器不能找到所请求的文件
501 未实现 服务器不支持请求的方法
505 HTTP 版本不支持 服务器不支持请求的版本

图 11-25 一些 HTTP 状态码

① 实际上,只有当浏览器请求内容时,这才是真的。如果代理服务器请求内容,那么这个 URI 必须是完整的 URL。

第 9~13 行的响应报头提供了关于响应的附加信息。针对我们的目的,两个最重要的报头是 Content-Type(第 12 行),它告诉客户端响应主体中内容的 MIME 类型;以及 Content-Length(第 13 行),用来指示响应主体的字节大小。

第 14 行的终止响应报头的空文本行,其后跟随着响应主体,响应主体中包含着被请求的内容。