状态码
| 码 | 含义 | 备注 |
|---|---|---|
| 100 | 目前一切正常,客户端继续发送内容 | |
| 204 | 请求成功,但响应报文中不包含具体内容 | 链接 |
| 205 | reset content,也不应该返回响应体 | 204,205 区别 |
| 206 | 客户端进行范围请求,服务端已返回 | 请求头中含有 Range 字段时返回的状态码 |
| 301 | 永久重定向 | |
| 302 | 临时重定义 | |
| 304 | 命中缓存 | 相当于重定向到本地 |
| 504 | 网关或代理服务器未从上游服务器中获取到信息 |
请求头与响应头
这里涉及到多个字段,按要处理的部分分类别记录。基本上,accept 打头的都用于请求头,表示可支持的功能列表;content 打头用于响应头,表示本次响应的数据的具体实现
Age 与 Date
Age 表示对象在代理服务器中存贮的时长,以秒为单位
Date 表示对象创建的日期和时间
Content-Length
本次发送的数据长度,单位字节
如果数据被压缩了,那么 Content-Length 指的是压缩后的大小。因此,为了得到数据的原始长度,必须指定不能压缩,即请求头中添加 ("Accept-Encoding", "identity")
Content-Type
携带数据的 Mime type
除数据格式外,它还额外带有别的信息,比如编码方式等。
content-type: text/html; charset=UTF-8
数据格式 accept 与 Content-Type
数据格式就是 mime type
前者表示能接受的数据类型,后者表示本次携带数据的格式
// 请求头。表示发送方可接收的数据格式
Accept: text/html...
// 响应头。表示本次响应内容是 text/html 格式
Content-Type: text/html
压缩方式 Content-Encoding 与 Accept-Encoding
前者表示本次响应发送的数据采用的压缩算法,后者表示接收方支持的压缩算法
// 请求头,表示本客户端支持以下的压缩算法
Accept-Encoding: gzip, deflate, br A
// 响应头。表示本次响应的数据采用 gzip 进行压缩
Content-Encoding: gzip
语言 Content-Language 与 Accept-Language
前者表示发送方发送数据的语言,后者表示接收方可处理的语言
字符集 Content-Type 与 Accept-Charset
这里发送方重复使用了 Content-Type,并没有定义一个 Content-Charset
前者表示本次发送的数据使用的字符符,后者表示请求方支持的字符集。
Content-Type 的完整示例如下。分号前表示内容类型,分号后表示使用的字符集
Content-Type: text/html; charset=utf-8
Content-Length 与 Transfer-Encoding
前者表示本次发送的数据长度。
对于简单的返回很容易获取到 Content-Length。但在某些情况下,Content-Length 不太容易计算(比如动态生成的内容),这种情况下使用 Transfer-Encoding。
也就是说如果不方便或无法计算出 Content-Length,就在返回头中使用 Transfer-Encoding,并将其值设置为 chunked,这时会将大块资源分成小块依次传输。关于它的讲解可参考该链接
范围请求 Accept-Ranges,Range 及 Content-Range
在断点上传下载时指定范围
Accept-Ranges:用于响应头。值为 none 时表示服务端不支持续传,值为 bytes 表示服务器可以接受范围请求,即可使用续传功能。
Range: 用于请求头,表示本次请求的范围。它的格式固定为 Range: bytes=x-y,表示从 x 请求到 y。如果需要请求多段数据,中间使用逗号隔开,如 0-9, 20-30。针对不同的请求形式,响应头的返回也不一样:
- 单段数据,即 Range:bytes=x-y: 此时
返回头会带 Content-Range。如 Content-Range: bytes 0-1/2443。其中 0-1 表示请求返回,2443 表示资源总大小。此时的 Content-Length 表示返回内容的大小, Content-Type 表示返回数据的格式# 通过 curl 请求 baidu,且带有请求头 Range:bytes=0-1 # -i 表示将响应头也打印出来 # 此时请求方式是 GET。返回的状态码是 206 ~ curl https://www.baidu.com/ --header "Range: bytes=0-1" -i HTTP/1.1 206 Partial Content Content-Length: 2 Content-Range: bytes 0-1/2443 Content-Type: text/html - 多段数据,即 Range:bytes=x-y, a-b: 此时
返回参数通过 Content-Type: multipart/byteranges; boundary=RANGE_SEPARATOR 指定响应体的分隔// 请求头 Range: bytes=0-10, 20-40 // 响应头。状态码为 206 HTTP/1.1 206 Partial Content // 此时返回 Content-Type,不会返回 Content-Range 了 Content-Type: multipart/byteranges; boundary=00000000000000000002 // 指返回体的大小,不是整个资源的大小 Content-Length: 231 // 响应体。会通过 Content-Type 中 boundary 进行分割 --00000000000000000002 Content-Type: text/html Content-Range: bytes 0-10/2443 <!DOCTYPE h --00000000000000000002 Content-Type: text/html Content-Range: bytes 20-40/2443 -STATUS OK--><html> < --00000000000000000002--
缓存
缓存实质上是请求头、响应头中几个特殊字段。
缓存分为
强制缓存与协商缓存。前者在缓存命中时不向服务器进行询问,直接使用缓存;后者会先询问,在缓存可用时才使用缓存
- 强制缓存优先级高于协商缓存
- 缓存应该分为
无缓存、强制缓存、协商缓存三种方式,通过使用cache-control进行区分- 值为 no-store 时,表示不使用缓存
- 值为 no-cache 时,表示使用协商缓存
- 除上述两值为,为强制缓存
- 使用协商缓存时,必须先将 Cache-control 设置成 no-cache
- 缓存有一个 revalidate 过程:缓存在过期后,重新验证的过程
Vary
跟缓存相关的一个响应头字段。同一份 url 可能会有不同的返回资源。最简单的比如有接收 gzip 压缩,有不接收压缩。因此,资源在缓存时必须根据不同的配置缓存不同的资源。这些配置有可能是上面已经列表过的 Accept 与 Content 等,也有可能是一些别的(比如 UA,Cookie 等),Vary 就是用来存储这些字段的。
Vary 用来列表缓存应根据哪些头信息进行区分。比如 Vary: Accept-Encoding 响应头,明确告知缓存服务器按照 Accept-Encoding 字段的内容,分别缓存不同的版本
cache-control
缓存的总控制字段
- no-store 表示不使用缓存,即内容不会被存储到本地
- no-cache 使用协商缓存。表示使用缓存,但使用前需要与服务端协商,即由强制缓存改为协商缓存
- private 只能被浏览器缓存,代理服务器不能缓存。所谓代理服务器指浏览器与服务器之间的一些服务器
- public 表示资源即可被浏览器缓存,又可被代理服务器缓存
- max-age:指定强制缓存时资源存活时长,如 Cache-Control: max-age=0。请求头与响应头都可指定,在计算缓存时以两者最小值为准
- must-revalidate:缓存过期前直接使用缓存,过期后必须跟服务端验证通过后才可使用。在某些情况下,缓存即使过期了,在协商失败时(服务器挂了),还可以继续使用。这个 must-revalidate 避免了这种情况
- only-if-cached:只用于请求头,客户端只希望获得缓存的响应,并且不应该联系原始服务器以查看是否存在新的数据
- max-stale:只用于请求头,表示客户端能接收过期的缓存,但过期时间不能超过 max-stale 指定的值
- min-refresh:用于请求头,表示客户端希望读取到的缓存能在 min-refresh 内不能过期,如果没有缓存这个值没啥用。有些缓存可能在请求的瞬间没有过期,但其存活时间不足 min-refresh,这个缓存就不满足当前请求,需要执行网络请求。
cache-control 可取多个值,之间使用逗号分隔开即可
cache-control: public, max-age=31536000
Expires
强制缓存
定义资源过期的绝对时间,在该时间内不需要请求服务器可直接使用缓存。它的缺点也很明显:严重依赖客户端本地时间 ,导致缓存过期时间不准确
cache-control + max-age
强制缓存。max-age 的值以秒为单位,表示该资源在被请求后多长时间内有效
由于 max-age 并没有直接指定资源的绝对过期时间,而是指定资源存活时间,所以它不依赖于客户端的时间准确性。
另外,max-age 优先级高于 expires
last-modified 与 if-modified-since
协商缓存。last-modified 用于响应头,表示资源的上次修改时间。if-modified-since 用于请求头,是上次响应头给的 last-modified。
服务器应先读取 if-modified-since,并和本地资源修改时间进行比较。如果两个时间一致,应该返回状态码 304,同时不返回文件
缺点:由于只是根据资源修改时间判断资源是否修改过,所以会存在 ABA 问题。即资源名由甲改为乙,再改回甲,资源的修改时间也会更新,导致缓存失效。但实际上资源内容并没有修改,理论上可以使用缓存。
另外,比对的时间单位是秒,如果修改过快,在毫秒级别,那即使资源修改了,也判断不出来
etag 与 if-none-match
协商缓存。etag 响应头,服务器生成的资源的哈希值。if-none-match 请求头,表示上次请求返回的 etag
etag 的生成会消耗一定性能,但不会出现 ABA 问题
协议版本
1.0
每一个请求单独使用一个 TCP 连接,有多少请求就有多少三次握手,四次挥手
连接无法复用。建立一个 tcp 连接很消耗资源,一个 http 请求就新建一个 tcp 连接很浪费。- 队头阻塞(前一个请求完成之后第二个请求才可以执行)
1.1
复用 tcp 连接
一个 http 请求后并不立即断开 tcp 连接,会保留一段时间。在连接有效期内,后续的 http 请求都会使用同一个 tcp 连接。
通过在请求头响应头中添加 Connection: Keep-Alive 实现。
缺点:http 请求是串行的,跟 h1 一样。即一个请求结束才发起另一个,只不过后者复用前者的 tcp 连接,所以依旧会出现队头阻塞
2.0
主要特性有:
-
头部压缩。具体细节参考。简单说,有一个静态表(static table),里面存储一些常用的健值对,能匹配上时就传对应的下标;同时维护一个动态表,该表根据每次请求的 header 进行添加、修改,如果后续的 header 在表中,依旧只发送下标。
-
二进制分帧。帧指的是连接中传输的最小数据单元。一个完整的 http 请求(包括请求行、头、体)会被拆分成不同的片段,每一个片段加上一些别的信息就构成了一个帧。而 h2 就是将不同的帧传输给对方。接收方接收到不同的帧后会按照帧中的 streamId 将若干个帧还原成一个 http 请求。
同时,与 h1 不同的是,h1 是文本传输,而 h2 将帧进行二进制编码,传输的是二进制数据。所以叫二进制分帧:
传输的数据经过二进制编码,传输的最小粒度是帧,每一帧携带有若干请求信息。常用的帧有:head frame,携带请求头信息;data frame 携带请求体信息。
-
多路复用:HTTP 1.1 中,一次链接成功后只要该链接还没断开就可以发送多个 http 请求,但这些请求是串行的,也就是说同一时间只有一个请求运行在网络中。而 h2 可以同时跑多个,这就是多路复用。多路复用的实现就是利用了二进制分帧技术。
https
非对称加密
https 内部使用了非对称加密,对非对称加密来说,公钥加密,私钥可以解密。私钥加密,公钥也可以解密。公钥与私钥是可以相互解密的,并不是私钥加密后就没办法解密。
数字证书
https 中,公钥并不是直接在网络上传输的,而是以数字证书的方式发送给对方。
假设公钥是直接传输的,那么就无法避免中间人攻击。服务端发送给客户端的公钥在传输过程中被四甲拦截,然后甲将公钥换成自己的。客户端收到公钥后,完全无法验证这个公钥是不是自己要请求的服务端的。
为了解决上述问题,引入了数字证书,因此数字证书的作用是:保证公钥是证书的中的持有者的。
证书的颁发机构叫做 CA,CA 也有一套自己的公私钥,且公钥一般内置在操作系统中,或者可由操作系统中内置的公钥验证它的正确性。这里就假定 CA 是正确的。
用户将身份信息和公钥提交给 ca,ca 用自己的私钥对用户的公钥和身份信息等信息进行签名,并将公钥、身份信息、签名等信息混合生成一个文件,该文件就是数字证书。客户端拿到证书之后首先使用 ca 的公钥解密,然后验证解密后的信息是否与证书中所携带的信息一致。如果一致就认为证书是合法的。
在生成证书的过程中,ca 使用自己的私钥进行签名,客户端使用 ca 的公钥解密。这里利用了非对称加密的身份验证功能。如果客户端能解密,说明使用 ca 的私钥进行加密的,而私钥只可能由 ca 自己拥有,所以可以证明该证书来源于 ca,且没有被修改。
数字证书也有自己的格式。常用的如 X.509
tls/ssl 握手过程
https 在 http 与 tcp 之间加了 tls/ssl 层,这一层用于对 http 层下发的明文加密。tsl 的过程如下:
-
客户端向服务端发 client hello 消息,里面包一个随机数1 ,tls 版本以及加密套件。加密套件指的是:将对称加密、非对称加密、摘要算法等算法组成一个整体,如下图中的 Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301),一旦选定某个套件后后续的对称加密、非对称加密、摘要等算法便确定了。
-
服务端回一个 server hello:商量好 tls 版本,随机数2,证书,以及选择一个加密套件
-
客户端收到证书后,会先验证证书是否安全。如果安全,会生成另一个随机数3(还有一个名字:预主密钥 premaster secret),同时使用公钥进行加密后发送给服务端
-
服务端收到后,使用自己的私钥解密,得到随机数 3。到目前为止,两端都拥有了三个随机数
-
两端使用约定好的算法,使用三个随机数生成相同的 key。这个 key 就是后续对称加密使用的密钥
-
随后两端使用生成的 key 对称加密 FINISHED 发送给对方。发送完后 tls 握手过程就结束了。