HTTP 报文是在 HTTP 应用程序之间发送的数据块。这些数据块以一些文本形式的元信息开头,这些信息描述了报文的内容及含义。
报文流
报文在客户端、服务器和代理之间的流动称为报文流。 HTTP 使用术语流入和流出来描述事务处理的方向。报文像水一样都是从上游流向下游,不会从下游流向上游。假定客户端和服务器之间有两个代理服务器,客户端发送一个请求,服务器进行响应,期间报文流向为:客户端 -> 代理1 ->代理2 -> 服务器 -> 代理2 -> 代理1 -> 客户端。
报文的组成部分
每条报文都包含一条来自客户端的请求,或者来自服务器的响应。它们由三部分组成:对报文进行描述的起始行,包含属性的首部块,以及可选的、包含数据的主体部分。
起始行和首部是由行分隔的 ASCII 文本,每行的行终止符都由一个回车符和一个换行符组成,可书写成 CRLF。主体可以包含文本数据或二进制数据,也可以为空。
延伸:Unix 系统里,每行结尾只有“”,即“\n”;Windows系统里面,每行结尾是“ ”,即“\r\n”;Mac系统里,每行结尾是“”。一个直接后果是,Unix/Mac系统下的文件在Windows里打 开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
所有的 HTTP 报文都可以分为两类:请求报文和响应报文,两者的基本报文结构相同,具体内容有些许差别。
请求报文的格式如下:
// 起始行 // 首部 // 主体
响应报文格式如下,
// 起始行 // 首部 // 主体
下面对报文格式中的每一项进行概述性解释,
方法(method)
请求的起始行以方法作为开始,用来告诉服务器要做什么。比如 GET /test/index.text HTTP/1.1
, 方法是 GET,请求从服务器拿到 /test/idnex.text 文件内容。常用的 HTTP 方法有,
HTTP 方法 | 描述 | 是否包含请求主体 |
---|---|---|
GET | 安全方法,服务器向客户端发送命名资源 | 否 |
PUT | 将来自客户端的数据存储到一个命名的服务器资源中去 | 是 |
POST | 将客户端数据发送到一个服务器网关应用程序 | 是 |
DELETE | 从服务器删除命名资源,客户端无法保证删除请求一定被执行,因为 HTTP 规范允许服务器在不通知客户端的情况下撤销请求 | 否 |
HEAD | 安全方法,仅发送命名资源响应中的 HTTP 首部 ,用于在不获取实际资源的情况下对资源首部进行检查,判断某个对象是否存在,测试资源是否被修改 | 否 |
TRACE | 对可能经过的代理服务器传送到服务器上去的报文进行追踪,用于查看原报文是否被更改过,或进行了何种更改 | 否 |
OPTIONS | 决定可以在服务器上执行哪些方法 | 否 |
以上方法并不是所有浏览器都支持,也不是所有服务器都支持。安全方法指不会在服务器上执行任何内容变更并产生任何结果的方法。
虽然方法有这么多,但目前浏览器上的请求几乎被 GET 和 POST 包揽,这是为什么呢?下面有个非官方的网友回答,觉得解释得通不错,于是摘录,按照定义,假设有资源组A,内部包含该1、2、3这些资源,则
获取资源A1:Get Url A1
新增资源A4:Post Url A + RAWDATA 4 修改资源A4:Put Url A + RAWDATA 4 删除资源A2:Delete Url A2然而实际上多数人都这么做
获取资源A1:Get Url A1
新增资源A4:Post Url A + RAWDATA 4 修改资源A4:Post Url A4 + RAWDATA 4 删除资源A2:Get Url delete A1即使是新浪的微博都 POST Url+access_token + RAWDATA empty 的方式使用。更多的人都在用 Get + delete 参数的方式来实现,不知道他们的理由是不知道、不理解、还是不愿意。
再者,网络环境恶劣,其实很多情况下我们网络的80端口是被劫持的么,而二级运营商则几乎100%都是被劫持的。某些设备劫持之后会代理你进行http访问,而这些设备是不认识其他请求方式的。代理不认识,就无法进行正常的识别和转发,这就导致其它协议无法正常使用。
某些二级运营商的劫持设备,Get、Post、Connection,如果遇到其他请求方式,就会返回你403错误或者500或者502。像现在有大量的跨域请求需要Option,遇到这种网络就完全报废了。本身这里就很不正常了,然而它返回的状态码也是错的,错误的请求方式,一般都是400、405等,但指责一个本身就不正常的设备有小毛病,是没有意义的。
(以上回答摘自:)版本(version)
报文所使用的 HTTP 版本格式为 HTTP/<major>.<minor>
,其中主要版本号(majoi) 和 次要版本号(minor)都是整数。版本号的说明是指应用程序支持的最高 HTTP 版本,而非明确的某一版本。与 HTTP 1.1 的应用程序进行通信的 HTTP 1.2 应用程序应该知道它不能支持任何 HTTP 1.2 的新特性,最多只能支持到 HTTP 1.1 的特性。
状态码(status)和原因短语(reason-phrase)
状态码和原因短语是服务器用来告诉客户端发生了什么的描述。原因短语 是 状态码 的可读版本,对人类阅读解释状态码有重要意义。比如:服务器返回状态码 304 Not Modified
,其中原因短语 Not Modified 就是对 状态码 304
的解释,可读性和可记忆性更强。
整体范围 | 分类 |
---|---|
100 ~ 199 | 信息提示, 如:101 Switching Protocols,说明服务器正在根据客户端的指定,将协议切换成 Update 首部所列的协议 |
200 ~ 299 | 成功,如: 200 OK,请求没问题,实体的主体部分包含了所请求的资源 |
300 ~ 399 | 重定向,如 301 Moved Permanently,请求的 URL 资源已被永久移除,响应头 location 首部应该包含资源现在的 URL;304 Not Modified,资源未更改,无需从服务器下载资源 |
400 ~ 499 | 客户端错误,如 404 Not Found,服务器无法找到所请求的 URL |
500 ~ 599 | 服务器错误, 如 504 Gateway Timeout,响应来自网关或代理,在等待另一服务器时对请求进行响应超时了 |
缓存
200 ok / 200 from cache/ 200 from disk cache/ 304 not modified首部(Headers)
通用首部
有些首部提供了与报文相关的最基本的信息,被称为通用首部。这些首部既可以用于请求报文,也可以用于响应报文。首部 | 描述 |
---|---|
Connection | 允许客户端和服务器指定与请求/响应连接有关的数据。如, HTTP 1.0 需要保持客户端与服务器之间的连接,就要设置 Connection: keep-alive |
Date | 提供日期和时间标志,说明报文是什么时间创建的 |
Trailer | 如果报文采用了分块传输代码方式,就可以用这个首部列出位于报文拖鞋部分的首部集合 |
Transfer-Encoding | 告知接收端为了保证报文的可靠传输,对报文采用什么编码方式 |
Via | 显示了报文经过的中间节点 |
Cache-control | 用于随报文传送缓存指示,浏览器请求返回 304 状态码便和 Cache-control 息息相关 |
请求首部
请求首部是只在请求报文中有意义的首部。用于说明是谁或说明在发送请求、请求来自何处,或者客户端的喜好及能力。首部 | 描述 |
---|---|
User-agent | 将发起请求的应用程序名称告知服务器,如: iMac 上使用 Chrome 发送请求, User-agent 可能就是 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36 |
Accept | 告诉服务器能够发送那些媒体类型 |
Accept-Encoding | 告诉服务器能够发送哪些编码方式 |
Accept-Language | 告诉服务器能够发送哪些自然语言 |
Cookie | 客户端向服务器发送一个令牌。用户登录的实现基本上需要用到 Cookie ,设置好之后,由浏览器在每个请求后自动发送 |
Proxy-Connection | 与 Connection 首部相同,但这个首部用在同哑代理(无法识别 connection: keep-alive 的代理)中使用 |
请求首部字段 Referer
一般Referer主要用于统计,像百度统计可以通过 Referer 统计访问流量的来源和搜索的关键词。Referer 是由浏览器自动加上的,刷新页面也不会消失,但某些情况不会发送 Referer 头部,如下: ● 直接输入网址或通过浏览器书签访问 ● 来源页面采用的协议为表示本地文件的 "file" 或者 "data" URI ● 当前请求页面采用的是非安全协议,而来源页面采用的是安全协议 HTTPS(摘自 , 对此持有怀疑态度,验证未通过,验证结果详情参见下文)通过 Google 搜索 “风铃” ,然后访问 zhan.qq.com ,可以看到请求头中包含 referer 头部,显示 https://www.google.com.hk/
(风铃的域名是 zhan.qq.com), 如下图,
直接在浏览器中输入网址: zhan.qq.com 可以看到,请求头中并没有出现 referer 头部,如下图,
通过 file 协议地址跳转至目标网站 zhan.qq.com ,可以看到,请求头中依旧没有出现 referer 头部,如下图,
Google 安全协议 HTTPS 跳转至 智慧校园 非安全协议 HTTP 依旧有 referer 出现 ,与 中描述的 “当前请求页面采用的是非安全协议,而来源页面采用的是安全协议 HTTPS ” 该情况不会产生 referer不一致,
响应首部
响应报文有自己的首部集,首部集为客户端提供一些额外的信息,比如 谁在发送响应、响应者的功能,甚至与响应相关的一些特殊指令。首部 | 描述 |
---|---|
Age | 从最初创建开始响应持续时间 |
Server | 服务器应用程序软件的名称和版本 |
Warning | 比原因短语更详细一些的警告报文 |
Proxy-Authenticate | 来自代理的对客户端的质询列表,指定了获取 proxy server(代理服务器)上的资源访问权限而采用的身份验证方式 |
www-Authenticate | 来自服务器的对客户端的质询列表,使用这些验证方式去获取对资源的连接 |
实体的主体部分
实体首部提供了有关实体机器内容的大量信息,从有关对象类型的信息,到能够对资源使用的各种有效的请求方法。
首部 | 描述 |
---|---|
Allow | 列出了可以对此实体执行的请求方法 |
Location | 告知客户端实体实际上位于何处,用于将接受端定位到资源的位置上去 |
Content-Length | 主体的长度或尺寸 |
Content-MD5 | 主体的 MD5 校验和 |
Content-type | 主体对象类型 |
ETag | 某个特定资源的版本标识符, 服务器返回 304 状态码与其息息相关 |
Expires | 实体不再有效,要从原始的源端再次获取此实体的日期和时间 |
Last-Modified | 这个实体最后一次被修改的日期和时间 |
Cache-Control与Expires的作用一致,Last-Modified与ETag的作用也相近。Cache-Control 与 ETag 出现于较新的 HTTP/1.1 版本,优先级高于 Expires 和 Lat-Modified 。一般情况使用两对中的一对即可。Cache-Control 和 ETag 决定了 服务器是否返回 304 状态码, 详情请参考
如果想知道 请求结果 200 from cache, 304 的区别,具体可以参看: , 而两者最大的区别是 200 from cache 不会向服务器发送请求,而 304 会向服务器发送请求,只是不会下载资源文件。
下图是京东(www.jd.com)首页请求部分截图,出现了 200 from disk cache 和 200 from memory cache , 两者由之前的 from cache 演变而来,如果想知道二者的区别,请参看 , 大意是 内存 比 硬盘读取速度快很多,但内存会随着进程的关闭(浏览器的关闭)而清除,但硬盘上的则不会,所以浏览器会根据实际情况选择两种缓存方式,先从内存中读取资源,没有,再从硬盘读取,再没有,发送请求,此时服务器可能返回 304,也可能返回 200 OK,不会返回 200 from disk cache 或 200 from memory cache。
【参考内容】
《HTTP 权威指南》David Gourley 等编. 陈涓 赵振平译. 人民邮电出版社](