在过去的几个月里,我写了很多有关 HTTP/2 的文章,也做过好几场相关分享。我在向大家介绍 HTTP/2 的过程中,有一些问题经常会被问到。例如要部署 HTTP/2 一定要先升级到 HTTPS 么?升级到 HTTP/2 之后,不支持 HTTP/2 的浏览器还能正常访问么?本文重点介绍 HTTP/2 的协商机制,明白了服务端和客户端如何协商出最终使用的 HTTP 协议版本,这两个问题就迎刃而解了。

HTTP Upgrade

为了更方便地部署新协议,HTTP/1.1 引入了 Upgrade 机制,它使得客户端和服务端之间可以借助已有的 HTTP 语法升级到其它协议。这个机制在 RFC7230 的「6.7 Upgrade」这一节中有详细描述。

要发起 HTTP/1.1 协议升级,客户端必须在请求头部中指定这两个字段:

Connection: Upgrade
Upgrade: protocol-name[/protocol-version]

客户端通过 Upgrade 头部字段列出所希望升级到的协议和版本,多个协议之间用英文逗号和空格(0x2C, 0x20)隔开。除了这两个字段之外,一般每种新协议还会要求客户端发送额外的新字段,这里略过不写。

如果服务端不同意升级或者不支持 Upgrade 所列出的协议,直接忽略即可(当成 HTTP/1.1 请求,以 HTTP/1.1 响应);如果服务端同意升级,那么需要这样响应:

HTTPHTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: protocol-name[/protocol-version] [... data defined by new protocol ...]

可以看到,HTTP Upgrade 响应的状态码是 101,并且响应正文可以使用新协议定义的数据格式。

如果大家之前使用过 WebSocket,应该已经对 HTTP Upgrade 机制有所了解。下面是建立 WebSocket 连接的 HTTP 请求:

HTTPGET ws://example.com/ HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Origin: http://example.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: d4egt7snxxxxxx2WcaMQlA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

这是服务端同意升级的 HTTP 响应:

HTTPHTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: gczJQPmQ4Ixxxxxx6pZO8U7UbZs=

在这之后,客户端和服务端之间就可以使用 WebSocket 协议进行双向数据通讯,跟 HTTP/1.1 没关系了。可以看到,WebSocket 连接的建立就是典型的 HTTP Upgrade 机制。

显然,这个机制也可以用做 HTTP/1.1 到 HTTP/2 的协议升级。例如:

HTTPGET / HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>

在 HTTP Upgrade 机制中,HTTP/2 的协议名称是 h2c,代表 HTTP/2 ClearText。如果服务端不支持 HTTP/2,它会忽略 Upgrade 字段,直接返回 HTTP/1.1 响应,例如:

HTTPHTTP/1.1 200 OK
Content-Length: 243
Content-Type: text/html ...

如果服务端支持 HTTP/2,那就可以回应 101 状态码及对应头部,并且在响应正文中可以直接使用 HTTP/2 二进制帧:

HTTPHTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c [ HTTP/2 connection ... ]

以下是通过 HTTP Upgrade 机制将 HTTP/1.1 升级到 HTTP/2 的 Wireshark 抓包(两张图可以对照来看):

根据 HTTP/2 协议中的描述,额外补充几点:

  • 41 号包中,客户端发起的协议升级请求中,必须通过 HTTP2-Settings 指定一个经过 Base64 编码过的 HTTP/2 SETTINGS 帧;
  • 45 号包中,服务端同意协议升级,响应正文中必须包含 HTTP/2 SETTING 帧(二进制格式,不需要 Base64 编码);
  • 62 号包中,客户端可以开始发送各种 HTTP/2 帧,但第一个帧必须是 Magic 帧(内容固定为 PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n),做为协议升级的最终确认;

HTTP Upgrade 机制本身没什么问题,但很容易受网络中间环节影响。例如不能正确处理 Upgrade 头部的代理节点,很可能造成最终升级失败。之前我们统计过 WebSocket 的连通情况,发现大量明明支持 WebSocket 的浏览器却无法升级,只能使用降级方案。

ALPN 扩展

HTTP/2 协议本身并没有要求它必须基于 HTTPS(TLS)部署,但是出于以下三个原因,实际使用中,HTTP/2 和 HTTPS 几乎都是捆绑在一起:

  • HTTP 数据明文传输,数据很容易被中间节点窥视或篡改,HTTPS 可以保证数据传输的保密性、完整性和不被冒充;
  • 正因为 HTTPS 传输的数据对中间节点保密,所以它具有更好的连通性。基于 HTTPS 部署的新协议具有更高的连接成功率;
  • 当前主流浏览器,都只支持基于 HTTPS 部署的 HTTP/2;

如果前面两个原因还不足以说服你,最后这个绝对有说服力,除非你的 HTTP/2 服务只打算给自己客户端用。

下面介绍在 HTTPS 中,浏览器和服务端之间怎样协商是否使用 HTTP/2。

基于 HTTPS 的协议协商非常简单,多了 TLS 之后,双方必须等到成功建立 TLS 连接之后才能发送应用数据。而要建立 TLS 连接,本来就要进行 CipherSuite 等参数的协商。引入 HTTP/2 之后,需要做的只是在原本的协商机制中把对 HTTP 协议的协商加进去。

Google 在 SPDY 协议中开发了一个名为 NPN(Next Protocol Negotiation,下一代协议协商)的 TLS 扩展。随着 SPDY 被 HTTP/2 取代,NPN 也被官方修订为 ALPN(Application Layer Protocol Negotiation,应用层协议协商)。二者的目标和实现原理基本一致,这里只介绍后者。如图:

可以看到,客户端在建立 TLS 连接的 Client Hello 握手中,通过 ALPN 扩展列出了自己支持的各种应用层协议。其中,HTTP/2 协议名称是 h2

如果服务端支持 HTTP/2,在 Server Hello 中指定 ALPN 的结果为 h2 就可以了;如果服务端不支持 HTTP/2,从客户端的 ALPN 列表中选一个自己支持的即可。

并不是所有 HTTP/2 客户端都支持 ALPN,理论上建立 TLS 连接后,依然可以再通过 HTTP Upgrade 进行协议升级,只是这样会额外引入一次往返。

小结

看到这里,相信你一定可以很好地回答本文开头提出的问题。

HTTP/2 需要基于 HTTPS 部署是当前主流浏览器的要求。如果你的 HTTP/2 服务要支持浏览器访问,那就必须基于 HTTPS 部署;如果只给自己客户端用,可以不部署 HTTPS(这个页面列举了很多支持 h2c 的 HTTP/2 服务端、客户端实现)。

支持 HTTP/2 的 Web Server 基本都支持 HTTP/1.1。这样,即使浏览器不支持 HTTP/2,双方也可以协商出可用的 HTTP 版本,没有兼容性问题。如下表:

浏览器 服务器 协商结果
不支持 HTTP/2 不支持 HTTP/2 不协商,使用 HTTP/1.1
不支持 HTTP/2 支持 HTTP/2 不协商,使用 HTTP/1.1
支持 HTTP/2 不支持 HTTP/2 协商,使用 HTTP/1.1
支持 HTTP/2 支持 HTTP/2 协商,使用 HTTP/2

当然,本文讨论的是通用情况。对于自己实现的客户端和服务端,如果打算使用 HTTP/2 ClearText,由于 HTTP Upgrade 协商会增加一次往返,可以要求双方必须支持 HTTP/2,直接发送 HTTP/2 数据,不走协商。

本文链接:https://imququ.com/post/protocol-negotiation-in-http2.html参与评论 »

--EOF--

发表于 2016-04-14 00:14:19 ,并被添加「 HTTPS 、 HTTP/2 」标签 。查看本文 Markdown 版本 »

本站使用「署名 4.0 国际」创作共享协议,相关说明 »

提醒:本文最后更新于 1871 天前,文中所描述的信息可能已发生改变,请谨慎使用。

谈谈 HTTP/2 的协议协商机制的更多相关文章

  1. SSL协议运行机制

    SSL/TLS协议运行机制 一.作用 不使用SSL/TLS的HTTP通信,就是不加密的通信.所有信息明文传播,带来了三大风险. (1) 窃听风险(eavesdropping):第三方可以获知通信内容. ...

  2. SSL/TLS协议运行机制的概述_转

    转自:SSL/TLS协议运行机制的概述 作者: 阮一峰 日期: 2014年2月 5日 互联网的通信安全,建立在SSL/TLS协议之上. 本文简要介绍SSL/TLS协议的运行机制.文章的重点是设计思想和 ...

  3. SSL/TLS 协议运行机制概述(二)

    SSL/TLS 协议运行机制概述(二) 在SSL/TLS 协议运行机制概述(一)中介绍了TLS 1.2 的运行机制,现在我们来看年 TLS 1.3 的运行机制.会涉及到SSL/TLS 协议运行机制概述 ...

  4. SSL/TLS 协议运行机制概述(一)

    SSL/TLS 协议运行机制概述(一) SSL/TLS 发展史 1994年,NetScape 设计了SSL协议(Secure Sockets Layer) 1.0,未正式发布 1995年,NetSca ...

  5. iOS 利用Socket UDP协议广播机制的实现

    1.前言 什么是UDP协议广播机制? 举一个例. 比如在一群人群中,一个人要找张三,于是你向人群里大喊一声(广播):"谁是张三" 假设它是张三,它就会回应你.在网络中也是一样的. ...

  6. iOS- 移动端Socket UDP协议广播机制的实现

    1.前言    什么是UDP协议广播机制?      举一个例, 例如在一群人群中,一个人要找张三,于是你向人群里大喊一声(广播):“谁是张三”   如果它是张三,它就会回应你,在网络中也是一样的. ...

  7. IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包

    IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 文章目录 IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 1. IKEv2 协商总体框架 2. 第二包流程图 3. ...

  8. SSL/TLS协议运行机制的概述

    互联网的通信安全,建立在SSL/TLS协议之上. 本文简要介绍SSL/TLS协议的运行机制.文章的重点是设计思想和运行过程,不涉及具体的实现细节.如果想了解这方面的内容,请参阅RFC文档. 一.作用 ...

  9. SSL/TLS协议运行机制

      转载自http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html 互联网的通信安全,建立在SSL/TLS协议之上. 本文简要介绍SSL/TLS协议的运行 ...

  10. 【转】SSL/TLS协议运行机制的概述

    互联网的通信安全,建立在SSL/TLS协议之上. 本文简要介绍SSL/TLS协议的运行机制.文章的重点是设计思想和运行过程,不涉及具体的实现细节.如果想了解这方面的内容,请参阅RFC文档. 一.作用 ...

随机推荐

  1. 忽略某个已经托管给git的文件,防止二次提交

    # 执行命令将文件加入不提交队列 git update-index --assume-unchanged 你的文件路径 # 执行命令将文件取消加入不提交队列 git update-index --no ...

  2. Solon 3.0 新特性:HttpUtils 了解一下

    Solon 3.0 引入一个叫 HttpUtils 小插件,这是一个简单的同步 HTTP 客户端,基于 URLConnection 适配(也支持切换为 OkHttp 适配).使得编写 HTTP 客户端 ...

  3. RocketMQ 全链路灰度探索与实践

    本文作者:肖京,Spring Cloud Alibaba PMC,阿里云智能技术专家. 01 全链路灰度背景介绍 发布新版本时,为了有效.谨慎地验证新版本代码逻辑的正确性,通常会采用灰度发布,从而达到 ...

  4. [快速阅读八] Matlab中bwlookup的实现及其在计算二值图像的欧拉数、面积及其他morph变形中的应用。

    以前看过matlab的bwlookup函数,但是总感觉有点神秘,一直没有去仔细分析,最近在分析计算二值图像的欧拉数时,发现自己写的代码和matlab的总是对不少,于是又去翻了下matlab的源代码,看 ...

  5. att&ck学习笔记1

    一.环境搭建 1.1环境搭建测试 最近想要开始学习内网渗透,搜集了一些教程,准备先实验一个vulnstack靶机,熟悉一下内网渗透操作再学习基础知识. 靶场下载地址:http://vulnstack. ...

  6. Python311新特性-特化指令specializing adaptive interpreter-typing-asyncio

    Python3新特性 python3.11增加了许多特性,让python更快更加安全,本文从应用层面来讲一下python3.11的这些新特性 特化自适应解析器是什么,如何利用特化写出更高性能的代码 如 ...

  7. Java学习十七—反射机制:解锁代码的无限可能

    Java学习十七-反射机制:解锁代码的无限可能 一.关于反射 1.1 简介 Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性.方法 ...

  8. DFS序求LCA

    DFS序求LCA 介绍 欧拉序求LCA 的数组总是会忘记开两倍,并且预处理的常数较大.用 DFS序求LCA 可以解决这些问题. 欧拉序:进节点和出节点会重复记录节点. DFS序:深度优先搜索的顺序,不 ...

  9. delphi Image32 动画演示2

    Image 32 自带的Demo,添加一些注解. unit uFrmAnimation2; interface uses Winapi.Windows, Winapi.Messages, System ...

  10. 鸿蒙NEXT开发案例:随机数生成

    [引言] 本项目是一个简单的随机数生成器应用,用户可以通过设置随机数的范围和个数,并选择是否允许生成重复的随机数,来生成所需的随机数列表.生成的结果可以通过点击"复制"按钮复制到剪 ...