# HTTP
HTTP是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。
超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准
(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
# 报文
HTTP报文分为两种:HTTP请求报文(请求行-HTTP头(通用信息头,请求头,实体头)-请求报文主体(只有POST才有报文主体)。响应报文则相对应的为状态行,响应报文主体。
通用信息头指的是请求和响应报文都支持的头域,分别为
Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via;实体头则是实体信息的实体头域,分别为Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header。
常用头部字段解释: 
TIP
Content-Length
服务器返回数据时会有该字段,表明本次的回应数据长度
Content-Type
⽤于服务器回应时,告诉客户端,本次数据是什么格式
Content-Type: text/html;charset=utf-8
Content-Type: application/json;charset=utf-8
2

客户端请求的时候,可以使用
Accept字段声明自己可以接受哪些数据格式
Accept: */*
# HTTP历史
HTTP/0.9
只有一个请求行,没有
HTTP请求头和请求体。同样,服务器也没有响应头信息,只是返回了数据。因为都是HTML格式的文件,决定了返回的文件内容通过ASCII字符流进行传输。
HTTP/1.0
引入请求头/响应头,引入状态码,为了减轻服务器压力提供了
Cache机制。服务器需要统计客户端的基础信息(Windows和macOS),加入了用户代理字段。有两个问题:
TCP连接无法复用,每次请求都需要重新建立TCP通道,这就需要重复进行三次握手和四次挥手,也就是说每个TCP连接只能发送一个请求。- 队头阻塞,即使多个请求并行发出,也只能一个一个进行响应。
HTTP/1.1
TIP
- 改进持久连接(长连接,默认开启
Connection:keep-alive)
一个TCP连接上可以传输多个HTTP请求,只要浏览器或者服务器没有断开连接,该TCP会一直保持。持久连接是默认开启的,如果想要关闭,在请求头中加上Connection:close即可关闭。目前浏览器中对于同一个域名,默认允许同时建立6个TCP持久连接。
- 增加对虚拟主机的支持
HTTP/1.0中每个域名都只绑定唯一的IP地址,因此一个服务器只能支持一个域名。但是随着虚拟主机技术的发展,一台物理主机上绑定多个虚拟主机的需求大大提升,每个虚拟主机都有自己单独的域名,这些单独的域名都公用同一个IP地址。因此,请求头中也增加了Host字段,表示当前的域名地址,服务器可根据不同的Host值做不同的处理。
- 增加对动态生产内容的支持
HTTP/1.0需要在响应头中设置完整的数据大小Content-Length:900,这样,浏览器就可以根据设置的数据大小来接收数据。由于服务器端技术发展,页面都是动态生成的,传输数据之前并不知道最终数据大小,导致浏览器不知道何时会接受完所有的文件数据。HTTP/1.1通过引入Chunk transfer机制来解决问题,服务器将数据分割成若干个任意大小的数据块,每个数据块发送时会附上上一个数据块的长度,最后使用一个长度为0的块作为发送数据完成的标志。
- 客户端
Cookie,安全机制 - 未完全解决
http 1.0的队头阻塞问题:请求之间是串行的。如果发送了A请求,那么A请求的响应返回之前,你的B请求不管多么着急发出去,都要取决了A请求是否结束了。http1.1则利用了不成熟的管线化来解决这个问题。管线化是指:允许多个HTTP请求批量地提交给服务器。不过这样做仍然没有从根本上解决队头阻塞的问题--虽然发送动作可以并行,不过服务器依然需要根据请求顺序来回复浏览器的请求,也就是说响应仍然是串行的。A如果一年没有被响应,B也休想被响应。 所以很多浏览器默认禁用管线化特性。 - 增加了与缓存相关的请求头。进行了带宽优化,并能够使用
range头等来支持断点续传功能。新增错误类型,并增强了错误和响应码的语义特性。新增了Host头处理,如果请求消息中没有Host头,则会报错。
HTTP 1.1缺点:
- 没有真正解决队头阻塞问题
- 明文传输,安全性问题
header中携带的内容过多,增加传输成本- 默认开启
keep-alive可能会给服务器端造成性能压力,比如一次性的请求,在文件被请求之后还保持了很长时间不必要的连接。- 由于
TCP慢启动机制,导致每个TCP连接在一开始的时候传输速率都不高,在处理多个请求后,才会慢慢达到 “合适” 的速率。对于请求数据量很小的HTTP请求来说,这种情况就是种灾难。HTTP/1.1无法为重要的资源指定优先级,每个HTTP请求都是一视同仁。受限的优先级设置
HTTP/2.0(基于HTTPS)
二进制协议将通信传输信息分解为帧,这些帧交织在客户端和服务端之间的双向逻辑流中,使所有通信都可以在单个
TCP连接上执行,而且该连接在整个对话期间一直处于打开状态。
使用多路复用机制解决
TCP慢启动(一旦一个TCP连接建立之后,就进入了发送数据状态,刚开始TCP协议会采用一个非常慢 的速度去发送数据,然后慢慢加快发送数据的速度,直到发送数据的速度达到一个理想状态,我们把这个过程称为慢启动慢启动是TCP为了减少网络拥塞的一种策略,我们是没有办法改变的);开启多条TCP连接会竞争固定带宽;队头阻塞的问题。(一个域名只使用一个TCP长连接和消除队头阻塞问题。通过引入二进制分帧层,实现了HTTP的多路复用技术。)。这是因为一个请求对应一个流并分配了一个id,这样一个连接上可以有多个流,所以流的帧都可以相互混杂在一起,接收方可以根据流的id将帧分配到各自不同的请求中。同一个TCP连接中可以发送多个请求,对端可以通过帧中的标识知道该帧属于哪一个请求。
服务器可以提前将数据推送到浏览器,浏览器有权选择是否接受。浏览器发送
RST_STREAM帧可以选择拒收。在浏览器刚请求HTML的时候,就提前把可能会⽤到的JS、CSS⽂件等静态资源主动发给客户端,减少延时的等待,也就是服务器推送(Server Push,也叫Cache Push)
头部的压缩大大的提升了传输效率。
HTTP/2开发了“HPACK”算法,在客户端和服务器建立“字典”,用索引号表示重复的字符串,还采用哈夫曼编码来压缩整数和字符串。客户端和服务器分别维护一份相同的静态字典,这个字典用来存储常见的头部名称,以及常见的头部名称和值的组合。同时还会维护一份相同的动态字典,这个字典可以随时更新。如此一来,第一次相互通信之后后面的请求只需要发送与前面请求之间头部不同的地方,其他头部信息都可以从字典里获取。相对于HTTP1.x中每次都要携带整个头部跑来跑去,大大节省了网络开销。
流优化先:消息帧通过流进行发送。我们提到了为每个流分配了一个
id,那么也同样可以为它们分配优先级。这样一来,服务器端可以根据优先级确定它的处理顺序。
可以设置让某些重要的数据优先被服务器处理并返回。针对服务器端希望发送的每个资源,服务器端都会发送一个
PUSH_PROMISE帧,但客户端可以通过RST_STREAM帧作为响应来拒绝推送。当一个客户端主动请求资源K时,如果服务器端知道它很可能也需要资源M,那么服务器端就会主动将资源M推送给客户端。当客户端真的请求M时,便可以从缓存中读取
流控制:客户端哦通知服务器停止发送数据,以免耗尽自身的缓存。
TIP
HTTP/2主要的问题在于,多个HTTP请求在复⽤⼀个TCP连接,下层的TCP协议是不知道有多少个HTTP请求的。所以⼀旦发⽣了丢包现象,就会触发TCP的重传机制,这样在⼀个TCP连接中的所有的HTTP请求都必须等待这个丢了的包被重传回来。这都是基于TCP传输层的问题,所以HTTP/3把HTTP下层的TCP协议改成了UDP
HTTP/3.0
TIP
TCP以及TCP+TLS建立连接的延时
HTTP/2都是使用TCP协议来传输的,而如果使用HTTPS的话,还需要使用TLS协议进行安全传输,而使用TLS也需要一个握手过程,这样就需要有两个握手延迟过程:
- 在建立
TCP连接的时候,需要和服务器进行三次握手来确认连接成功,也就是说需要在消耗完1.5个RTT之后才能进行数据传输。 - 进行
TLS连接,TLS有两个版本——TLS1.2和TLS1.3,每个版本建立连接所花的时间不同,大致是需要1~2个RTT。
总之,在传输数据之前,我们需要花掉 3~4 个 RTT。
TCP的队头阻塞并没有彻底解决 上文我们提到在HTTP/2中,多个请求是跑在一个TCP管道中的。但当出现了丢包时,HTTP/2的表现反倒不如HTTP/1了。因为TCP为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2出现丢包时,整个TCP都要开始等待重传,那么就会阻塞该TCP连接中的所有请求。而对于HTTP/1.1来说,可以开启多个TCP连接,出现这种情况反到只会影响其中一个连接,剩余的TCP连接还可以正常传输数据。
UDP发⽣是不管顺序,也不管丢包的,所以不会出现HTTP/1.1的队头阻塞 和HTTP/2的⼀个丢包全部重传问题。⼤家都知道UDP是不可靠传输的,但基于UDP的QUIC协议 可以实现类似TCP的可靠性传输。
QUIC有⾃⼰的⼀套机制可以保证传输的可靠性的。当某个流发⽣丢包时,只会阻塞这个流,其他流不会受到影响。TLS3升级成了最新的1.3版本,头部压缩算法也升级成了QPack。HTTPS要建⽴⼀个连接,要花费6次交互,先是建⽴三次握⼿,然后是TLS/1.3的三次握⼿。QUIC直接把以往的TCP和TLS/1.3的6次交互合并成了3次,减少了交互次数
QUIC是⼀个在UDP之上的伪TCP + TLS + HTTP/2的多路复⽤的协议。QUIC是新协议,对于很多⽹络设备,根本不知道什么是QUIC,只会当做UDP,这样会出现新的问题。
# HTTP状态码
1xx:请求已经收到,但是处理过程还没结束,需要客户端再抛出一个请求才能完成整个过程(即需要进一步处理才能完成),HTTP1.0不支持100 Continue:上传大文件前使用101 Switch Protocols:协议升级使用102 Processing:服务器已经收到并正在处理请求,但无响应可用
2xx:成功处理请求200 OK:成功返回响应201 Created:有新资源在服务器端被成功创建202 Accepted:服务器接受并开始处理请求,但请求未处理完成206 Partial Content:使用range协议时返回部分响应内容时的响应码
重定向是服务器发起的跳转,要求客户端使用新的
URI重新发送请求。在响应头字段Location中指示了要跳转的URI。使用Refresh字段,还可以实现延时重定向。
3xx:重定向301/302是常用的重定向状态码。分别代表永久性重定向和临时性重定向。303:类似于302,重定向后的请求方法改为GET方法307:类似于302,含义比302更明确,重定向后请求的方法和实体不允许变动308:类似于301,代表永久重定向,重定向后请求的方法和实体不允许变动300:是一个特殊的重定向状态码,会返回一个有多个链接选项的页面,由用户自行选择304:是一个特殊的重定向状态码,服务端验证过期缓存有效后,要求客户端使用该缓存
4xx:客户端出现错误400 Bad Request:服务器认为客户端出现了错误,但不明确,一般是HTTP请求格式错误
产生原因;前端提交数据的字段名称和字段类型与后台的实体没有保持一致;前端提交到后台的数据应该是
json字符串类型,但是前端没有将对象JSON.stringfy转换为字符串401 Unauthorized:用户认证信息确实或者不正确。发送的请求需要通过HTTP认证。403 Forbidden:服务器理解请求的含义,但没有权限执行407 Proxy Authentication Required:对需要经由代理的请求,认证信息未通过代理服务器的验证404 Not Found:服务器没有找到对应的资源408 Request Timeout:服务器接收请求超时405 Method Not Allowed:请求方法不被服务端允许。
5xx:服务器端出现错误500 Internal Server Error:服务器内部错误,且不属于以下错误类型502 Bad Gateway:代理服务器无法获取到合法响应503 Service Unavailable:服务器资源尚未准备好处理当前请求。服务器当前很忙,暂时无法响应505 HTTP Version Not Supported:请求使用的HTTP协议版本不支持
# HTTP几种方法
GET:发送一个请求来获取服务器上的某一些资源POST:向URL指定的资源提交数据或附加新的数据 GET和POST的区别PUT:跟POST方法一样,可以向服务器提交数据,但是它们之间也所有不同,PUT指定了资源在服务器的位置,而POST没有哦。HEAD:指请求页面的首部。DELETE:删除服务器上的某资源OPTIONS:它用于获取当前URL所支持的方法,如果请求成功,在Allow的头包含类似GET,POST等的信息。 复杂请求的理解TRACE:用于激发一个远程的,应用层的请求消息回路。CONNECT:把请求连接转换到TCP/TP通道。