HTTP 协议概貌

HTTP 协议诞生于 1990 年,最初被设计用于传输 WWW 的超媒体数据。在二十多年的互联网发展过程中,web 应用始终都是互联网重要角色,HTTP 协议也得到了广泛的应用。HTTP 协议发展时间较长,在开发上基于 HTTP 的服务端与客户端组件非常丰富,用 HTTP 协议搭建不论是 B/S 模式的应用程序还是 C/S 模式的应用程序都十分简便。再加上它具有很好的灵活性,在互联网技术高度发达的今天,除了常规的 web 应用之外,HTTP 协议还广泛被运用于各种多媒体流服务、下载服务、桌面和移动客户端应用等。

HTTP 是一个基于请求/响应模型的协议,用户代理程序(user agent,对于 web 应用来说,是浏览器,以下简称“用户”)向原始服务器(origin server,以下简称“服务器”)发送 HTTP 请求(request);服务器收到请求后,解析请求并向用户发送相应的 HTTP 响应(response)*

HTTP 请求与响应消息格式都由以下几个部分按顺序组成:

  • 起始行;
  • 消息头部;
  • 标识头部结束的空行;
  • 可选的消息主体(message body)。

除了消息主体可能存在二进制格式的信息之外,HTTP 协议的其他部分都由 ASCII 字符流组成。消息各部分按“行”分割,每一行以 CRLF 结束。像上面提到的,HTTP 的请求和响应消息的第一行都称作起始行,起始行中包含请求资源的类型、资源位置、HTTP 版本号、响应的状态等信息,以下将具体讨论 HTTP 协议的起始行。

请求起始行

HTTP 请求的起始行由方式(method)、资源ID(URI)和协议版本号组成,三个部分以空格分隔。下面是一个 HTTP 请求行的示例:

GET http://luodichen.com/ HTTP/1.1

这个请求起始行的含义是以 GET 方式向服务端请求URI http://luodichen.com/ 的资源,HTTP 协议版本号为 1.1。

GET 是最常见的请求方式,表示希望服务端返回请求 URI 所在的资源数据。另外一个最常见的请求方式是 POST,POST 常用于像提交表单之类需要向服务端传送少量信息的场合,服务端收到 POST 请求之后,也可以视情况响应相应的消息。当发送一个 POST 请求时,需要将要传送的数据填写在消息主体中。除了 GET 和 POST 方式之外,HTTP 协议还定义了其他多种请求方式,要了解每种请求方式的详细情况请查阅参考文献中引用的 RFC 2616 文档。HTTP 1.1 协议中定义的所有请求方式如下:

OPTIONS
GET
HEAD
POST
PUT
DELETE
TRACE
CONNECT

请求 URI 可以是一个完整的 HTTP URL,如 http://luodichen.com/blog/,也可以是一个相对路径,如 /blog/。相对路径表示相对于服务器 HTTP 根目录的路径,当 URI 本身就是根目录时,应当将 URI 设置为 ‘/’。当 URI 是一个相对路径时,必须在请求的 HTTP 头部中包含 Host 字段,如 Host: luodichen.com。有关 HTTP 头部的详细情况将在下文中讨论。

URI 中还可以携带一些发送给服务端的参数,参数以符号 ‘?’ 开头,由 key=value 形式组成,每一组参数用符号 ‘&’ 分隔。当参数的 key 或 value 中含有一些特殊字符时,应当对它们进行 urlencode 编码。下面是一个典型的带有参数的 URI:

/login/?user=username&pass=pass%26%7bword%7d

响应起始行

响应的起始行在 RFC 中被称为状态行(status-line)。状态行依次由 HTTP 协议版本号、三位数数字状态码和原因语句组成,三者由空格分隔,中间无换行或回车符。

接收到请求之后,服务端根据具体情况会产生 5 类响应,这 5 类响应对应状态码的头一位数字 1-5。这些响应状态码的具体描述如下:

  • 1xx – 消息。请求已收到,将继续处理;
  • 2xx – 成功。请求已经成功收到,并且是合法的请求,且已经执行;
  • 3xx – 重定向。要求执行另一个动作;
  • 4xx – 客户端错误。请求包含错误的格式,或请求不能被通过;
  • 5xx – 服务端错误。请求格式应该是正确的,但服务端执行请求时发生了错误。

200 是最常见的响应代码,表示请求成功,请求所要求的信息已经包含在响应中。301 和 303 也是比较常见的响应代码,通常用于服务端要求跳转。其中 301 表示永久转移(Moved Permanently),303 表示临时跳转到另一个资源。404 是最常见的错误代码,它的含义是未找到(Not Found),当向服务端发送一个 GET 请求,而服务端没有找到请求 URI 指示的资源时,会响应 404 错误代码,提示用户端发送了不存在的 URI 请求。

除此之外还有很多 HTTP 状态码,下面是在 HTTP 1.1 中定义的所有状态码和他们对应的原因语句。

100 Continue
101 Switching Protocols

200 OK
201 Created
202 Accepted
203 Non-Authoritative Information
204 No Content
205 Reset Content
206 Partial Content

300 Multiple Choices
301 Moved Permanently
302 Found
303 See Other
304 Not Modified
305 Use Proxy
307 Temporary Redirect

400 Bad Request
401 Unauthorized
402 Payment Required
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Time-out
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Request Entity Too Large
414 Request-URI Too Large
415 Unsupported Media Type
416 Requested range not satisfiable
417 Expectation Failed

500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Time-out
505 HTTP Version not supported

关于这些状态更详细的说明,请参阅文末参考文献中引用的 RFC 2616 文档中的第 10 节。

HTTP 协议还允许服务端程序根据具体情况定义扩充的状态码,并且也有一些服务端程序这样做了。例如 nginx 定义了状态码 495、496、497、499 等[2]

头部字段

HTTP 的头部字段均为 key-value 形式,每个字段各占一行,行末由 CRLF 结束。其中有一些字段是请求或响应特有的字段,还有一些是既可能出现在请求中又可能出现在响应中的字段,称为通用字段。HTTP 1.1 协议定义全部通用头部字段列出如下:

Cache-Control
Connection
Date
Pragma
Trailer
Transfer-Encoding
Upgrade
Via
Warning

HTTP 请求特有的字段列出如下:

Accept
Accept-Charset
Accept-Encoding
Accept-Language
Authorization
Expect
From
Host
If-Match
If-Modified-Since
If-None-Match
If-Range
If-Unmodified-Since
Max-Forwards
Proxy-Authorization
Range
Referer
TE
User-Agent

HTTP 响应特有的字段列出如下:

Accept-Ranges
Age
ETag
Location
Proxy-Authenticate
Retry-After
Server
Vary
WWW-Authenticate

与数据实体有关的字段列出如下:

Allow
Content-Encoding
Content-Language
Content-Length
Content-Location
Content-MD5
Content-Range
Content-Type
Expires
Last-Modified

HTTP 协议同样允许用户端和服务端自行定义新的扩充字段。下文中会提及几个重要的头部字段的含义和协议细节,关于完整的 HTTP 协议头部字段的定义请参阅文末参考文献中引用的 RFC 2616 文档中的第 14 节。

定义用户端接受的内容格式

HTTP 协议用于传送多种格式的资源,用户端程序可能不能接受全部类型的格式,因此,在向服务端提交请求时,用户端程序可以在 HTTP 头部中设置 Accept 字段以告知服务端自己支持的资源格式。Accept 字段由一个或多个媒体范围组成,多个媒体范围之间用逗号分隔,媒体范围还可以接一个可选的 q 参数。下面是一个 Accept 字段的示例:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

媒体范围的格式是 主类型/子类型,* 表示匹配所有的类型。q 参数是一个范围在 0-1 的浮点数,用于标识它所附加的媒体范围的优先级,当服务端同时支持 Accept 中列的多个媒体范围时,优先匹配 q  值较大的那个。当 q 值缺省时,它的默认值是 1。

q 参数在多个头部字段中都可能出现,含义均是在多个选项中选择时的优先级,关于这一点,下文不再赘述。

定义用户端接受的字符集

当 HTTP 用于传送文本内容时,由于不同的用户端程序可能接受不同的字符集,因此在向服务端提交请求时,用户端可以在 HTTP 头部设置 Accept-Charset 字段,以通知服务端自己支持的字符集。当支持多个字符集时,字符集之间用逗号分隔,并可以加上一个可选的 q 参数。下面是一个 Accept-Charset 字段的示例:

Accept-Charset: iso-8859-5, unicode-1-1;q=0.8

其他 Accept-XXX 字段

此外,还有用于压缩格式的 Accept-Encoding 字段、用于指示支持的语言的 Accept-Language 字段等。

消息内容的格式

当请求或响应中包含消息实体(内容)时,需要设置 Content-Type 指定消息的类型。这个字段是一个 MIME Type 值。下面是一个 Content-Type 的示例:

Content-Type: application/json

最初的 HTTP 协议在设计上并没有考虑处理状态和会话等问题,HTTP 是一个无状态的协议,直到 1994 年 Netscape 浏览器支持了一种叫 cookie 的技术。目前,cookie 技术在 web 应用中的地位已经十分重要,关于 HTTP cookie 的话题留到后续博文中讨论。


* 这里的 user agent / origin server 术语与 TCP/IP 中的 client / server 术语有微小的差别。术语 client / server 着重表示发起连接的程序(客户端)与接受连接的程序(服务端)之间具有一条可通信的链路,而 user agent / origin server 着重表示请求的发送者(user agent)与请求的接收和响应者(origin server)。事实上,在存在代理的情况下,user agent 与 origin server 之间不存在可以直接通信的链路,反之它们之间存在可直接通信的链路。

(允许转载文本,转载请注明出处:http://luodichen.com/blog/?p=344 )

参考文献
[1] R. Fielding, J. Gettys, J. Mogul, et al, Hypertext Transfer Protocol — HTTP/1.1, RFC 2616, June 1999; www.ietf.org/rfc/rfc2616.txt
[2] Wikipedia. List of HTTP status codes[EB/OL]. http://en.wikipedia.org/wiki/List_of_HTTP_status_codes