io.helidon.webclient.api.HttpClientConfig Maven / Gradle / Ivy
/*
* Copyright (c) 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.
*/
package io.helidon.webclient.api;
import java.net.URI;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
import java.util.function.Supplier;
import io.helidon.builder.api.Prototype;
import io.helidon.common.Errors;
import io.helidon.common.Generated;
import io.helidon.common.HelidonServiceLoader;
import io.helidon.common.config.Config;
import io.helidon.common.media.type.ParserMode;
import io.helidon.common.socket.SocketOptions;
import io.helidon.common.uri.UriFragment;
import io.helidon.common.uri.UriQuery;
import io.helidon.http.Header;
import io.helidon.http.HeaderName;
import io.helidon.http.encoding.ContentEncodingContext;
import io.helidon.http.encoding.ContentEncodingContextConfig;
import io.helidon.http.media.MediaContext;
import io.helidon.http.media.MediaContextConfig;
import io.helidon.http.media.MediaSupport;
import io.helidon.webclient.spi.DnsResolver;
import io.helidon.webclient.spi.WebClientService;
import io.helidon.webclient.spi.WebClientServiceProvider;
/**
* This can be used by any HTTP client version, and does not act as a factory, for easy extensibility.
*
* @see #builder()
* @see #create()
*/
@Generated(value = "io.helidon.builder.codegen.BuilderCodegen", trigger = "io.helidon.webclient.api.HttpClientConfigBlueprint")
public interface HttpClientConfig extends HttpClientConfigBlueprint, Prototype.Api, HttpConfigBase {
/**
* Create a new fluent API builder to customize configuration.
*
* @return a new builder
*/
static HttpClientConfig.Builder builder() {
return new HttpClientConfig.Builder();
}
/**
* Create a new fluent API builder from an existing instance.
*
* @param instance an existing instance used as a base for the builder
* @return a builder based on an instance
*/
static HttpClientConfig.Builder builder(HttpClientConfig instance) {
return HttpClientConfig.builder().from(instance);
}
/**
* Create a new instance from configuration.
*
* @param config used to configure the new instance
* @return a new instance configured from configuration
*/
static HttpClientConfig create(Config config) {
return HttpClientConfig.builder().config(config).buildPrototype();
}
/**
* Create a new instance with default values.
*
* @return a new instance
*/
static HttpClientConfig create() {
return HttpClientConfig.builder().buildPrototype();
}
/**
* Fluent API builder base for {@link HttpClientConfig}.
*
* @param type of the builder extending this abstract builder
* @param type of the prototype interface that would be built by {@link #buildPrototype()}
*/
abstract class BuilderBase, PROTOTYPE extends HttpClientConfig> extends HttpConfigBase.BuilderBase implements Prototype.ConfiguredBuilder {
private final List mediaSupports = new ArrayList<>();
private final List services = new ArrayList<>();
private final Map defaultHeadersMap = new LinkedHashMap<>();
private final Set headers = new LinkedHashSet<>();
private boolean isMediaSupportsMutated;
private boolean isServicesMutated;
private boolean relativeUris = false;
private boolean sendExpectContinue = true;
private boolean servicesDiscoverServices = true;
private boolean shareConnectionCache = true;
private ClientUri baseUri;
private Config config;
private ContentEncodingContext contentEncoding;
private DnsAddressLookup dnsAddressLookup;
private DnsResolver dnsResolver;
private Duration readContinueTimeout = Duration.parse("PT1S");
private ExecutorService executor;
private int connectionCacheSize = 256;
private int maxInMemoryEntity = 131072;
private MediaContext mediaContext = MediaContext.create();
private ParserMode mediaTypeParserMode = ParserMode.STRICT;
private SocketOptions socketOptions;
private UriFragment baseFragment;
private UriQuery baseQuery;
private WebClientCookieManager cookieManager;
/**
* Protected to support extensibility.
*/
protected BuilderBase() {
}
/**
* Update this builder from an existing prototype instance. This method disables automatic service discovery.
*
* @param prototype existing prototype to update this builder from
* @return updated builder instance
*/
public BUILDER from(HttpClientConfig prototype) {
super.from(prototype);
baseUri(prototype.baseUri());
baseQuery(prototype.baseQuery());
baseFragment(prototype.baseFragment());
socketOptions(prototype.socketOptions());
dnsResolver(prototype.dnsResolver());
dnsAddressLookup(prototype.dnsAddressLookup());
addDefaultHeadersMap(prototype.defaultHeadersMap());
addHeaders(prototype.headers());
mediaTypeParserMode(prototype.mediaTypeParserMode());
contentEncoding(prototype.contentEncoding());
mediaContext(prototype.mediaContext());
if (!isMediaSupportsMutated) {
mediaSupports.clear();
}
addMediaSupports(prototype.mediaSupports());
if (!isServicesMutated) {
services.clear();
}
addServices(prototype.services());
servicesDiscoverServices = false;
relativeUris(prototype.relativeUris());
executor(prototype.executor());
sendExpectContinue(prototype.sendExpectContinue());
connectionCacheSize(prototype.connectionCacheSize());
cookieManager(prototype.cookieManager());
readContinueTimeout(prototype.readContinueTimeout());
shareConnectionCache(prototype.shareConnectionCache());
maxInMemoryEntity(prototype.maxInMemoryEntity());
return self();
}
/**
* Update this builder from an existing prototype builder instance.
*
* @param builder existing builder prototype to update this builder from
* @return updated builder instance
*/
public BUILDER from(HttpClientConfig.BuilderBase, ?> builder) {
super.from(builder);
builder.baseUri().ifPresent(this::baseUri);
builder.baseQuery().ifPresent(this::baseQuery);
builder.baseFragment().ifPresent(this::baseFragment);
builder.socketOptions().ifPresent(this::socketOptions);
builder.dnsResolver().ifPresent(this::dnsResolver);
builder.dnsAddressLookup().ifPresent(this::dnsAddressLookup);
addDefaultHeadersMap(builder.defaultHeadersMap);
addHeaders(builder.headers);
mediaTypeParserMode(builder.mediaTypeParserMode());
builder.contentEncoding().ifPresent(this::contentEncoding);
mediaContext(builder.mediaContext());
if (isMediaSupportsMutated) {
if (builder.isMediaSupportsMutated) {
addMediaSupports(builder.mediaSupports);
}
} else {
mediaSupports.clear();
addMediaSupports(builder.mediaSupports);
}
if (isServicesMutated) {
if (builder.isServicesMutated) {
addServices(builder.services);
}
} else {
services.clear();
addServices(builder.services);
}
servicesDiscoverServices = builder.servicesDiscoverServices;
relativeUris(builder.relativeUris());
builder.executor().ifPresent(this::executor);
sendExpectContinue(builder.sendExpectContinue());
connectionCacheSize(builder.connectionCacheSize());
builder.cookieManager().ifPresent(this::cookieManager);
readContinueTimeout(builder.readContinueTimeout());
shareConnectionCache(builder.shareConnectionCache());
maxInMemoryEntity(builder.maxInMemoryEntity());
return self();
}
/**
* Base URI of the client.
*
* @param baseUri base URI to use, query is extracted to base query (if any)
* @return updated builder instance
*/
public BUILDER baseUri(URI baseUri) {
HttpClientConfigSupport.HttpCustomMethods.baseUri(this, baseUri);
return self();
}
/**
* Base URI of the client.
*
* @param baseUri base URI to use, query is extracted to base query (if any)
* @return updated builder instance
*/
public BUILDER baseUri(String baseUri) {
HttpClientConfigSupport.HttpCustomMethods.baseUri(this, baseUri);
return self();
}
/**
* Add a default header value.
*
* @param name name of the header
* @param value value of the header
* @return updated builder instance
*/
public BUILDER addHeader(HeaderName name, String value) {
HttpClientConfigSupport.HttpCustomMethods.addHeader(this, name, value);
return self();
}
/**
* Add a default header value.
*
* @param name name of the header
* @param value value of the header
* @return updated builder instance
*/
public BUILDER addHeader(HeaderName name, int value) {
HttpClientConfigSupport.HttpCustomMethods.addHeader(this, name, value);
return self();
}
/**
* Add a default header value.
*
* @param name name of the header
* @param value value of the header
* @return updated builder instance
*/
public BUILDER addHeader(HeaderName name, long value) {
HttpClientConfigSupport.HttpCustomMethods.addHeader(this, name, value);
return self();
}
/**
* Add default header value. This method is not optimal and should only be used when the header name is really
* obtained from a string, in other cases, use an alternative with {@link io.helidon.http.HeaderName}
* or {@link io.helidon.http.Header}.
*
* @param name name of the header
* @param value value of the header
* @return updated builder instance
* @see #addHeader(io.helidon.http.Header)
*/
public BUILDER addHeader(String name, String value) {
HttpClientConfigSupport.HttpCustomMethods.addHeader(this, name, value);
return self();
}
/**
* Add default header value. This method is not optimal and should only be used when the header name is really
* obtained from a string, in other cases, use an alternative with {@link io.helidon.http.HeaderName}
* or {@link io.helidon.http.Header}.
*
* @param name name of the header
* @param value value of the header
* @return updated builder instance
* @see #addHeader(io.helidon.http.Header)
*/
public BUILDER addHeader(String name, int value) {
HttpClientConfigSupport.HttpCustomMethods.addHeader(this, name, value);
return self();
}
/**
* Add default header value. This method is not optimal and should only be used when the header name is really
* obtained from a string, in other cases, use an alternative with {@link io.helidon.http.HeaderName}
* or {@link io.helidon.http.Header}.
*
* @param name name of the header
* @param value value of the header
* @return updated builder instance
* @see #addHeader(io.helidon.http.Header)
*/
public BUILDER addHeader(String name, long value) {
HttpClientConfigSupport.HttpCustomMethods.addHeader(this, name, value);
return self();
}
/**
* Update builder from configuration (node of this type).
* If a value is present in configuration, it would override currently configured values.
*
* @param config configuration instance used to obtain values to update this builder
* @return updated builder instance
*/
@Override
public BUILDER config(Config config) {
Objects.requireNonNull(config);
this.config = config;
super.config(config);
config.get("base-uri").map(HttpClientConfigBlueprint::createBaseUri).ifPresent(this::baseUri);
config.get("socket-options").map(SocketOptions::create).ifPresent(this::socketOptions);
config.get("default-headers").detach().asMap().ifPresent(this::defaultHeadersMap);
config.get("media-type-parser-mode").as(ParserMode.class).ifPresent(this::mediaTypeParserMode);
config.get("content-encoding").map(ContentEncodingContextConfig::create).ifPresent(this::contentEncoding);
config.get("media-context").map(MediaContextConfig::create).ifPresent(this::mediaContext);
config.get("relative-uris").as(Boolean.class).ifPresent(this::relativeUris);
config.get("send-expect-continue").as(Boolean.class).ifPresent(this::sendExpectContinue);
config.get("connection-cache-size").as(Integer.class).ifPresent(this::connectionCacheSize);
config.get("cookie-manager").map(WebClientCookieManagerConfig::create).ifPresent(this::cookieManager);
config.get("read-continue-timeout").as(Duration.class).ifPresent(this::readContinueTimeout);
config.get("share-connection-cache").as(Boolean.class).ifPresent(this::shareConnectionCache);
config.get("max-in-memory-entity").as(Integer.class).ifPresent(this::maxInMemoryEntity);
return self();
}
/**
* Clear existing value of this property.
*
* @return updated builder instance
* @see #baseUri()
*/
public BUILDER clearBaseUri() {
this.baseUri = null;
return self();
}
/**
* Base uri used by the client in all requests.
*
* @param baseUri base uri of the client requests
* @return updated builder instance
* @see #baseUri()
*/
public BUILDER baseUri(ClientUri baseUri) {
Objects.requireNonNull(baseUri);
this.baseUri = baseUri;
return self();
}
/**
* Base uri used by the client in all requests.
*
* @param baseUriConfig base uri of the client requests
* @return updated builder instance
* @see #baseUri()
*/
public BUILDER baseUri(Config baseUriConfig) {
Objects.requireNonNull(baseUriConfig);
this.baseUri = HttpClientConfigBlueprint.createBaseUri(baseUriConfig);
return self();
}
/**
* Clear existing value of this property.
*
* @return updated builder instance
* @see #baseQuery()
*/
public BUILDER clearBaseQuery() {
this.baseQuery = null;
return self();
}
/**
* Base query used by the client in all requests.
*
* @param baseQuery base query of the client requests
* @return updated builder instance
* @see #baseQuery()
*/
public BUILDER baseQuery(UriQuery baseQuery) {
Objects.requireNonNull(baseQuery);
this.baseQuery = baseQuery;
return self();
}
/**
* Clear existing value of this property.
*
* @return updated builder instance
* @see #baseFragment()
*/
public BUILDER clearBaseFragment() {
this.baseFragment = null;
return self();
}
/**
* Base fragment used by the client in all requests (unless overwritten on
* per-request basis).
*
* @param baseFragment fragment to use
* @return updated builder instance
* @see #baseFragment()
*/
public BUILDER baseFragment(UriFragment baseFragment) {
Objects.requireNonNull(baseFragment);
this.baseFragment = baseFragment;
return self();
}
/**
* Socket options for connections opened by this client.
* If there is a value explicitly configured on this type and on the socket options,
* the one configured on this type's builder will win:
*
* - {@link #readTimeout()}
* - {@link #connectTimeout()}
*
*
* @param socketOptions socket options
* @return updated builder instance
* @see #socketOptions()
*/
public BUILDER socketOptions(SocketOptions socketOptions) {
Objects.requireNonNull(socketOptions);
this.socketOptions = socketOptions;
return self();
}
/**
* Socket options for connections opened by this client.
* If there is a value explicitly configured on this type and on the socket options,
* the one configured on this type's builder will win:
*
* - {@link #readTimeout()}
* - {@link #connectTimeout()}
*
*
* @param consumer consumer of builder for
* socket options
* @return updated builder instance
* @see #socketOptions()
*/
public BUILDER socketOptions(Consumer consumer) {
Objects.requireNonNull(consumer);
var builder = SocketOptions.builder();
consumer.accept(builder);
this.socketOptions(builder.build());
return self();
}
/**
* Socket options for connections opened by this client.
* If there is a value explicitly configured on this type and on the socket options,
* the one configured on this type's builder will win:
*
* - {@link #readTimeout()}
* - {@link #connectTimeout()}
*
*
* @param supplier supplier of
* socket options
* @return updated builder instance
* @see #socketOptions()
*/
public BUILDER socketOptions(Supplier extends SocketOptions> supplier) {
Objects.requireNonNull(supplier);
this.socketOptions(supplier.get());
return self();
}
/**
* DNS resolver to be used by this client.
*
* @param dnsResolver dns resolver
* @return updated builder instance
* @see #dnsResolver()
*/
public BUILDER dnsResolver(DnsResolver dnsResolver) {
Objects.requireNonNull(dnsResolver);
this.dnsResolver = dnsResolver;
return self();
}
/**
* DNS address lookup preferences to be used by this client.
* Default value is determined by capabilities of the system.
*
* @param dnsAddressLookup dns address lookup strategy
* @return updated builder instance
* @see #dnsAddressLookup()
*/
public BUILDER dnsAddressLookup(DnsAddressLookup dnsAddressLookup) {
Objects.requireNonNull(dnsAddressLookup);
this.dnsAddressLookup = dnsAddressLookup;
return self();
}
/**
* This method replaces all values with the new ones.
*
* @param defaultHeadersMap default headers
* @return updated builder instance
* @see #defaultHeadersMap()
*/
public BUILDER defaultHeadersMap(Map extends String, ? extends String> defaultHeadersMap) {
Objects.requireNonNull(defaultHeadersMap);
this.defaultHeadersMap.clear();
this.defaultHeadersMap.putAll(defaultHeadersMap);
return self();
}
/**
* This method keeps existing values, then puts all new values into the map.
*
* @param defaultHeadersMap default headers
* @return updated builder instance
* @see #defaultHeadersMap()
*/
public BUILDER addDefaultHeadersMap(Map extends String, ? extends String> defaultHeadersMap) {
Objects.requireNonNull(defaultHeadersMap);
this.defaultHeadersMap.putAll(defaultHeadersMap);
return self();
}
/**
* Default headers to be used in every request.
*
* @param headers default headers
* @return updated builder instance
* @see #headers()
*/
public BUILDER headers(Set extends Header> headers) {
Objects.requireNonNull(headers);
this.headers.clear();
this.headers.addAll(headers);
return self();
}
/**
* Default headers to be used in every request.
*
* @param headers default headers
* @return updated builder instance
* @see #headers()
*/
public BUILDER addHeaders(Set extends Header> headers) {
Objects.requireNonNull(headers);
this.headers.addAll(headers);
return self();
}
/**
* Default headers to be used in every request.
*
* @param header default headers
* @return updated builder instance
* @see #headers()
*/
public BUILDER addHeader(Header header) {
Objects.requireNonNull(header);
this.headers.add(header);
return self();
}
/**
* Configure media type parsing mode for HTTP {@code Content-Type} header.
*
* @param mediaTypeParserMode media type parsing mode
* @return updated builder instance
* @see #mediaTypeParserMode()
*/
public BUILDER mediaTypeParserMode(ParserMode mediaTypeParserMode) {
Objects.requireNonNull(mediaTypeParserMode);
this.mediaTypeParserMode = mediaTypeParserMode;
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.encoding.ContentEncodingContext}.
* This method discards all previously registered ContentEncodingContext.
* If no content encoding context is registered, default encoding context is used.
*
* @param contentEncoding content encoding context
* @return updated builder instance
* @see #contentEncoding()
*/
public BUILDER contentEncoding(ContentEncodingContext contentEncoding) {
Objects.requireNonNull(contentEncoding);
this.contentEncoding = contentEncoding;
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.encoding.ContentEncodingContext}.
* This method discards all previously registered ContentEncodingContext.
* If no content encoding context is registered, default encoding context is used.
*
* @param contentEncodingConfig content encoding context
* @return updated builder instance
* @see #contentEncoding()
*/
public BUILDER contentEncoding(ContentEncodingContextConfig contentEncodingConfig) {
Objects.requireNonNull(contentEncodingConfig);
this.contentEncoding = ContentEncodingContext.create(contentEncodingConfig);
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.encoding.ContentEncodingContext}.
* This method discards all previously registered ContentEncodingContext.
* If no content encoding context is registered, default encoding context is used.
*
* @param consumer consumer of builder for
* content encoding context
* @return updated builder instance
* @see #contentEncoding()
*/
public BUILDER contentEncoding(Consumer consumer) {
Objects.requireNonNull(consumer);
var builder = ContentEncodingContextConfig.builder();
consumer.accept(builder);
this.contentEncoding(builder.build());
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.encoding.ContentEncodingContext}.
* This method discards all previously registered ContentEncodingContext.
* If no content encoding context is registered, default encoding context is used.
*
* @param supplier supplier of
* content encoding context
* @return updated builder instance
* @see #contentEncoding()
*/
public BUILDER contentEncoding(Supplier extends ContentEncodingContext> supplier) {
Objects.requireNonNull(supplier);
this.contentEncoding(supplier.get());
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.media.MediaContext}.
* This method discards all previously registered MediaContext.
* If no media context is registered, default media context is used.
*
* @param mediaContext media context
* @return updated builder instance
* @see #mediaContext()
*/
public BUILDER mediaContext(MediaContext mediaContext) {
Objects.requireNonNull(mediaContext);
this.mediaContext = mediaContext;
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.media.MediaContext}.
* This method discards all previously registered MediaContext.
* If no media context is registered, default media context is used.
*
* @param mediaContextConfig media context
* @return updated builder instance
* @see #mediaContext()
*/
public BUILDER mediaContext(MediaContextConfig mediaContextConfig) {
Objects.requireNonNull(mediaContextConfig);
this.mediaContext = MediaContext.create(mediaContextConfig);
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.media.MediaContext}.
* This method discards all previously registered MediaContext.
* If no media context is registered, default media context is used.
*
* @param consumer consumer of builder for
* media context
* @return updated builder instance
* @see #mediaContext()
*/
public BUILDER mediaContext(Consumer consumer) {
Objects.requireNonNull(consumer);
var builder = MediaContextConfig.builder();
consumer.accept(builder);
this.mediaContext(builder.build());
return self();
}
/**
* Configure the listener specific {@link io.helidon.http.media.MediaContext}.
* This method discards all previously registered MediaContext.
* If no media context is registered, default media context is used.
*
* @param supplier supplier of
* media context
* @return updated builder instance
* @see #mediaContext()
*/
public BUILDER mediaContext(Supplier extends MediaContext> supplier) {
Objects.requireNonNull(supplier);
this.mediaContext(supplier.get());
return self();
}
/**
* Media supports (manually added). If both {@link #mediaContext()} and this is configured,
* there will be a new context created from return of this method, with fallback of {@link #mediaContext()}.
*
* @param mediaSupports list of explicitly added media supports
* @return updated builder instance
* @see #mediaSupports()
*/
public BUILDER mediaSupports(List extends MediaSupport> mediaSupports) {
Objects.requireNonNull(mediaSupports);
isMediaSupportsMutated = true;
this.mediaSupports.clear();
this.mediaSupports.addAll(mediaSupports);
return self();
}
/**
* Media supports (manually added). If both {@link #mediaContext()} and this is configured,
* there will be a new context created from return of this method, with fallback of {@link #mediaContext()}.
*
* @param mediaSupports list of explicitly added media supports
* @return updated builder instance
* @see #mediaSupports()
*/
public BUILDER addMediaSupports(List extends MediaSupport> mediaSupports) {
Objects.requireNonNull(mediaSupports);
isMediaSupportsMutated = true;
this.mediaSupports.addAll(mediaSupports);
return self();
}
/**
* Media supports (manually added). If both {@link #mediaContext()} and this is configured,
* there will be a new context created from return of this method, with fallback of {@link #mediaContext()}.
*
* @param mediaSupport list of explicitly added media supports
* @return updated builder instance
* @see #mediaSupports()
*/
public BUILDER addMediaSupport(MediaSupport mediaSupport) {
Objects.requireNonNull(mediaSupport);
this.mediaSupports.add(mediaSupport);
isMediaSupportsMutated = true;
return self();
}
/**
* WebClient services.
*
* @param discoverServices whether to discover implementations through service loader
* @return updated builder instance
* @see #services()
*/
public BUILDER servicesDiscoverServices(boolean discoverServices) {
this.servicesDiscoverServices = discoverServices;
return self();
}
/**
* WebClient services.
*
* @param services services to use with this web client
* @return updated builder instance
* @see #services()
*/
public BUILDER services(List extends WebClientService> services) {
Objects.requireNonNull(services);
isServicesMutated = true;
this.services.clear();
this.services.addAll(services);
return self();
}
/**
* WebClient services.
*
* @param services services to use with this web client
* @return updated builder instance
* @see #services()
*/
public BUILDER addServices(List extends WebClientService> services) {
Objects.requireNonNull(services);
isServicesMutated = true;
this.services.addAll(services);
return self();
}
/**
* WebClient services.
*
* @param service services to use with this web client
* @return updated builder instance
* @see #services()
*/
public BUILDER addService(WebClientService service) {
Objects.requireNonNull(service);
this.services.add(service);
isServicesMutated = true;
return self();
}
/**
* Can be set to {@code true} to force the use of relative URIs in all requests,
* regardless of the presence or absence of proxies or no-proxy lists.
*
* @param relativeUris relative URIs flag
* @return updated builder instance
* @see #relativeUris()
*/
public BUILDER relativeUris(boolean relativeUris) {
this.relativeUris = relativeUris;
return self();
}
/**
* Client executor service.
*
* @param executor executor service to use when needed (such as for HTTP/2)
* @return updated builder instance
* @see #executor()
*/
public BUILDER executor(ExecutorService executor) {
Objects.requireNonNull(executor);
this.executor = executor;
return self();
}
/**
* Whether Expect-100-Continue header is sent to verify server availability before sending an entity.
*
* Defaults to {@code true}.
*
*
* @param sendExpectContinue whether Expect:100-Continue header should be sent on streamed transfers
* @return updated builder instance
* @see #sendExpectContinue()
*/
public BUILDER sendExpectContinue(boolean sendExpectContinue) {
this.sendExpectContinue = sendExpectContinue;
return self();
}
/**
* Maximal size of the connection cache.
* For most HTTP protocols, we may cache connections to various endpoints for keep alive (or stream reuse in case of HTTP/2).
* This option limits the size. Setting this number lower than the "usual" number of target services will cause connections
* to be closed and reopened frequently.
*
* @param connectionCacheSize
* @return updated builder instance
* @see #connectionCacheSize()
*/
public BUILDER connectionCacheSize(int connectionCacheSize) {
this.connectionCacheSize = connectionCacheSize;
return self();
}
/**
* Clear existing value of this property.
*
* @return updated builder instance
* @see #cookieManager()
*/
public BUILDER clearCookieManager() {
this.cookieManager = null;
return self();
}
/**
* WebClient cookie manager.
*
* @param cookieManager cookie manager to use
* @return updated builder instance
* @see #cookieManager()
*/
public BUILDER cookieManager(WebClientCookieManager cookieManager) {
Objects.requireNonNull(cookieManager);
this.cookieManager = cookieManager;
return self();
}
/**
* WebClient cookie manager.
*
* @param cookieManagerConfig cookie manager to use
* @return updated builder instance
* @see #cookieManager()
*/
public BUILDER cookieManager(WebClientCookieManagerConfig cookieManagerConfig) {
Objects.requireNonNull(cookieManagerConfig);
this.cookieManager = WebClientCookieManager.create(cookieManagerConfig);
return self();
}
/**
* WebClient cookie manager.
*
* @param consumer cookie manager to use
* @return updated builder instance
* @see #cookieManager()
*/
public BUILDER cookieManager(Consumer consumer) {
Objects.requireNonNull(consumer);
var builder = WebClientCookieManager.builder();
consumer.accept(builder);
this.cookieManager(builder.build());
return self();
}
/**
* Socket 100-Continue read timeout. Default is 1 second.
* This read timeout is used when 100-Continue is sent by the client, before it sends an entity.
*
* @param readContinueTimeout read 100-Continue timeout duration
* @return updated builder instance
* @see #readContinueTimeout()
*/
public BUILDER readContinueTimeout(Duration readContinueTimeout) {
Objects.requireNonNull(readContinueTimeout);
this.readContinueTimeout = readContinueTimeout;
return self();
}
/**
* Whether to share connection cache between all the WebClient instances in JVM.
*
* @param shareConnectionCache true if connection cache is shared
* @return updated builder instance
* @see #shareConnectionCache()
*/
public BUILDER shareConnectionCache(boolean shareConnectionCache) {
this.shareConnectionCache = shareConnectionCache;
return self();
}
/**
* If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance.
* If bigger, streaming will be used.
*
* Note that for some entity types we cannot use streaming, as they are already fully in memory (String, byte[]), for such
* cases, this option is ignored. Default is 128Kb.
*
* @param maxInMemoryEntity maximal number of bytes to buffer in memory for supported writers
* @return updated builder instance
* @see #maxInMemoryEntity()
*/
public BUILDER maxInMemoryEntity(int maxInMemoryEntity) {
this.maxInMemoryEntity = maxInMemoryEntity;
return self();
}
/**
* Base uri used by the client in all requests.
*
* @return the base uri
*/
public Optional baseUri() {
return Optional.ofNullable(baseUri);
}
/**
* Base query used by the client in all requests.
*
* @return the base query
*/
public Optional baseQuery() {
return Optional.ofNullable(baseQuery);
}
/**
* Base fragment used by the client in all requests (unless overwritten on
* per-request basis).
*
* @return the base fragment
*/
public Optional baseFragment() {
return Optional.ofNullable(baseFragment);
}
/**
* Socket options for connections opened by this client.
* If there is a value explicitly configured on this type and on the socket options,
* the one configured on this type's builder will win:
*
* - {@link #readTimeout()}
* - {@link #connectTimeout()}
*
*
* @return the socket options
*/
public Optional socketOptions() {
return Optional.ofNullable(socketOptions);
}
/**
* DNS resolver to be used by this client.
*
* @return the dns resolver
*/
public Optional dnsResolver() {
return Optional.ofNullable(dnsResolver);
}
/**
* DNS address lookup preferences to be used by this client.
* Default value is determined by capabilities of the system.
*
* @return the dns address lookup
*/
public Optional dnsAddressLookup() {
return Optional.ofNullable(dnsAddressLookup);
}
/**
* Default headers to be used in every request from configuration.
*
* @return the default headers map
*/
public Map defaultHeadersMap() {
return defaultHeadersMap;
}
/**
* Default headers to be used in every request.
*
* @return the headers
*/
public Set headers() {
return headers;
}
/**
* Configure media type parsing mode for HTTP {@code Content-Type} header.
*
* @return the media type parser mode
*/
public ParserMode mediaTypeParserMode() {
return mediaTypeParserMode;
}
/**
* Configure the listener specific {@link io.helidon.http.encoding.ContentEncodingContext}.
* This method discards all previously registered ContentEncodingContext.
* If no content encoding context is registered, default encoding context is used.
*
* @return the content encoding
*/
public Optional contentEncoding() {
return Optional.ofNullable(contentEncoding);
}
/**
* Configure the listener specific {@link io.helidon.http.media.MediaContext}.
* This method discards all previously registered MediaContext.
* If no media context is registered, default media context is used.
*
* @return the media context
*/
public MediaContext mediaContext() {
return mediaContext;
}
/**
* Media supports (manually added). If both {@link #mediaContext()} and this is configured,
* there will be a new context created from return of this method, with fallback of {@link #mediaContext()}.
*
* @return the media supports
*/
public List mediaSupports() {
return mediaSupports;
}
/**
* WebClient services.
*
* @return the services
*/
public List services() {
return services;
}
/**
* Can be set to {@code true} to force the use of relative URIs in all requests,
* regardless of the presence or absence of proxies or no-proxy lists.
*
* @return the relative uris
*/
public boolean relativeUris() {
return relativeUris;
}
/**
* Client executor service.
*
* @return the executor
*/
public Optional executor() {
return Optional.ofNullable(executor);
}
/**
* Whether Expect-100-Continue header is sent to verify server availability before sending an entity.
*
* Defaults to {@code true}.
*
*
* @return the send expect continue
*/
public boolean sendExpectContinue() {
return sendExpectContinue;
}
/**
* Maximal size of the connection cache.
* For most HTTP protocols, we may cache connections to various endpoints for keep alive (or stream reuse in case of HTTP/2).
* This option limits the size. Setting this number lower than the "usual" number of target services will cause connections
* to be closed and reopened frequently.
*
* @return the connection cache size
*/
public int connectionCacheSize() {
return connectionCacheSize;
}
/**
* WebClient cookie manager.
*
* @return the cookie manager
*/
public Optional cookieManager() {
return Optional.ofNullable(cookieManager);
}
/**
* Socket 100-Continue read timeout. Default is 1 second.
* This read timeout is used when 100-Continue is sent by the client, before it sends an entity.
*
* @return the read continue timeout
*/
public Duration readContinueTimeout() {
return readContinueTimeout;
}
/**
* Whether to share connection cache between all the WebClient instances in JVM.
*
* @return the share connection cache
*/
public boolean shareConnectionCache() {
return shareConnectionCache;
}
/**
* If the entity is expected to be smaller that this number of bytes, it would be buffered in memory to optimize performance.
* If bigger, streaming will be used.
*
* Note that for some entity types we cannot use streaming, as they are already fully in memory (String, byte[]), for such
* cases, this option is ignored. Default is 128Kb.
*
* @return the max in memory entity
*/
public int maxInMemoryEntity() {
return maxInMemoryEntity;
}
/**
* If this instance was configured, this would be the config instance used.
*
* @return config node used to configure this builder, or empty if not configured
*/
public Optional config() {
return Optional.ofNullable(config);
}
@Override
public String toString() {
return "HttpClientConfigBuilder{"
+ "baseUri=" + baseUri + ","
+ "baseQuery=" + baseQuery + ","
+ "baseFragment=" + baseFragment + ","
+ "socketOptions=" + socketOptions + ","
+ "dnsResolver=" + dnsResolver + ","
+ "dnsAddressLookup=" + dnsAddressLookup + ","
+ "defaultHeadersMap=" + defaultHeadersMap + ","
+ "headers=" + headers + ","
+ "mediaTypeParserMode=" + mediaTypeParserMode + ","
+ "contentEncoding=" + contentEncoding + ","
+ "mediaContext=" + mediaContext + ","
+ "mediaSupports=" + mediaSupports + ","
+ "services=" + services + ","
+ "relativeUris=" + relativeUris + ","
+ "executor=" + executor + ","
+ "sendExpectContinue=" + sendExpectContinue + ","
+ "connectionCacheSize=" + connectionCacheSize + ","
+ "cookieManager=" + cookieManager + ","
+ "readContinueTimeout=" + readContinueTimeout + ","
+ "shareConnectionCache=" + shareConnectionCache + ","
+ "maxInMemoryEntity=" + maxInMemoryEntity
+ "};"
+ super.toString();
}
/**
* Handles providers and decorators.
*/
@SuppressWarnings("unchecked")
protected void preBuildPrototype() {
super.preBuildPrototype();
var config = this.config == null ? Config.empty() : this.config;
{
var serviceLoader = HelidonServiceLoader.create(ServiceLoader.load(WebClientServiceProvider.class));
this.addServices(discoverServices(config, "services", serviceLoader, WebClientServiceProvider.class, WebClientService.class, servicesDiscoverServices, services));
}
new HttpClientConfigSupport.HttpBuilderDecorator().decorate(this);
}
/**
* Validates required properties.
*/
protected void validatePrototype() {
super.validatePrototype();
Errors.Collector collector = Errors.collector();
if (socketOptions == null) {
collector.fatal(getClass(), "Property \"socket-options\" must not be null, but not set");
}
if (dnsResolver == null) {
collector.fatal(getClass(), "Property \"dnsResolver\" must not be null, but not set");
}
if (dnsAddressLookup == null) {
collector.fatal(getClass(), "Property \"dnsAddressLookup\" must not be null, but not set");
}
if (contentEncoding == null) {
collector.fatal(getClass(), "Property \"content-encoding\" must not be null, but not set");
}
if (executor == null) {
collector.fatal(getClass(), "Property \"executor\" must not be null, but not set");
}
collector.collect().checkValid();
}
/**
* Base uri used by the client in all requests.
*
* @param baseUri base uri of the client requests
* @return updated builder instance
* @see #baseUri()
*/
BUILDER baseUri(Optional extends ClientUri> baseUri) {
Objects.requireNonNull(baseUri);
this.baseUri = baseUri.map(io.helidon.webclient.api.ClientUri.class::cast).orElse(this.baseUri);
return self();
}
/**
* Base query used by the client in all requests.
*
* @param baseQuery base query of the client requests
* @return updated builder instance
* @see #baseQuery()
*/
BUILDER baseQuery(Optional extends UriQuery> baseQuery) {
Objects.requireNonNull(baseQuery);
this.baseQuery = baseQuery.map(io.helidon.common.uri.UriQuery.class::cast).orElse(this.baseQuery);
return self();
}
/**
* Base fragment used by the client in all requests (unless overwritten on
* per-request basis).
*
* @param baseFragment fragment to use
* @return updated builder instance
* @see #baseFragment()
*/
BUILDER baseFragment(Optional extends UriFragment> baseFragment) {
Objects.requireNonNull(baseFragment);
this.baseFragment = baseFragment.map(io.helidon.common.uri.UriFragment.class::cast).orElse(this.baseFragment);
return self();
}
/**
* WebClient cookie manager.
*
* @param cookieManager cookie manager to use
* @return updated builder instance
* @see #cookieManager()
*/
BUILDER cookieManager(Optional extends WebClientCookieManager> cookieManager) {
Objects.requireNonNull(cookieManager);
this.cookieManager = cookieManager.map(io.helidon.webclient.api.WebClientCookieManager.class::cast).orElse(this.cookieManager);
return self();
}
/**
* Generated implementation of the prototype, can be extended by descendant prototype implementations.
*/
protected static class HttpClientConfigImpl extends HttpConfigBaseImpl implements HttpClientConfig {
private final boolean relativeUris;
private final boolean sendExpectContinue;
private final boolean shareConnectionCache;
private final ContentEncodingContext contentEncoding;
private final DnsAddressLookup dnsAddressLookup;
private final DnsResolver dnsResolver;
private final Duration readContinueTimeout;
private final ExecutorService executor;
private final int connectionCacheSize;
private final int maxInMemoryEntity;
private final List mediaSupports;
private final List services;
private final Map defaultHeadersMap;
private final MediaContext mediaContext;
private final Optional baseFragment;
private final Optional baseQuery;
private final Optional baseUri;
private final Optional cookieManager;
private final ParserMode mediaTypeParserMode;
private final Set headers;
private final SocketOptions socketOptions;
/**
* Create an instance providing a builder.
*
* @param builder extending builder base of this prototype
*/
protected HttpClientConfigImpl(HttpClientConfig.BuilderBase, ?> builder) {
super(builder);
this.baseUri = builder.baseUri();
this.baseQuery = builder.baseQuery();
this.baseFragment = builder.baseFragment();
this.socketOptions = builder.socketOptions().get();
this.dnsResolver = builder.dnsResolver().get();
this.dnsAddressLookup = builder.dnsAddressLookup().get();
this.defaultHeadersMap = Collections.unmodifiableMap(new LinkedHashMap<>(builder.defaultHeadersMap()));
this.headers = Collections.unmodifiableSet(new LinkedHashSet<>(builder.headers()));
this.mediaTypeParserMode = builder.mediaTypeParserMode();
this.contentEncoding = builder.contentEncoding().get();
this.mediaContext = builder.mediaContext();
this.mediaSupports = List.copyOf(builder.mediaSupports());
this.services = List.copyOf(builder.services());
this.relativeUris = builder.relativeUris();
this.executor = builder.executor().get();
this.sendExpectContinue = builder.sendExpectContinue();
this.connectionCacheSize = builder.connectionCacheSize();
this.cookieManager = builder.cookieManager();
this.readContinueTimeout = builder.readContinueTimeout();
this.shareConnectionCache = builder.shareConnectionCache();
this.maxInMemoryEntity = builder.maxInMemoryEntity();
}
@Override
public Optional baseUri() {
return baseUri;
}
@Override
public Optional baseQuery() {
return baseQuery;
}
@Override
public Optional baseFragment() {
return baseFragment;
}
@Override
public SocketOptions socketOptions() {
return socketOptions;
}
@Override
public DnsResolver dnsResolver() {
return dnsResolver;
}
@Override
public DnsAddressLookup dnsAddressLookup() {
return dnsAddressLookup;
}
@Override
public Map defaultHeadersMap() {
return defaultHeadersMap;
}
@Override
public Set headers() {
return headers;
}
@Override
public ParserMode mediaTypeParserMode() {
return mediaTypeParserMode;
}
@Override
public ContentEncodingContext contentEncoding() {
return contentEncoding;
}
@Override
public MediaContext mediaContext() {
return mediaContext;
}
@Override
public List mediaSupports() {
return mediaSupports;
}
@Override
public List services() {
return services;
}
@Override
public boolean relativeUris() {
return relativeUris;
}
@Override
public ExecutorService executor() {
return executor;
}
@Override
public boolean sendExpectContinue() {
return sendExpectContinue;
}
@Override
public int connectionCacheSize() {
return connectionCacheSize;
}
@Override
public Optional cookieManager() {
return cookieManager;
}
@Override
public Duration readContinueTimeout() {
return readContinueTimeout;
}
@Override
public boolean shareConnectionCache() {
return shareConnectionCache;
}
@Override
public int maxInMemoryEntity() {
return maxInMemoryEntity;
}
@Override
public String toString() {
return "HttpClientConfig{"
+ "baseUri=" + baseUri + ","
+ "baseQuery=" + baseQuery + ","
+ "baseFragment=" + baseFragment + ","
+ "socketOptions=" + socketOptions + ","
+ "dnsResolver=" + dnsResolver + ","
+ "dnsAddressLookup=" + dnsAddressLookup + ","
+ "defaultHeadersMap=" + defaultHeadersMap + ","
+ "headers=" + headers + ","
+ "mediaTypeParserMode=" + mediaTypeParserMode + ","
+ "contentEncoding=" + contentEncoding + ","
+ "mediaContext=" + mediaContext + ","
+ "mediaSupports=" + mediaSupports + ","
+ "services=" + services + ","
+ "relativeUris=" + relativeUris + ","
+ "executor=" + executor + ","
+ "sendExpectContinue=" + sendExpectContinue + ","
+ "connectionCacheSize=" + connectionCacheSize + ","
+ "cookieManager=" + cookieManager + ","
+ "readContinueTimeout=" + readContinueTimeout + ","
+ "shareConnectionCache=" + shareConnectionCache + ","
+ "maxInMemoryEntity=" + maxInMemoryEntity
+ "};"
+ super.toString();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof HttpClientConfig other)) {
return false;
}
return super.equals(other)
&& Objects.equals(baseUri, other.baseUri())
&& Objects.equals(baseQuery, other.baseQuery())
&& Objects.equals(baseFragment, other.baseFragment())
&& Objects.equals(socketOptions, other.socketOptions())
&& Objects.equals(dnsResolver, other.dnsResolver())
&& Objects.equals(dnsAddressLookup, other.dnsAddressLookup())
&& Objects.equals(defaultHeadersMap, other.defaultHeadersMap())
&& Objects.equals(headers, other.headers())
&& Objects.equals(mediaTypeParserMode, other.mediaTypeParserMode())
&& Objects.equals(contentEncoding, other.contentEncoding())
&& Objects.equals(mediaContext, other.mediaContext())
&& Objects.equals(mediaSupports, other.mediaSupports())
&& Objects.equals(services, other.services())
&& relativeUris == other.relativeUris()
&& Objects.equals(executor, other.executor())
&& sendExpectContinue == other.sendExpectContinue()
&& connectionCacheSize == other.connectionCacheSize()
&& Objects.equals(cookieManager, other.cookieManager())
&& Objects.equals(readContinueTimeout, other.readContinueTimeout())
&& shareConnectionCache == other.shareConnectionCache()
&& maxInMemoryEntity == other.maxInMemoryEntity();
}
@Override
public int hashCode() {
return 31 * super.hashCode() + Objects.hash(baseUri, baseQuery, baseFragment, socketOptions, dnsResolver, dnsAddressLookup, defaultHeadersMap, headers, mediaTypeParserMode, contentEncoding, mediaContext, mediaSupports, services, relativeUris, executor, sendExpectContinue, connectionCacheSize, cookieManager, readContinueTimeout, shareConnectionCache, maxInMemoryEntity);
}
}
}
/**
* Fluent API builder for {@link HttpClientConfig}.
*/
class Builder extends HttpClientConfig.BuilderBase implements io.helidon.common.Builder {
private Builder() {
}
@Override
public HttpClientConfig buildPrototype() {
preBuildPrototype();
validatePrototype();
return new HttpClientConfigImpl(this);
}
@Override
public HttpClientConfig build() {
return buildPrototype();
}
}
}