本文主要研究下java9+springboot2+undertow2启用http2及server pushhtml
<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依赖
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生成实例
keytool -genkey -keyalg RSA -alias selfsigned -keystore src/main/resources/keystore.jks -storepass xxx -validity 360 -keysize 2048
@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功能
@RestController public class IndexController { /** * curl -Ik --http2 https://localhost:8443/hello * @return */ @GetMapping("/hello") public String hello(){ return "hello"; } }
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参数忽略校验https,不然报错
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.
<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
@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()); }
没有用server push,在在Initiator那栏,看到的是/demo这个触发的。点开waterfall,会看到Content Download的耗时。
能够看到若是是用server push的,在Initiator那栏,有个Push标识,点开waterfall,会看到reading push的耗时。
随着java9支持HTTP2,servlet4引入PushBuilder支持server push,使用java做为服务端开发语言的开发者能够更方便地将HTTP2实践起来。java
截止到写这篇文章之时,几大servlet容器的servlet4支持状况: