逐步解读HTTP报文的组成及含义

如果说HTTP是因特网的信使,那么HTTP报文就是运送的包裹。所有的HTTP程序都是通过互相发送报文来完成工作的。本文将介绍HTTP报文的流动方式,报文的组成部分,请求和响应报文之间的区别等。

报文流
HTTP报文是在HTTP应用程序之间发送的数据块,这些数据块以文本形式存在,以描述了报文的内容及含义的元信息开头,后面跟着可选的数据部分。这些报文在客户端、服务器和代理之间流动。一般来说,报文流根据流向引用以下三种术语:报文流入(inbound)向服务器,工作完成之后,会流出(outbound)向客户端或用户Agent代理;不管是请求报文还是响应宝安温,所有报文的接收者都在发送者的下游(downstream),报文只会向下游流动。

报文的组成部分
报文由起始行、首部,以及可选的包含数据的主体三个部分组成。所有的HTTP报文分为两类:请求(request)报文和响应(response)报文,如下图示例。前者会向Web服务器请求对资源进行一些操作,后者承载了状态信息和操作产生的所有结果数据,把结果返回给客户端。
2016616114838764.jpg (789×217)

起始行
所有的HTTP报文都以一个起始行作为开始。请求报文的起始行又称为请求行,说明了要做些什么,响应报文的起始行又称为响应行,说明了发生了什么。以下是两种请求行的基本格式:

// 请求行格式
<method> <request-URL> <version>
// 响应行格式
<version> <status> <reason-phrase>

请求行的方法描述了服务器应该执行的操作,请求URL描述了要对哪个资源执行这个方法,HTTP版本用来告知服务器,客户端使用的是哪个版本的HTTP协议。响应行包含了报文使用的HTTP版本、数字状态吗,以及描述操作状态的文本形式的原因短语。所有这些字段都由空格分隔。

请求行方法
HTTP规范中定义了一组常用的请求方法,用来告知服务器要做些什么,如下所示:
GET:从服务器获取请求URL所指定的资源。
HEAD:只从服务器获取文档的首部。和GET方法的行为很类似,但服务器只返回首部,不包含主体。此方法可以:在不获取资源的情况下了解资源的情况(比如判断类型);通过查看响应中的状态吗,看看某个对象是否存在;通过查看首部,测试资源是否被修改了。
POST:向服务器发送需要处理的数据(包含主体)。通常用它来支持HTML的表单。
PUT:将请求的主体部分存储在服务器上(包含主体)。有些系统允许用户创建Web页面或上传文档,该方法的语义就是让服务器用请求的主体来创建一个由请求URL命名的新文档,如果URL已存在则让这个主体替代它。
DELETE:从服务器上删除请求URL所指定的资源。但客户端无法保证删除操作一定被执行,因为HTTP规范允许服务器在不通知客户端的情况下撤销请求。
TRACE:对可能经过代理、网管、防火墙等服务器的报文进行追踪,主要用于诊断。报文行程最后一站的服务器会弹回一条TRACE响应,在主体中携带它收到的原始请求报文,这样客户端就可以查看报文在一整条请求/响应链上是如何被修改的。
OPTIONS:查询可以在服务器上执行哪些方法,让客户端不用实际访问那些资源就能判定访问各种资源的最优方式。
如果一台服务器要与HTPP 1.1兼容,只要为其资源实现GET和HEAD方法即可,这两种方法被认为是安全的,它们产生的请求不会在服务器上产生什么结果(实际上,这是由Web开发者决定的,完全可以使用GET方法来提交一个表单,但严重不建议这么做!)。

HTTP还允许定义HTTP/1.1规范中没有定义的扩展方法,这些方法为开发者提供了一种扩展HTTP服务能力的手段。很可能大部分HTTP应用程序都无法理解这些扩展方法,所以服务器最好对扩展方法宽容一些。

状态码与原因短语
每条响应报文都会包含一个3位数字和可读的状态,用来告诉客户端,服务器发生了什么事情。数字状态码便于程序处理差错,原因短语更便于人们理解。状态码分为5类(括号中为已定义范围):100~199为信息提示(100~101);200~299为成功(200~206);300~399为重定向(300~305),用于告知客户端使用替代位置来访问资源;400~499为客户端错误(400~415);500~599为服务器错误(500~505)。限于篇幅下面只介绍常见的状态码,详情参见HTTP状态码维基百科

101 Switching Protocols:服务器正在根据客户端的指定,将协议切换成Update首部所示的协议。
200 OK:服务器已成功处理了请求并提供了请求的网页
204 No Content:服务器成功处理了请求,但没有返回任何内容
301 Moved Permanently:请求的网页已永久移动到新位置。响应的Location首部应包含资源现在所处的URL。
302 Found:与301类似,但这里的移除是临时的。将来的请求仍应使用老的URL。
304 Not Modified:客户的缓存资源是最新的,要客户端使用缓存。
400 Bad Request:告知客户端发送了一个错误的请求。
403 Forbidden:请求被服务器拒绝了。(可能是没有访问服务器的权限)
404 Not Found:服务器无法找到所请求的URL。
410 Gone:服务器曾经有这个资源,现在没有了,与404类似。
500 Internal Server Error:服务器遇到一个错误,使其无法为请求提供服务。
502 Bad Gateway:作为代理或网关使用的服务器收到了上游的无效响应。
503 Service Unavailable:服务器现在无法为请求提供服务,但过一段时间就可以恢复服务。

首部
首部和方法配合工作,共同决定了客户端和服务器能做什么事情。可以将首部分为5个主要类型,以下将分类列举一些首部。

(1)通用首部:客户端和服务器都可以使用的通用首部,提供了与报文相关的最基本的信息。
Connection:允许客户端和服务器指定与请求/响应连接相关的选项
Date:日期和时间标志,说明报文是什么时刻创建的
MIME-Version:给出了发送端使用的MIME版本
Transfer-Encoding:告知接收端为了保证报文的可靠传输,对报文采用了什么编码方式
Via:显示了报文经过的中间节点(代理、网关等等)
Cache-Control:用于随报文传送缓存指示
Pragma:另一种随报文传送指示的方式,但并不专用于缓存

(2)请求首部:只在请求报文中有意义,用于说明是谁或什么在发送请求、请求源自何处,或客户端的喜好及能力等。
Accept:告诉服务器能够发送哪些媒体类型。该首部可以使连接的两端都受益,客户端会得到它们想要的内容,服务器则不会浪费时间好带宽来发送客户端无法使用的东西
Accept-Encoding:告诉服务器能够发送哪些编码方式
Accept-Language:告诉服务器能够发送哪些语言
Referer:提供了包含当前请求URI的文档的URL
User-Agent:告诉服务器发起请求的应用程序名称
有时客户端希望为请求加上某些限制,要求服务器在对请求进行响应之前,确保某个条件为真,则可以添加条件请求首部,如Expect(允许客户端列出某请求所要求的服务器行为)、If-Match(若实体标记相匹配,则获取这份文档)、If-Modified-Since(除非在某个指定日期之后资源被修改过,否则限制该请求)等等。
HTTP本身就支持一种对请求进行质询/响应的认证机制,这样可以使事务稍微安全一些。因此有安全请求首部,如Authorization(客户端提供给服务器的对其自身进行认证的数据)、Cookie(这个不是真正的安全首部,但却是隐含了安全功能)。

(3)响应首部:为客户端提供了一些额外信息,比如谁在发送响应、响应者的功能、其它一些特殊指令等
Age:(从最初创建开始)响应持续时间
Public:服务器为其资源支持的请求方法列表
Retry-After:如果资源不可用,在此时间重试
Accept-Ranges:(协商首部)对此资源来说,服务器可接受的范围类型
Set-Cookie:(安全首部)类似Cookie,用于设置Cookie

(4)实体首部:用来描述HTTP报文的负荷,提供了有关实体及其内容的大量信息,可以告知报文的接收者它在对什么进行处理
Allow:列出可以对此实体执行的请求方法
Location:告知客户端实体实际上位于何处,用于重定向资源
Content-Length:主体的长度
Content-Type:主体的对象类型
(还有很多关于主体的首部,如Content-Encoding、Content-Base、Content-MD5等等)
ETag:与此实体相关的实体标记(用于缓存,下同)
Expires:实体不再有效,要从源端再次获取此实体的日期和时间
Last-Modified:这个实体最后一次被修改的日期和时间

HTTP请求报文实例解剖  
2016616114935723.png (874×471)

①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。不过,当前的大多数浏览器只支持GET和POST,Spring 3.0提供了一个HiddenHttpMethodFilter,允许你通过“_method”的表单参数指定这些特殊的HTTP方法(实际上还是通过POST提交表单)。服务端配置了HiddenHttpMethodFilter后,Spring会根据_method参数指定的值模拟出相应的HTTP方法,这样,就可以使用这些HTTP方法对处理方法进行映射了。 
②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL,③是协议名称及版本号。 
④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。 
⑤是报文体,它将一个页面表单中的组件值通过param1=value1&param2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1&param2=value2”的方式传递请求参数。 
对照上面的请求报文,我们把它进一步分解,你可以看到一幅更详细的结构图: 
2016616115015857.jpg (427×151)