## HTTP协议发展历史
| 版本 | 诞生时间 |
| --- | --- |
| [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html) | 1991年|
| [HTTP 1.0](https://tools.ietf.org/html/rfc1945) | 1996年5月 |
| [HTTP 1.1](https://tools.ietf.org/html/rfc2068) | 1997年1月 |
| [HTTP 2](https://tools.ietf.org/html/rfc7540) (NG) | 2015年5月 |
**HTTP 0.9**: 请求和响应均不带协议及版本,以及首部。 请求无首部,无body, 只有请求方法和URL; 响应无首部, 无状态码, 只有body。 HTTP 0.9支持的响应内容类型只有html.
## HTTP报文交换过程
HTTP是一个建立在TCP之上的应用协议,所以HTTP的在浏览器和服务器数据交换之前,需要先建立TCP连接。一个TCP连接包含三个部分:三次握手建立连接,数据传输,四次挥手断开连接。HTTP的数据交换就是传输部分。
![](https://box.kancloud.cn/5b4bc53bc9893116ec2035f67968bb6a_640x691.png)
图中展现的是HTTP 1.1的数据传输过程,即客户端发送请求之后,服务端响应一个请求,客户端收到响应之后才能继续发送请求。
HTTP 0.9, HTTP 1.0 中每次TCP连接只能发送一个请求和一个响应。HTTP 2则可以同时发送多个请求和接收多个响应。如图所示。
![](https://box.kancloud.cn/6bb8b45c28519dfe7627b78e1e26bafa_1202x691.png)
## HTTP 报文结构
**请求报文结构**
<table>
<tr>
<td>请求方法(HTTP Verb)</td>
<td>空格(Space)</td>
<td>资源路径(URI)</td>
<td>空格</td>
<td>HTTP版本(HTTP Version)</td>
<td>回车换行(CRLF)</td>
</tr>
<tr>
<td>首部名称(Header Field Name)</td>
<td>:</td>
<td colspan="3">首部值(Header Field Value)</td>
<td>回车换行</td>
</tr>
<tr>
<td colspan="6">...</td>
</tr>
<tr>
<td>首部名称</td>
<td>:</td>
<td colspan="3">首部值</td>
<td>回车换行</td>
</tr>
<tr>
<td colspan="6">回车换行</td>
</tr>
<tr>
<td colspan="6">请求体(Body)</td>
</tr>
</table>
1. 第1行称为起始行(Start Line), 包含了请求方法(标准叫法为HTTP谓词), 资源路径,和请求协议及版本。 这三个之间以一个空格分隔,并以回车换行(CRLF)结束。请求方法详见《HTTP 请求方法》一节。
2. 第2行起至第1个空行(仅CRLF)之间为HTTP首部,每个一行,并以回车换行结束。每一个首部以KeyValue的形式呈现,Key和Value之间以冒号和空格分隔。 详见《HTTP 首部》一节。
3. 空行之后的内容为请求体(body), 请求体是可选的, 可以是文本,也可以是二进制文件,还可以为空。
例如Chrome使用百度搜索hello world, 请求报文结构如下:
```xxx
GET /s?wd=hello%20world HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,zh-TW;q=0.7,ja;q=0.6
Cookie: locale=zh; sug=3
```
#### 响应报文结构
<table>
<tr>
<td>HTTP版本</td>
<td>空格</td>
<td>状态码(Status Code)</td>
<td>空格</td>
<td>状态注释(Status Annotation)</td>
<td>回车换行</td>
</tr>
<tr>
<td>首部名称</td>
<td>:</td>
<td colspan="3">首部值</td>
<td>回车换行</td>
</tr>
<tr>
<td colspan="6">...</td>
</tr>
<tr>
<td>首部名称</td>
<td>:</td>
<td colspan="3">首部值</td>
<td>回车换行</td>
</tr>
<tr>
<td colspan="6">回车换行</td>
</tr>
<tr>
<td colspan="6">响应体(Body)</td>
</tr>
</table>
1. 第1行称为起始行,包含了协议及版本,状态码和状态注释, 这三个之间用空格分隔,并以回车换行结束。状态注释在实际使用中用处不大。
2. 和请求报文一行,第2行起至第1个空行之间为HTTP首部,每个一行,并以回车结束。//由于客户端和服务器之间采用的是协商机制,客户端请求部分首部会提供多个值给服务器端选择,而服务器端往往提供确定的一个值告诉客户端服务器端选择的值。当然,有时候服务器端响应部分首部也会提供多个值给客户端选择,以便进一步的协商。
3. 空行之后的内容为响应体(body). 响应体是可选的,可以是文本,也可以是二进制文件,还可以为空。
依然以Chrome使用百度搜索hello world, 响应报文结构如下:
```xxx
HTTP/1.1 200 OK
Bdpagetype: 3
Bdqid: 0xbb459f9700043636
Cache-Control: private
Ckpacknum: 2
Ckrndstr: 700043636
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Fri, 01 Jun 2018 00:57:37 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Server: BWS/1.1
Set-Cookie: PSINO=1; domain=.baidu.com; path=/
Set-Cookie: BDSVRTM=32; path=/
Strict-Transport-Security: max-age=172800
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
<!DOCTYPE html>
<html>
...
</html>
```
#### HTTP 0.9报文结构
以上是标准的请求和响应报文结构I, HTTP 0.9相对简单,他们的请求和响应报文结构如下:
请求报文结构
```xxx
GET /path?query
```
响应报文结构
```xxx
<html>
...
</html>
```
## HTTP 请求方法
| 请求方法 | 支持协议版本 | 说明|
| --- | --- |--- |
|GET | 0.9, 1.0, 1.1, 2.0 |获取资源实体(entity),如果请求的是数据处理程序处理的,那么应当返回实体, GET方法有很多缓存策略, 条件请求策略来减少网络宽带的使用。 GET请求理论上也可以传输body,不过服务端不应该去处理body或者应当响应错误,**主流浏览器并不支持在GET请求里传输body,普通请求和Ajax请求均不支持**|
| POST | 1.0, 1.1, 2.0 |提交信息到服务器, 在语义上,它指创建新的资源,非幂等的, 理论上提交成功之后,服务端应返回新资源你的地址(URI)。这一点在创建RESTful接口的时候有明显体现|
| HEAD | 1.0, 1.1, 2.0 |HEAD方法和GET类似, 只是响应的结果没有实体,只有头部信息,这个方法可以用来检查连接的有效性,可访问性和是否放生过改变|
| OPTIONS |1.1, 2.0 |用于获取服务所支持的通信选项, 如请求方法,请求来源等, 该请求的响应没有body, 只有首部。主要用于:<br>1. 检测服务器所支持的请求方法, 服务器返回Allow首部表明允许使用的请求方法 <br>2. 跨域时预检请求|
| PUT |1.1, 2.0 |提交信息到服务器, 在语义上,它指更新对应的资源,幂等的. 理论上,可以不返回新资源的地址, 因为请求提交的地址就是资源的地址|
| DELETE |1.1, 2.0 |指示服务器删除一个资源, 这只是一个指示, 具体如何执行由服务器决定(例如有可能软删除),即使返回成功,也不一定表示服务器上的资源真的删除了|
| TRACE | 1.1, 2.0 |回显服务端收到的请求,用于测试或诊断|
|CONNECT | 2.0 |使客户端和最终服务器开启直接连接的通道, 使客户端和最终服务器可以直接相互传递信息(https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/CONNECT)|
1. 在协议上,请求方法是大小写敏感的,在发送请求时应该大写。
2. GET请求可以带body, 不过一般浏览器不支持,普通请求和Ajax都不支持,但是其他的一些客户端可以支持,例如curl。
3. POST和PUT提交信息的时候需要加入`Content-Type`(详见HTTP首部部分)首部,表明所提交的信息的数据类型,以便服务端解析。
4. OPTIONS方法跨域发送预检请求时, 应当带上`Origin`, `Access-Control-Request-Method`, `Access-Control-Request-Headers` 三个首部,告知服务端请求来源域名,将要请求的方法以及自定义的请求头部。详细参考《专题:跨域请求》一节。
5. CONNECT举例:
![](https://box.kancloud.cn/1406b7058f02a72820b77c3a01240da2_895x282.png)
详细参考[https://stackoverflow.com/a/11698002](https://stackoverflow.com/a/11698002)
6. 过去Web API的实现大部分只用GET和POST方法,在语义上存在误用,且响应状态码也存在滥用,这不但提高了他人使用API的难度,而且提高了代理和客户端缓存实现的难度。近年来RESTful风格Web API逐渐占主流,这种风格的API首先要求请求方法(谓词)使用准确,返回的状态码也要准确,在API路由上也更加清晰。
## HTTP 状态码
1xx 表示请求已经接受,但是在进一步处理请求时遇到了一些问题。
2xx 表示服务器已经成功处理了请求。
3xx 表示要完成请求还需要进一步的动作, 多表示重定向。
4xx 表示客户端请求有误,错误的地址,错误的方法,错误的授权等。
5xx 表示服务端处理请求错误,代码错误,网关/代理请求超时等。
| 状态码 | 支持协议版本 | 说明|
| --- | --- |--- |
| 100 |1.1 |服务端接收到了客户端发送的首部,客户端可以继续传输请求体(body)了|
| 101 |1.1 |服务端已经了解客户端的请求,并且通知客户端切换协议,服务端通过返回`Upgrade`首部来告知客户端可以切换的协议及版本|
| **200** |1.0, 1.1 |请求响应成功|
| 201 |1.0, 1.1 |请求执行了并且创建了一个新的资源,新资源的URL将在设置放置到响应的`Location`首部|
| 202 |1.0, 1.1 |请求已接收,但是请求还未处理完成。对于一些程序处理耗时较长的请求(如果转存资源到CDN), 可以提前释放TCP连接|
| 203 |1.1 |文档被正常的返回,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝|
| 204 | 1.0, 1.1 |服务端处理请求成功,返回了正确的首部信息,但是并没有返回响应实体。如果请求方是浏览器的话,页面视图上不应该有任何变化。|
| 205 |1.1 |和204一样,服务器成功处理了请求,返回了正确的首部信息,但是并没有返回响应实体。 与204不同之处在于,205要求客户端(浏览器)重置文档视图,比如清空表单已输入信息|
| 206 |1.1 |服务器已经成功处理了部分 GET 请求。在下载过程中断点续传可以使用该状态码。打算获得206响应的请求的请求头中必须携带`Range`首部,响应中必须包含`Content-Range`或`Content-Length`首部|
| 300 |1.1 |被请求的资源有多个地址,客户端(浏览器)可以选择其中一个重定向。响应的内容(body)应该是一个包含资源名称和响应连接的列表|
| **301** |1.0, 1.1 |请求永久重定向,表示资源已经永久性的使用新的访问地址了,在响应的`Location`首部中会给出资源的新地址,客户端应当使用新地址重新发送请求。一般来说,301请求是会被客户端缓存的。**该请求只适用于GET和HEAD请求方法, 如果POST或者其他请求方法返回301的话,可能导致重定向之后的请求时GET请求**。|
| **302** |1.0, 1.1 |请求临时重定向,表示资源只是临时使用该地址访问,在响应的`Location`首部中会给资源的出临地址, 客户端需要使用新地址发送请求获得响应。一般来说,302请求时不缓存的,除非包含`cache-control`,`expires`等首部。和301一样,只适用GET和HEAD请求,POST请求重定向之后会变成GET请求|
| 303 |1.1 |对应当前请求的响应可以在另一个 URI 上被找到,而且客户端应当采用 GET 的方式访问那个资源。303的主要作用是POST请求成功之后跳转到新生成的资源地址上。303请求不应当被缓存。|
| **304** |1.0, 1.1 |资源未修改,服务器应该只响应头部,不返回实体,客户端(浏览器)将从本地缓存中读取资源|
| 305 |1.1 |被请求的资源必须通过指定的代理(如Nginx)才能被访问, 响应的首部`Location`提供了代理所在的地址|
| **307** |未知 |请求临时重定向, 和302的区别支出在于307支持POST请求,重定向之后POST请求还是POST请求|
| 400 |1.0, 1.1 |语义有误,服务端无法理解当前请求, 除非修改请求,否则不应重复提交。如请求方法不合法,请求体太大,请求体的编码格式和`Content-Type`中不一致等|
| **401** |1.0, 1.1 |当前请求需要用户认证。用户访问网站时,服务端会返回401和`WWW-Authenticate`首部,如果这个首部的值为空,客户端(浏览器)会要求用户输入用户名和密码,客户端将添加`Authorization`首部再次发送请求,验证通过则返回200和带有值的`WWW-Authenticate`首部, 否则继续返回401和无值得`WWW-Authenticate`首部, 具体过程见《HTTP首部》部分|
| 402 |1.1 |需要支付,该状态码给未来预留, 暂无浏览器支持|
| **403** |1.0, 1.1 |服务器能够理解请求,但是拒绝执行。理论上响应体中应返回拒绝的原因,但目前的服务器返回的原因并不具体|
| **404**|1.0, 1.1 |请求失败,所请求的资源在服务器端不存在。理论上这个请求的响应不应该返回响应体,如果需要返回响应体,应该用403比较准确。|
| 405 |1.1 |请求方法不适用。响应中会包含`Allow`首部来表明所请求的URI允许使用的请求方法|
| 406 |1.1 |请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。|
| 407 |1.1 |和401类似,区别之处是客户端需要代理服务器(如nginx)的授权,而401是客户端需要应用的授权,但是在实际使用中,往往用401,即使在代理服务器层面 |
| 408 |1.1 |请求超时,服务端已经准备接收客户端请求,但是客户端没有在指定的时间内发送请求。例如客户端在资源接收完毕之后没有发送`Connection`:`close`首部|
| 409 |1.1 |请求与当前服务器端的状态相冲突。冲突最有可能发生在对 PUT 请求的响应中。例如,当上传文件的版本比服务器上已存在的要旧,从而导致版本冲突的时候,那么就有可能收到状态码为 409 的响应。|
| 410 |1.1 |请求的资源在服务器端已经不存在了,和404类似,区别在于410会被客户端或者代理服务器缓存。|
| 411 |1.1 |客户端发送请求的首部中未包含`Content-Length`. 当使用chunk将数据传输到服务器时,需要用`Content-Length`指明每一个chunk的长度, 否则服务器会返回411错误|
| 412 |1.1 ||
| 413 |1.1 |请求体过大, 服务器无法处理。服务器返回413的同时会中断TCP连接,以防后面发送更多的请求。|
| 414 |1.1 |客户端所请求的 URI 超过了服务器允许的范围。造成这个的原因可能是 1. 误将POST请求写成了GET请求 2. 黑客试图攻击服务器以图找到漏洞|
| 415 |1.1 |服务器不支持的媒体类型。对于媒体类型的探测可能利用`Content-Type`首部,也可能具体检测实体的类型, 具体看服务器的实现|
| 421 |2 |部分资源获取时,所指定的部分无法满足。比如一个资源的大小是100字节, 客户端请求第120到150字节部分,则服务器无法满足请求|
| **500** |1.0, 1.1 |服务器内部异常, 通常是应用程序在处理请求时出bug了|
| 501 |1.0, 1.1 |请求方法不支持, 比如PUT,DELETE请求在HTTP 1.1才开始支持, 如果是1.0版本发送该请求,则会返回501|
| **502** |1.0, 1.1 |网关或代理无法收到上游应用的响应结果。比如Nginx代理了Rails程序,而Rails程序已经停止运行,或代理到无法处理请求的服务,则请求Nginx时会返回502|
| **503** |1.0, 1.1 |由于临时的服务器维护或者过载,服务器当前无法处理请求|
| **504** |1.1 |网关超时,代理或者网关在规定的时间内无法获得上游服务的响应|
| 505 |1.1 |服务器不支持客户端请求的HTTP的版本|
HTTP 0.9 由于响应报文只有响应实体(body), 所以并无状态码。
## HTTP 首部
| 首部 | 支持协议版本 | 类型|说明|可选值|
| --- | --- |--- |--- |--- |
| Date | 1.0 |通用 |--- |--- |
| Pragma | 1.0 |通用 |--- |--- |
| Authorization | 1.0 |请求 |--- |--- |
| From | 1.0 |请求 |--- |--- |
| User-Agent | 1.0 |请求 |--- |--- |
| If-Modified-Since | 1.0 |请求 |--- |--- |
| Referer | 1.0 |请求 |--- |--- |
| Allow |1.0 |响应 |--- |--- |
| Content-Type | 1.0 |响应 |--- |--- |
| Content-Type | 1.0 |响应 |--- |--- |
|Content-Length |1.0 | 响应|--- |--- |
|Content-Encoding |1.0 |响应 |--- |--- |
|Expires | 1.0 | 响应|--- |--- |
| Last-Modified |1.0 | 响应|--- |--- |
| Location |1.0 | 响应|--- |--- |
|Server | 1.0 |响应 |--- |--- |
| WWW-Authenticate |1.0 | 响应|--- |--- |
## HTTP 2.0
## HTTPS详解
## 专题: Web应用典型网络架构
随着Web技术的发展,在应对大流量,Web应用的稳定性,可访问性方面,已经有一套比较成熟的网络架构了,下图Web应用的典型网络架构。虽然各大公司在很多细节方面会有所改进,但是大体结构不会脱离这个。
![](https://box.kancloud.cn/0626900f964236d44636d8af44db5e99_2276x1434.jpg)
目前主要的细节改进主要体现在:
1. 智能DNS: 用户访问相关Web应用前,服务器会根据域名查询域名所解析的IP,普通DNS直接返回域名的固定IP, 而智能DNS可以根据用户的具体情况返回不同的IP, 比如用户访问 www.google.com, 如果用户是电信宽带,智能DNS会返回谷歌电信机房服务器的IP,如果是联通宽带,则返回谷歌联通机房服务器的IP, 如果是美国用户,返回谷歌美国机房服务器的IP, 香港用户则返回香港机房服务器的IP. 总之,智能DNS可以根据用户的请求信息返回最适合用户访问的服务器的IP,具体策略可以在智能DNS上设置。
2. 动态CDN:普通CDN
3. WAF
4. ELB
5. 内部
## 专题: 跨域请求
## 专题: 缓存策略
## 专题: 性能优化
## 专题: 安全