HTTP协议

基础概念

批注 2020-03-07 204209

202037204550

202037204611

HTTP方法

获取资源

与GET类似,但不返回报文的实体主体

主要用来传输数据

上传文件

对资源进行部分修改

删除文件

查询指定的 URL 能够支持的方法

要求在与代理服务器通信时建立隧道

服务器会将通信路径返回给客户端

状态码

分类 分类描述
1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

1XX

100 Continue :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应

2XX

3XX

4XX

5XX

Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一客户端

用途:

创建过程

服务的响应头Set-Cookie头部:

Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

客户端之后对同一服务器发送请求时,都会在请求头Cookie头部带上这个Cookie

Cookie: yummy_cookie=choco; tasty_cookie=strawberry

分类

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2020 07:28:00 GMT;

作用域

Domain 标识Cookie在哪些域名下有效,如果不指定,默认是当前文档的主机

如果指定了Domain,则一般包括子域名,如baidu.com,包含map.baidu.com

JS访问

JavaScript可以通过document.cookie来创建cookie或者访问非HttpOnly的Cookie

HttpOnly

标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2020 07:28:00 GMT; Secure; HttpOnly

Secure

标记为 Secure 的 Cookie 只能通过被 HTTPS 协议加密过的请求发送给服务端

Session

Session是通过在服务端生成一个key,使用这个key为索引在服务器端存放用户信息,后将这个key作为cookie返回给客户端,让客户端使用这个key来操作

应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,还需要使用二重验证的方式

Cookie与Session

比较类别 Session Cookie
存储方式 服务端 客户端
大小限制
安全 较安全 较不安全

gzip只能对body进行压缩 cookie位于头上 需要手动编程对http header 进行处理

浏览器禁用cookie

当浏览器无法使用Cookie,只能使用session,此外,session id也不能通过cookie来传递,而是需要通过URL传参的方式来传递,如wap时代的sid

注意事项

连接管理

连接类型

短连接

每进行一次 HTTP 通信就要新建一个 TCP 连接

并行连接

通过连续发起多个不同连接 来解决串行HTTP请求的长时延

也并非并行连接就能更快 还需要考虑到带宽 服务器等因素

长连接

从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 Connection : close,HTTP/2 里,连接关闭是通过另外的机制实现的,与 Connection 头部无关

加快VIP切换时的收敛速度:

sequenceDiagram
  客户端 ->> LB: 请求
  LB ->> 客户端: serverA
  客户端 ->> serverA: HTTP
  serverA ->> 客户端: Connection:Keep-Alive
  客户端 ->> serverA: 其他事务
  serverA ->> 客户端: Connection:Close
  客户端 ->> LB: 请求
  LB ->> 客户端: serverB
  客户端 ->> serverB: HTTP

在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 Connection : Keep-Alive

对于不认识Connection的代理服务器,会将Connection首部也一并转发,从而造成连接被挂住

哑代理挂住了请求

Keep-Alive: max=5, timeout=120 // 最多为5个事务保持连接状态 或最多保持120秒的空闲时间

流水线

流水线是在同一条长连接上连续发出请求,而不用等待响应返回,这样可以减少延迟

流水线连接

要求服务端必须按客户端发送的请求顺序响应数据

连接关闭

HTTP的连接可以在任意时刻关闭,针对HTTP编程要处理这种情况

使用Content-Length 来提供接下去所传输的数据大小,从而让对方知悉传输情况

基于TCP的HTTP继承了TCP的关关闭功能,也就是可以关闭输入或者输出通道,当对方的输入通道关闭后,己方若继续发送数据,就会得到一个连接被重置的错误

良好的关闭实现应该是首先关闭己方的输出,然后等待对方的输出通道关闭,这样就可以安全地关闭

内容协商

服务端驱动

客户端设置Accept、Accept-Charset、Accept-Encoding、Accept-Language等首部,服务端根据这些首部返回特定资源

代理驱动

服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源

vary

一个客户端发送了一个包含 Accept-Language 首部字段的请求之后,源服务器返回的响应包含 Vary: Accept-Language 内容,缓存服务器对这个响应进行缓存之后,在客户端下一次访问同一个 URL 资源,并且 Accept-Language 与缓存中的对应的值相同时才会返回该缓存

内容编码

内容编码有:gzip、compress、deflate、identity

浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级。服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法

范围请求

请求报文中添加 Range 首部字段指定请求的范围

Range: bytes=0-1023

成功的话服务器返回的响应包含 206 Partial Content

请求的范围越界的情况下,服务器会返回 416 Requested Range Not Satisfiable 状态码

不支持范围请求的情况下,服务器会返回 200 OK 状态码

用于告知客户端是否能处理范围请求,可以处理使用 bytes,否则使用 none

分块传输

Chunked Transfer Encoding,可以把数据分割成多块,让浏览器逐步显示页面

多部分对象集合

一份报文主体内可含有多种类型的实体同时发送,每个部分之间用 boundary 字段定义的分隔符进行分隔

Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="submit-name"

Larry
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--AaB03x--

缓存

实现方法:

缓存控制

禁止对响应进行缓存

Cache-Control: no-store

强制确认缓存

只有当客户端确认缓存资源有效时,才能使用这个响应

Cache-Control: no-cache

must-revalidate表示在资源过期后,一定需要从服务器中进行获取 proxy-revalidate用于提示代理、CDN等设备资源过期后的缓存行为

禁止修改

Cache-Control: no-transform

禁止代理服务器修改HTTP响应头或者响应体

私有缓存

只能单独给用户使用,一般用在浏览器

Cache-Control: private

公共缓存

可以被多个用户使用,一般存储在代理服务器中

Cache-Control: public

客户端缓存新鲜度限制

HTML的HTTP-EQUIV

可以控制服务器解析这个标签 然后将这个标签里的添加到首部响应返回给客户端

缓存验证

If-None-Match

ETag 是资源的唯一标识

If-None-Match: "82e22293907ce725faf67773957acd12"

如果服务器接收到ETage后,判断资源没有发生改变,会返回一个304

弱验证器:

If-None-Match: W/"82e22293907ce725faf67773957acd12"

弱验证器当文件内容发生变化后,缓存也不会失效

If-Modified-Since

Last-Modified 首部字段也可以用于缓存验证,如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有实体主体的 304 Not Modified 响应报文

缓存验证-新鲜度检测

缓存过期

出现在响应报文,超过这个时间 缓存就被认为过期

Cache-Control: max-age=31536000

Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期

Expires: Wed, 04 Jul 2012 08:26:05 GMT

但Expires有如下问题:

  1. 受限于客户端的时间
  2. 无法缓存客户私有资源
  3. 无法描述“不缓存”,如js文件引用后面要加个版本号

试探性过期

对于没有提供max-age跟Expires的响应

通信数据转发

代理

目的:

代理流量获取

正向代理

用户可以察觉正向代理的存在

sequenceDiagram
  客户端 ->> 代理: 请求
  代理 ->> 服务端: 请求
  服务端 ->> 代理: 响应
  代理 ->> 客户端: 响应

反向代理

反向代理一般位于内部网络中,用户察觉不到

sequenceDiagram
  客户端 ->> 代理: 请求
  opt 内部网络
    代理 ->> 服务端: 请求
    服务端 ->> 代理: 响应
  end
  代理 ->> 客户端: 响应

客户端代理配置

有关代理的一些问题

GET http://baidu.com HTTP/1.0

报文追踪

Via: [ <protocol-name> "/" ] <protocol-version> <host> [ ":" <port> ]

经过多层代理 通过Via标识

每经过一层代理,代理都可以向该字段加入自己的标识 在多层代理的情况下,通过检查这个字段有没有自己,可以检测环路,该字段与Server的区别在于Server是源服务器信息,代理不应修改

TRACE流程

通过指定Max-Forwards头部 每经过一层代理该值就会减1 当为0时,及时当前服务器不是源服务器,也必须马上将结果返回给客户端

认证

兼容性

为了保证代理的兼容性,代理对于不认识的首部,必须原样转发,并且首部的顺序,也不能随意修改

网关

网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务

隧道

使用 SSL 等加密手段,在客户端和服务器之间建立一条安全的通信线路

sequenceDiagram
  客户端->>隧道网关: CONNECT请求 CONNECT baidu.com:888 HTTP/1.0
  隧道网关->>客户端: HTTP/1.0 407 Proxy authentication required
  客户端->>隧道网关: CONNECT带上凭证
  隧道网关->>baidu.com: 打开888端口连接
  baidu.com->>隧道网关: 连接已建立
  隧道网关->>客户端: 连接就绪 HTTP/1.0 Connection established
  客户端->>隧道网关: 非HTTP流量
  隧道网关->>baidu.com: 非HTTP流量
  baidu.com->>隧道网关: 非HTTP流量
  隧道网关->>客户端: 非HTTP流量

中继

sequenceDiagram
    客户端->>中继: 二进制流A
    中继->>目标服务器: 二进制流A
    目标服务器->>中继: 二进制流B
    中继->>客户端: 二进制流B

重定向原理

当服务端对客户端进行重定向时,会设置一个Location响应头,并将状态码设置为302

客户端(浏览器)接收到这样的响应之后,就会跳转到Location里面的网址

HTTPS

HTTP的问题

原理

  1. 客户端向服务端发送HTTPS请求
  2. 服务端收到HTTPS请求返回公钥证书
  3. 客户端收到服务端的公钥证书,验证是否有效(验证颁发机构、过期时间等等)
  4. 如果有效,生成一个随机数用公钥加密,然后发送给服务端
  5. 服务端使用私钥将该随机数解密,然后用该随机数作为密钥加密一串字符给客户端
  6. 如果客户端解密这串字符成功,这串字符将作为接下来客户端与服务端通信的密钥

这个过程的关键在于密钥传递使用了非对称加密,数据传输采用了对称加密

所以这就保证了对称加密的密钥不会通过网络直接传输,之所以数据传输采用了对称加密,主要是因为非对称加密性能很低

SSL记录

屏幕截图 2022-06-13 210046

握手

在初始阶段会进行协商选择对称加密算法来进行加密会话密钥,同时为了防止中间人篡改选择的对称加密算法,客户端在服务端完成会话密钥的校验后,还需要发送所有握手报文的一个MAC,服务端再回送一个MAC

挥手

基于SSL的加密不断单纯地发送一个FIN关闭底层TCP连接,这是因为可能会被中间人伪造一个FIN,从而破坏数据的完整性,而是需要通过SSL记录的类型字段来确定是否关闭

证书

通过使用 证书 来对通信方进行认证

数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构

服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起

证书信任链

2022812172548

TLS 证书都有签名部分,这个签名就是用签发者的私钥加密的, 客户端的 Trust store 里就有这个 CA 的公钥(在 CA 证书里),它用这个公钥去尝试解开签名,能成功的话,就说明这张叶子证书确实是这个 CA 签发的

完整性保护

SSL 提供报文摘要功能来进行完整性保护

CA将用户的个人身份跟公开密钥链接在一起 可以防抵赖

通过操作系统内置的证书 可以在不通过网络的情况下对证书进行认证

TLS1.3 只允许前向加密(PFS)的密钥交换算法,主要是为了防止黑客取得了服务端私钥,并且抓取了历史上的 TLS 密文,那么就可以用这个私钥和抓包文件,把这些 TLS 会话的对称密钥给还原出来,前向加密算法每次的私钥都不一样

HTTPS的缺点

nginx配置证书

 server {
     ....  
     ssl on;
     ssl_certificate fullchain.pem;
     ssl_certificate_key privkey.pem;
 }

HTTP/2.0

HTTP/1.x缺陷

二进制分帧

20203894642

只会有一个TCP连接,一个连接会有任意数量的双向数据流

一个数据流会有一个一个唯一的标识符,一个数据流可以承载一来一回双向信息

消息是请求消息或者响应消息

帧是最小的通信单位,不同数据流的帧可以交错发送,然后根据唯一标识符来重新组装

20203895038

服务端推送

HTTP/2.0 在客户端请求一个资源时,服务端会把相关的资源一起发送给客户端

20203895124

首部压缩

HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输

2020389543

不仅如此,HTTP/2.0 也使用 哈夫曼编码对首部字段进行压缩

HTTP3

HTTP/2 和 HTTP/3 协议栈

解决的问题:

  1. 连接迁移:当网络环境发生变化,如果还使用原来的 TCP 连接,则会导致连接失败,QUIC 的连接不受四元组的影响,使用一个 64 位的随机数作为 Connection ID来表示连接
  2. 零 RTT 建立连接
  3. 队头阻塞:UDP接收没有顺序,中间丢包也不会影响其他资源的处理
  4. 拥塞控制:相比TCP的拥塞控制,QUIC可以在客户端动态调整算法

零 RTT 建立连接

GET与POST

GET 用于获取资源,而 POST 用于传输实体主体。

参数

GET是通过URL携带参数的,而 POST 的参数存储在实体主体中

安全

GET语义来说是安全的,因为GET操作只是获取资源

而POST的语义是不安全的,因为POST是上传数据

幂等性

幂等方法不应该具有副作用,所有的安全方法也都是幂等的

在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的

可缓存

一般来说GET和HEAD是可缓存的,PUT和DELETE不可缓存,POST在大多数情况下不可缓存

跨域问题

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互,所以通常情况下一个源无法通过ajax与另外一个源进行交互

批注 2020-02-29 141744

解决方案

服务端将返回数据封装成js函数调用并返回,客户端js通过动态加载script标签加载服务器的js数据,加载完成后执行封装的js函数获取数据

所以jsonp这种请求方式与ajax有着本质的不同

response.setHeader("Access-Control-Allow-Origin","*");

前端所在的服务端调用被调服务端,将结果返回给前端

server {
    listen 80;
    server_name api.domain;
    location /api1 {
        proxy_pass http://outter_server;
    }
}

使可以通过一个统一入口访问各个项目

传输链路优化

前端会通过一些诸如精灵图、文件合并、请求合并的方式方法来降低HTTP请求数 又或者是HTTP1.1引入的Keep-Alive机制 使用一条连接来处理请求 但这就会导致请求被阻塞的问题

HTTP2的出现很好地解决了这个问题 它将数据拆分为小端 并且使用id标识 在客户端进行组装,这样多个请求阻塞整条连接的情况出现的概率就比较小了

另外一种优化方式是通过压缩 但一旦压缩 并且只使用一个连接 那如何判断内容以及传输完毕?使用分块编码 最后以一个长度值为0的分块来表示资源结束

快速UDP网络连接

QUIC的可靠传输能力并不是由底层协议提供的,而是完全由自己来实现

QUIC的另一个设计目标是面向移动设备的专门支持,使用了一个标识符来处理由于移动网络断开重新连接的客户端识别问题

DASH

基于HTTP的动态自适应流(英语:Dynamic Adaptive Streaming over HTTP,缩写DASH,也称MPEG-DASH)

将内容分解成一系列小型的基于HTTP的文件片段,每个片段包含很短长度的可播放内容,而内容总长度可能长达数小时。内容将被制成多种比特率的备选片段,以提供多种比特率的版本供选用。当内容被DASH客户端回放时,客户端将根据当前网络条件自动选择下载和播放哪一个备选方案

CDN

CDN加速意思就是在用户和我们的服务器之间加一个缓存机制,动态获取IP地址根据地理位置,让用户到最近的服务器访问

屏幕截图 2020-09-27 113806

原理

  1. 用户向浏览器提供要访问的域名;

  2. 路由解析:浏览器调用域名解析库对域名进行解析,由于CDN对域名解析过程进行了调整,所以解析函数库一般得到的是该域名对应的CNAME记录(CDN地址),为了得到实际IP地址,浏览器需要再次对获得的CNAME进行解析以得到实际的IP地址;在此过程中,使用的全局负载均衡DNS解析,如根据地理位置信息解析对应的IP地址,使得用户能就近访问;

  3. 此次解析得到CDN缓存服务器的IP地址,浏览器在得到实际的IP地址以后,向缓存服务器发出访问请求

  4. 缓存服务器根据浏览器提供的要访问的域名,通过Cache内部专用DNS解析得到此域名的实际IP地址,再由缓存服务器向此实际IP地址提交访问请求

  5. 缓存服务器从实际IP地址得得到内容以后,一方面在本地进行保存,以备以后使用,二方面把获取的数据返回给客户端,完成数据服务过程

  6. 客户端得到由缓存服务器返回的数据以后显示出来并完成整个浏览的数据请求过程

路由解析

sequenceDiagram 
  浏览器 ->> 本地DNS: 查询网站CNAME
  loop 递归查询 
    本地DNS ->> 域名的权威DNS: 查询网站CNAME 
  end 
  域名的权威DNS -->> 本地DNS: 查询到CNAME: cname.xx
  本地DNS -->> CNAME的权威DNS: 查询CNAME:cname.xx
  loop 递归查询 
    CNAME的权威DNS ->> CNAME的权威DNS: 经过递归查询和负载均衡,确定合适的CDN 
  end 
  CNAME的权威DNS -->> 本地DNS: 地址:xx.xx.xx.xx 
  本地DNS -->> 浏览器: 地址:xx.xx.xx.xx 
  浏览器 ->> CDN服务器: 请求 
  CDN服务器 ->> 源站服务器: 请求 
  源站服务器 -->> CDN服务器: 响应 
  CDN服务器 -->> 浏览器: 缓存并响应

内容分发

缓存节点中必须有用户想要请求的资源副本,那么这些节点资源时如何获取以及存储的?

对于资源的管理,一般分为主动失效与被动失效:

CDN 动态加速

通过动态的链路探测来寻找回源最好的一条路径

屏幕截图 2020-09-27 114519

CDN应用

CDN容灾

美团CDN容灾方案 美团CDN容灾流程

目标

端侧实现

Web端实现:

传统的标签资源如css img等通过监听失败回调实现

而js脚本则需要统一使用动态加载的方式

Native端实现:

域名动态计算:

域名A -> 请求失败 -> 域名B -> 请求失败 -> 域名C

PCDN

在CDN产品的基础上融合新一代P2P技术,充分利用边缘网络海量碎片化的计算、存储、网络等闲置资源

对于诸如热门视频之类的文件,开头部分从CDN上获取,后续的数据跟其他用户进行P2P互相传输

RESTful

一套关于设计请求的规范,本质上谈不上规范,更多的是一种风格

URI代表一种资源、客户端与服务器,传递资源的某种表现层、客户端通过HTTP动词,对服务器资源进行操作

GET: 获取数据 POST: 添加数据 PUT: 更新数据 DELETE: 删除数据

常见错误

范例

请求方式 URL 含义
GET: http://www.example.com/users 获取用户列表数据
POST: http://www.example.com/users 创建(添加)用户数据
GET: http://www.example.com/users/1 获取用户ID为1的用户信息
PUT: http://www.example.com/users/1 修改用户ID为1的用户信息
DELETE: http://www.example.com/users/1 删除用户ID为1的用户信息

RESTful的系统

  1. 服务端与客户端分离
  2. 无状态
  3. 可缓存
  4. 分层系统
  5. 统一接口
  6. 按需代码

REST风格的好处

  1. 降低服务接口的学习成本
  2. 资源之间有天然的集合或者层次结构

RMM成熟度

  1. The Swamp of Plain Old XML:完全不REST。另外,关于Plain Old XML这说法,SOA表示感觉有被冒犯到。
  2. Resources:开始引入资源的概念。
  3. HTTP Verbs:引入统一接口,映射到HTTP协议的方法上。
  4. Hypermedia Controls:超媒体控制在本文里面的说法是“超文本驱动”

不足