# 关于 Http

关于 tcp握手/挥手 相关内容在 从输入URL到页面加载过程

参考

# 从 http1.0 到 http1.1

在浏览器中,越来越多的使用更多的图片和更多的懒加载组件,然后会导致页面加载时会加载很多的几kb文件,

因此 http 经过了好几代的更新。

从古老的 http1.0 开始,它存在的以下的问题

  1. 在http1.0中,发送一次请求时,需要等待服务端响应了才可以继续发送请求。
  2. 连接方式是短链接

为了解决上述 http1.0 的问题,出现了 http 1.1,它主要做了以下改动

  1. 持久连接:出现了默认长连接方式 keep-alive
  2. 请求管道化:发送一次请求时,不需要等待服务端响应了就可以发送请求了。
    • 理想情况中 HTTP 请求管道化(流水线)启动时,后续请求都可以不用等待第一个请求的成功响应就被发送。
    • 然而HTTP管道化,很难在现有网络中有很多老旧的软件与现代版本的软件共存
  3. 增加缓存处理:新的字段如 cache-control
  4. 增加Host字段、支持断点传输等

keep-alive:在规定时间内,同一域名多次请求数据,只建立一次HTTP请求,后续请求可复用之前的连接通道。该规定时间默认为2小时,可以通过 Apache 或者 nginx 配置。

但是,http1.1的问题依然存在

  1. 在 http1.1中,基于请求-响应的模型,在同一个 TCP 长连接中,前面的请求没有得到响应,后面的请求就会被阻塞,服务器回送数据给客户端的时候,客户端需要按照响应的顺序来一一接收。也就是线头阻塞
  2. 在 http1.1 中无论是请求还是响应都是基于文本的,在数据传输的过程中服务器并不知道字符的顺序。所以:keep-alive 对于单文件的传输是block级的,数据必须按顺序传输,不能并行。

# 从 http1.1 到 http2

2015年 出现了 http2, 它在 http1.1 语法和底层传输协议之间增加了一个新的中间层,而没有从根本上修改它,即它是建立在经过验证的机制之上,这个中间层就是帧机制。它引入 二进制数据帧 的概念,利用帧对数据进行顺序标识,利用流进行数据并行传输

主要改进点

  1. 头部压缩 -> 减少体积
    • 采用HPACK压缩:利用服务器和客户端之间建立哈希表的映射,传递索引来精简和复用请求头部
  2. 二进制 + 分帧传输 -> 减少体积 + 提高安全性
    • 由于 HTTP 的明文传输解析太过复杂(比如 \n 到底是换行还是字符串?),而且并不安全。所以,采用二进制进行传输
    • 将数据以流的形式进行传输,并将请求和响应数据分割成更小的帧,而多个帧之间可以乱序发送,根据帧首部的流标识来重新组装。
  3. 多路复用 -> 解决队头阻塞
    • 只需要占用一个 TCP 连接
  4. 服务器推送

其他改动点

  1. http2.0 只用于 https

# 关于 HTTPS

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 超文本传输安全协议)

HTTPS在传统的HTTP和TCP之间加了一层用于加密解密的SSL/TLS层(安全套接层Secure Sockets Layer/安全传输层Transport Layer Security)层。

PS: 当SSL发展到第三大版本时才被标准版,成为 TLS。即:TLS1.0 = SSL3.1

HTTPS解决的问题

  • 信息加密传输:第三方无法窃听;
  • 校验机制:一旦被篡改,通信双方会立刻发现;
  • 身份证书:防止身份被冒充。

HTTPS在传输数据阶段依然使用对称加密,但是对称加密的密钥我们采用非对称加密来传输,使用HTTPS必须要有一套自己的数字证书(包含公钥和私钥)。

HTTPS加密过程:

  1. 客户端请求服务器获取证书公钥(第三版返回的是数字证书,之前是直接返回的公钥)
  2. 客户端(SSL/TLS)解析证书(无效会弹出警告)
  3. 生成随机值
  4. 公钥加密随机值生成密钥
  5. 客户端将秘钥发送给服务器
  6. 服务端用私钥解密秘钥得到随机值
  7. 将信息和随机值混合在一起进行对称加密
  8. 将加密的内容发送给客户端
  9. 客户端用秘钥解密信息

HTTPS

加密过程使用了对称加密和非对称加密。

  • 对称加密: 客户端和服务端采用相同的密钥经行加密
        encrypt(明文,秘钥) = 密文
    
        decrypt(密文,秘钥) = 明文
    
  • 非对称加密:客户端通过公钥加密。服务端通过私钥解密
        encrypt(明文,公钥) = 密文
    
        decrypt(密文,私钥) = 明文
    

因为TLS握手的过程中采用了非对称加密,客户端本身不知道服务器的秘钥,这样通信就不会被中间人劫持。此外这一步服务端还提供了证书,并且可能要求客户端提供证书。关于证书下文会提到,只要有了证书,就能保证和你通信的对方是真实的,而不是别人伪造的。

那然后如何验证证书?

  1. 客户端获取到了站点证书,拿到了站点的公钥
  2. 客户端找到其站点证书颁发者的信息
  3. 站点证书的颁发者验证服务端站点是否可信
  4. 往上回溯,找到根证书颁发者
  5. 通过根证书颁发者一步步验证站点证书颁布者是否可信

附:

  • HTTPS默认使用443端口,而HTTP默认使用80端口。
  • TLS就是从SSL发展而来的,只是SSL发展到3.0版本后改成了TLS
  • 第一次请求中TLS握手的代价很大
  • 后续的请求会共用第一次请求的协商结果

参考:

# http 和 ajax 的区别

ajax:基于 xmlHttpRequest

http:基于 httpRequest

ps:ajax 请求头会多一个 x-requested-with 参数,值为 XMLHttpRequest

# QUIC

Google 研发了一种以 UDP 为基础,能提供更可靠更高效的传输协议 QUIC,目前为止浏览器支持很有限。

可能成为 http3.0

# TCP慢启动

最初的TCP的实现方式是,在连接建立成功后便会向网络中发送大尺寸的数据包,假如网络出现问题,很多这样的大包会积攒在路由器上,很容易导致网络中路由器缓存空间耗尽,从而发生拥塞。

因此现在的TCP协议规定了,新建立的连接不能够一开始就发送大尺寸的数据包,而只能从一个小尺寸的包开始发送,在发送和数据被对方确认的过程中去计算对方的接收速度,来逐步增加每次发送的数据量(最后到达一个稳定的值,进入高速传输阶段。相应的,慢启动过程中,TCP通道处在低速传输阶段),以避免上述现象的发生。这个策略就是慢启动。

# 关于cookie

首先 http1.x 是无状态的,既然是无状态的那么就存在会话状态问题,比如:登录信息在客户端的留存。因此就出现了 cookie。

cookie 的属性:

  • Expires: 过期时间(缺省则浏览器关闭时清除)
  • Max-Age:失效前需要建立的毫秒数(优先级大于Expires)
  • Domain:指定的域名(这个域名不可跨域指定)
  • Path:指定path路径
  • Secure:只能通过 https 传递
  • HttpOnly:只能通过在http响应头中添加Set-Cookie时才能设置cookie,通过document.cookie设置无效
  • SameSite:在跨站请求时是否发送cookie
    • None:无论是否跨站都会发送 cookie
    • Lax:允许导航到目标网址的get请求发送cookie
    • Strict:最严格,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

ps:由于2020年2月chrome-v80更改了SameSite 属性,默认屏蔽了第三方cookie(从 None 改为 Lax),用以预防CSRF 攻击,因此也导致很多大厂的跨域交互出现了问题,这时,网站可以选择显式关闭SameSite属性,将其设为None。前提是必须同时设置Secure属性,否则无效。