序
本文主要研究下java9+springboot2+undertow2啟用http2及server push
maven
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>9</java.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
注意這里使用undertow端壳,移除掉了starter-web中的tomcat依賴
配置
application.yml
server:
port: 8443
ssl:
key-store: classpath:keystore.jks
key-store-password: xxx
key-password: xxx
protocol: TLSv1.2
http2:
enabled: true
use-forward-headers: true
keystore生成實(shí)例
keytool -genkey -keyalg RSA -alias selfsigned -keystore src/main/resources/keystore.jks -storepass xxx -validity 360 -keysize 2048
ENABLE_HTTP2及ENABLE_PUSH
@Configuration
public class Http2Config {
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.addBuilderCustomizers(
builder -> {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true)
.setServerOption(UndertowOptions.HTTP2_SETTINGS_ENABLE_PUSH,true);
});
return factory;
}
}
這里開啟了HTTP2以及server push功能
HTTP2實(shí)例
controller
@RestController
public class IndexController {
/**
* curl -Ik --http2 https://localhost:8443/hello
* @return
*/
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
運(yùn)行
curl -Ivk --http2 https://localhost:8443/hello
* Trying ::1...
* Connected to localhost (::1) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /usr/local/etc/openssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=ZH; ST=guangdong; L=shenzhen; O=spring; OU=springboot; CN=localhost
* start date: Mar 9 14:10:54 2018 GMT
* expire date: Mar 4 14:10:54 2019 GMT
* issuer: C=ZH; ST=guangdong; L=shenzhen; O=spring; OU=springboot; CN=localhost
* SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* TCP_NODELAY set
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f8a5280ea00)
> HEAD /hello HTTP/1.1
> Host: localhost:8443
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/2.0 200
HTTP/2.0 200
< content-type:text/plain;charset=UTF-8
content-type:text/plain;charset=UTF-8
< content-length:5
content-length:5
< date:Fri, 09 Mar 2018 15:18:36 GMT
date:Fri, 09 Mar 2018 15:18:36 GMT
<
* Connection #0 to host localhost left intact
注意宴卖,這里curl命令使用-k參數(shù)忽略校驗(yàn)https羊赵,否則報(bào)錯(cuò)
curl -I --http2 https://localhost:8443/hello
curl: (60) SSL certificate problem: self signed certificate
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
server push實(shí)例
maven改動(dòng)
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>9</java.version>
<servlet-api.version>4.0.0</servlet-api.version>
</properties>
<dependencies>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-websockets-jsr</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
<exclusions>
<exclusion>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
</exclusion>
<exclusion>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
</exclusion>
<exclusion>
<groupId>io.undertow</groupId>
<artifactId>undertow-websockets-jsr</artifactId>
</exclusion>
</exclusions>
</dependency>
由于server push需要servlet4版本再菊,目前springboot2依賴的undertow還是1.4版本的還只是servlet3吱雏,因此這里需要額外exclude掉再引入undertow2版本以支持servelt4
controller
@GetMapping("/demo")
public void http2ServerPush(HttpServletRequest request, HttpServletResponse response) throws IOException {
PushBuilder pushBuilder = request.newPushBuilder();
pushBuilder
.path("/demo.png")
.addHeader("content-type", "image/png")
.push();
try(PrintWriter respWriter = response.getWriter()){
respWriter.write("<html>" +
"<img src='/demo.png'>" +
"</html>");
}
}
@GetMapping(value = "/demo.png")
public void download(HttpServletResponse response) throws IOException {
InputStream data = getClass().getClassLoader().getResourceAsStream("demo.png");
response.setHeader("content-type", "image/png");
FileCopyUtils.copy(data,response.getOutputStream());
}
運(yùn)行
-
不啟用server push
沒(méi)有用server push碧信,在在Initiator那欄垫挨,看到的是/demo這個(gè)觸發(fā)的。點(diǎn)開waterfall,會(huì)看到Content Download的耗時(shí)祸轮。
-
啟用server push
可以看到如果是用server push的兽埃,在Initiator那欄,有個(gè)Push標(biāo)識(shí)适袜,點(diǎn)開waterfall柄错,會(huì)看到reading push的耗時(shí)。
小結(jié)
隨著java9支持HTTP2苦酱,servlet4引入PushBuilder支持server push鄙陡,使用java作為服務(wù)端開發(fā)語(yǔ)言的開發(fā)者可以更方便地將HTTP2實(shí)踐起來(lái)。
截止到寫這篇文章之時(shí)躏啰,幾大servlet容器的servlet4支持情況:
- jetty尚且沒(méi)有看到支持servlet4的實(shí)現(xiàn)版本發(fā)布趁矾;
- tomcat有9.x版本支持servlet4,但是在springboot2上替換依賴報(bào)錯(cuò)给僵,整體實(shí)踐起來(lái)稍稍麻煩毫捣;
- undertow2.0.1.Final版本支持servlet4,在springboot2上替換依賴帝际,非常簡(jiǎn)單蔓同,沒(méi)有報(bào)錯(cuò),這也是本文選擇undertow的原因蹲诀。