Skip to content

Commit

Permalink
Configuration of server socket(s) (#2189)
Browse files Browse the repository at this point in the history
* Advanced configuration of HTTP request processing in Netty.
* Documentation update.

Signed-off-by: Tomas Langer <[email protected]>
  • Loading branch information
tomas-langer authored Jul 20, 2020
1 parent 7c10e26 commit 545f8d8
Show file tree
Hide file tree
Showing 13 changed files with 459 additions and 21 deletions.
12 changes: 5 additions & 7 deletions docs/mp/jaxrs/02_server-configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,16 @@ server.port=7011
# Helidon configuration (optional)
# Length of queue for incoming connections. Default is 1024
server.backlog: 512
server.backlog=512
# TCP receive window. Default is 0 to use implementation default
server.receive-buffer: 256
server.receive-buffer=256
# Socket timeout milliseconds - defaults to 0 (infinite)
server.timeout: 30000
server.timeout=30000
# Defaults to Runtime.availableProcessors()
server.workers=4
# Default is not to use SSL
ssl:
private-key:
keystore-resource-path: "certificate.p12"
keystore-passphrase: "abcd"
ssl.private-key.keystore-resource-path=certificate.p12
ssl.private-key.keystore-passphrase="abcd"
----
== Configuring additional ports
Expand Down
21 changes: 21 additions & 0 deletions docs/se/webserver/02_configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,24 @@ just use `Config.create()`
See all configuration options
link:{javadoc-base-url-api}/WebServer.html[here].
Available socket configuration options:
[cols="^2s,<2,<2,<6"]
|===
|Configuration key |Default value ^|Java type ^|Description
|`port` |{nbsp} |int |Port to open server socket on, defaults to an available ephemeral port
|`bind-address` |all local addresses |String |Address to listen on (may be an IPV6 address as well)
|`backlog` |`1024` |int |Maximum length of the queue of incoming connections on the server socket.
|`max-header-size` |`8192` |int |Maximal number of bytes of all header values combined. Returns `400` if headers are bigger
|`max-initial-line-length` |`4096` |int |Maximal number of characters in the initial HTTP line. Returns `400` if line is longer
|`timeout-millis` |no timeout| long |Server socket timeout.
|`receive-buffer-size` |implementation default |int |Proposed value of the TCP receive window that is advertised to the remote peer on the server socket.
|`name` |`@default` for default socket |String |Name used for named sockets, to support additional server sockets (and their named routing)
|`enabled` |`true` |boolean |A socket can be disabled through configuration, in which case it is never opened
|`max-chunk-size` | `8192` |int |Maximal size of a chunk to read from incoming requests
|`validate-headers` |`true` |boolean |Whether to validate header names, if they contain illegal characters.
|`initial-buffer-size` |`128` |int |Initial size of buffer used to parse HTTP line and headers
|`tls` |{nbsp} |Object |Configuration of SSL, please see our SSL example in repository
|===
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,18 @@ public Builder keepAlive(boolean keepAlive) {
return this;
}

/**
* Whether to validate header names.
* Defaults to {@code true}.
*
* @param validate whether to validate the header name contains only allowed characters
* @return updated builder instance
*/
public Builder validateHeaders(boolean validate) {
configuration.validateHeaders(validate);
return this;
}

WebClientConfiguration configuration() {
configuration.clientServices(services());
return configuration.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class WebClientConfiguration {
private final MessageBodyWriterContext writerContext;
private final WebClientTls webClientTls;
private final URI uri;
private final boolean validateHeaders;

/**
* Creates a new instance of client configuration.
Expand Down Expand Up @@ -107,6 +108,7 @@ class WebClientConfiguration {
this.clientServices = Collections.unmodifiableList(builder.clientServices);
this.uri = builder.uri;
this.keepAlive = builder.keepAlive;
this.validateHeaders = builder.validateHeaders;
}

/**
Expand Down Expand Up @@ -267,6 +269,10 @@ boolean keepAlive() {
return keepAlive;
}

boolean validateHeaders() {
return validateHeaders;
}

/**
* A fluent API builder for {@link WebClientConfiguration}.
*/
Expand Down Expand Up @@ -295,6 +301,7 @@ static class Builder<B extends Builder<B, T>, T extends WebClientConfiguration>
private MessageBodyReaderContext readerContext;
private MessageBodyWriterContext writerContext;
private List<WebClientService> clientServices;
private boolean validateHeaders = true;
@SuppressWarnings("unchecked")
private B me = (B) this;

Expand Down Expand Up @@ -486,6 +493,18 @@ public B uri(URI uri) {
return me;
}

/**
* Whether to validate header names.
* Defaults to {@code true}.
*
* @param validate whether to validate the header name contains only allowed characters
* @return updated builder instance
*/
B validateHeaders(boolean validate) {
this.validateHeaders = validate;
return me;
}

@Override
public B addReader(MessageBodyReader<?> reader) {
this.readerContext.registerReader(reader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ private HttpVersion toNettyHttpVersion(Http.Version version) {
}

private HttpHeaders toNettyHttpHeaders() {
HttpHeaders headers = new DefaultHttpHeaders();
HttpHeaders headers = new DefaultHttpHeaders(this.configuration.validateHeaders());
try {
Map<String, List<String>> cookieHeaders = this.configuration.cookieManager().get(uri, new HashMap<>());
List<String> cookies = new ArrayList<>(cookieHeaders.get(Http.Header.COOKIE));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2020 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -57,10 +57,15 @@ class HttpInitializer extends ChannelInitializer<SocketChannel> {

private final SslContext sslContext;
private final NettyWebServer webServer;
private final SocketConfiguration soConfig;
private final Routing routing;
private final Queue<ReferenceHoldingQueue<DataChunk>> queues = new ConcurrentLinkedQueue<>();

HttpInitializer(SslContext sslContext, Routing routing, NettyWebServer webServer) {
HttpInitializer(SocketConfiguration soConfig,
SslContext sslContext,
Routing routing,
NettyWebServer webServer) {
this.soConfig = soConfig;
this.routing = routing;
this.sslContext = sslContext;
this.webServer = webServer;
Expand Down Expand Up @@ -91,7 +96,11 @@ public void initChannel(SocketChannel ch) {

// Set up HTTP/2 pipeline if feature is enabled
ServerConfiguration serverConfig = webServer.configuration();
HttpRequestDecoder requestDecoder = new HttpRequestDecoder();
HttpRequestDecoder requestDecoder = new HttpRequestDecoder(soConfig.maxInitialLineLength(),
soConfig.maxHeaderSize(),
8192,
soConfig.validateHeaders(),
soConfig.initialBufferSize());
if (serverConfig.isHttp2Enabled()) {
ExperimentalConfiguration experimental = serverConfig.experimental();
Http2Configuration http2Config = experimental.http2();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ class NettyWebServer implements WebServer {
bootstrap.option(ChannelOption.SO_RCVBUF, soConfig.receiveBufferSize());
}

HttpInitializer childHandler = new HttpInitializer(sslContext, namedRoutings.getOrDefault(name, routing), this);
HttpInitializer childHandler = new HttpInitializer(soConfig,
sslContext,
namedRoutings.getOrDefault(name, routing),
this);
initializers.add(childHandler);
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ public int receiveBufferSize() {
return socketConfig.receiveBufferSize();
}

@Override
public int maxHeaderSize() {
return socketConfig.maxHeaderSize();
}

@Override
public int maxInitialLineLength() {
return socketConfig.maxInitialLineLength();
}

@Override
public int maxChunkSize() {
return socketConfig.maxChunkSize();
}

@Override
public boolean validateHeaders() {
return socketConfig.validateHeaders();
}

@Override
public int initialBufferSize() {
return socketConfig.initialBufferSize();
}

@Override
public Tracer tracer() {
return tracer;
Expand Down Expand Up @@ -141,6 +166,11 @@ static class SocketConfig implements SocketConfiguration {
private final String name;
private final boolean enabled;
private final ClientAuthentication clientAuth;
private final int maxHeaderSize;
private final int maxInitialLineLength;
private final int maxChunkSize;
private final boolean validateHeaders;
private final int initialBufferSize;

/**
* Creates new instance.
Expand All @@ -153,6 +183,12 @@ static class SocketConfig implements SocketConfiguration {
this.backlog = builder.backlog() < 0 ? DEFAULT_BACKLOG_SIZE : builder.backlog();
this.timeoutMillis = Math.max(builder.timeoutMillis(), 0);
this.receiveBufferSize = Math.max(builder.receiveBufferSize(), 0);
this.maxHeaderSize = builder.maxHeaderSize();
this.maxInitialLineLength = builder.maxInitialLineLength();
this.maxChunkSize = builder.maxChunkSize();
this.validateHeaders = builder.validateHeaders();
this.initialBufferSize = builder.initialBufferSize();

WebServerTls webServerTls = builder.tlsConfig();
if (webServerTls.enabled()) {
this.sslContext = webServerTls.sslContext();
Expand Down Expand Up @@ -214,5 +250,30 @@ public String name() {
public boolean enabled() {
return enabled;
}

@Override
public int maxHeaderSize() {
return maxHeaderSize;
}

@Override
public int maxInitialLineLength() {
return maxInitialLineLength;
}

@Override
public int maxChunkSize() {
return maxChunkSize;
}

@Override
public boolean validateHeaders() {
return validateHeaders;
}

@Override
public int initialBufferSize() {
return initialBufferSize;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,18 @@ public Builder receiveBufferSize(int bytes) {
return this;
}

@Override
public Builder maxHeaderSize(int size) {
defaultSocketBuilder.maxHeaderSize(size);
return this;
}

@Override
public Builder maxInitialLineLength(int length) {
defaultSocketBuilder.maxInitialLineLength(length);
return this;
}

/**
* Adds an additional named server socket configuration. As a result, the server will listen
* on multiple ports.
Expand Down
Loading

0 comments on commit 545f8d8

Please sign in to comment.