pre:先发一些参考文档:
一、HTTP协议简介
HTTP(HyperText Transfer Protocol,超文本传输协议)是基于可靠传输的应用层协议,在互联网上应用非常广泛。绝大部分网络请求都是基于HTTP,乃至当前应用广泛的音视频传输,也有很大一部分是基于HTTP。
- 注:(对于此处的可靠传输,是由于HTTP假定其下层协议提供的数据传输可靠,通常在网络应用中是基于TCP协议,但实际上HTTP并未指定必须基于TCP传输)
- 注:(对于此处的应用层协议,是由于HTTP只规定客户端和服务器之间的通信方式,默认使用80端口,但并未规定具体的连接方式,也未指定数据包如何传输。所以,所谓的“HTTP连接”都是通俗而又错误的说法,因为HTTP未指定如何建立连接,如何断开连接。对于一个应用层协议来说,我们应该关注的是HTTP请求及HTTP响应,连接是传输层应该关注的事情。)
二、HTTP协议演变
- 前身:1960年Ted Nelson构思的通过计算机处理文本信息的方法,即hypertext。
- HTTP/0.9于1990年正式发布,用于网络上原始数据传输。
- HTTP/1.0于1996年在RFC 1945中提出,允许消息以类MIME格式传送,但对分层代理、缓存、持久连接、连接复用、虚拟主机需求等无规定。
- HTTP/1.1于1999年在RFC 2616中提出,主要是对HTTP/1.0中的一些问题进行了优化。比如默认使用持久连接及连接复用,缓存处理,带宽优化及网络连接使用,错误通知,消息发送,互联网IP维护,安全性等。
- HTTP/2于2015年在RFC 7540中提出,是基于Google提出的SPDY协议进行了进一步开发和完善。主流浏览器支持良好,主要在减少网络延迟方面拥有更好的特性。在各个网络应用场景中,(虽然HTTP/2未强制要求数据加密),但各个客户端均采用只支持通过TLS加密的方式,导致HTTP/2实际上基于HTTPS(Hypertext Transfer Protocol Secure, HTTP over TLS)成为一个业内事实标准。其中涉及到TLS加密部分详见ssl/tls协议
当前使用情况:据说,HTTP/1.0仍旧在一些古老的网络设备(尤其是代理服务器)上使用;HTTP/1.1的使用在互联网应用中占据了大部分,在服务器上的部署尤为广泛;HTTP/2的使用主要是在网页请求上,有统计数据称截至2018年大约40%~50%的网站使用了HTTP/2。
三、HTTP请求及响应
3.1 请求REQUEST

- 第一部分:请求行,用来说明请求方法类型、要访问的资源及所使用的HTTP版本【必须】;
- 第二部分:请求头部,紧接着请求行之后的部分,用来记录服务器所需信息【必须】;
- 第三部分:空行,请求头部后面的空行【必须】;
- 第四部分:主体,自定义【非必须】
HTTP/1.1规定了8种请求方法(其中GET/HEAD两种为服务器必须支持的),如下所示。“请求request”是指由客户端向服务器发起的一种通信方式。此外,服务器可以扩展自定义方法:
| 方法名称 | 注释 |
|---|---|
| GET | 向服务器上指定的资源发出“显示”请求,只用于读取数据。服务器会返回HEADER和BODY【必须实现】 |
| HEAD | 向服务器上指定资源发出“显示”请求,仅用于读取。服务器只返回元数据,不包括资源的本文部分【必须实现】 |
| POST | 向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有 |
| PUT | 向指定资源位置上传其最新内容 |
| DELETE | 请求服务器删除Request-URI所标识的资源 |
| TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
| OPTIONS | 使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作 |
| CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器) |
3.2 响应RESPONSE

- 第一部分:状态行,包括HTTP版本号,状态码,状态消息【必须】;
- 第二部分:响应报头,用来说明客户端解析响应时要使用的一些信息【必须】;
- 第三部分:空行,消息报头后的空行【必须】;
- 第四部分:响应实体,服务器返回的消息主体【非必须】
3.3 状态码
所有的状态码都是三位数字,有对应的状态消息。除了RFC 2616中规定的状态消息,开发人员仍可以自定义某个状态码对应的消息内容,不过服务端和客户端需要约定好。
- 1xx消息——请求已被服务器接收,继续处理
- 2xx成功——请求已成功被服务器接收、理解、并接受
- 3xx重定向——需要后续操作才能完成这一请求
- 4xx请求错误——请求含有词法错误或者无法被执行
- 5xx服务器错误——服务器在处理某个正确请求时发生错误
具体状态码信息——红色和橙色为错误状态码,加粗为常用状态码
| 状态码 | 状态消息(原因) | 备注 |
|---|---|---|
| 100 | Continue | 继续 |
| 101 | Switching Protocols | 协议切换,服务端建议客户端更换协议 |
| 102 | Processing | 处理中,防止客户端超时 |
| 200 | OK | 成功返回 |
| 201 | Created | 当前请求已经被实现且有一个新资源已经根据请求需要被创立 |
| 202 | Accepted | 服务器已接受请求,但尚未处理。最终该请求可能会也可能不会被执行,并且可能在处理发生时被禁止 |
| 203 | Non-Authoritative Information | 未认证信息,服务器是一个转换代理服务器,以200 OK状态码为起源,但回应了原始响应的修改版本 |
| 204 | No Content | 服务器成功处理请求,没有返回任何内容 |
| 205 | Reset Content | 服务器成功处理了请求,但没有返回任何内容。与204响应不同,此响应要求请求者重置文档视图 |
| 206 | Partial Content | 服务器已经成功处理了部分GET请求。用于断点续传之类的 |
| 207 | Multi-Status | 代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码 |
| 208 | Already Reported | DAV绑定的成员已经在(多状态)响应之前的部分被列举,且未被再次包含 |
| 226 | IM Used | 服务器已经满足了对资源的请求,对实体请求的一个或多个实体操作的结果表示 |
| 300 | Multiple Choices | 被请求的资源在服务器上有一系列可供选择的资源,除非请求为HEAD请求,否则响应实体中应包括资源特性及地址的列表。服务器亦可以指定首选的资源。响应可缓存 |
| 301 | Moved Permanently | 资源被永久移动到新位置。除非是HEAD请求,否则响应实体中应包含新URI的超链接及简短说明。如果不是GET/HEAD请求,浏览器应禁止自动重定向。响应可缓存 |
| 302 | Found | 临时重定向(本次)。除非是HEAD请求,否则响应实体内应包含新的URI及简短说明。如果不是GET/HEAD请求,浏览器应禁止自动重定向。响应一般不可缓存 |
| 303 | See Other | 对应请求的响应可以重定向到另一个URI,类似302,主要为了允许由脚本激活的POST请求输出重定向到新资源。303响应禁止被缓存 |
| 304 | Not Modified | 表示资源在由请求头中的If-Modified-Since或If-None-Match参数指定的这一版本之后,未曾被修改 |
| 305 | Use Proxy | 被请求的资源必须通过指定的代理才能被访问 |
| 306 | Switch Proxy | 目前新规范不使用该状态码 |
| 307 | Temporary Redirect | 在这种情况下,本次请求应该用另一个URI重复,但后续的请求应仍使用原始的URI。与302相反,当重新发出原始请求时,不允许更改请求方法 |
| 308 | Permanent Redirect | 本次请求和后续请求都应用另一个URI重复或替换 |
| 400 | Bad Request | 客户端错误导致错误的请求 |
| 401 | Unauthorized | 用户的请求未通过认证(所请求资源需认证) |
| 402 | Payment Required | 一般不用。预留用来让请求人员付费的 |
| 403 | Forbidden | 服务器已理解请求但拒绝执行,可以在响应实体中描述拒绝原因。不建议重复该请求 |
| 404 | Not Found | 请求失败,或所请求资源未在服务器上发现,但允许后续请求 |
| 405 | Method Not Allowed | 请求方法与所对应的资源不匹配。比如用GET请求某个需POST的表单资源 |
| 406 | Not Acceptable | 请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体,该请求不可接受 |
| 407 | Proxy Authentication Required | 客户端需先在代理服务器上进行身份验证 |
| 408 | Request Timeout | 请求超时,超过服务器等待时间 |
| 409 | Conflict | 存在冲突无法处理该请求,比如多个同步更新的编辑请求 |
| 410 | Gone | 一般不用,表示该资源不再可用 |
| 411 | Length Required | 客户端请求未标明Content-Length |
| 412 | Precondition Failed | 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个 |
| 413 | Request Entity Too Large | 由于请求提交的实体数据大小超服务器预期,所以拒绝响应。若容许客户端重试,响应中应携带Retry-After |
| 414 | Request-URI Too Long | 由于请求的URI长度超出服务器解释长度而拒绝响应 |
| 415 | Unsupported Media Type | 请求中提交的某资源格式,不符合服务器指定的格式 |
| 416 | Requested Range Not Satisfiable | 服务器未涵盖客户端请求所要求的资源范围 |
| 417 | Expectation Failed | 请求中期待的资源无法被服务器满足 |
| 420 | Enhance Your Caim | Twitter Search与Trends API在客户端被限速的情况下返回 |
| 421 | Misdirected Request | 服务器无法产生响应(例如因为连接重用) |
| 422 | Unprocessable Entity | 请求格式正确但有语义错误 |
| 423 | Locked | 当前资源被锁定 |
| 424 | Failed Dependency | 由于之前某个请求错误,导致当前请求失败 |
| 425 | Unordered Collection | 目前未使用的状态码 |
| 426 | Upgrade Required | 客户端应当切换到TLS/1.0,并在HTTP/1.1 Upgrade header中给出 |
| 428 | Precondition Required | 原服务器要求该请求满足一定条件,防止“未更新”,即客户端所操作资源与第三方改写发生冲突 |
| 429 | Too Many Requests | 客户端在某段时间内发送请求过多 |
| 431 | Request Header Fields Too Large | 请求头部中一个或多个字段过大 |
| 444 | No Response | Nginx上HTTP扩展,服务端主动关闭连接 |
| 450 | Blocked by Windows Parental Controls | Microsoft扩展,用于信息和测试 |
| 451 | Unavailable For Legal Reasons | 该访问因法律的要求而被拒绝 |
| 494 | Request Header Too Large | 431码提出前用于Nginx服务器 |
| 500 | Internal Server Error | 服务器发生不可预期的错误 |
| 501 | Not Implemented | 服务器不支持当前请求所需要的某个功能 |
| 502 | Bad Gateway | 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应 |
| 503 | Server Unavailable | 服务器临时维护或过载导致无法处理请求,暂时的,响应中应包含Retry-After |
| 504 | Gateway Timeout | 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器或辅助服务器收到响应 |
| 505 | HTTP Version Not Supported | 服务器不支持当前请求中的HTTP版本 |
| 506 | Variant Also Negotiates | 服务器存在内部配置错误 |
| 507 | Insufficient Storage | 服务器无法存储完成请求所必须的内容。这个状况被认为是临时的 |
| 508 | Loop Detected | 服务器在处理请求时陷入死循环 |
| 510 | Not Extended | 服务器未支持获取该请求资源时所需策略 |
| 511 | Network Authentication Required | 客户端需要进行身份验证才能获得网络访问权限,旨在限制用户群访问特定网络 |
四、HTTP请求流程解析
- 客户端与服务器建立TCP连接
- 客户端发送HTTP请求
- 服务器接收请求,定位资源,构建响应,通过TCP通道返回响应
- 按需关闭或保持TCP连接(keep-alive则不会此时关闭)
- 客户端解析请求
五、HTTP中的缓存
单独把缓存机制作为一个模块列出来,是因为在HTTP请求和响应中,使用缓存技术实在太常见&&重要了!!HTTP中的缓存主要为了(在合理场景下)尽可能减少发送请求和发送完整的响应,这对于减轻服务器的负载以及节省客户端带宽有很重要的作用。
HTTP中缓存机制需要降低语义透明性要求。但这要求在整个请求——缓存——响应的结构中,客户端始终能够发现任何语义透明性的潜在放松规则。(总之就是,不管降低语义透明性是由客户端/缓存/服务器中任意一方执行,最终客户端都知道。)设计HTTP缓存机制中各端的时候,必须严格遵守。
- 缓存基本机制——用于响应的缓存必须是最新的,等价于当前服务器应该给出的响应。若缓存模块此时无法与服务器通信,除非已缓存的响应确定是请求所需,否则应返回一个错误或警告;若缓存模块从服务器接收到某个已过期的响应,应直接转发给客户端(不添加警告,但也不移除已有警告),且不会因为响应过期而向服务器重复该请求。警告在某个响应既不是最新获取,也不在保鲜期内时必须添加。Cache-control字段是用于服务器或客户端给缓存模块显式的指令。在缓存机制中,客户端可以显式要求不使用缓存,可是设置某个响应的最大保鲜期,可以显式要求缓存模块定时验证某个请求,等等。
- 缓存过期——缓存机制是指在保鲜期内由缓存模块代替源服务器向客户端返回某个请求的响应。服务器可以指定某个请求的显式过期时间,可以强制缓存模块总是不断验证该缓存项,但只作用于服务器缓存模块而非用户代理。除了显式的过期时间,还可以用启发式过期时间算法。
- 验证模型——是指缓存模块利用超过保鲜期的缓存项作为响应之前,应利用缓存验证器向源服务器或拥有最新响应的中间缓存对此内容进行验证。验证时无需重传整个响应内容。
- 构建响应——有时候缓存模块是简单转发源服务器的响应,但有时候需要根据一定规则构建响应。
- 标记缓存无效——如果源服务器上某资源已变更,需要使用“invalidate an entity”来让缓存删除某个实体,或标记为无效
- 强制写通过——除了GET/HEAD外,所有对源服务器资源的操作都需要先经过缓存模块对源服务器的写操作,不过此时缓存模块可以先发一个100响应。
- 缓存替换——接收到服务端响应的一个新的可缓存资源,除非其Date域值比当前缓存的更旧,否则应替换缓存内容。