如果说 HTTP 是互联网的信使, 那么 HTTP 报文就是他用来搬东西的包裹了. --- 《HTTP 权威指南》
HTTP 程序之间是通过互相发送 HTTP报文(以下简称报文) 来工作的,那么了解报文相关的知识就非常有必要了。知道报文的结构、内部的字段以及各字段的功能等信息,这对以后的工作具有重要的意义。
1. HTTP报文 的概念和报文流的概念
1.1 报文的概念
报文是 HTTP 程序之间传递的数据块,这个数据块内包含了发送者的目的、服务器返回的结果以及其他相关信息。
报文可以分为顾名就能思义的两种:请求报文和响应报文。
请求报文是客户端发出的,向服务器请求资源的报文;响应报文就是服务端发出的,向客户端进行回复的报文。~(无论你的请求能不能实现,我总要回复你一下啊)~
1.2 关于报文流的两个概念
程序之间进行通信时会传递许多的报文,可以想象现实中的河流,水在小河中流动,而报文在各个程序间流动,称为报文流。关于报文流有两个概念:
-
流入(inbound)和流出(outbound) 这是个绝对的概念,这里的绝对是对于服务器来说的绝对:流向服务器的报文流就是流入;从服务器发出(流出)的报文流就是流出。流入和流出只是对于服务器来说的,所以是绝对的。
-
上下游的概念 (upstream & downstream) 这是个相对的概念。
对于报文的发送者来说,它就是这个报文的上游,后面接收到这个报文的节点就是他的下游;
对于报文的接收者来说,它就是报文的下游,在它之前收到这个报文的节点就是他的上游;如果它继续将这个报文传递下去,那么接下来收到这个报文的就是它的下游。
如图:
上图中的请求报文流从客户端流向服务器,中间经过了几个代理,按流动的方向来看,代理1位于代理2的上游,而代理2位于代理3的上游。
而在响应流中,响应报文从服务器流向客户端,中间经过了几个同样的代理。此时按流动的方向来看,代理3位于代理2的上游,而代理2位于代理1的上游。
由此可见,由于报文只能向下流动,所以请求报文流中的上游就会成为响应报文流中的下游。这就体现了报文流中上下游的相对性。~风水轮流转?~
2. HTTP 报文的结构
报文由3大部分组成,分别是:
- 起始行(start line):用来表明报文的目的或者执行的结果
- 首部(header):提供报文的附加信息
- 实体的主体(body):要传递给对方的内容,比如文本、图片、文件
这样看起来有些难以理解,可以用在淘宝买东西的过程打个比方:先决定自己要买什么,收货地址是什么,然后下单,这就相当于起始行里包含的信息;卖家寄来的包裹里有你要买的东西,还有使用说明书,那么这个使用说明书就可以理解为首部(header); 你收到的东西就相当于主体(body)了。
虽然这个例子不是很严谨,但是也可以用来形成对这三个部分的感性理解了。
下面来依次讨论一下这三个部分。
2.1 起始行 (start line)
首先要明确的一点是,请求报文和响应报文由于目的不同,起始行中所包含的信息是有差别的。下面的内容会用 [C] (Client)来表示请求报文中包含的信息, 用 [S] (Server)来表示响应报文中包含的信息。
2.1.1 起始行中包含的信息, 这些信息在报文中会以空格来分隔
-
[C] 方法(method): 客户端希望服务器对资源做出的动作,例如我们熟悉的 GET 和 POST 方法。更详细的内容将在文章后部分讨论.
-
[C] 要请求的资源的URL(request URL): 要请求的资源在服务器上的路径
-
[C & S] 版本(version): 客户端和服务器所使用的HTTP协议的 版本号. 这样就可以提示对方自己支持的 HTTP 方法了,防止版本不同造成的不兼容问题。 例如: HTTP/1.1 表示这个报文的发送者遵循的是1.1版本的 HTTP 协议.
-
[S] 状态码(status code): 描述服务器根据客户端的请求进行操作的结果的代码。例如著名的 404. 网上专门讲状态码的文章已经很多了,在此不赘述. 更多更详细的状态码描述见 MDN的相应内容.HTTP 响应代码
-
[S] 状态短语(reason-phrase): 和状态码对应,是状态码所表示的信息的另一种表示方法,为了人类能理解,而不是看到状态码的时候一下子反应不过来服这几个数字代表的信息到底是什么。例如和 状态码404对应的 Not Found.
下面是两个具体的例子:
-
请求报文的起始行
GET /test/hello.txt HTTP/1.1
可以看到这个字符串包含了用空格分隔的三部分GET
,/test/hello.txt
和HTTP/1.1
. 则这个部分的含义就是用 GET 方法来请求服务器中路径是/test/hello.txt
的资源, 客户端使用的 HTTP 版本是 1.1. -
响应报文的起始行
HTTP/1.0 200 OK
和例1类似的, 可以看到这个字符串包含了用空格分隔的三部分HTTP/1.0
,200
和OK
. 含义是: 服务器的 HTTP 版本是 1.0, 对客户端的请求操作的状态码是 200, 表示完成; 同时也用 OK 来说明操作的完成, 便于人类阅读.
2.2 首部 (header)
就像之前提到的,首部类似于商品说明书,起到对报文附加说明的作用。它是一些名值对的集合. 例如
Content-length: 19
, 表明这个报文的主体部分的长度是 19. 网上专门讲首部字段的文章已经很多了,在此不赘述. 更多更详细的首部描述见MDN的相应内容. HTTP Headers
首部也可以分为几种顾名思义的类型,包括:通用首部、请求报文专用首部、响应报文专用首部、实体的专用首部等类别。下面举例来讨论一下这几种类型:
2.2.1 通用首部
顾名思义,通用首部提供了无论是请求报文还是响应报文都能使用的首部, 在此仅举几个例子来说明:
- Date: 日期标志
- Transfer-Encoding: 对报文的传输采用的编码方式
- Via: 报文经过的中间节点(代理、网关)
- Cache-Control: 缓存指示
2.2.2 请求报文专用首部
此类首部用来说明客户端从何而来,具有的能力,希望得到的回应类型以及最重要的不希望得到的响应的类型等信息, 以便服务器根据它的喜好回复数据.
这类首部种类繁多, 在此仅举几个例子:
- Client-IP: 客户端的 IP 地址
- Host: 接收请求的服务器的主机名和端口号
- User-Agent: 发起请求的客户端的程序的名称
- Accept-Charset: 客户端期望的字符集
- Accept-Encoding: 客户端期望的编码方式
- Accept-Charset: 客户端期望的字符集
2.2.3 实体专用的首部, 在下一部分讨论
2.3 实体 (body)
实体由两部分组成: 实体首部和实体主体.
2.3.1 实体首部
实体首部提供了实体主体的描述性信息,包括长度、编码、类型等等等等. 下面举几个例子:
-
Content-Type: 实体主体的类型, 例如 HTML 文档会被标记为 text/html; gif 格式的图片会被标记为 image/gif. 如果要传递的数据类型不止一种, 这种情况可能会出现在 form 表格中, 用户可能会一次上传文字和文件类型的数据, 那么此时的属性值会是
Content-Type: multipart/form-data
. 用 multipart 来表示此时传递的数据是包含多个主体的. -
Content-length: 实体主体的大小
-
Expires: 实体主体数据的过期时间
-
Content-Encoding: 实体的处理方式,例如压缩或者重新编码
2.3.2 实体主体
实体主体就是要传递的原始数据~~~(网购的商品的主体)~~~, 比如文档、图片、音乐等等.
文章只是对 HTTP 报文的一个概览, 并没有进行全面的介绍, 而且为了便于部分内容的理解, 做了不是完全恰当的类比.
更多更详细的内容可见:《HTTP 权威指南》