Skip to content

Latest commit

 

History

History
629 lines (506 loc) · 22 KB

http-server.adoc

File metadata and controls

629 lines (506 loc) · 22 KB

HTTP Server

Reactor Netty provides the easy-to-use and easy-to-configure HttpServer class. It hides most of the Netty functionality that is needed in order to create a HTTP server and adds Reactive Streams backpressure.

Starting and Stopping

To start an HTTP server, you must create and configure a HttpServer instance. By default, the host is configured for any local address, and the system picks up an ephemeral port when the bind operation is invoked. The following example shows how to create an HttpServer instance:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/create/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/create/Application.java[role=include]
  1. Creates an HttpServer instance ready for configuring.

  2. Starts the server in a blocking fashion and waits for it to finish initializing.

The returned DisposableServer offers a simple server API, including disposeNow(), which shuts the server down in a blocking fashion.

Host and Port

To serve on a specific host and port, you can apply the following configuration to the HTTP server:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/address/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/address/Application.java[role=include]
  1. Configures the HTTP server host

  2. Configures the HTTP server port

To serve on multiple addresses, after having configured the HttpServer you can bind it multiple times to obtain separate DisposableServer`s. All created servers will share resources such as `LoopResources because they use the same configuration instance under the hood.

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/address/MultiAddressApplication.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/address/MultiAddressApplication.java[role=include]
  1. Configures the first HTTP server host

  2. Configures the first HTTP server port

  3. Configures the second HTTP server host

  4. Configures the second HTTP server port

Eager Initialization

By default, the initialization of the HttpServer resources happens on demand. This means that the bind operation absorbs the extra time needed to initialize and load:

  • the event loop groups

  • the native transport libraries (when native transport is used)

  • the native libraries for the security (in case of OpenSsl)

When you need to preload these resources, you can configure the HttpServer as follows:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/warmup/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/warmup/Application.java[role=include]
  1. Initialize and load the event loop groups, the native transport libraries and the native libraries for the security

Routing HTTP

Defining routes for the HTTP server requires configuring the provided HttpServerRoutes builder. The following example shows how to do so:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/routing/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/routing/Application.java[role=include]
  1. Serves a GET request to /hello and returns Hello World!

  2. Serves a POST request to /echo and returns the received request body as a response.

  3. Serves a GET request to /path/{param} and returns the value of the path parameter.

  4. Serves websocket to /ws and returns the received incoming data as outgoing data.

Note
The server routes are unique and only the first matching in order of declaration is invoked.

SSE

The following code shows how you can configure the HTTP server to serve Server-Sent Events:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/sse/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/sse/Application.java[role=include]

Static Resources

The following code shows how you can configure the HTTP server to serve static resources:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/staticresources/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/staticresources/Application.java[role=include]

Writing Data

To send data to a connected client, you must attach an I/O handler by using either handle(…​) or route(…​). The I/O handler has access to HttpServerResponse, to be able to write data. The following example uses the handle(…​) method:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/send/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/send/Application.java[role=include]
  1. Sends hello string to the connected clients

Adding Headers and Other Metadata

When you send data to the connected clients, you may need to send additional headers, cookies, status code, and other metadata. You can provide this additional metadata by using HttpServerResponse. The following example shows how to do so:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/send/headers/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/send/headers/Application.java[role=include]

Compression

You can configure the HTTP server to send a compressed response, depending on the request header Accept-Encoding.

Reactor Netty provides three different strategies for compressing the outgoing data:

  • compress(boolean): Depending on the boolean that is provided, the compression is enabled (true) or disabled (false).

  • compress(int): The compression is performed once the response size exceeds the given value (in bytes).

  • compress(BiPredicate<HttpServerRequest, HttpServerResponse>): The compression is performed if the predicate returns true.

The following example uses the compress method (set to true) to enable compression:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/compression/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/compression/Application.java[role=include]

Consuming Data

To receive data from a connected client, you must attach an I/O handler by using either handle(…​) or route(…​). The I/O handler has access to HttpServerRequest, to be able to read data.

The following example uses the handle(…​) method:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/read/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/read/Application.java[role=include]
  1. Receives data from the connected clients

Reading Headers, URI Params, and other Metadata

When you receive data from the connected clients, you might need to check request headers, parameters, and other metadata. You can obtain this additional metadata by using HttpServerRequest. The following example shows how to do so:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/read/headers/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/read/headers/Application.java[role=include]

Reading Post Form or Multipart Data

When you receive data from the connected clients, you might want to access POST form (application/x-www-form-urlencoded) or multipart (multipart/form-data) data. You can obtain this data by using HttpServerRequest.

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/multipart/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/multipart/Application.java[role=include]
  1. Receives POST form/multipart data.

When you need to change the default settings, you can configure the HttpServer or you can provide a configuration per request:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/multipart/custom/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/multipart/custom/Application.java[role=include]
  1. Configuration on the HttpServer that specifies that the data is stored on disk only.

  2. Configuration per request that specifies that if the data size exceed the specified size, the data is stored on the disk.

The following listing shows the available configurations:

Configuration name Description

baseDirectory

Configures the directory where to store the data on the disk. Default to generated temp directory.

charset

Configures the Charset for the data. Default to StandardCharsets#UTF_8.

maxInMemorySize

Configures the maximum in-memory size per data i.e. the data is written on disk if the size is greater than maxInMemorySize, else it is in memory. If set to -1 the entire contents is stored in memory. If set to 0 the entire contents is stored on disk. Default to 16kb.

maxSize

Configures the maximum size per data. When the limit is reached, an exception is raised. If set to -1 this means no limitation. Default to -1 - unlimited.

scheduler

Configures the scheduler to be used for offloading disk operations in the decoding phase. Default to Schedulers#boundedElastic()

streaming

When set to true, the data is streamed directly from the parsed input buffer stream, which means it is not stored either in memory or file. When false, parts are backed by in-memory and/or file storage. Default to false. NOTE that with streaming enabled, the provided data might not be in a complete state i.e. HttpData#isCompleted() has to be checked. Also note that enabling this property effectively ignores maxInMemorySize, baseDirectory, and scheduler.

Obtaining the Remote (Client) Address

In addition to the metadata that you can obtain from the request, you can also receive the host (server) address, the remote (client) address and the scheme. Depending on the chosen factory method, you can retrieve the information directly from the channel or by using the Forwarded or X-Forwarded-* HTTP request headers. The following example shows how to do so:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/clientaddress/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/clientaddress/Application.java[role=include]
  1. Specifies that the information about the connection is to be obtained from the Forwarded and X-Forwarded-* HTTP request headers, if possible.

  2. Returns the address of the remote (client) peer.

It is also possible to customize the behavior of the Forwarded or X-Forwarded-* header handler. The following example shows how to do so:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/clientaddress/CustomForwardedHeaderHandlerApplication.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/clientaddress/CustomForwardedHeaderHandlerApplication.java[role=include]
  1. Add a custom header handler.

  2. Returns the address of the remote (client) peer.

HTTP Request Decoder

By default, Netty configures some restrictions for the incoming requests, such as:

  • The maximum length of the initial line.

  • The maximum length of all headers.

  • The maximum length of the content or each chunk.

For more information, see HttpRequestDecoder and HttpServerUpgradeHandler

By default, the HTTP server is configured with the following settings:

./../../reactor-netty-http/src/main/java/reactor/netty/http/HttpDecoderSpec.java
link:./../../reactor-netty-http/src/main/java/reactor/netty/http/HttpDecoderSpec.java[role=include]
./../../reactor-netty-http/src/main/java/reactor/netty/http/server/HttpRequestDecoderSpec.java
link:./../../reactor-netty-http/src/main/java/reactor/netty/http/server/HttpRequestDecoderSpec.java[role=include]

When you need to change these default settings, you can configure the HTTP server as follows:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/requestdecoder/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/requestdecoder/Application.java[role=include]
  1. The maximum length of all headers will be 16384. When this value is exceeded, a TooLongFrameException is raised.

Lifecycle Callbacks

The following lifecycle callbacks are provided to let you extend the HttpServer:

Callback Description

doOnBind

Invoked when the server channel is about to bind.

doOnBound

Invoked when the server channel is bound.

doOnChannelInit

Invoked when initializing the channel.

doOnConnection

Invoked when a remote client is connected

doOnUnbound

Invoked when the server channel is unbound.

The following example uses the doOnConnection and doOnChannelInit callbacks:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/lifecycle/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/lifecycle/Application.java[role=include]
  1. Netty pipeline is extended with ReadTimeoutHandler when a remote client is connected.

  2. Netty pipeline is extended with LoggingHandler when initializing the channel.

TCP-level Configuration

When you need to change configuration on the TCP level, you can use the following snippet to extend the default TCP server configuration:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/channeloptions/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/channeloptions/Application.java[role=include]

See [tcp-server] for more detail about TCP-level configuration.

SSL and TLS

When you need SSL or TLS, you can apply the configuration shown in the next example. By default, if OpenSSL is available, SslProvider.OPENSSL provider is used as a provider. Otherwise SslProvider.JDK is used. You can switch the provider by using SslContextBuilder or by setting -Dio.netty.handler.ssl.noOpenSsl=true.

The following example uses SslContextBuilder:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/security/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/security/Application.java[role=include]

Server Name Indication

You can configure the HTTP server with multiple SslContext mapped to a specific domain. An exact domain name or a domain name containing a wildcard can be used when configuring the SNI mapping.

The following example uses a domain name containing a wildcard:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/sni/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/sni/Application.java[role=include]

HTTP Access Log

You can enable the HTTP access log either programmatically or by configuration. By default, it is disabled.

You can use -Dreactor.netty.http.server.accessLogEnabled=true to enable the HTTP access log by configuration.

You can use the following configuration (for Logback or similar logging frameworks) to have a separate HTTP access log file:

<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
    <file>access_log.log</file>
    <encoder>
        <pattern>%msg%n</pattern>
    </encoder>
</appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="accessLog" />
</appender>

<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
    <appender-ref ref="async"/>
</logger>

The following example enables it programmatically:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/Application.java[role=include]

Calling this method takes precedence over the system property configuration.

By default, the logging format is Common Log Format, but you can specify a custom one as a parameter, as in the following example:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/CustomLogAccessFormatApplication.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/CustomLogAccessFormatApplication.java[role=include]

You can also filter HTTP access logs by using the AccessLogFactory#createFilter method, as in the following example:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/FilterLogAccessApplication.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/FilterLogAccessApplication.java[role=include]

Note that this method can take a custom format parameter too, as in this example:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/CustomFormatAndFilterAccessLogApplication.java.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/accessLog/CustomFormatAndFilterAccessLogApplication.java[role=include]
  1. Specifies the filter predicate to use

  2. Specifies the custom format to apply

HTTP/2

By default, the HTTP server supports HTTP/1.1. If you need HTTP/2, you can get it through configuration. In addition to the protocol configuration, if you need H2 but not H2C (cleartext), you must also configure SSL.

Note
As Application-Layer Protocol Negotiation (ALPN) is not supported “out-of-the-box” by JDK8 (although some vendors backported ALPN to JDK8), you need an additional dependency to a native library that supports it — for example, netty-tcnative-boringssl-static.

The following listing presents a simple H2 example:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/http2/H2Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/http2/H2Application.java[role=include]
  1. Configures the server to support only HTTP/2

  2. Configures SSL

The application should now behave as follows:

$ curl --http2 https://localhost:8080 -i
HTTP/2 200

hello

The following listing presents a simple H2C example:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/http2/H2CApplication.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/http2/H2CApplication.java[role=include]

The application should now behave as follows:

$ curl --http2-prior-knowledge http://localhost:8080 -i
HTTP/2 200

hello

Protocol Selection

./../../reactor-netty-http/src/main/java/reactor/netty/http/HttpProtocol.java
link:./../../reactor-netty-http/src/main/java/reactor/netty/http/HttpProtocol.java[role=include]

Metrics

The HTTP server supports built-in integration with Micrometer. It exposes all metrics with a prefix of reactor.netty.http.server.

The following table provides information for the HTTP server metrics:

metric name type description

reactor.netty.http.server.connections.active

Gauge

The number of http connections currently processing requests. See [observability-metrics-connections-active]

reactor.netty.http.server.connections.total

Gauge

The number of all opened connections. See [observability-metrics-connections-total]

reactor.netty.http.server.data.received

DistributionSummary

Amount of the data received, in bytes. See [observability-metrics-data-received]

reactor.netty.http.server.data.sent

DistributionSummary

Amount of the data sent, in bytes. See [observability-metrics-data-sent]

reactor.netty.http.server.errors

Counter

Number of errors that occurred. See [observability-metrics-errors-count]

reactor.netty.http.server.data.received.time

Timer

Time spent in consuming incoming data. See [observability-metrics-http-server-data-received-time]

reactor.netty.http.server.data.sent.time

Timer

Time spent in sending outgoing data. See [observability-metrics-http-server-data-sent-time]

reactor.netty.http.server.response.time

Timer

Total time for the request/response See [observability-metrics-http-server-response-time]

These additional metrics are also available:

The following example enables that integration:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/metrics/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/metrics/Application.java[role=include]
  1. Applies upper limit for the meters with URI tag

  2. Templated URIs will be used as an URI tag value when possible

  3. Enables the built-in integration with Micrometer

Note
In order to avoid a memory and CPU overhead of the enabled metrics, it is important to convert the real URIs to templated URIs when possible. Without a conversion to a template-like form, each distinct URI leads to the creation of a distinct tag, which takes a lot of memory for the metrics.
Note
Always apply an upper limit for the meters with URI tags. Configuring an upper limit on the number of meters can help in cases when the real URIs cannot be templated. You can find more information at maximumAllowableTags.

When HTTP server metrics are needed for an integration with a system other than Micrometer or you want to provide your own integration with Micrometer, you can provide your own metrics recorder, as follows:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/metrics/custom/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/metrics/custom/Application.java[role=include]
  1. Enables HTTP server metrics and provides HttpServerMetricsRecorder implementation.

Unix Domain Sockets

The HTTP server supports Unix Domain Sockets (UDS) when native transport is in use.

The following example shows how to use UDS support:

./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/uds/Application.java
link:./../../reactor-netty-examples/src/main/java/reactor/netty/examples/documentation/http/server/uds/Application.java[role=include]
  1. Specifies DomainSocketAddress that will be used