Introduction
We know that although the HTTP2 protocol does not force the use of HTTPS, for most browsers, if you want to use HTTP2, you must use HTTPS, so we need to understand how to support http2 in netty's TLS.
TLS extension protocol NPN and ALPN
The HTTP2 protocol is developed from the spdy protocol. Both spdy and http2 have developed an extension of the TLS protocol in order to work in the HTTPS environment.
They are called NPN (Next Protocol Negotiation) and ALPN (Application Layer Protocol Negotiation).
They specify the protocol for application data communication between the client and the server after the TLS protocol handshake. Among them, ALPN can list the application layer data protocols supported by the client when the client handshake with the server for the first time, and the server can directly select it, so it can have one less interaction process than NPN, which is more excellent.
So what are the protocols supported by spdy and http2?
Netty provides an ApplicationProtocolNames class, in which the corresponding protocols are defined. ALPN corresponds to http2 and http1.1, and sydy corresponds to spdy/1, spdy/2, and spdy/3:
/**
* HTTP version 2
*/
public static final String HTTP_2 = "h2";
/**
* {@code "http/1.1"}: HTTP version 1.1
*/
public static final String HTTP_1_1 = "http/1.1";
/**
* {@code "spdy/3.1"}: SPDY version 3.1
*/
public static final String SPDY_3_1 = "spdy/3.1";
/**
* {@code "spdy/3"}: SPDY version 3
*/
public static final String SPDY_3 = "spdy/3";
/**
* {@code "spdy/2"}: SPDY version 2
*/
public static final String SPDY_2 = "spdy/2";
/**
* {@code "spdy/1"}: SPDY version 1
*/
public static final String SPDY_1 = "spdy/1";
SslProvider
Currently, there are two implementations of SSL in netty, one is JDK and the other is OPENSSL. Different implementations support different TLS protocol extensions. It provides an isAlpnSupported method to determine whether to support ALPN according to the difference of the incoming provider.
public static boolean isAlpnSupported(final SslProvider provider) {
switch (provider) {
case JDK:
return JdkAlpnApplicationProtocolNegotiator.isAlpnSupported();
case OPENSSL:
case OPENSSL_REFCNT:
return OpenSsl.isAlpnSupported();
default:
throw new Error("Unknown SslProvider: " + provider);
}
}
If you are using JDK8, you may get the following error message after running:
ALPN is only supported in Java9 or if you use conscrypt as your provider or have the jetty alpn stuff on the class path.
In other words, if JDK is used as the default SSL provider, it does not support ALPN. Must upgrade to java9.
According to the prompt, if you add conscrypt to the classpath:
<dependency>
<groupId>org.conscrypt</groupId>
<artifactId>conscrypt-openjdk-uber</artifactId>
<version>2.5.2</version>
</dependency>
After running, you will get the following error:
Unable to wrap SSLEngine of type 'sun.security.ssl.SSLEngineImpl'
How to do it? The answer is to use Open SSL, you also need to add:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>2.0.40.Final</version>
</dependency>
After testing, perfect execution.
ApplicationProtocolConfig
ApplicationProtocolConfig is a protocol configuration class that netty provides to pass to SSLEngine. It has four main properties:
private final List<String> supportedProtocols;
private final Protocol protocol;
private final SelectorFailureBehavior selectorBehavior;
private final SelectedListenerFailureBehavior selectedBehavior;
SupportedProtocols is the supported data transfer protocol, like HTTP2, HTTP1.1 or spdy/1, spdy/2, spdy/3, etc. above.
protocol is an extended protocol of TLS, like ALPN or NPN.
selectorBehavior is the way to behave when selecting a protocol, there are 3 ways:
FATAL_ALERT: If the node that selects the application protocol does not find a match, the handshake will fail.
NO_ADVERTISE: If the node that selects the application protocol does not find a match, it will pretend that it does not support TLS extension in the handshake.
CHOOSE_MY_LAST_PROTOCOL: If the node that selects the application protocol does not find a match, the last recommended protocol will be used.
SelectedBehavior is the way to behave after notifying the selected protocol, there are also 3 ways:
ACCEPT: If the node does not support the application protocol selected by the other node, the node does not support the TLS extension by default, and then the handshake is continued.
FATAL_ALERT: If the node does not support the application protocol selected by the other node, the handshake fails.
CHOOSE_MY_LAST_PROTOCOL: If the node does not support the application protocol selected by the other node, the last recommended protocol will be used.
Build SslContext
With the provider, ApplicationProtocolConfig, SslContext can be constructed. First create an SSL provider:
SslProvider provider = SslProvider.isAlpnSupported(SslProvider.OPENSSL) ? SslProvider.OPENSSL : SslProvider.JDK;
By default, JDK is used as the ssl provider. If you are using OpenSSL, use OpenSSL.
We use SslContextBuilder.forServer to create SslContext. This method needs to pass in certificate and privateKey. For simplicity, we use self-signed SelfSignedCertificate:
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
You can also set information such as sslProvider, ciphers and applicationProtocolConfig for it:
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(provider)
//支持的cipher
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
.applicationProtocolConfig(new ApplicationProtocolConfig(
Protocol.ALPN,
// 目前 OpenSsl 和 JDK providers只支持NO_ADVERTISE
SelectorFailureBehavior.NO_ADVERTISE,
// 目前 OpenSsl 和 JDK providers只支持ACCEPT
SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1))
.build();
ProtocolNegotiationHandler
Finally, we need to perform different treatments according to the different protocols used in the negotiation. Netty provides an ApplicationProtocolNegotiationHandler. If you customize it, you only need to inherit this class. For example, we handle HTTP1 and HTTP2 requests separately according to the name of the protocol:
public class MyNegotiationHandler extends ApplicationProtocolNegotiationHandler {
public MyNegotiationHandler() {
super(ApplicationProtocolNames.HTTP_1_1);
}
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
if (ApplicationProtocolNames.HTTP_2.equals(protocol) {
configureHttp2(ctx);
} else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
configureHttp1(ctx);
} else {
throw new IllegalStateException("unknown protocol: " + protocol);
}
}
}
Then add it to ChannelPipeline:
public class MyInitializer extends ChannelInitializer<Channel> {
private final SslContext sslCtx;
public MyInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
protected void initChannel(Channel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(sslCtx.newHandler(...)); // Adds SslHandler
p.addLast(new MyNegotiationHandler());
}
}
Summarize
The above is the complete process of configuring TLS to support HTTP2 in netty.
For the examples in this article, please refer to: learn-netty4
This article has been included in http://www.flydean.com/26-netty-secure-http2/
The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to discover!
Welcome to pay attention to my official account: "Program those things", know technology, know you better!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。