add TLS protocol selection to server
This commit is contained in:
parent
47a1176048
commit
1c1260bba6
8 changed files with 138 additions and 31 deletions
|
@ -1,6 +1,6 @@
|
|||
group = org.xbib
|
||||
name = netty-http
|
||||
version = 4.1.39.0
|
||||
version = 4.1.39.1
|
||||
|
||||
# netty
|
||||
netty.version = 4.1.39.Final
|
||||
|
|
|
@ -111,7 +111,21 @@ public final class Server implements AutoCloseable {
|
|||
if (serverConfig.isDebug()) {
|
||||
bootstrap.handler(new LoggingHandler("bootstrap-server", serverConfig.getDebugLogLevel()));
|
||||
}
|
||||
DomainNameMapping<SslContext> domainNameMapping = createDomainNameMapping();
|
||||
if (serverConfig.getDefaultDomain() == null) {
|
||||
throw new IllegalStateException("no default named server (with name '*') configured, unable to continue");
|
||||
}
|
||||
DomainNameMapping<SslContext> domainNameMapping = null;
|
||||
if (serverConfig.getAddress().isSecure() && serverConfig.getDefaultDomain().getSslContext() != null) {
|
||||
DomainNameMappingBuilder<SslContext> mappingBuilder =
|
||||
new DomainNameMappingBuilder<>(serverConfig.getDefaultDomain().getSslContext());
|
||||
for (Domain domain : serverConfig.getDomains()) {
|
||||
String name = domain.getName();
|
||||
if (!"*".equals(name)) {
|
||||
mappingBuilder.add(name, domain.getSslContext());
|
||||
}
|
||||
}
|
||||
domainNameMapping = mappingBuilder.build();
|
||||
}
|
||||
if (serverConfig.getAddress().getVersion().majorVersion() == 1) {
|
||||
HttpChannelInitializer httpChannelInitializer = new HttpChannelInitializer(this,
|
||||
serverConfig.getAddress(), domainNameMapping);
|
||||
|
@ -160,13 +174,14 @@ public final class Server implements AutoCloseable {
|
|||
* @throws IOException if channel future sync is interrupted
|
||||
*/
|
||||
public ChannelFuture accept() throws IOException {
|
||||
logger.log(Level.INFO, () -> "trying to bind to " + serverConfig.getAddress());
|
||||
HttpAddress httpAddress = serverConfig.getAddress();
|
||||
logger.log(Level.INFO, () -> "trying to bind to " + httpAddress);
|
||||
try {
|
||||
this.channelFuture = bootstrap.bind(serverConfig.getAddress().getInetSocketAddress()).await().sync();
|
||||
this.channelFuture = bootstrap.bind(httpAddress.getInetSocketAddress()).await().sync();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
logger.log(Level.INFO, () -> ServerName.getServerName() + " ready, listening on " + serverConfig.getAddress());
|
||||
logger.log(Level.INFO, () -> ServerName.getServerName() + " ready, listening on " + httpAddress);
|
||||
return channelFuture;
|
||||
}
|
||||
|
||||
|
@ -270,25 +285,6 @@ public final class Server implements AutoCloseable {
|
|||
return channelClass;
|
||||
}
|
||||
|
||||
private DomainNameMapping<SslContext> createDomainNameMapping() {
|
||||
if (serverConfig.getDefaultDomain() == null) {
|
||||
throw new IllegalStateException("no default named server (with name '*') configured, unable to continue");
|
||||
}
|
||||
DomainNameMapping<SslContext> domainNameMapping = null;
|
||||
if (serverConfig.getAddress().isSecure() && serverConfig.getDefaultDomain().getSslContext() != null) {
|
||||
DomainNameMappingBuilder<SslContext> mappingBuilder =
|
||||
new DomainNameMappingBuilder<>(serverConfig.getDefaultDomain().getSslContext());
|
||||
for (Domain domain : serverConfig.getDomains()) {
|
||||
String name = domain.getName();
|
||||
if (!"*".equals(name)) {
|
||||
mappingBuilder.add(name, domain.getSslContext());
|
||||
}
|
||||
}
|
||||
domainNameMapping = mappingBuilder.build();
|
||||
}
|
||||
return domainNameMapping;
|
||||
}
|
||||
|
||||
static class HttpServerParentThreadFactory implements ThreadFactory {
|
||||
|
||||
private int number = 0;
|
||||
|
@ -453,12 +449,12 @@ public final class Server implements AutoCloseable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setEnablCcompression(boolean enablCcompression) {
|
||||
this.serverConfig.setCompression(enablCcompression);
|
||||
public Builder enableCompression(boolean enableCompression) {
|
||||
this.serverConfig.setCompression(enableCompression);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEnableDecompression(boolean enableDecompression) {
|
||||
public Builder enableDeompression(boolean enableDecompression) {
|
||||
this.serverConfig.setDecompression(enableDecompression);
|
||||
return this;
|
||||
}
|
||||
|
@ -468,6 +464,11 @@ public final class Server implements AutoCloseable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setTransportLayerSecurityProtocols(String[] protocols) {
|
||||
this.serverConfig.setProtocols(protocols);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addServer(Domain domain) {
|
||||
this.serverConfig.putDomain(domain);
|
||||
logger.log(Level.FINE, "adding named server: " + domain);
|
||||
|
|
|
@ -8,11 +8,16 @@ import io.netty.util.Mapping;
|
|||
import org.xbib.netty.http.common.HttpAddress;
|
||||
import org.xbib.netty.http.server.ServerConfig;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
||||
public class ExtendedSNIHandler extends SniHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExtendedSNIHandler.class.getName());
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final HttpAddress httpAddress;
|
||||
|
@ -39,6 +44,7 @@ public class ExtendedSNIHandler extends SniHandler {
|
|||
SSLParameters params = engine.getSSLParameters();
|
||||
params.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
engine.setSSLParameters(params);
|
||||
logger.log(Level.FINE, () -> "set enabled TLS protocols in SSL engine: " + Arrays.asList(serverConfig.getProtocols()));
|
||||
engine.setEnabledProtocols(serverConfig.getProtocols());
|
||||
return sslHandler;
|
||||
}
|
||||
|
|
|
@ -107,7 +107,6 @@ public class HttpChannelInitializer extends ChannelInitializer<SocketChannel> {
|
|||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
logger.log(Level.FINE, "channelRead: " + msg.getClass().getName());
|
||||
if (msg instanceof HttpPipelinedRequest) {
|
||||
HttpPipelinedRequest httpPipelinedRequest = (HttpPipelinedRequest) msg;
|
||||
if (httpPipelinedRequest.getRequest() instanceof FullHttpRequest) {
|
||||
|
|
|
@ -17,6 +17,7 @@ public class NettyHttpTestExtension implements BeforeAllCallback {
|
|||
@Override
|
||||
public void beforeAll(ExtensionContext context) {
|
||||
if (Security.getProvider("BC") == null) {
|
||||
// for insecure trust manager
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
System.setProperty("io.netty.noUnsafe", Boolean.toString(true));
|
||||
|
|
|
@ -38,7 +38,6 @@ class SecureHttp1Test {
|
|||
.withContentType("text/plain")
|
||||
.write(request.getContent().retain()))
|
||||
.build())
|
||||
.enableDebug()
|
||||
.build();
|
||||
Client client = Client.builder()
|
||||
.trustInsecure()
|
||||
|
|
|
@ -55,8 +55,7 @@ class SecureHttp2Test {
|
|||
String payload = 0 + "/" + 0;
|
||||
Request request = Request.get()
|
||||
.setVersion("HTTP/2.0")
|
||||
.uri("/")
|
||||
//.url(server.getServerConfig().getAddress().base())
|
||||
.url(server.getServerConfig().getAddress().base())
|
||||
.content(payload, "text/plain")
|
||||
.build()
|
||||
.setResponseListener(responseListener);
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package org.xbib.netty.http.server.test;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.xbib.netty.http.client.Client;
|
||||
import org.xbib.netty.http.client.Request;
|
||||
import org.xbib.netty.http.client.listener.ResponseListener;
|
||||
import org.xbib.netty.http.client.transport.Transport;
|
||||
import org.xbib.netty.http.common.HttpAddress;
|
||||
import org.xbib.netty.http.common.HttpResponse;
|
||||
import org.xbib.netty.http.server.Domain;
|
||||
import org.xbib.netty.http.server.Server;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ExtendWith(NettyHttpTestExtension.class)
|
||||
class TransportLayerSecurityServerTest {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TransportLayerSecurityServerTest.class.getName());
|
||||
|
||||
@Test
|
||||
void testTLS12() throws Exception {
|
||||
HttpAddress httpAddress = HttpAddress.secureHttp1("localhost", 8143);
|
||||
Server server = Server.builder(Domain.builder(httpAddress)
|
||||
.setSelfCert()
|
||||
.singleEndpoint("/", (request, response) ->
|
||||
response.withStatus(HttpResponseStatus.OK)
|
||||
.withContentType("text/plain")
|
||||
.write(request.getContent().retain()))
|
||||
.build())
|
||||
.setTransportLayerSecurityProtocols(new String[]{ "TLSv1.2"})
|
||||
.build();
|
||||
Client client = Client.builder()
|
||||
.trustInsecure()
|
||||
.build();
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
final ResponseListener<HttpResponse> responseListener = resp -> {
|
||||
logger.log(Level.INFO, "response listener: headers = " + resp.getHeaders() +
|
||||
" response body = " + resp.getBodyAsString(StandardCharsets.UTF_8));
|
||||
counter.incrementAndGet();
|
||||
};
|
||||
try {
|
||||
server.accept();
|
||||
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
|
||||
.url(server.getServerConfig().getAddress().base())
|
||||
.content("Hello Jörg", "text/plain")
|
||||
.build()
|
||||
.setResponseListener(responseListener);
|
||||
Transport transport = client.execute(request).get();
|
||||
logger.log(Level.INFO, "HTTP 1.1 TLS protocol = " + transport.getSession().getProtocol());
|
||||
assertEquals("TLSv1.2", transport.getSession().getProtocol());
|
||||
} finally {
|
||||
client.shutdownGracefully();
|
||||
server.shutdownGracefully();
|
||||
}
|
||||
assertEquals(1, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTLS13() throws Exception {
|
||||
HttpAddress httpAddress = HttpAddress.secureHttp2("localhost", 8143);
|
||||
Server server = Server.builder(Domain.builder(httpAddress)
|
||||
.setSelfCert()
|
||||
.singleEndpoint("/", (request, response) ->
|
||||
response.withStatus(HttpResponseStatus.OK)
|
||||
.withContentType("text/plain")
|
||||
.write(request.getContent().retain()))
|
||||
.build())
|
||||
.setTransportLayerSecurityProtocols(new String[]{ "TLSv1.3"})
|
||||
.build();
|
||||
Client client = Client.builder()
|
||||
.trustInsecure()
|
||||
.build();
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
final ResponseListener<HttpResponse> responseListener = resp -> {
|
||||
logger.log(Level.INFO, "response listener: headers = " + resp.getHeaders() +
|
||||
" response body = " + resp.getBodyAsString(StandardCharsets.UTF_8));
|
||||
counter.incrementAndGet();
|
||||
};
|
||||
try {
|
||||
server.accept();
|
||||
Request request = Request.get()
|
||||
.setVersion("HTTP/2.0")
|
||||
.url(server.getServerConfig().getAddress().base())
|
||||
.content("Hello Jörg", "text/plain")
|
||||
.build()
|
||||
.setResponseListener(responseListener);
|
||||
Transport transport = client.execute(request).get();
|
||||
logger.log(Level.INFO, "HTTP/2 TLS protocol = " + transport.getSession().getProtocol());
|
||||
assertEquals("TLSv1.3", transport.getSession().getProtocol());
|
||||
} finally {
|
||||
client.shutdownGracefully();
|
||||
server.shutdownGracefully();
|
||||
}
|
||||
assertEquals(1, counter.get());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue