前面有文章介紹了Spring boot 2.0 中配置實(shí)現(xiàn)HTTP/2協(xié)議的各種情形芽唇,但是其中介紹都是
h2
協(xié)議肌厨。HTTP/2協(xié)議有兩個版本:h2
和h2c
斋泄,h2c
是h2
的明文版本昆婿,沒有建立在TLS基礎(chǔ)上,沒有安全保障撞蜂。正是因為沒有TLS層的加解密相關(guān)步驟盲镶,比較適合用在后端服務(wù)之間通信,gRPC就是同時支持這兩個版本的HTTP/2協(xié)議蝌诡。
00 前言
HTTP/2連接是建立在TCP連接之上的應(yīng)用層協(xié)議溉贿,客戶端是TCP連接的發(fā)起者。
HTTP/2使用和HTTP/1.1一樣的 URI schemes:"http" 和 "https"浦旱,并且還是共享同樣的默認(rèn)端口:http的80宇色,https的443。這意味著颁湖,對于"http" 和 "https"確定其是否支持HTTP/2協(xié)議的方式是不同的宣蠕。
在官方文檔中,為HTTP/2協(xié)議定義了兩個版本:h2
和 h2c
:
- h2版本的協(xié)議是建立在TLS層之上的HTTP/2協(xié)議甥捺,這個標(biāo)志被用在TLS應(yīng)用層協(xié)議協(xié)商(TLS-ALPN)域和任何其它的TLS之上的HTTP/2協(xié)議植影。
- h2c版本是建立在明文的TCP之上的HTTP/2協(xié)議,這個標(biāo)志被用在HTTP/1.1的升級協(xié)議頭域和其它任何直接在TCP層之上的HTTP/2協(xié)議涎永。
既然在HTTP/2協(xié)議的官方介紹中有兩個版本,我們之前的文章介紹的是Spring boot 2.0如何實(shí)現(xiàn)h2
鹿响,本文我們會介紹如何讓Spring boot 2.0支持h2c
協(xié)議羡微。
01 Spring boot 2.0 h2c協(xié)議服務(wù)端
由于h2c協(xié)議相對于h2來說簡單些,應(yīng)該要實(shí)現(xiàn)也不難惶我,但是從Spring boot 2.0的官方文檔上看妈倔,明確寫明了“Spring boot 不支持h2c —— HTTP/2協(xié)議的明文版本”,于是就想其它辦法來支持h2c绸贡。
首先盯蝴,想的是通過外置的tomcat配置來支持h2c,因為tomcat8.5中的server.xml配置中听怕,有HTTP/2協(xié)議相關(guān)的配置:
<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協(xié)議,但是從配置上可以禁用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的官方文檔發(fā)現(xiàn)如下配置:
文檔中示例是通過 java configure 的方式配置一個https的connector很魂,在application.yaml中不支持配置多個connector扎酷。
因此我就模仿這外置tomcat配置h2c的方式,在Spring boot 2.0 的內(nèi)置tomcat中通過java configure的方式配置h2c協(xié)議遏匆,具體代碼如下:
@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應(yīng)用法挨,會發(fā)現(xiàn)最后的啟動日志,我們同時啟動三個端口拉岁,也即是三個Connector:
Tomcat started on port(s): 8080 (http) 8443 (https) 5080 (http) with context path '/demo-h2c'
02 Curl 工具驗證h2c協(xié)議服務(wù)端
首先需要檢查你的curl工具是否支持HTTP/2協(xié)議坷剧,驗證方式:
? ~ 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 信息發(fā)現(xiàn),我的curl工具是支持HTTP/2協(xié)議的喊暖。
下面開始驗證我Spring boot 的服務(wù)在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協(xié)議的java客戶端
在上一節(jié)中使用Curl工具驗證了Spring boot 2.0 的服務(wù)的h2c協(xié)議陵叽,但是在java語言中狞尔,還需要有支持h2c的Java客戶端。目前一般的 java 客戶端都只是支持h2巩掺,有的還不支持HTTP/2協(xié)議偏序,而且瀏覽器一般也不支持h2c。
現(xiàn)在找到一個支持h2c的java客戶端也不容易胖替,目前找到okhttp3的最新版本3.10.0
也不支持h2c研儒,但是發(fā)現(xiàn)在最新的github代碼中3.11.0-SNAPSHOT
版本已經(jīng)支持了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());
}
執(zhí)行這個main函數(shù)之后独令,輸出的結(jié)果:
h2c
Hello h2c: guoyankui
從輸出的結(jié)果來看端朵,驗證了h2c
協(xié)議的客戶端和服務(wù)端。
04 結(jié)束
目前燃箭,找到了okhttp3的3.11.0
版本支持h2c
冲呢,有支持h2c的Java 客戶端了,我們也在Spring boot 2.0中實(shí)現(xiàn)了h2c的服務(wù)端招狸,因此也算有了支持h2c
協(xié)議的完整方案敬拓。