WebClient
Overview
WebClient is an HTTP client for Helidon. It can be used to send requests and retrieve corresponding responses in a programmatic way.
Helidon WebClient provides the following features:
- Blocking approach The WebClient uses the blocking approach to
synchronously process a request and its corresponding response. Both
HTTP/1.1andHTTP/2request and response will run in the thread of the user. Additionally, forHTTP/2, virtual thread is employed to manage the connection. - Builder-like setup and execution Creates every client and request as a builder pattern. This improves readability and code maintenance.
- Redirect chain Follows the redirect chain and perform requests on the correct endpoint by itself.
- Tracing and security propagation Automatically propagates the configured tracing and security settings of the Helidon WebServer to the WebClient and uses them during request and response.
Maven Coordinates
To enable WebClient, add the following dependency to your project’s pom.xml
(see Managing Dependencies).
<dependency>
<groupId>io.helidon.webclient</groupId>
<artifactId>helidon-webclient</artifactId>
</dependency>
The helidon-webclient dependency has built-in support for HTTP/1.1.
If support for HTTP/2 is a requirement, below dependency needs to be added:
<dependency>
<groupId>io.helidon.webclient</groupId>
<artifactId>helidon-webclient-http2</artifactId>
</dependency>
Usage
Instantiating the WebClient
You can create an instance of a WebClient by executing WebClient.create()
which will have default settings and without a base uri set.
To change the default settings and register additional services, you can use simple builder that allows you to customize the client behavior.
Create a WebClient with simple builder:
WebClient client = WebClient.builder()
.baseUri("http://localhost")
.build();
Alt-Svc Discovery
Client Alt-Svc support is a preview feature. WebClient uses alternative
services only when the optional common ClientAltSvcConfig (alt-svc)
configuration is present. Within a present configuration, enabled defaults
to true; setting it to false provides an explicit override without removing
the configuration. An empty protocols list allows every available client
protocol provider that supports Alt-Svc. Otherwise, the list is an exact,
case-sensitive ALPN protocol filter. The HTTP/2 protocol ID is h2.
Initial client support accepts advertisements only from https origins and
only for alternatives on the same host; the alternative port may differ. Using
an alternative changes the connection endpoint, not the request scheme or
authority. WebClient does not support h2c alternatives. It also does not
upgrade a plain http origin to TLS-based HTTP/2 because the RFC 8164
/.well-known/http-opportunistic opt-in is not implemented.
Alternative-service routing never bypasses the configured proxy policy. The
current HTTP/2 provider uses alternatives only when proxying is disabled. When
any proxy policy is configured, including a no-proxy exception that selected
a direct route for the origin, WebClient ignores the advertisement and
continues with the selected route. WebClient uses the system proxy policy by
default; configure proxy.type as none, or use Proxy.noProxy()
programmatically, to use an advertised alternative.
Requests with caller-supplied connections or Unix domain socket transport
addresses do not learn or use alternative services. An InetSocketAddress
supplied through client base-address or request address updates the request
URI host and port, so Alt-Svc applies to that resulting origin.
WebClient honors the configured TLS policy as-is when connecting to an
alternative. This includes custom TLS managers, custom SSL contexts, disabled
endpoint identification, and trust-all. An unsafe or permissive TLS
configuration therefore makes Alt-Svc steering equally unsafe or permissive.
Configure TLS trust and endpoint identification according to the security
requirements of the application.
The HTTP/2 provider accounts for Age and apparent age derived from Date,
honors ma, clear, and persist, and falls back to the origin if an
advertised endpoint cannot be established. Advertisement freshness controls
creation of new connections; an already-opening or reusable connection can
continue after the advertisement expires. Discovery state belongs to the
HTTP/2 connection cache. Shared connection caches share that state; disabling
connection-cache sharing isolates it. persist does not make discovery state
durable across a process or beyond that cache lifecycle.
Creating the Request
WebClient offers a set of request methods that are used to specify the type of action to be performed on a given resource. Below are some examples of request methods:
get()post()put()method(Method method)
Check out HttpClient API to learn more about request methods. These methods will create a new instance of HttpClientRequest which can then be configured to add optional settings that will customize the behavior of the request.
Use the generic method API for a QUERY request and submit the query as request content with its media type:
var response = client.method(Method.QUERY)
.uri("http://example.com/search")
.contentType(MediaTypes.APPLICATION_JSON)
.submit(query);
When redirects are enabled, WebClient preserves the request method and entity
for 307 and 308 responses, and for QUERY requests receiving a 301 or
302 response. Other followed redirects change the request method to GET and
discard the entity; a 304 response is not treated as a redirect. In
particular, a 303 response changes a QUERY request to GET without the
original query content, as required by
RFC 10008.
For a redirect that preserves the method and entity, WebClient copies only the
Accept, Accept-Charset, Accept-Encoding, Accept-Language,
Content-Encoding, Content-Language, Content-Location, and Content-Type
headers from the preceding request. A redirect that changes the method does
not copy request-specific headers. Each redirected request still starts with
the client default headers, invokes the configured client services, and
selects stored cookies for the target URI.
Two URIs have the same origin when their schemes and hosts match
case-insensitively and their ports are equal. WebClient follows a same-origin
redirect that preserves a non-empty entity, but rejects such a redirect across
origins by default. Set follow-cross-origin-entity-redirects to true only
when every possible redirect target is trusted.
With redirect header filtering enabled, a cross-origin redirect removes the
configured redirect-sensitive headers, including Authorization, Cookie,
and Proxy-Authorization. This filtering remains active for later hops after
a redirect chain crosses an origin boundary. Disabling the filtering does not
change which request-specific headers WebClient copies from the preceding
request.
Customizing the Request
Configuration can be set for every request type before it is sent.
Customizing a request:
For more information about these optional parameters, check out ClientRequestBase API, which is a parent class of HttpClientRequest.
HttpClientRequest class also provides specific header methods that help the user to set a particular header. Some examples of these are:
contentType(MediaType contentType)accept(MediaType... mediaTypes)
For more information about these methods, check out ClientRequest API, which is a parent class of HttpClientRequest.
Sending the Request
Once the request setup is completed, the following methods can be used to send it:
HttpClientResponse request()<E> ClientResponseTyped<E> request(Class<E> type)<E> E requestEntity(Class<E> type)HttpClientResponse submit(Object entity)<T> ClientResponseTyped<T> submit(Object entity, Class<T> requestedType)HttpClientResponse outputStream(OutputStreamHandler outputStreamConsumer)<T> ClientResponseTyped<T> outputStream(OutputStreamHandler outputStreamConsumer, Class<T> requestedType)
Each of the methods will provide a way to allow response to be retrieved in a particular response type. Refer to ClientRequest API for more details about these methods.
Execute a simple GET request to endpoint and receive a String response:
ClientResponseTyped<String> response = client.get()
.path("/endpoint")
.request(String.class);
String entityString = response.entity();
Protocol Used
WebClient currently supports HTTP/1.1 and HTTP/2 protocols. Below are the
rules on which specific protocol will be used:
- Using plain socket triggers WebClient to process a request using
HTTP/1.1. - When using TLS, the client will use ALPN (protocol negotiation) to use
appropriate HTTP version (either 1.1, or 2).
HTTP/2has a higher weight, so it is chosen if supported by both sides. - A specific protocol can be explicitly selected by calling
HttpClientRequest#protocolId(String).String result = client.get() .protocolId("http/1.1") .requestEntity(String.class); - If
HTTP/2is used, an upgrade attempt will be performed. If it fails, the client falls-back toHTTP/1.1. - The parameter
prior-knowledgecan be defined usingHTTP/2protocol configuration. Please refer to Setting Protocol configuration on how to customizeHTTP/2. In such a case,prior-knowledgewill be used and fail if it is unable to switch toHTTP/2. - When the common
alt-svcconfiguration is present, enabled, and allows the exacth2protocol ID, anhttpsorigin can advertise a same-host TLS HTTP/2 alternative. A later generic WebClient request can use that alternative while retaining the original request authority. WebClient addsAlt-Usedonly to the request sent to the alternative. See Alt-Svc Discovery for current limitations and TLS, proxy, and lifecycle behavior.
Adding Media Support
WebClient supports the following built-in Helidon Media Support libraries:
- JSON Processing (JSON-P)
- JSON Binding (JSON-B)
- Jackson
They can be activated by adding their corresponding libraries into the classpath. This can simply be done by adding their corresponding dependencies.
Add JSON-P support:
<dependency>
<groupId>io.helidon.http.media</groupId>
<artifactId>helidon-http-media-jsonp</artifactId>
</dependency>
Add JSON-B support:
<dependency>
<groupId>io.helidon.http.media</groupId>
<artifactId>helidon-http-media-jsonb</artifactId>
</dependency>
Add Jackson support:
<dependency>
<groupId>io.helidon.http.media</groupId>
<artifactId>helidon-http-media-jackson</artifactId>
</dependency>
Users can also create their own Custom Media Support library and make them work by following either of the approaches:
- Create a Provider of the Custom Media Support and expose it via Service Loader followed by adding the Media Support library to the classpath.
- Explicitly register the Custom Media Support from WebClient.
DNS Resolving
WebClient provides three DNS resolver implementations out of the box:
Java DNS resolutionis the default.First DNS resolutionuses the first IP address from a DNS lookup. To enable this option, add below dependency:
<dependency>
<groupId>io.helidon.webclient.dns.resolver</groupId>
<artifactId>helidon-webclient-dns-resolver-first</artifactId>
</dependency>
Round-Robin DNS resolutioncycles through IP addresses from a DNS lookup. To enable this option, add this dependency:
<dependency>
<groupId>io.helidon.webclient.dns.resolver</groupId>
<artifactId>helidon-webclient-dns-resolver-round-robin</artifactId>
</dependency>
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
default- | Map< | Default headers to be used in every request from configuration | |
follow- | Boolean | true | Whether to follow redirects |
read- | Duration | Read timeout | |
content- | Content | Configure the listener specific io. | |
media- | Media | create( | Configure the listener specific io. |
media- | Parser | STRICT | Configure media type parsing mode for HTTP Content- header |
keep- | Boolean | true | Determines if connection keep alive is enabled (NOT socket keep alive, but HTTP connection keep alive, to re-use the same connection for multiple requests) |
share- | Boolean | true | Whether to use protocol-specific JVM-wide connection caches shared by compatible WebClient instances |
socket- | Socket | Socket options for connections opened by this client | |
follow- | Boolean | false | Whether redirects that preserve request method and entity may be followed across origins |
redirect- | List< | Request header names to strip on cross-origin redirects | |
read- | Duration | PT1S | Socket 100-Continue read timeout |
base- | Http | Base uri used by the client in all requests | |
connection- | Integer | 256 | Maximum number of reusable physical connections each HTTP protocol retains for a single connection target |
base- | Http | Base address used by the client in all requests | |
cookie- | Web | WebClient cookie manager | |
services | Map< | WebClient services | |
protocol- | List< | List of HTTP protocol IDs by order of preference | |
filter- | Boolean | true | Whether headers sensitive to cross-origin redirects should be filtered before the redirected request is sent |
relative- | Boolean | false | Can be set to true to force the use of relative URIs in all requests, regardless of the presence or absence of proxies or no-proxy lists |
send- | Boolean | true | Whether Expect-100-Continue header is sent to verify server availability before sending an entity |
sni | Sni | Client-side TLS SNI configuration | |
connect- | Duration | Connect timeout | |
proxy | Proxy | Proxy configuration to be used for requests | |
max- | Integer | 131072 | If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance |
services- | Boolean | true | Whether to enable automatic service discovery for services |
max- | Integer | 10 | Max number of followed redirects |
protocol- | Map< | Configuration of client protocols | |
protocol- | Boolean | true | Whether to enable automatic service discovery for protocol- |
tls | Tls | TLS configuration for any TLS request from this client | |
write- | Integer | 4096 | Buffer size used when writing data to the underlying socket on a client TCP connection |
alt- | Client | Client policy for accepting and using HTTP Alt- response advertisements | |
properties | Map< | Properties configured for this client |
Registry-managed WebClients
The service registry provides a default WebClient even when no clients are
configured. The unqualified WebClient service and the client named @default
refer to the same instance.
Configure the default client and additional named clients under the clients
root:
clients:
"@default":
base-uri: "https://default.example"
inventory:
base-uri: "https://inventory.example"
Inject the default client without a qualifier and named clients using
@Service.Named:
@Service.Singleton
class ClientConsumer {
private final WebClient defaultClient;
private final WebClient inventoryClient;
@Service.Inject
ClientConsumer(WebClient defaultClient,
@Service.Named("inventory") WebClient inventoryClient) {
this.defaultClient = defaultClient;
this.inventoryClient = inventoryClient;
}
}
The same clients can be obtained programmatically:
WebClient defaultClient = serviceRegistry.get(WebClient.class);
WebClient inventoryClient = serviceRegistry.getNamed(WebClient.class, "inventory");
The clients root defines registry-managed WebClient instances. The singular
client root used in the examples below is passed explicitly to a WebClient
builder and does not define registry services.
Protocol Configuration
Protocol specific configuration can be set using the protocol-configs
parameter. WebClient currently supports HTTP/1.1. and HTTP/2.
HTTP1 Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
validate- | Boolean | true | Whether to validate response headers |
max- | Size | 64 KB | Configure the maximum size allowed for an entity that can be explicitly buffered by the application by calling io. |
log | Http | HTTP Log configuration | |
validate- | Boolean | true | Whether to validate request headers |
max- | Integer | 256 | Configure the maximum allowed length of the status line from the response |
name | String | http_ | Name of this protocol configuration |
max- | Integer | 16384 | Configure the maximum allowed headers size, which must be greater than 0 |
default- | Boolean | true | Whether to use keep alive by default |
Deprecated Options
| Key | Type | Default | Description |
|---|---|---|---|
max- | Integer | 16384 | Configure the maximum allowed headers size of the response, which must be greater than 0 |
HTTP2 Configuration options
See HTTP/2 configuration options.
Example of a WebClient Runtime Configuration
Config config = Config.create();
WebClient client = WebClient.builder()
.baseUri("http://localhost")
.config(config.get("client"))
.build();
Configuration Example
Configured WebClient metric method names use exact, case-sensitive matching.
For example, get matches only a request method whose text is get, not GET.
Examples
WebClient with Proxy
Proxy can be set directly from WebClient builder.
WebClient.builder()
.proxy(Proxy.builder()
.type(Proxy.ProxyType.HTTP)
.host(PROXY_HOST)
.port(PROXY_PORT)
.build())
.build();
Alternative is to set proxy directly from the request via HttpClientRequest.
Proxy can also be configured in WebClient through the application.yaml
configuration file.
client:
proxy:
host: "hostName"
port: 80
no-proxy: ["localhost:8080", ".helidon.io", "192.168.1.1"]
Then, in your application code, load the configuration from that file.
WebClient initialization using the application.yaml file located on the
classpath:
var config = Config.create();
WebClient.builder()
.config(config.get("client"))
.build();
application.yamlis a default configuration source loaded when YAML support is on classpath, so we can just useConfig.create()- Passing the client configuration node
WebClient TLS Setup
Configure TLS either programmatically or by the Helidon configuration framework.
Configuring TLS in your code
One way to configure TLS in WebClient is in your application code as shown below.
WebClient.builder()
.tls(it -> it.trust(t -> t
.keystore(k -> k.passphrase("password")
.trustStore(true)
.keystore(r -> r.resourcePath("client.p12")))))
.build();
Configuring TLS in the config file
Another way to configure TLS in WebClient is through the application.yaml
configuration file.
WebClient TLS configuration in application.yaml:
client:
tls:
trust:
keystore:
passphrase: "password"
trust-store: true
resource:
resource-path: "client.p12"
In the application code, load the settings from the configuration file.
WebClient initialization using the application.yaml file located on the
classpath:
Configuring TLS SNI
When sni is not configured, WebClient preserves the configured TLS
SSLParameters server names and uses the JDK TLS defaults for server name
indication. Configure sni when a client or request must choose the TLS peer
host explicitly. Request-level SNI overrides client-level SNI. If sni is
configured without a mode, WebClient uses the resolved request URI host.
When WebClient chooses a DNS name, it also sends that name as SNI. When it
chooses an IP literal, it uses the IP literal as the TLS peer host for endpoint
identification but does not send a DNS SNI server name. The disabled mode
clears the SNI server name without disabling endpoint identification.
WebClient SNI configuration in application.yaml:
client:
sni:
mode: "host-header" # uri-host, host-header, explicit, or disabled
The uri-host mode uses the resolved request URI host. The host-header mode
uses the effective HTTP Host authority. HTTP/2 requests use the same effective
authority for the generated :authority pseudo header. The explicit mode
uses host as the TLS peer host. Configure host only with explicit mode.
The configured value must be a host without a port; bracketed IPv6 literals are
accepted and normalized without brackets. The disabled mode clears the SNI
server name.
WebClient explicit SNI configuration in application.yaml:
client:
sni:
mode: "explicit"
host: "service.example"
Configuring TLS over Unix Domain Sockets
When base-address is a Unix domain socket address, it selects only the
physical transport. Configure base-uri or use absolute request URIs to provide
the logical HTTPS authority. The logical host and port are used for the Host
header, HTTP/2 :authority, SNI, and endpoint identification.
WebClient TLS over Unix domain socket configuration in application.yaml:
client:
base-uri: "https://service.example:8443"
base-address: "unix:/var/run/service.sock"
tls:
trust:
keystore:
passphrase: "password"
trust-store: true
resource:
resource-path: "client.p12"
Adding Service to WebClient
WebClient currently supports several built-in services, namely
discoverymetricstracingtelemetry(following OpenTelemetry semantic conventions)metricstracing
security.
Enabling the service
In order for a service to function, its dependencies need to be added in the
application’s pom.xml. Below are examples on how to enable the built-in
services:
discovery(see its documentation)pom.xml<dependency> <groupId>io.helidon.webclient</groupId> <artifactId>helidon-webclient-discovery</artifactId> <scope>runtime</scope> </dependency>metricspom.xml<dependency> <groupId>io.helidon.webclient</groupId> <artifactId>helidon-webclient-metrics</artifactId> </dependency>tracingpom.xml<dependency> <groupId>io.helidon.webclient</groupId> <artifactId>helidon-webclient-tracing</artifactId> </dependency>telemetry metricsandtracingpom.xml<dependency> <groupId>io.helidon.webclient</groupId> <artifactId>helidon-webclient-telemetry</artifactId> </dependency>securitypom.xml<dependency> <groupId>io.helidon.webclient</groupId> <artifactId>helidon-webclient-security</artifactId> </dependency>
Adding a service in your code
Services can be added in WebClient as shown in the code below.
Adding service in the config file
Adding service in WebClient can also be done through the application.yaml
configuration file.
WebClient Service configuration in application.yaml:
webclient:
services:
metrics:
- type: METER
name-format: "client.meter.overall"
- type: TIMER
# meter per method
name-format: "client.meter.%1$s"
- methods: ["PUT", "POST", "DELETE"]
type: COUNTER
success: false
name-format: "wc.counter.%1$s.error"
description: "Counter of failed PUT, POST and DELETE requests"
tracing:
Then, in your application code, load the configuration from that file.
WebClient initialization using the application.yaml file located on the
classpath:
Setting Protocol configuration
Individual protocols can be customized using the protocol-config parameter.
Setting up protocol configuration in your code
Below is an example of customizing HTTP/1.1 protocol in the application code.
WebClient.builder()
.addProtocolConfig(Http1ClientProtocolConfig.builder()
.defaultKeepAlive(false)
.validateRequestHeaders(true)
.validateResponseHeaders(false)
.build())
.build();
Setting up protocol configuration in the config file
Protocol configuration can also be set in the application.yaml configuration
file.
Setting up HTTP/1.1 and HTTP/2 protocol using application.yaml file:
webclient:
protocol-configs:
http_1_1:
max-headers-size: 20000
validate-request-headers: true
h2:
prior-knowledge: true
Then, in your application code, load the configuration from that file.
WebClient initialization using the application.yaml file located on the
classpath:
Configuring Telemetry
The telemetry webclient services provide metrics and tracing spans which follow
the OpenTelemetry semantic conventions for clients. These are separate from the
services.metrics and services.tracing services described elsewhere on this
page.
To enable the telemetry webclient services, take the following two steps:
- Add the appropriate dependency.
- Add configuration or code to activate the telemetry services.
To set up metrics and tracing, add the following single dependency to your project:
Dependency for webclient telemetry metrics and tracing:
<dependency>
<groupId>io.helidon.webclient</groupId>
<artifactId>helidon-webclient-telemetry</artifactId>
<scope>runtime</scope>
</dependency>
To transmit the metrics semantic conventions to a backend, add a dependency on
an OpenTelemetry exporter and in the telemetry configuration set up an
exporter under signals.metrics.
Dependency for exporting metrics semantic conventions data using OTLP:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<scope>runtime</scope>
</dependency>
Configuration for an OpenTelemetry exporter:
telemetry:
service: my-app
signals:
metrics:
exporters:
type: otlp
To activate webclient telemetry collection using configuration, add the
telemetry config section under client.services and, below it, add metrics,
tracing, or both.
Enabling metrics and tracing telemetry using configuration:
client:
services:
telemetry:
metrics:
tracing:
The metrics and tracing subsections have no explicit settings.
Telemetry method classification is exact and case-sensitive. Recognized
standard methods use their exact names. Other methods, including case variants
such as get, use _OTHER for the method attribute. Tracing also records the
received method in http.request.method_original and uses HTTP as the method
portion of the span name.
Alternatively, trigger webclient telemetry collection by modifying your client code to add one or more webclient telemetry services to the webclient builder. This example shows adding only telemetry metrics.
Enabling telemetry using code:
WebClient.builder()
.addService(WebClientTelemetryMetrics.create())
.build();
Context Propagation
WebClient supports the capability to propagate values from
io.helidon.common.context.Context over HTTP headers.
To enable this feature (implemented as a WebClient service), add the following dependency to your pom file:
<dependency>
<groupId>io.helidon.webclient</groupId>
<artifactId>helidon-webclient-context</artifactId>
</dependency>
Example configuration:
Configuration options
| Key | Type | Description |
|---|---|---|
records | List< | List of propagation records |
See the manifest for all available types.
Reference
- Helidon WebClient API
- Helidon WebClient HTTP/1.1 Support
- Helidon WebClient HTTP/2 Support
- Helidon WebClient DNS Resolver First Support
- Helidon WebClient DNS Resolver Round Robin Support
- Helidon WebClient Discovery Support
- Helidon WebClient Metrics Support
- Helidon WebClient Security Support
- Helidon WebClient Tracing Support