blob: 9bcf749cf51f10e9933633295121f643555a03e8 [file] [log] [blame]
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.config;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
/**
* Immutable class encapsulating request configuration items.
*/
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class RequestConfig implements Cloneable {
private static final Timeout DEFAULT_CONNECTION_REQUEST_TIMEOUT = Timeout.ofMinutes(3);
private static final Timeout DEFAULT_CONNECT_TIMEOUT = Timeout.ofMinutes(3);
private static final TimeValue DEFAULT_CONN_KEEP_ALIVE = TimeValue.ofMinutes(3);
public static final RequestConfig DEFAULT = new Builder().build();
private final boolean expectContinueEnabled;
private final HttpHost proxy;
private final String cookieSpec;
private final boolean redirectsEnabled;
private final boolean circularRedirectsAllowed;
private final int maxRedirects;
private final boolean authenticationEnabled;
private final Collection<String> targetPreferredAuthSchemes;
private final Collection<String> proxyPreferredAuthSchemes;
private final Timeout connectionRequestTimeout;
private final Timeout connectTimeout;
private final Timeout responseTimeout;
private final TimeValue connectionKeepAlive;
private final boolean contentCompressionEnabled;
private final boolean hardCancellationEnabled;
/**
* Intended for CDI compatibility
*/
protected RequestConfig() {
this(false, null, null, false, false, 0, false, null, null,
DEFAULT_CONNECTION_REQUEST_TIMEOUT, DEFAULT_CONNECT_TIMEOUT, null, DEFAULT_CONN_KEEP_ALIVE, false, false);
}
RequestConfig(
final boolean expectContinueEnabled,
final HttpHost proxy,
final String cookieSpec,
final boolean redirectsEnabled,
final boolean circularRedirectsAllowed,
final int maxRedirects,
final boolean authenticationEnabled,
final Collection<String> targetPreferredAuthSchemes,
final Collection<String> proxyPreferredAuthSchemes,
final Timeout connectionRequestTimeout,
final Timeout connectTimeout,
final Timeout responseTimeout,
final TimeValue connectionKeepAlive,
final boolean contentCompressionEnabled,
final boolean hardCancellationEnabled) {
super();
this.expectContinueEnabled = expectContinueEnabled;
this.proxy = proxy;
this.cookieSpec = cookieSpec;
this.redirectsEnabled = redirectsEnabled;
this.circularRedirectsAllowed = circularRedirectsAllowed;
this.maxRedirects = maxRedirects;
this.authenticationEnabled = authenticationEnabled;
this.targetPreferredAuthSchemes = targetPreferredAuthSchemes;
this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes;
this.connectionRequestTimeout = connectionRequestTimeout;
this.connectTimeout = connectTimeout;
this.responseTimeout = responseTimeout;
this.connectionKeepAlive = connectionKeepAlive;
this.contentCompressionEnabled = contentCompressionEnabled;
this.hardCancellationEnabled = hardCancellationEnabled;
}
/**
* @see Builder#setExpectContinueEnabled(boolean)
*/
public boolean isExpectContinueEnabled() {
return expectContinueEnabled;
}
/**
* @see Builder#setProxy(HttpHost)
*/
public HttpHost getProxy() {
return proxy;
}
/**
* @see Builder#setCookieSpec(String)
*/
public String getCookieSpec() {
return cookieSpec;
}
/**
* @see Builder#setRedirectsEnabled(boolean)
*/
public boolean isRedirectsEnabled() {
return redirectsEnabled;
}
/**
* @see Builder#setCircularRedirectsAllowed(boolean)
*/
public boolean isCircularRedirectsAllowed() {
return circularRedirectsAllowed;
}
/**
* @see Builder#setMaxRedirects(int)
*/
public int getMaxRedirects() {
return maxRedirects;
}
/**
* @see Builder#setAuthenticationEnabled(boolean)
*/
public boolean isAuthenticationEnabled() {
return authenticationEnabled;
}
/**
* @see Builder#setTargetPreferredAuthSchemes(Collection)
*/
public Collection<String> getTargetPreferredAuthSchemes() {
return targetPreferredAuthSchemes;
}
/**
* @see Builder#setProxyPreferredAuthSchemes(Collection)
*/
public Collection<String> getProxyPreferredAuthSchemes() {
return proxyPreferredAuthSchemes;
}
/**
* @see Builder#setConnectionRequestTimeout(Timeout)
*/
public Timeout getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
/**
* @see Builder#setConnectTimeout(Timeout)
*/
public Timeout getConnectTimeout() {
return connectTimeout;
}
/**
* @see Builder#setResponseTimeout(Timeout)
*/
public Timeout getResponseTimeout() {
return responseTimeout;
}
/**
* @see Builder#setConnectionKeepAlive(TimeValue)
*/
public TimeValue getConnectionKeepAlive() {
return connectionKeepAlive;
}
/**
* @see Builder#setContentCompressionEnabled(boolean)
*/
public boolean isContentCompressionEnabled() {
return contentCompressionEnabled;
}
/**
* @see Builder#setHardCancellationEnabled(boolean)
*/
public boolean isHardCancellationEnabled() {
return hardCancellationEnabled;
}
@Override
protected RequestConfig clone() throws CloneNotSupportedException {
return (RequestConfig) super.clone();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append("expectContinueEnabled=").append(expectContinueEnabled);
builder.append(", proxy=").append(proxy);
builder.append(", cookieSpec=").append(cookieSpec);
builder.append(", redirectsEnabled=").append(redirectsEnabled);
builder.append(", maxRedirects=").append(maxRedirects);
builder.append(", circularRedirectsAllowed=").append(circularRedirectsAllowed);
builder.append(", authenticationEnabled=").append(authenticationEnabled);
builder.append(", targetPreferredAuthSchemes=").append(targetPreferredAuthSchemes);
builder.append(", proxyPreferredAuthSchemes=").append(proxyPreferredAuthSchemes);
builder.append(", connectionRequestTimeout=").append(connectionRequestTimeout);
builder.append(", connectTimeout=").append(connectTimeout);
builder.append(", responseTimeout=").append(responseTimeout);
builder.append(", connectionKeepAlive=").append(connectionKeepAlive);
builder.append(", contentCompressionEnabled=").append(contentCompressionEnabled);
builder.append(", hardCancellationEnabled=").append(hardCancellationEnabled);
builder.append("]");
return builder.toString();
}
public static RequestConfig.Builder custom() {
return new Builder();
}
public static RequestConfig.Builder copy(final RequestConfig config) {
return new Builder()
.setExpectContinueEnabled(config.isExpectContinueEnabled())
.setProxy(config.getProxy())
.setCookieSpec(config.getCookieSpec())
.setRedirectsEnabled(config.isRedirectsEnabled())
.setCircularRedirectsAllowed(config.isCircularRedirectsAllowed())
.setMaxRedirects(config.getMaxRedirects())
.setAuthenticationEnabled(config.isAuthenticationEnabled())
.setTargetPreferredAuthSchemes(config.getTargetPreferredAuthSchemes())
.setProxyPreferredAuthSchemes(config.getProxyPreferredAuthSchemes())
.setConnectionRequestTimeout(config.getConnectionRequestTimeout())
.setConnectTimeout(config.getConnectTimeout())
.setResponseTimeout(config.getResponseTimeout())
.setConnectionKeepAlive(config.getConnectionKeepAlive())
.setContentCompressionEnabled(config.isContentCompressionEnabled())
.setHardCancellationEnabled(config.isHardCancellationEnabled());
}
public static class Builder {
private boolean expectContinueEnabled;
private HttpHost proxy;
private String cookieSpec;
private boolean redirectsEnabled;
private boolean circularRedirectsAllowed;
private int maxRedirects;
private boolean authenticationEnabled;
private Collection<String> targetPreferredAuthSchemes;
private Collection<String> proxyPreferredAuthSchemes;
private Timeout connectionRequestTimeout;
private Timeout connectTimeout;
private Timeout responseTimeout;
private TimeValue connectionKeepAlive;
private boolean contentCompressionEnabled;
private boolean hardCancellationEnabled;
Builder() {
super();
this.redirectsEnabled = true;
this.maxRedirects = 50;
this.authenticationEnabled = true;
this.connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
this.connectTimeout = DEFAULT_CONNECT_TIMEOUT;
this.contentCompressionEnabled = true;
this.hardCancellationEnabled = true;
}
/**
* Determines whether the 'Expect: 100-Continue' handshake is enabled
* for entity enclosing methods. The purpose of the 'Expect: 100-Continue'
* handshake is to allow a client that is sending a request message with
* a request body to determine if the origin server is willing to
* accept the request (based on the request headers) before the client
* sends the request body.
* <p>
* The use of the 'Expect: 100-continue' handshake can result in
* a noticeable performance improvement for entity enclosing requests
* (such as POST and PUT) that require the target server's
* authentication.
* </p>
* <p>
* 'Expect: 100-continue' handshake should be used with caution, as it
* may cause problems with HTTP servers and proxies that do not support
* HTTP/1.1 protocol.
* </p>
* <p>
* Default: {@code false}
* </p>
*/
public Builder setExpectContinueEnabled(final boolean expectContinueEnabled) {
this.expectContinueEnabled = expectContinueEnabled;
return this;
}
/**
* Returns HTTP proxy to be used for request execution.
* <p>
* Default: {@code null}
* </p>
*/
public Builder setProxy(final HttpHost proxy) {
this.proxy = proxy;
return this;
}
/**
* Determines the name of the cookie specification to be used for HTTP state
* management.
* <p>
* Default: {@code null}
* </p>
*/
public Builder setCookieSpec(final String cookieSpec) {
this.cookieSpec = cookieSpec;
return this;
}
/**
* Determines whether redirects should be handled automatically.
* <p>
* Default: {@code true}
* </p>
*/
public Builder setRedirectsEnabled(final boolean redirectsEnabled) {
this.redirectsEnabled = redirectsEnabled;
return this;
}
/**
* Determines whether circular redirects (redirects to the same location) should
* be allowed. The HTTP spec is not sufficiently clear whether circular redirects
* are permitted, therefore optionally they can be enabled
* <p>
* Default: {@code false}
* </p>
*/
public Builder setCircularRedirectsAllowed(final boolean circularRedirectsAllowed) {
this.circularRedirectsAllowed = circularRedirectsAllowed;
return this;
}
/**
* Returns the maximum number of redirects to be followed. The limit on number
* of redirects is intended to prevent infinite loops.
* <p>
* Default: {@code 50}
* </p>
*/
public Builder setMaxRedirects(final int maxRedirects) {
this.maxRedirects = maxRedirects;
return this;
}
/**
* Determines whether authentication should be handled automatically.
* <p>
* Default: {@code true}
* </p>
*/
public Builder setAuthenticationEnabled(final boolean authenticationEnabled) {
this.authenticationEnabled = authenticationEnabled;
return this;
}
/**
* Determines the order of preference for supported authentication schemes
* by their names when authenticating with the target host.
* <p>
* Default: {@code null}
* </p>
*/
public Builder setTargetPreferredAuthSchemes(final Collection<String> targetPreferredAuthSchemes) {
this.targetPreferredAuthSchemes = targetPreferredAuthSchemes;
return this;
}
/**
* Determines the order of preference for supported authentication schemes
* by their names when authenticating with the proxy host.
* <p>
* Default: {@code null}
* </p>
*/
public Builder setProxyPreferredAuthSchemes(final Collection<String> proxyPreferredAuthSchemes) {
this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes;
return this;
}
/**
* Returns the connection lease request timeout used when requesting
* a connection from the connection manager.
* <p>
* A timeout value of zero is interpreted as an infinite timeout.
* </p>
* <p>
* Default: 3 minutes.
* </p>
*/
public Builder setConnectionRequestTimeout(final Timeout connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
return this;
}
/**
* @see #setConnectionRequestTimeout(Timeout)
*/
public Builder setConnectionRequestTimeout(final long connectionRequestTimeout, final TimeUnit timeUnit) {
this.connectionRequestTimeout = Timeout.of(connectionRequestTimeout, timeUnit);
return this;
}
/**
* Determines the timeout until a new connection is fully established.
* This may also include transport security negotiation exchanges
* such as {@code SSL} or {@code TLS} protocol negotiation).
* <p>
* A timeout value of zero is interpreted as an infinite timeout.
* </p>
* <p>
* Default: 3 minutes
* </p>
*/
public Builder setConnectTimeout(final Timeout connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
/**
* @see #setConnectTimeout(Timeout)
*/
public Builder setConnectTimeout(final long connectTimeout, final TimeUnit timeUnit) {
this.connectTimeout = Timeout.of(connectTimeout, timeUnit);
return this;
}
/**
* Determines the timeout until arrival of a response from the opposite
* endpoint.
* <p>
* A timeout value of zero is interpreted as an infinite timeout.
* </p>
* <p>
* Please note that response timeout may be unsupported by
* HTTP transports with message multiplexing.
* </p>
* <p>
* Default: {@code null}
* </p>
*
* @since 5.0
*/
public Builder setResponseTimeout(final Timeout responseTimeout) {
this.responseTimeout = responseTimeout;
return this;
}
/**
* @see #setResponseTimeout(Timeout)
*/
public Builder setResponseTimeout(final long responseTimeout, final TimeUnit timeUnit) {
this.responseTimeout = Timeout.of(responseTimeout, timeUnit);
return this;
}
/**
* Determines the default of value of connection keep-alive time period when not
* explicitly communicated by the origin server with a {@code Keep-Alive} response
* header.
* <p>
* A negative value is interpreted as an infinite keep-alive period.
* </p>
* <p>
* Default: 3 minutes
* </p>
*
* @since 5.0
*/
public Builder setConnectionKeepAlive(final TimeValue connectionKeepAlive) {
this.connectionKeepAlive = connectionKeepAlive;
return this;
}
/**
* @see #setConnectionKeepAlive(TimeValue)
*/
public Builder setDefaultKeepAlive(final long defaultKeepAlive, final TimeUnit timeUnit) {
this.connectionKeepAlive = TimeValue.of(defaultKeepAlive, timeUnit);
return this;
}
/**
* Determines whether the target server is requested to compress content.
* <p>
* Default: {@code true}
* </p>
*
* @since 4.5
*/
public Builder setContentCompressionEnabled(final boolean contentCompressionEnabled) {
this.contentCompressionEnabled = contentCompressionEnabled;
return this;
}
/**
* Determines whether request cancellation, such as through {@code
* Future#cancel(boolean)}, should kill the underlying connection. If this
* option is set to false, the client will attempt to preserve the
* underlying connection by allowing the request to complete in the
* background, discarding the response.
* <p>
* Note that when this option is {@code true}, cancelling a request may
* cause other requests to fail, if they are waiting to use the same
* connection.
* </p>
* <p>
* On HTTP/2, this option has no effect. Request cancellation will always
* result in the stream being cancelled with a {@code RST_STREAM}. This
* has no effect on connection reuse.
* </p>
* <p>
* On non-asynchronous clients, this option has no effect. Request
* cancellation, such as through {@code HttpUriRequestBase#cancel()}, will
* always kill the underlying connection.
* </p>
* <p>
* Default: {@code true}
* </p>
*
* @since 5.0
*/
public Builder setHardCancellationEnabled(final boolean hardCancellationEnabled) {
this.hardCancellationEnabled = hardCancellationEnabled;
return this;
}
public RequestConfig build() {
return new RequestConfig(
expectContinueEnabled,
proxy,
cookieSpec,
redirectsEnabled,
circularRedirectsAllowed,
maxRedirects,
authenticationEnabled,
targetPreferredAuthSchemes,
proxyPreferredAuthSchemes,
connectionRequestTimeout != null ? connectionRequestTimeout : DEFAULT_CONNECTION_REQUEST_TIMEOUT,
connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
responseTimeout,
connectionKeepAlive != null ? connectionKeepAlive : DEFAULT_CONN_KEEP_ALIVE,
contentCompressionEnabled,
hardCancellationEnabled);
}
}
}