【HTTP 2】启用 HTTP 2(Starting HTTP/2)

前情提要

在上一篇文章《【HTTP
2】HTTP/2 协议概述(HTTP/2 Protocol Overview)
》中,简单介绍了 HTTP 2 规范的文档结构以及约定和术语。

本文变对 HTTP 2 规范的第一部分进行介绍,来说明如何发起一个 HTTP 2 连接。

启用 HTTP/2(Starting HTTP/2)

HTTP 2 连接是一个运行在 TCP 连接之上的应用层协议。客户端是
TCP 连接的发起者。

HTTP 2 保持了 HTTP 1.1 的风格,同样使用“http”和“https”作为 URI 的开始。并且共享相同的默认端口号:“http”使用 80 端口,“https”使用 443 端口。这意味着,相关实现要处理针对目标资源的请求,比如 http://example.org/foo 或者 https://example.com/bar,需要先知道上游服务器(该客户端当前希望建立的连接)是否支持 HTTP 2。

“http”和“https”支持 HTTP 2 的手段有所不同。“http”支持 HTTP 2 的方法在 3.2
详述,而“https”支持 HTTP 2 的方法在 3.3
详述。

HTTP/2 版本标识(HTTP/2 Version Identification)

本文档中所定义的协议有两个标识符。

字符串“h2”标识了使用传输安全层(TLS)的协议。该标识符用于
TLS 应用层协议协商(ALPN)扩展(TLS-ALPN)字段,在任何 TLS 之上的 HTTP 2 都会被该标识符标记。

“h2”字符串在 ALPN 协议标识中被序列化成两个八进制序列:0x68, 0x32。

字符串“h2c”标识了 HTTP 2 运行在明文 TCP 中。该标识符用于 HTTP 1.1 Upgrade 报文头中,在任何 TCP 之上的 HTTP 2 都会被该标识符标记。

选择“h2”或“h2c”意味着将会导致不同的传输、安全性、帧构成,以及在本文档中所描述的消息语法。

“http”中启用 HTTP/2(Starting HTTP/2 for “http” URIs)

一个客户端使用“http”发起请求,并不知道下一站是否支持 HTTP 升级机制。客户端通过发送 HTTP 1.1 请求,并包括 Upgrade 报文头,以“h2c”标识。这样的 HTTP 1.1 请求必须(MUST)有且仅有一个 HTTP2-Settings 报文头字段。

例如:

1
2
3
4
5
GET / HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>

在客户端发送 HTTP 2 帧之前,包含 Payload 的请求必须(MUST)被完整的发送。这意味着一个较大的请求将会阻塞连接,直到它被完全发送完毕。

如果初始请求与后续请求的并发非常重要,OPTIONS 请求可以用于升级至 HTTP 2 ,不过需要额外增加一次通信的成本。

不支持 HTTP 2 的服务器可以视 Upgrade 报文头不存在,进行如下响应:

1
2
3
4
5
HTTP/1.1
200
OK
Content-Length:
243
Content-Type:
text/html
 
...

服务器必须(MUST)忽略 Upgrade 报文头中的“h2”标识。“h2”标识意味着基于 TLS 的 HTTP/2,应该参考 3.3
进行协商。

支持 HTTP 2 的服务器接受了升级请求,并以状态码 101(转换协议)进行响应。在 101 响应的空行后,服务器可以开始发送 HTTP 2 帧。这些帧必须(MUST)包括升级请求对应的响应。

例如:

1
2
3
4
5
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
 
[ HTTP/2 connection ...

由服务器发送的第一个 HTTP 2 帧必须(MUST)是一个服务器连接序言(Connection Preface)(参考 3.5 章),并且包含一个 SETTINGS 帧(参考章节 6.5)。根据接收的 101 响应,客户端必须(MUST)发送一个同样包含 SETTINGS 帧的连接序言(Connection Preface)(参考章节 3.5)。

在升级之前所发送的 HTTP 1.1 请求,将被分配一个流标识符 “1”(参考章节 5.1.1),其优先级为默认级别(参考章节 5.3.5)。流 “1” 从 HTTP 1.1 请求完成后,是一个从客户端至服务器的隐性“半关闭”的流(参考章节 5.1)。在开始 HTTP 2 连接后,流 “1” 被用于响应。

HTTP2-Settings 报文头(HTTP2-Settings Header Field)

一个 HTTP 1.1 请求升级至 HTTP 2,必须(MUST)包含有且仅有一个 HTTP2-Settings 报文头。HTTP2-Settings 报文头是一个特定于连接的报头字段,其中的参数用于管理 HTTP 2 连接,在服务器接收升级请求时预先提供的。

1
HTTP2-Settings    =
token68

如果没有提供 HTTP2-Settings 报文头,或者提供了超过一个该报文头,服务器不可以(MUST NOT)升级该连接至 HTTP 2。服务器也不可以(MUST NOT)发送这个报文头。

HTTP2-Settings 报文头中的内容,是一个以 Base64 编码的 SETTINGS 帧的 Payload(即:URL 和安全文件名的 Base64 描述(RFC4648)的
5 章
,任何结尾的‘=’都将被忽略)。token68 的 ABNF(RFC5234)产物在 RFC7235 有所定义。

由于升级(Upgrade)只应用于直接连接,发送 HTTP2-Settings 的客户端必须(MUST)将 HTTP2-Settings 作为连接选项,包含在 Connection 报文头中,以防止它被转发(参考 RFC7230章节
6.1
)。

服务器解码并解释这些值,并将其看做其他 SETTINGS 帧。由于 101 响应作为了隐式确认,所以显式确认这些设置(参考章节 6.5.3)是没必要的。在升级请求中提供这些值,使得一个客户端有机会在接收服务器发 送的任何帧之前提供参数信息。

“https”中启用 HTTP/2(Starting HTTP/2 for “https” URIs)

一个发起“https”请求的客户端,需要用到 TLS(TLS1.2
和应用层协议协商(ALPN) 扩展(TLS-ALPN)。

TLS 之上的 HTTP 2 使用“h2”作为协议标识符。客户端一定不能(MUST NOT)发送“h2c”协议标识符,服务器也一定不能(MUST NOT)选择“h2c”标识符使用。“h2c”协议标识符描述了不适用 TLS 的协议。

一旦 TLS 协商完成,客户端和服务器都必须(MUST)发送一个连接序言(Connection preface)。

先验下启用 HTTP/2(Starting HTTP/2 with Prior Knowledge)

客户端可以通过其他方式判断服务器是否支持 HTTP 2。例如 ALT-SVC,它描述了一种机制使得 HTTP 具有广播能力。

客户端必须(MUST)发送连接序言(Connection preface,参考章节 3.5),然后可以(MAY)立即发送 HTTP 2 帧给服务器。服务端可以通过已存在的连接序言识别出这个连接。这只对基于明文 TCP 的 HTTP 2 连接建立有影响,TLS 之上的 HTTP 2 必须(MUST)使用 TLS 协议协商(TLS-ALPN)。

同样的,服务器必须(MUST) 发送连接序言(参考章节 3.5)。

如果没有其他信息,对 HTTP 2 之前的支持并不代表着指定服务器一定会在之后的连接中支持 HTTP 2。比如,服务器配置有可能改变,或者服务器集群中不同的实例配置有差异,亦或者网络状况的变化。

HTTP 2 连接序言(HTTP/2 Connection Preface)

在 HTTP 2 中,每个终端都需要发送一个连接序言作为协议的最终确认以及 HTTP 2 的连接初始设置。客户端和服务器均需要发送一个不同的连接序言。

客户端连接序言以 24 个字节序列开始,以十六进制表示为:

1
0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a

也就是说,连接序言以字符串 PRI
* HTTP/2.0\r\n\r\nSM\r\n\r\n 
开头。这个序列后必须(MUST)跟着一个 SETTINGS 帧(参考章节 6.5),其可以(MAY)为空。客户端收到 101 (协议转换)响应后,立刻发送客户端连接序言。或者作为 TLS 连接的第一个应用数据字节。如果在语言验证服务器支持 HTTP 2 的情况下启用 HTTP 2 连接,客户端连接序言在连接建立后发送。

注意:客户端连接序言是用来让大部分 HTTP 1.1 或 HTTP 1.0 服务端及中介端不试图进一步处理帧。注意这并不能解决【TALKING】中提到的问题。

服务器连接序言包含一个可能是空的 SETTINGS 帧(参考章节 6.5),它必须在 HTTP 2 连接中首个发送。

从一个节点接收到的连接序言中所包含的 SETTINGS 帧必须在连接序言发送后被确认(参考章节 6.5.3)。

为了避免不必要的延迟,允许客户端在发送客户端连接序言之后立即发送其他额外的帧,不需要等待收到服务器端连接序言。不过,值得注意的是,服务端连接序言 SETTINGS 帧可能包含一些关于期望客户端如何与服务器端通信的必须修改的参数。在收到这些 SETTINGS 帧后,客户端应当遵守所有设置的参数。在有些配置中,服务器可能在客户端发送其他额外帧之前传输 SETTINGS,以提供一个避免这些问题的机会。

客户端和服务器必须(MUST)将一个非法的连接序言当做一个 PROTOCOL_ERROR 类型的连接错误(Connection error,参考章节 5.4.1)。在这个案例中,当一个节点不支持 HTTP 2 而出现非法序言时,GOAWAY 帧(参考章节 6.8)可以(MAY)被省略。


本文以 CC
BY-NC-SA 3.0 CN
 协议共享,转载、共享及二次创作时请保留原文出处及链接,请勿用于商业用途。

本文链接:http://litecodes.com/dev/http-2-spec-starting-http-2/

本系列文章将会在我的 GitBook:http2-spec-zh 同步更新,

下一篇文章将会翻译协议的第三部分:HTTP Frames(HTTP 帧),不要错过哟~

【HTTP 2】启用 HTTP 2(Starting HTTP/2)的更多相关文章

  1. 启用CentOS6.5 64位安装时自带的MySQL数据库服务器

    本人在虚拟机上又安装了一台linux机器,作为MySQL数据库服务器用,在安装时选择了系统自带的MySQL服务器端,以下是启用步骤. 首先开启mysqld服务 #service mysqld star ...

  2. springMVC注解启用及优化

    使用注解的原因 最方便的还是启用注解 注解方便,而且项目中很流行. 配置文件尽量减少,主要使用注解方式. Springmvc的注解是在2.5版本后有了注解,如何开启注解配置文件 Web.xml文件中不 ...

  3. CentOS 7.1 Bridge启用STP报错"Master connection not found or invalid"

    今天在公司测试Linux bridge搭建,为了使内部docker容器的网络能够不经过2层封装转发对外公布,顾试用一下bridge功能,结果碰到报错:"Bringing up interfa ...

  4. Nginx 启用 https

    在nginx.conf中增加新server配置 server { listen ; server_name www.some.com; ssl on; ssl_certificate sslkey/s ...

  5. 安装ESXi5.5遇到Relocating modules and starting up the kernel的处理

    在一些Dell较旧的服务器上安装ESXi 5.x时, 会遇到卡在Relocating modules and starting up the kernel过不去的问题. 比如我装的这台CS24VSS. ...

  6. [转]Hive:简单查询不启用Mapreduce job而启用Fetch task

    转自:http://www.iteblog.com/archives/831 如果你想查询某个表的某一列,Hive默认是会启用MapReduce Job来完成这个任务,如下: hive> SEL ...

  7. 启用VTX技术支持启动android的虚拟机 - 报错

    第一次启用VTX技术支持启动android的虚拟机,启动时提示如下错误: Starting emulator for AVD 'AVD_for_Android_TV_1080p_by_Google'e ...

  8. SpringMVC注释启用

    这篇文章是我学习的网络视频SpringMVC写的过程. 谢谢公布各位前辈的视频 以下评论SpringMVC几个关键步骤,注意事项启用: 首先需要加载配置文件(假设请使用自定义路径) <? xml ...

  9. ADS1.2和JlinkV8 erro starting external process,Process error code 87(0x57)参数错误

    ADS1.2和JlinkV8  erro starting external process,Process error code 87(0x57)参数错误 网上的大致说法是说这个跟W7有关 说是将I ...

随机推荐

  1. ie的selectNodes函数和firefox的document.evaluate

    selectNodes() 方法用一个 XPath 查询选择节点. nodeObject.selectNodes(query)query  XPath 查询串. 包含了匹配查询的节点的一个 NodeL ...

  2. ArcSde for Oracle服务注册

    1.首先安装ArcSde,安装完成之后在dos命令窗口运行如下命令: sdeservice -o create -d oracle,instance -p sde -i port; 参数说明: ins ...

  3. VC获取精确时间的做法

    声明:本文章是我整合网上的资料而成的,其中的大部分文字不是我所为的,我所起的作用只是归纳整理并添加我的一些看法.非常感谢引用到的文字的作者的辛勤劳动,所参考的文献在文章最后我已一一列出. 对关注性能的 ...

  4. C# Windows Sockets (Winsock) 接口 (转)

    在.Net中,System.Net.Sockets 命名空间为需要严密控制网络访问的开发人员提供了 Windows Sockets (Winsock) 接口的托管实现.System.Net 命名空间中 ...

  5. Java中volatile的作用以及用法

    volatile让变量每次在使用的时候,都从主存中取.而不是从各个线程的“工作内存”. volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正 ...

  6. Desert King(最优比率生成树)

    Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 22717   Accepted: 6374 Desc ...

  7. ORACLE 使用RMAN管理归档日志 archived log

    oracle 归档日志通常使用rman进行管理,作为备份集的一部分 正常情况下,可以根据方法删除 1 过期策略 crosscheck archivelog all; delete expired ar ...

  8. openstack安装配置

    openstack:1.控制节点安装所有,计算节点只有nova-compute:2.网络选择: nova-network还是neutron: nova-network比较简单, neutron功能强大 ...

  9. 关于java项目导入,所碰到的版本问题

    怎么修改myeclipse 项目中用的jdk版本 工具/原料 MyEclipse java项目 方法/步骤  首先打开MyEclipse,如下图所示  打开之后,找到我们的java项目  然后右键-- ...

  10. 如何隐藏DLL中,导出函数的名称?

    一.引言 很多时候,我们写了一个Dll,不希望别人通过DLL查看工具,看到我们的导出函数名称.可以通过以下步骤实现: 1. 在def函数中做如下定义: LIBRARY EXPORTS HideFunc ...