关键词搜索

源码搜索 ×
×

互联网协议 — HTTP/2 版本

发布2023-03-12浏览1732次

详情内容

目录

SPDY 协议

SPDY 协议是 Google 于 2009 年发布的基于 TCP 传输层协议的应用层协议,是一种更加快速的内容传输协议。

相较于 HTTP/1.1,SPDY 引入了多种新特性来提升传输效率。

  1. 数据帧机制
  2. 多路复用机制
  3. 头压缩机制
  4. 优先级排序机制
  5. 等等

SPDY 协议设定的目标:

  1. 无缝过渡 HTTP/1.1。
  2. 页面加载时间(PLT,Page-Load Time)降低 50%。

SPDY 协议作为 HTTPhttps://files.jxasp.com/image/2 的前身,在 2015 年 9 月,Google 宣布拥抱 HTTPhttps://files.jxasp.com/image/2,并将在 Chrome 51 中生效。

HTTPhttps://files.jxasp.com/image/2 协议

在这里插入图片描述

HTTPhttps://files.jxasp.com/image/2 是 SPDY 的升级版,HTTP-WG(HTTP Working Group)在 2012 年初把 HTTP 2.0 提到了议事日程,吸取 SPDY 的经验教训,并在此基础上制定官方标准。

HTTPhttps://files.jxasp.com/image/2 的标准化工作由 Chrome、Opera、Firefox、Internet Explorer 11、Safari、Amazon Silk 及 Edge 等浏览器提供支持。HTTPhttps://files.jxasp.com/image/2 标准于 2015 年 5 月以 RFC 7540 正式发表。

HTTPhttps://files.jxasp.com/image/2 的主要目标是改进 HTTP/1.1 的传输性能,更有效地利用网络资源,实现低延迟和高吞吐量。同时,HTTPhttps://files.jxasp.com/image/2 保留了 HTTP/1.1 的高层协议语义,支持 HTTP/1.1 生态中绝大部分的 UseCase,例如:桌面浏览器、移动浏览器、Web Server、代理服务器、反向代理服务器、防火墙和 CDN 等。

这一初衷,使得 Web Application 可以无缝使用 HTTPhttps://files.jxasp.com/image/2,而 Web Server 则必须升级到 HTTPhttps://files.jxasp.com/image/2 协议。例如 Nginx 只要在配置文件中启动 HTTPhttps://files.jxasp.com/image/2 即可,对于不支持 HTTPhttps://files.jxasp.com/image/2 的浏览器,Nginx 会自动向下兼容。

现今,多数主流浏览器已支持了该协议。根据 W3Techs 的数据,截至 2019 年 6 月,全球有 36.5% 的网站支持了 HTTPhttps://files.jxasp.com/image/2。

HTTPhttps://files.jxasp.com/image/2 对比 HTTP/1.1

在这里插入图片描述

HTTPhttps://files.jxasp.com/image/2 协议的基本概念

在这里插入图片描述

  • Connecttion(连接):指一个基于 TCP Connection 的 HTTPhttps://files.jxasp.com/image/2 Connection,一个 HTTPhttps://files.jxasp.com/image/2 Connection 可以包含若干条 Stream。

  • Stream(流):指一个 Virtual Tunnel,每条 Stream 都有一个唯一的整数标识符(1、2…N),每条 Stream 都可以进行双向的 Message 通信。

  • Message(消息):指逻辑上的 HTTPhttps://files.jxasp.com/image/2 Message,与 HTTP/1.1 Message 概念等同,包括:Request Message、Response Message。一个 HTTPhttps://files.jxasp.com/image/2 Message 由一或多个 Frames 组成。

  • Frame(帧):采用了二进制编码,而非文本格式,作为 HTTPhttps://files.jxasp.com/image/2 传输的最小单位。在一条 Stream 中,Client 与 Server 通过 Frame 来封装 Message,包括:控制面的 Headers Frame、数据面的 Data Frame。

HTTPhttps://files.jxasp.com/image/2 的帧(Frame)

HTTPhttps://files.jxasp.com/image/2 Frame 采用二进制编码,相较于 HTTP/1.1 采用的(明文)文本格式,二进制编码对于计算机显然有着更好的处理效率,因为可以省去一道文本解析工序。只是不便于人类阅读了,这从 Wireshark 抓包可以看出,阅读之前需要解码翻译。

下图对比了 HTTP/1.1 文本格式和 HTTPhttps://files.jxasp.com/image/2 Stream & Frame 格式。

在这里插入图片描述

  • HTTP/1 Message 通过 “换行符“ 和 “空格” 来作为 Message 各组成部分的边界。
  • HTTPhttps://files.jxasp.com/image/2 Message 通过 Stream 和 Frame 来进行解析。多个 Frames 之间可以乱序发送,C/S 之间根据 Headers Frame 来理解和解析或组装 Data Frame。

所以,对于开发 HTTPhttps://files.jxasp.com/image/2 Server 的 Frame 解析/构造功能而言,就类似于开发 L3 IP Packet、L4 TCP Segment 一般,根据协议标准来实现即可。区别于 HTTP/1.1 Server 需要通过解析文本中的 “换行符“ 和 “空格” 来理解内容。

可见,Frame 格式的设计,还回避了 TCP Byte steam 对于上层应用协议透明,而造成的开发困难。

在这里插入图片描述

Frame 的报文格式

在这里插入图片描述

HTTPhttps://files.jxasp.com/image/2 的流(Stream)

HTTP/1.1 协议虽然基于 TCP Continuous ARQ 实现了 Pipelining 机制,可以在一个 TCP Connection 中,并行传输多个 Response Segments,但常常会发生响应队头拥塞,继而浪费 Connection 的带宽资源。而 HTTPhttps://files.jxasp.com/image/2 实现了在同一个 TCP Connection 中存在多条 Stream,以此来缓解了由于队头拥塞导致的整个 Connection 的阻塞。

HTTPhttps://files.jxasp.com/image/2 Stream 本质是一个 Virtual Tunnel,具有一个 Stream ID,作为 Virtual Tunnel Endpoint ID,Frames 通过 Stream ID 来进行划分。

在这里插入图片描述
在这里插入图片描述

Stream 的多路复用(Multiplexing)

HTTPhttps://files.jxasp.com/image/2 Stream 是一种 TCP 多路复用实现,即:在一个 TCP Connection(由 IP:Port 标识)上同时传输多个 Stream(由 ID 标识)的过程。多条 Stream 共用同一个 Connection 阻塞对象(IP:Port),使得 Connection 的带宽可以被充分利用。

如下图所示,基于 HTTPhttps://files.jxasp.com/image/2 的 Stream & Frame 设计,可以在一个 TCP Connection 中,并发传输多个 Requests 和 Responses。

在这里插入图片描述

如下图所示,由于缩小了 TCP 响应队头阻塞的作用域(Stream 粒度),这样即使一个 Stream 的响应 TCP 序列被阻塞了,也不会影响其他的 Stream,带宽得以被充分利用。

在这里插入图片描述

Stream 的优先级

此外,Stream 还具有 “优先级“ 特性,每个 Stream 都可以带有一个 31bit 的优先值:

  • 0:表示最高优先级;
  • 2-30:表示低优先级。

Server 可以根据 Stream 的优先级,去控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将最高优先级的 Frames 返回给 Client。

HTTPhttps://files.jxasp.com/image/2 的头压缩

HTTP1.x 的每次通信都会携带一组 Headers,用于描述这次通信的的资源、浏览器属性、Cookie 等,而且每次都要重复发送。

在这里插入图片描述

HTTPhttps://files.jxasp.com/image/2 使用 Encoder 来减少需要传输的 Header 大小,通讯双方各自 Cache 一份 Header fields 表,既避免了重复 Header 的传输,又减小了需要传输的大小。

在这里插入图片描述

HTTPhttps://files.jxasp.com/image/2 实现了 HPACK 算法用于对 HTTP 头部做压缩。其原理在于:

  • C/S 都维护一份共同的静态字典(Static Table),其中包含了常见头部名及常见头部名称与值的组合的代码;
  • C/S 根据先入先出的原则,维护一份可动态添加内容的共同动态字典(Dynamic Table);
  • C/S 支持基于该静态哈夫曼码表的哈夫曼编码(Huffman Coding)。

在 HTTP 头里,有些 key:value 是固定,例如:

 :method: GET
 :scheme: http

    在编码时,它们直接用一个 index 编号代替,例如 :method:GET 的 index 是 2,这些在一个静态表中定义。静态表总共有 61 个 Header Name:

    在这里插入图片描述

    可以在 https://tools.ietf.org/html/rfc7541#appendix-A 查看所有的静态表定义。

    使用静态表、动态表、以及 Huffman 编码可以极大地提升压缩效果。对于静态表里的字段,原来需要 N 个字符表示的,现在只需要一个索引即可,对于静态、动态表中不存在的内容,还可以使用哈夫曼编码来减小体积。HTTPhttps://files.jxasp.com/image/2 标准里也给出了一份详细的静态哈夫曼码表(https://tools.ietf.org/html/rfc7541#appendix-B),它们需要内置在 Client 和 Server 之中。

    例如:下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。

    在这里插入图片描述

    我们来看一个实际的例子,下面是用 WireShark 抓取的访问 Google 首页的包:

    在这里插入图片描述
    上图是是访问 https://www.google.com/ 抓到的第一个请求的头部,可以看到头部的内容,总共占用了 437bytes,我们选中头部的 Cookie,可以看到 Cookie 总共占用了 118bytes。接下来我们看看第二个请求的头部:

    在这里插入图片描述

    从上图可以看到,得益于头部压缩,第二个请求 Cookie 只占用了 1 个字节,我们来看看变化了的 Accept 字段:由于 Accept 字段与请求一中的内容不同,需要发送给服务器,所以占用了 29bytes。

    在这里插入图片描述

    HTTPhttps://files.jxasp.com/image/2 的服务端推送

    网站为了使请求数减少,通常采用对页面上的图片、脚本进行极简化处理。但是,这一举措十分不方便,也不高效,依然需要诸多 HTTP 链接来加载页面和页面资源。

    在 HTTP 1.1 里,在同一个 TCP 连接里面,上一个 RESP 发送完了,服务器才能发送下一个,但在 HTTPhttps://files.jxasp.com/image/2 里,可以将多个回应一起发送。

    在这里插入图片描述

    HTTPhttps://files.jxasp.com/image/2 引入了服务器推送(Server PUSH),当请求一个 HTML 时,如果 HTML 里有 CSS 文件,Server 会一并推给 Client,而不像在 HTTP 1.1,还需要再发一个 CSS 的请求。即服务端向客户端发送比客户端请求更多的数据。这允许服务器直接提供浏览器渲染页面所需资源,而无须浏览器在收到、解析页面后再提起一轮请求,节约了加载时间。

    • 服务器可以对一个客户端请求发送多个响应。服务器向客户端推送资源无需客户端明确地请求。
    • HTTP 2.0 连接后,客户端与服务器交换 SETTINGS 帧,借此可以限定双向并发的流的最大数量。
    • 所有推送的资源都遵守同源策略。换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。
    • 服务器必须遵循请求-响应的循环,只能借着对请求的响应推送资源。

    服务端推送能把客户端所需要的资源伴随着 index.html 一起发送到客户端,省去了客户端重复请求的步骤。正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。

    在这里插入图片描述

    在这里插入图片描述

    从理论上 PUSH 模式下性能会好很多。

    • 普通的客户端请求过程
      在这里插入图片描述
    • 服务端推送的过程
      在这里插入图片描述

    如果开启了 Server Push 模式,我们很容易意识到一个问题,那就是缓存问题。Server 见到 HTML 页面就把外部资源 Push 给 Client,如果没有缓存,其实很浪费。为了解决这个问题,可以在第一次请求时 Push,后面的请求都不 Push 了。

    服务器推送有一个很麻烦的问题。所要推送的资源文件,如果浏览器已经有缓存,推送就是浪费带宽。即使推送的文件版本更新,浏览器也会优先使用本地缓存。下面是 Nginx 官方给出的示例,根据 Cookie 判断是否为第一次访问(https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/)。

    server {
        listen 443 ssl http2 default_server;
    
        ssl_certificate ssl/certificate.pem;
        ssl_certificate_key ssl/key.pem;
    
        root /var/www/html;
        http2_push_preload on;
    
        location = /demo.html {
            add_header Set-Cookie "session=1";
            add_header Link $resources;
        }
    }
    
    map $http_cookie $resources {
        "~*session=1" "";
        default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";
    
      3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    HTTPhttps://files.jxasp.com/image/2 的 ALPN 应用层协议协商

    HTTPhttps://files.jxasp.com/image/2 协议里有个 negotiation(协商)的机制,让客户端和服务器选择使用 HTTP 1.1 还是 2.0,这个是由 ALPN 来实现。

    ALPN(Application-Layer Protocol Negotiation,应用层协议协商)使得客户端能够从 HTTP/1.0、HTTP/1.1、HTTPhttps://files.jxasp.com/image/2 乃至其他非 HTTP 协议中做出选择。

    ALPN 是一个 TLS 的扩展,ALPN 使得应用层可以协商在安全连接层之上使用什么协议,避免了额外的往返通讯,并且独立于应用层协议。 ALPN 用于 HTTPhttps://files.jxasp.com/image/2 连接,和 HTTP/1.x 相比,ALPN 的使用增强了网页的压缩率减少了网络延时。ALPN 和 HTTPhttps://files.jxasp.com/image/2 协议是伴随着 Google 开发 SPDY 协议出现的。

    下面是抓包截图,在 TLS 里的 Client Hello 的包里,我们可以看到 ALPN 里有 H2 和 HTTP/1.1,这就是说客户端支持 HTTPhttps://files.jxasp.com/image/2 以及 HTTP 1.1。

    在这里插入图片描述

    当 Server 收到后,会识别 Client 发过来的协议列表,如果不认识就忽略掉。如果认识多个,则选择一个最合适的协议发布给 Client。也是在 Server Hello 里的 ALPN 返回,见下图。

    在这里插入图片描述

    在使用 curl 指令时可以使用 --http2-prior-knowledge 选型来标识不进行协商:

    curl --http2-prior-knowledge -v -i “XXX”
    
    • 1

    使用 Wireshark 抓取 HTTPhttps://files.jxasp.com/image/2 报文

    Wireshark 启用 HTTPhttps://files.jxasp.com/image/2 协议

    菜单:分析 => 启用的协议 => 选中需要启动的协议。

    在这里插入图片描述

    使用 Wireshark 调试 HTTPhttps://files.jxasp.com/image/2 over TCP 流量

    HTTPhttps://files.jxasp.com/image/2 引入了二进制分帧层(Binary Framing),将每个请求和响应分割成为更小的帧,并对它们进行了二进制编码。那么要如何调试 HTTPhttps://files.jxasp.com/image/2 流量呢?HTTPhttps://files.jxasp.com/image/2 中的 Server Push、Prioritization 等功能,要如何才能看到并分析呢?所幸,新版本的 Wireshark 是支持解析 HTTPhttps://files.jxasp.com/image/2 数据报文的,可以自动解压头部数据。

    如果 HTTPhttps://files.jxasp.com/image/2 Server 没有启用 TLS,那么就可以通过 Wireshark 直接将 TCP decode as HTTPhttps://files.jxasp.com/image/2 了。

    在这里插入图片描述

    使用 Wireshark 调试 HTTPhttps://files.jxasp.com/image/2 or HTTP/1.1 over TLS 流量

    现在市面上的主流浏览器实现的 HTTP2 都是基于 TLS 的,也就是说要分析 HTTP2 报文得先过了 TLS 这一关。

    Wireshark 的抓包原理是直接读取并分析网卡数据,要想让它解密 HTTPS 流量,有两个办法:

    1. 如果你拥有 HTTPS 网站的加密私钥,可以用来解密这个网站的加密流量;
    2. 某些浏览器支持将 TLS 会话中使用的对称密钥保存在外部文件中,可供 Wireshark 加密使用。

    使用浏览器自动生成的 SSLKEYLOG

    以 OSX 为例:

    mkdir ~/tls
    touch ~/tls/sslkeylog.log
    touch ~/tls/ssl.log
    
    # zsh
    echo "\nexport SSLKEYLOGFILE=~/tls/sslkeylog.log" >> ~/.zshrc && source ~/.zshrc
    
      3
    • 4
    • 5
    • 6

    配置了 SSLKEYLOGFILE 环境变量之后,可以指定 Firefox 和 Chrome 浏览器在访问 SSL/TLS 网站时将对应的密钥保存到本地文件中。

    接着,在 Wireshark 的 TLS 配置面板的 “(Pre)-Master-Secret log filename” 选项中这个文件选上。“SSL debug file” 也建议配上,这样解密过程中的日志都会记录下来,如下图:

    在这里插入图片描述

    最后,重启 Chrome 和 Wireshark 并访问 HTTPhttps://files.jxasp.com/image/2 网站 https://http2.akamai.com/demo

    export SSLKEYLOGFILE=/Users/mickeyfan/tls/sslkeylog.log && open /Applications/Google\ Chrome.app && open /Applications/Wireshark.app
    tail -f /Users/mickeyfan/tls/sslkeylog.log
    

      :打开 Chrome 扩展程序中的 “开发者模式”。
      在这里插入图片描述

      新版 Wireshark 在配置了 TLS 加密后,会自动识别并解析 HTTPhttps://files.jxasp.com/image/2 流量。访问想要抓包的 HTTPhttps://files.jxasp.com/image/2 网站,根据 IP 和协议过滤一下,就可以轻松看到想要的 HTTPhttps://files.jxasp.com/image/2 数据包了。这种方法也可以用在解密使用 HTTP/1 的 HTTPS 网站上。

      在这里插入图片描述

      使用 cURL 手动生成的 SSLKEYLOG

      这种解密方式不需要私钥文件,所以适用于客户端对 HTTPS 进行解密。

      • 安装抓包工具
      yum install -y tcpdump curl
      
      • 1
      • 抓包
      tcpdump -i eth0 -ne -w https.pcap
      
      • 1
      • 访问 HTTPS 服务端
      SSLKEYLOGFILE=ssl_log.txt curl --ciphers dhe_rsa_aes_128_cbc_sha_256 --insecure https://www.csdn.net/
      
      • 1

      注意:使用 --ciphers 选项强制 curl 作为客户端使用 RSA 密钥交换技术进行密钥交换。

      如果报错:

      curl: (35) Cannot communicate securely with peer: no common encryption algorithm(s).
      
      • 1

      说明 HTTPS 服务端不支持 dhe_rsa_aes_128_cbc_sha_256 加密算法,从 Server Hello 可以看出服务端采用的是 ECDHE_RSA_AES_128_GCM_SHA256。
      在这里插入图片描述

      所以我们可以不知道加密算法,让双方协商:

      SSLKEYLOGFILE=ssl_log.txt curl --insecure https://www.csdn.net/
      
      • 1

      如此的,我们得到了两个文件:

      • .pcap:抓包文件
      • ssl_log.txt:包含在密钥交换期间生成的临时密钥 CLIENT_RANDOM。e.g.
      # SSL/TLS secrets log file, generated by NSS
      CLIENT_RANDOM a101a3296bb043a3f8a5cb583426fc2b079c14f41fb8f09621efc256f7b3a0b5 949c519906a38fb0f76468649aedf85f2c11fe31393ff7bfaf74a31d170d9cd5ebe6ff6dc7d3f86bdf6c070741270b13
      CLIENT_RANDOM 2e99d3681c39a5b2de92524aa2914991d6d7e1a91131680e2323c7c67befd80a 45be6964023d6ead919a46de449684d8e544df692b2f5f5c7757fa4435d1ecef3b04cc7a55af7edeaf27cf43d64324db
      
        3

      接下来导入 .pcap 到 Wireshark,配置 TLS 协议的 (Pre)-Master-Secretlog filename 加载路径。

      在这里插入图片描述

      可以看见被 TLS 封装的 HTTP 报文就暴露出来了:

      在这里插入图片描述
      查看 TLS 流:
      在这里插入图片描述

      就可以得到 HTTPS 的解密数据了:
      在这里插入图片描述

      :如果你在 CentOS7 上使用 Wireshark 来对加密算法 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 进行解密,那么你需要编译安装最新版本。

      使用 HTTPS Server 的 BEGIN RSA PRIVATE KEY

      这种方式需要 HTTPS 服务端(Web 服务器)的私钥,所以适用于在服务端对 HTTPS 进行解密。

      :这种方式不适用于 TLS1 1.3,因为 1.3 版本使用了 ephemeral Diffie-Hellman 秘钥交换技术。

      接下来的步骤与上述大同小异,区别在于导入到 Wireshark 的是服务端的私钥文件。

      在这里插入图片描述
      To decrypt the traffic go to Edit -> Preferences, find SSL under Protocols and add a new RSA key. The key should be the private key from the web server, the protocol should be http, the port should be 443 and the IP address should match the IP address of the web server in the packet capture:

      在这里插入图片描述

      If everything works as expected, after clicking Apply the HTTP requests should now be decrypted and visible in Wireshark:

      在这里插入图片描述

      相关技术文章

      点击QQ咨询
      开通会员
      返回顶部
      ×
      微信扫码支付
      微信扫码支付
      确定支付下载
      请使用微信描二维码支付
      ×

      提示信息

      ×

      选择支付方式

      • 微信支付
      • 支付宝付款
      确定支付下载