如何让Spring boot 2.0 支持h2c协议

前面有文章介绍了Spring boot 2.0 中配置实现HTTP/2协议的各种情形,但是其中介绍都是h2协议。HTTP/2协议有两个版本:h2h2ch2ch2 的明文版本,没有建立在TLS基础上,没有安全保障。正是因为没有TLS层的加解密相关步骤,比较适合用在后端服务之间通信,gRPC就是同时支持这两个版本的HTTP/2协议。

00 前言

HTTP/2连接是建立在TCP连接之上的应用层协议,客户端是TCP连接的发起者。

HTTP/2使用和HTTP/1.1一样的 URI schemes:"http" 和 "https",并且还是共享同样的默认端口:http的80,https的443。这意味着,对于"http" 和 "https"确定其是否支持HTTP/2协议的方式是不同的。

官方文档中,为HTTP/2协议定义了两个版本:h2h2c

  • h2版本的协议是建立在TLS层之上的HTTP/2协议,这个标志被用在TLS应用层协议协商(TLS-ALPN)域和任何其它的TLS之上的HTTP/2协议。
  • h2c版本是建立在明文的TCP之上的HTTP/2协议,这个标志被用在HTTP/1.1的升级协议头域和其它任何直接在TCP层之上的HTTP/2协议。

既然在HTTP/2协议的官方介绍中有两个版本,我们之前的文章介绍的是Spring boot 2.0如何实现h2,本文我们会介绍如何让Spring boot 2.0支持h2c协议。

01 Spring boot 2.0 h2c协议服务端

由于h2c协议相对于h2来说简单些,应该要实现也不难,但是从Spring boot 2.0的官方文档上看,明确写明了“Spring boot 不支持h2c —— HTTP/2协议的明文版本”,于是就想其它办法来支持h2c。

首先,想的是通过外置的tomcat配置来支持h2c,因为tomcat8.5中的server.xml配置中,有HTTP/2协议相关的配置:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
           maxThreads="150" SSLEnabled="true" >
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
    <SSLHostConfig>
        <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
                     certificateFile="conf/localhost-rsa-cert.pem"
                     certificateChainFile="conf/localhost-rsa-chain.pem"
                     type="RSA" />
    </SSLHostConfig>
</Connector>

这里显示要添加证书,明显支持的是基于TLS层之上的h2版本的HTTP/2协议,但是从配置上可以禁用SSL,于是就尝试了一下,果然成功了。配置成如下示例,就支持h2c了:

<Connector port="5080" protocol="HTTP/1.1" connectionTimeout="20000">
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
</Connector>

tomcat容器自身是支持多个connector的配置的,想到这里,就想在Spring boot 2.0 中是否支持同时配置多个Connector,查看Spring boot的官方文档发现如下配置:

Spring boot 2.0 tomcat Connector

文档中示例是通过 java configure 的方式配置一个https的connector,在application.yaml中不支持配置多个connector。

因此我就模仿这外置tomcat配置h2c的方式,在Spring boot 2.0 的内置tomcat中通过java configure的方式配置h2c协议,具体代码如下:

@Bean
public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    tomcat.addAdditionalTomcatConnectors(createH2cConnector());
    return tomcat;
}

private Connector createH2cConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    Http2Protocol upgradeProtocol = new Http2Protocol();
    connector.addUpgradeProtocol(upgradeProtocol);
    //connector.setScheme("http");
    connector.setPort(5080);

    return connector;
}

这时启动我们的Spring boot应用,会发现最后的启动日志,我们同时启动三个端口,也即是三个Connector:

Tomcat started on port(s): 8080 (http) 8443 (https) 5080 (http) with context path '/demo-h2c'

02 Curl 工具验证h2c协议服务端

首先需要检查你的curl工具是否支持HTTP/2协议,验证方式:

➜  ~ curl -V
curl 7.54.0 (x86_64-apple-darwin17.0) libcurl/7.54.0 LibreSSL/2.0.20 zlib/1.2.11 nghttp2/1.24.0
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets HTTPS-proxy

从上面的 Features 信息发现,我的curl工具是支持HTTP/2协议的。

下面开始验证我Spring boot 的服务在5080端口是否是h2c,具体如下:

➜  ~ curl -v --http2 http://127.0.0.1:5080/demo-h2c/h2c/hello\?name\=guoyankui
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 5080 (#0)
> GET /demo-h2c/h2c/hello?name=guoyankui HTTP/1.1
> Host: 127.0.0.1:5080
> User-Agent: curl/7.54.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
>
< HTTP/1.1 101
< Connection: Upgrade
< Upgrade: h2c
< Date: Sun, 06 May 2018 13:12:37 GMT
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
< content-type: text/plain;charset=UTF-8
< date: Sun, 06 May 2018 13:12:37 GMT
<
* Connection #0 to host 127.0.0.1 left intact
Hello h2c: null%

OK, 验证通过了。

03 支持h2c协议的java客户端

在上一节中使用Curl工具验证了Spring boot 2.0 的服务的h2c协议,但是在java语言中,还需要有支持h2c的Java客户端。目前一般的 java 客户端都只是支持h2,有的还不支持HTTP/2协议,而且浏览器一般也不支持h2c。

现在找到一个支持h2c的java客户端也不容易,目前找到okhttp3的最新版本3.10.0也不支持h2c,但是发现在最新的github代码中3.11.0-SNAPSHOT版本已经支持了h2c,代码示例:

public static void main(String[] args) throws Exception {
    testH2C();
}

public static void testH2C() throws Exception {
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .protocols(Arrays.asList(Protocol.H2C))
            .build();

    Request request = new Request.Builder()
            .url("http://127.0.0.1:5080/demo-h2c/h2c/hello?name=guoyankui")
            .build();
    Response response = okHttpClient.newCall(request).execute();
    System.out.println(response.protocol());
    System.out.println(response.body().string());
}

执行这个main函数之后,输出的结果:

h2c
Hello h2c: guoyankui

从输出的结果来看,验证了h2c协议的客户端和服务端。

04 结束

目前,找到了okhttp3的3.11.0版本支持h2c,有支持h2c的Java 客户端了,我们也在Spring boot 2.0中实现了h2c的服务端,因此也算有了支持h2c协议的完整方案。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容