All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.webserver.adoc Maven / Gradle / Ivy

The newest version!
///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2018, 2024 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.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

///////////////////////////////////////////////////////////////////////////////

= WebServer Introduction
:description: Helidon WebServer Introduction
:keywords: helidon, java, webserver, tls
:feature-name: WebServer
:rootdir: {docdir}/..
:requested-uri-discovery-inc: {rootdir}/includes/server/requested-uri-discovery.adoc

include::{rootdir}/includes/se.adoc[]

== Contents

- <>
- <>
- <>
** <>
** <>
** <>
** <>
*** <>
*** <>
*** <>
- <>
** <>
** <>
- <>
** <>
** <>
** <>
** <>
** <>
- <>
- <>

== Overview

WebServer provides an API for creating HTTP servers. It uses virtual threads and can handle nearly unlimited concurrent requests.

include::{rootdir}/includes/dependencies.adoc[]

[source,xml]
----

    io.helidon.webserver
    helidon-webserver

----

== Configuration

You can configure the WebServer either programmatically or by the Helidon configuration framework.

=== Configuring the WebServer in Your Code

The easiest way to configure the WebServer is in your
application code.

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_1, indent=0]
----

=== Configuring the WebServer in a Configuration File

You can also define the configuration in a file.

[source,yaml]
.WebServer configuration file `application.yaml`
----
server:
  port: 8080
  host: "0.0.0.0"
----

Then, in your application code, load the configuration from that file.

[source,java]
.WebServer initialization using the `application.yaml` file located on the classpath
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_2, indent=0]
----
<1> `application.yaml` is a default configuration source loaded when YAML support is on classpath, so we can
just use `Config.create()`
<2> Server expects the configuration tree located on the node of `server`

=== Configuring TLS

Configure TLS either programmatically, or by the Helidon configuration framework.

==== Configuring TLS in Your Code

To configure TLS in WebServer programmatically create your keystore configuration and pass it to the WebServer builder.

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_30, indent=0]
----


==== Configuring TLS in the Config File

It is also possible to configure TLS via the config file.

[source,yaml]
.WebServer TLS configuration file `application.yaml`
----
server:
  tls:
    #Truststore setup
    trust:
      keystore:
        passphrase: "password"
        trust-store: true
        resource:
          # load from classpath
          resource-path: "keystore.p12" # <1>
    # Keystore with private key and server certificate
    private-key:
      keystore:
        passphrase: "password"
        resource:
          # load from file system
          path: "/path/to/keystore.p12" # <2>
----
<1> File loaded from classpath.
<2> File loaded from file system.

Then, in your application code, load the configuration from that file.

[source,java]
.WebServer initialization using the `application.yaml` file located on the classpath
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_2, indent=0]
----
<1> `application.yaml` is a default configuration source loaded when YAML support is on classpath, so we can
just use `Config.create()`
<2> Server expects the configuration tree located on the node of `server`

Or you can only create WebServerTls instance based on the config file.

[source,java]
.WebServerTls instance based on `application.yaml` file located on the classpath
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_31, indent=0]
----

This can alternatively be configured with paths to PKCS#8 PEM files rather than KeyStores:

[source,yaml]
.WebServer TLS configuration file `application.yaml`
----
server:
  tls:
    #Truststore setup
    trust:
      pem:
        certificates:
          resource:
            resource-path: "ca-bundle.pem"
    private-key:
      pem:
        key:
          resource:
            resource-path: "key.pem"
        cert-chain:
          resource:
            resource-path: "chain.pem"
----

=== Configuration Options

include::{rootdir}/config/io_helidon_webserver_WebServer.adoc[leveloffset=+2,tag=config]

== Routing

Routing lets you use request matching criteria to bind requests to a `handler` that implements
your custom business logic. Matching criteria include one or more *HTTP Method(s)* and, optionally,
a request *path matcher*. Use the `RequestPredicate` class to specify more routing
criteria.

=== Routing Basics

Routing also supports _Error Routing_ which binds Java `Throwable` to the
handling logic.

Configure HTTP request routing using `HttpRouting.Builder`.

[source,java]
.Using HttpRouting.Builder to specify how HTTP requests are handled
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_3, indent=0]
----
<1> Handle all GETs to `/hello` path. Send the `Hello World!` string.
<2> Create a server instance with the provided routing

=== HTTP Method Routing

`HttpRouting.Builder` lets you specify how to handle each HTTP method. For example:

[cols="1,5", role="flex, sm7"]
|===
|HTTP Method|HttpRouting.Builder example

|*GET*|`+.get(handler)+`
|*PUT*| `+.put(handler)+`
|*POST*| `+.post(handler)+`
|*HEAD*| `+.head(handler)+`
|*DELETE*| `+.delete(handler)+`
|*TRACE*| `+.trace(handler)+`
|*OPTIONS*| `+.options(handler)+`
|_any method_|`+.any(handler)+`
|_multiple methods_|`+.route(Method.predicate(Method.GET, Method.POST), path, handler)+`
|_custom method_|`+.route(Method.create("CUSTOM"), handler)+`
|===

=== Path Matcher Routing
You can combine HTTP method routing with request path matching.

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_4, indent=0]
----

You can use *path pattern* instead of _path_ with the following syntax:

* `/foo/bar/baz` - Exact path match against resolved path even with non-usual characters
* `/foo/*` - convenience method to match `/foo` or any subpath (but not `/foobar`)
* `/foo/{}/baz` - `{}` Unnamed regular expression segment `([^/]+)`
* `/foo/{var}/baz` - Named regular expression segment `([^/]+)`
* `/foo/{var:\d+}` - Named regular expression segment with a specified expression
* `/foo/{:\d+}` - Unnamed regular expression segment with a specified expression
* `/foo/{+var}` - Convenience shortcut for `{var:.+}`
* `/foo/{+}` - Convenience shortcut for unnamed segment with regular expression `{:.+}`
* `/foo[/bar]` - An optional block, which translates to the `/foo(/bar)?` regular expression
* `/*` or `/foo*` - `*` Wildcard character can be matched with any number of characters.


IMPORTANT: Path (matcher) routing is *exact*. For example, a `/foo/bar` request is *not* routed to `.post('/foo', ...)`.

TIP: Always start _path_ and _path patterns_ with the `/` character.

For more precise setup of path, you can use factory methods on `io.helidon.http.PathMatchers`
and register using `HttpRouting.Builder.route(Predicate, PathMatcher, Handler)` method.

=== Using full `HttpRoute`

To have more control over selecting which requests should be handled by a specific route,
 you can use the `io.helidon.webserver.http.HttpRoute` interface using its `Builder`.

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_5, indent=0]
----
<1> The route is specified for `GET` and `POST` requests
<2> The handler consumes the request payload and echoes it back


=== Organizing Code into Services

By implementing the `io.helidon.webserver.http.HttpService` interface you can organize your code into one
or more services, each with its own path prefix and set of handlers.

[source,java]
.Use `HttpRouting.Builder.register` to register your service
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_6, indent=0]
----

[source,java]
.Service implementation
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_7, indent=0]
----
In this example, the `GET` handler matches requests to `/hello/subpath`.

[[anchor-http-feature]]
=== Using `HttpFeature`

By implementing the `io.helidon.webserver.http.HttpFeature` interface, you can organize multiple routes and/or filters into
a feature, that will be setup according to its defined `io.helidon.common.Weight` (or using `io.helidon.common.Weighted`).

Each service has access to the routing builder. HTTP Features are configured for each routing builder. If there is a need
to configure a feature for multiple sockets, you can use <> instead.

== Request Handling

Implement the logic to handle requests to WebServer in a `Handler`, which is a `FunctionalInterface`.
Handlers:

* Process the request and <> a response.
* Act as a filter and forward requests to downstream handlers using the `response.next()`
method.
* Throw an exception to begin <>.

=== Process Request and Produce Response
Each `Handler` has two parameters. `ServerRequest` and `ServerResponse`.

* Request provides access to the request method, URI, path, query parameters,
headers and entity.
* Response provides an ability to set response code, headers, and entity.

[[anchor-filtering]]
=== Filtering
Filtering can be done either using a dedicated `Filter`, or through routes.

==== Filter
You can register a `io.helidon.webserver.http.Filter` with HTTP routing to handle
filtering in interception style.

A simple filter example:
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_8, indent=0]
----

==== Routes

The handler forwards the request to the downstream handlers by
_nexting_. There are two options:

* call `res.next()`
+
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_9, indent=0]
----
<1> handler for any HTTP method using the `/hello` path
<2> business logic implementation
<3> forward the current request to the downstream handler

* throw an exception to forward to <>
+
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_10, indent=0]
----
<1> handler for any HTTP method using the `/hello` path
<2> custom logic
<3> forward the current request to the downstream handler
<4> forward the request to the error handler

[[anchor-sending-response]]
=== Sending a Response
To complete the request handling, you must send a response by calling the `res.send()` method.

IMPORTANT: one of the variants of `send` method MUST be invoked in the same thread the request is started in; as we run in Virtual Threads, you can simply wait for any asynchronous tasks that must complete before sending a response

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_11, indent=0]
----
<1> handler that terminates the request handling for any HTTP method using the `/hello` path
<2> send the response

== Protocol-Specific Routing
Handling routes based on the protocol version is possible by registering specific routes
on routing builder.

[source,java]
.Routing based on HTTP version
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_12, indent=0]
----

<1> An HTTP route registered on `/any-version` path that prints the version of HTTP protocol
<2> An HTTP/1.1 route registered on `/version-specific` path
<3> An HTTP/2 route registered on `/version-specific` path

While `Http1Route` for Http/1 is always available with Helidon webserver, other routes like `Http2Route` for <> needs to be added as additional dependency.

[[_requested_uri_discovery]]
== Requested URI Discovery
include::{requested-uri-discovery-inc}[tag=intro]

=== Setting Up Requested URI Discovery Programmatically
To set up requested URI discovery on the default socket for your server, use the link:{webserver-javadoc-base-url}/io/helidon/webserver/WebServerConfig.Builder.html[`WebServerConfig.Builder`]:
[source,java]
.Requested URI set-up for the default server socket
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_13, indent=0]
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_14, indent=0]

include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_15, indent=0]
----
<1> Create the `AllowList` describing the intermediate networks nodes to trust and not trust.
Presumably the `lbxxx.mycorp.com` nodes are trusted load balancers except for the test load balancer `lbtest`,
and no other nodes are trusted. `AllowList` accepts prefixes, suffixes, predicates, regex patterns, and exact matches.
See the link:{configurable-javadoc-base-url}/io/helidon/common/configurable/AllowList.html[`AllowList`] JavaDoc for complete information.
<2> Use `Forwarded` first, then try `X-Forwarded-*` on each request.
<3> Set the `AllowList` for trusted intermediaries.

If you build your server with additional sockets, you can control requested URI discovery separately for each.

=== Setting Up Requested URI Discovery using Configuration
include::{requested-uri-discovery-inc}[tag=config-example-intro]

[source,yaml]
.Configuring requested URI behavior
----
server:
  port: 0
  requested-uri-discovery:
    types: FORWARDED,X_FORWARDED
    trusted-proxies:
      allow:
        pattern: "lb.*\\.mycorp\\.com"
      deny:
        exact: "lbtest.mycorp.com""
----

=== Obtaining the Requested URI Information
Your code obtains the requested URI information from the Helidon server request object:

[source,java]
.Retrieving Requested URI Information
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_16, indent=0]

include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_17, indent=0]
----
See the link:{uri-javadoc-base-url}/io/helidon/common/uri/UriInfo.html[`UriInfo`] JavaDoc for more information.

== Error Handling

=== Error Routing

You may register an error handler for a specific `Throwable` in a
`HttpRouting.Builder` method.
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_18, indent=0]
----
<1> Registers an error handler that handles `MyException` that are thrown from the upstream handlers
<2> Finishes the request handling by sending a response

Error handlers are called when

* an exception is thrown from a handler

As with the standard handlers, the error handler must either

* send a response
+
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_19, indent=0]
----
* or throw an exception
+
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_20, indent=0]
----

Exceptions thrown from error handlers are not error handled, and will end up in an `InternalServerError`.

=== Default Error Handling
If no user-defined error handler is matched, or if the error handler of the
exception threw an exception, then the exception is translated to an HTTP response as follows:

* Subtypes of `HttpException` are translated to their associated HTTP error codes.
+
[source,java]
.Reply with the `406` HTTP error code by throwing an exception
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_21, indent=0]
----

* Otherwise, the exceptions are translated to an Internal Server Error HTTP
error code `500`.

=== Configuration Options

include::{rootdir}/config/io_helidon_common_tls_Tls.adoc[leveloffset=+2,tag=config]

== Server Features
Server features provide additional functionality to the WebServer, through modification of the server configuration,
listener configuration, or routing.

A server feature can be added by implementing `io.helidon.webserver.spi.ServerFeature`.
Server features support automated discovery, as long as the implementation is available through Java `ServiceLoader`.
Server features can also be added through configuration, as can be seen above in <>,
configuration key `features`.

All features (both `ServerFeature` and <>) honor weight of the feature
(defined either through `@Weight` annotation, or by implementing `Weighted` interface) when registering routes,
`HttpService`, or `Filter` to the routing.

The following table shows available server features and their weight. The highest weight is always registered (and invoked)
first.


|===
|Feature |Weight

|<>
|1100

|<>
|1000

|xref:tracing.adoc[Tracing]
|900

|xref:cors.adoc[CORS]
|850

|xref:security/introduction.adoc[Security]
|800

|Routing (all handlers and filters)
|100

|xref:openapi/openapi.adoc[OpenAPI]
|90

|xref:observability.adoc[Observability]
|80
|===

=== Context

Context feature adds a filter that executes all requests within the context of `io.helidon.common.context.Context`.
A `Context` instance is available on `ServerRequest` even if this feature is not added. This feature adds support for
obtaining request context through `io.helidon.common.context.Contexts.context()`.

This feature will provide the same behavior as previous versions of Helidon. Since Helidon 4.0.0, this feature is not
automatically added.

To enable execution of routes within Context, add the following dependency to project's `pom.xml`:

[source,xml]
----

    io.helidon.webserver
    helidon-webserver-context

----

Context feature can be configured, all options shown below are also available both in config, and programmatically
when using builder.

include::{rootdir}/config/io_helidon_webserver_context_ContextFeature.adoc[leveloffset=+1]

=== Access Log

Access logging in Helidon is done by a dedicated module that can be
added to WebServer and configured.

Access logging is a Helidon WebServer `ServerFeature`. Access Log feature has a
very high weight, so it is registered before other features (such as security) that may
terminate a request. This is to ensure the log contains all requests with appropriate status codes.

To enable Access logging add the following dependency to project's `pom.xml`:

[source,xml]
----

    io.helidon.webserver
    helidon-webserver-access-log

----

==== Configuring Access Log in Your Code

`AccessLogFeature` is discovered automatically by default, and configured through `server.features.access-log`.
You can also configure this feature in code by registering it with WebServer (which will replace the discovered feature).

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_29, indent=0]
----

==== Configuring Access Log in a Configuration File

Access log can be configured as follows:

[source, yaml]
.Access Log configuration file
----
server:
  port: 8080
  features:
    access-log:
      format: "%h %l %u %t %r %s %b %{Referer}i"
----

All options shown below are also available programmatically when using builder.

include::{rootdir}/config/io_helidon_webserver_accesslog_AccessLogConfig.adoc[leveloffset=+1]

== Supported Technologies

== HTTP/2 Support

Helidon supports HTTP/2 upgrade from HTTP/1, HTTP/2 without prior knowledge, HTTP/2 with prior knowledge, and HTTP/2 with ALPN over TLS.
HTTP/2 support is enabled in WebServer by default when it's artifact is available on classpath.

=== Maven Coordinates
To enable HTTP/2 support add the following dependency to your project's `pom.xml`.

[source,xml]
----

    io.helidon.webserver
    helidon-webserver-http2>

----

== Static Content Support

+Use the `io.helidon.webserver.staticcontent.StaticContentService` class to serve files and classpath resources.
`StaticContentService` can be created for any readable directory or classpath
context root and registered on a path in `HttpRouting`.

You can combine dynamic handlers with `StaticContentService` objects: if no file matches the request path, then the request is forwarded to
the next handler.

=== Maven Coordinates

To enable Static Content Support add the following dependency to your project's `pom.xml`.

[source,xml]
----

    io.helidon.webserver
    helidon-webserver-static-content

----

=== Registering Static Content

To register static content based on a file system (`/pictures`), and classpath (`/`):

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_22, indent=0]
----
<1> Create a new `StaticContentService` object to serve data from the file system,
and associate it with the `"/pictures"` context path.
<2> Create a `StaticContentService` object to serve resources from the contextual
`ClassLoader`. The specific classloader can be also
defined. A builder lets you provide more configuration values.
<3> `index.html` is the file that is returned if a directory is requested.

A `StaticContentService` object can be created using `create(...)` factory methods or a
`builder`. The `builder` lets you provide more configuration values, including _welcome file-name_
and mappings of filename extensions to media types.

== Media types support
WebServer and WebClient share the HTTP media support of Helidon, and any supported media type can be used in both.
The media type support is automatically discovered from classpath.
Programmatic support is of course enabled as well through `MediaContext`.

Customized media support for WebServer
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_23, indent=0]
----

Each registered (or discovered) media support adds support for writing and reading entities of a specific type.

The following table lists JSON media supports:

[cols="1,1,5,3", role="flex, sm7"]
|===
|Media type|TypeName|Maven groupId:artifactId|Supported Java type(s)

|*JSON-P*|JsonpSupport|`io.helidon.http.media:helidon-http-media-jsonp`|`JsonObject, JsonArray`
|*JSON-B*|JsonbSupport|`io.helidon.http.media:helidon-http-media-jsonb`|Any *
|*Jackson*|JacksonSupport|`io.helidon.http.media:helidon-http-media-jackson`|Any *
|===

* JSON-B and Jackson have lower weight, so they are used only when no other media type matched the object being written or read

=== JSON-P Support
The WebServer supports JSON-P. When enabled, you can send and
receive JSON-P objects transparently.

==== Maven Coordinates

To enable JSON Support add the following dependency to your project's `pom.xml`.

[source,xml]
----

    io.helidon.http.media
    helidon-http-media-jsonp

----

==== Usage

[source,java]
.Handler that receives and returns JSON objects
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_24, indent=0]

include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_25, indent=0]
----
<1> Using a `JsonBuilderFactory` is more efficient than `Json.createObjectBuilder()`
<2> Get the request entity as `JsonObject`
<3> Create a new `JsonObject` for the response entity
<4> Send `JsonObject` in response

[source,bash]
.Example of posting JSON to sayHello endpoint
----
curl --noproxy '*' -X POST -H "Content-Type: application/json" \
    http://localhost:8080/sayhello -d '{"name":"Joe"}'
----

[source, json]
.Response body
----
{"message":"Hello Joe"}
----

=== JSON-B Support
The WebServer supports the http://json-b.net/[JSON-B
specification]. When this support is enabled, Java objects will be
serialized to and deserialized from JSON automatically using
https://github.com/eclipse-ee4j/yasson[Yasson], an implementation of
the link:{jakarta-jsonb-spec-url}[JSON-B specification].

==== Maven Coordinates

To enable JSON-B Support add the following dependency to your project's `pom.xml`.

[source,xml]
----

    io.helidon.http.media
    helidon-http-media-jsonb

----

==== Usage

Now that automatic JSON serialization and deserialization facilities
have been set up, you can register a `Handler` that works with Java
objects instead of raw JSON.  Deserialization from and serialization
to JSON will be handled according to the
https://jcp.org/en/jsr/detail?id=367[JSON-B specification].

Suppose you have a `Person` class that looks like this:

[source,java]
.Hypothetical `Person` class
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_26, indent=0]
----

Then you can set up a `Handler` like this:

[source,java]
.A `Handler` that works with Java objects instead of raw JSON
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_27, indent=0]
----
<1> This handler consumes a `Person` instance and simply echoes it back.  Note that there is not working with raw JSON here.

[source,bash]
.Example of posting JSON to the `/echo` endpoint
----
curl --noproxy '*' -X POST -H "Content-Type: application/json" \
    http://localhost:8080/echo -d '{"name":"Joe"}'
{"name":"Joe"}
----

=== Jackson Support
The WebServer supports
https://github.com/FasterXML/jackson#jackson-project-home-github[Jackson].
When this support is enabled, Java objects will be serialized to and
deserialized from JSON automatically using Jackson.

==== Maven Coordinates

To enable Jackson Support add the following dependency to your project's `pom.xml`.

[source,xml]
----

    io.helidon.http.media
    helidon-http-media-jackson

----

==== Usage

Now that automatic JSON serialization and deserialization facilities
have been set up, you can register a `Handler` that works with Java
objects instead of raw JSON.  Deserialization from and serialization
to JSON will be handled by
https://github.com/FasterXML/jackson#jackson-project-home-github[Jackson].

Suppose you have a `Person` class that looks like this:

[source,java]
.Hypothetical `Person` class
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_26, indent=0]
----

Then you can set up a `Handler` like this:

[source,java]
.A `Handler` that works with Java objects instead of raw JSON
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_28, indent=0]
----
<1> This handler consumes a `Person` instance and simply echoes it back.  Note that there is no working with raw JSON here.

[source,bash]
.Example of posting JSON to the `/echo` endpoint
----
curl --noproxy '*' -X POST -H "Content-Type: application/json" \
    http://localhost:8080/echo -d '{"name":"Joe"}'
----

[source, json]
.Response body
----
{"name":"Joe"}
----

== HTTP Content Encoding

HTTP encoding can improve bandwidth utilization and transfer speeds in certain scenarios. It
requires a few extra CPU cycles for compressing and uncompressing, but these can be offset
if data is transferred over low-bandwidth network links.

A client advertises the compression encodings it supports at request time, and the
WebServer responds by selecting an encoding it supports and setting it in a header,
effectively _negotiating_ the content encoding of the response. If none of the
advertised encodings is supported by the WebServer, the response is returned
uncompressed.

=== Configuring HTTP Encoding

HTTP encoding support is discovered automatically by WebServer from the classpath, or it can be customized programmatically.

Encoding can be configured per socket.

Disabling discovery and registering a Gzip encoding support:
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_32, indent=0]
----

Or use a config file using the following options:

include::{rootdir}/config/io_helidon_http_encoding_ContentEncodingContext.adoc[leveloffset=+2,tag=config]

The following providers are currently available (simply add the library on the classpath):

[cols="1,2,5", role="flex, sm7"]
|===
|Encoding type|TypeName|Maven groupId:artifactId

|*gzip*|GzipEncoding|`io.helidon.http.encoding:helidon-http-encoding-gzip`
|*deflate*|DeflateSupport|`io.helidon.http.encoding:helidon-http-encoding-deflate`
|===

=== HTTP Compression Negotiation

HTTP compression negotiation is controlled by clients using the `Accept-Encoding` header.
The value of this header is a comma-separated list of encodings. The WebServer
will select one of these encodings for compression purposes; it currently supports `gzip`
and `deflate`.

For example, if the request includes `Accept-Encoding: gzip, deflate`, and HTTP compression
has been enabled as shown above, the response shall include the header `Content-Encoding: gzip`
and a compressed payload.

== Proxy Protocol Support

The link:https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt[Proxy Protocol]
provides a way to convey client information across reverse
proxies or load balancers which would otherwise be lost given that new connections
are established for each network hop. Often times, this information
can be carried in HTTP headers, but not all proxies support this feature.
Helidon is capable of
parsing a proxy protocol header (i.e., a network preamble) that is based on
either V1 or V2 of the protocol, thus making client information available to
service developers.

Proxy Protocol support is enabled via configuration, and
can be done either declaratively or programmatically. Once enabled, every new
connection on the corresponding port **MUST** be preambled by a proxy header
for the connection not to be rejected as invalid --that is, proxy headers are
never optional.

Programmatically, support for the Proxy Protocol is enabled as follows:
[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_33, indent=0]
----

Declaratively, support for the Proxy Protocol is enabled as follows:
[source,yaml]
----
server:
  port: 8080
  host: 0.0.0.0
  enable-proxy-protocol: true
----

=== Accessing Proxy Protocol Data

There are two ways in which the header data can be accessed in your application.
One way is by obtaining the protocol data directly from a request as shown
next:

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_34, indent=0]
----

NOTE: Every request associated with a certain connection shall have
access to the Proxy Protocol data received when the connection was opened.

Alternatively, the WebServer also makes the original client source
address and source port available in the HTTP headers `X-Forwarded-For`
and `X-Forwarded-Port`, respectively. In some cases, it is just simpler
to inspect these headers instead of getting the complete `ProxyProtocolData`
instance as shown above.

== Additional Information

Here is the code for a minimalist web application that runs on a random free port:

[source,java]
----
include::{sourcedir}/se/WebServerSnippets.java[tag=snippet_35, indent=0]
----

<1> For any kind of request, at any path, respond with `It works!`.
<2> Build the server with the provided configuration
<3> Start the server (and wait for it to open the port).
<4> The server is bound to a random free port.

== Reference

* link:{webserver-javadoc-base-url}/module-summary.html[Helidon WebServer JavaDoc]
* link:{webserver-staticcontent-javadoc-base-url}/module-summary.html[Helidon WebServer Static Content JavaDoc]
* link:{media-jsonp-javadoc-base-url}/module-summary.html[Helidon JSON-B Support JavaDoc]
* link:{media-jsonb-javadoc-base-url}/module-summary.html[Helidon JSON-P Support JavaDoc]
* link:{media-jackson-javadoc-base-url}/module-summary.html[Helidon Jackson Support JavaDoc]
* link:https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt[Proxy Protocol Specification]




© 2015 - 2024 Weber Informatics LLC | Privacy Policy