View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.config;
29  
30  import java.util.Collection;
31  import java.util.concurrent.TimeUnit;
32  
33  import org.apache.hc.core5.annotation.Contract;
34  import org.apache.hc.core5.annotation.ThreadingBehavior;
35  import org.apache.hc.core5.http.HttpHost;
36  import org.apache.hc.core5.util.TimeValue;
37  import org.apache.hc.core5.util.Timeout;
38  
39  /**
40   *  Immutable class encapsulating request configuration items.
41   */
42  @Contract(threading = ThreadingBehavior.IMMUTABLE)
43  public class RequestConfig implements Cloneable {
44  
45      private static final Timeout DEFAULT_CONNECTION_REQUEST_TIMEOUT = Timeout.ofMinutes(3);
46      private static final Timeout DEFAULT_CONNECT_TIMEOUT = Timeout.ofMinutes(3);
47      private static final TimeValue DEFAULT_CONN_KEEP_ALIVE = TimeValue.ofMinutes(3);
48  
49      public static final RequestConfig DEFAULT = new Builder().build();
50  
51      private final boolean expectContinueEnabled;
52      private final HttpHost proxy;
53      private final String cookieSpec;
54      private final boolean redirectsEnabled;
55      private final boolean circularRedirectsAllowed;
56      private final int maxRedirects;
57      private final boolean authenticationEnabled;
58      private final Collection<String> targetPreferredAuthSchemes;
59      private final Collection<String> proxyPreferredAuthSchemes;
60      private final Timeout connectionRequestTimeout;
61      private final Timeout connectTimeout;
62      private final Timeout responseTimeout;
63      private final TimeValue connectionKeepAlive;
64      private final boolean contentCompressionEnabled;
65      private final boolean hardCancellationEnabled;
66  
67      /**
68       * Intended for CDI compatibility
69      */
70      protected RequestConfig() {
71          this(false, null, null, false, false, 0, false, null, null,
72                  DEFAULT_CONNECTION_REQUEST_TIMEOUT, DEFAULT_CONNECT_TIMEOUT, null, DEFAULT_CONN_KEEP_ALIVE, false, false);
73      }
74  
75      RequestConfig(
76              final boolean expectContinueEnabled,
77              final HttpHost proxy,
78              final String cookieSpec,
79              final boolean redirectsEnabled,
80              final boolean circularRedirectsAllowed,
81              final int maxRedirects,
82              final boolean authenticationEnabled,
83              final Collection<String> targetPreferredAuthSchemes,
84              final Collection<String> proxyPreferredAuthSchemes,
85              final Timeout connectionRequestTimeout,
86              final Timeout connectTimeout,
87              final Timeout responseTimeout,
88              final TimeValue connectionKeepAlive,
89              final boolean contentCompressionEnabled,
90              final boolean hardCancellationEnabled) {
91          super();
92          this.expectContinueEnabled = expectContinueEnabled;
93          this.proxy = proxy;
94          this.cookieSpec = cookieSpec;
95          this.redirectsEnabled = redirectsEnabled;
96          this.circularRedirectsAllowed = circularRedirectsAllowed;
97          this.maxRedirects = maxRedirects;
98          this.authenticationEnabled = authenticationEnabled;
99          this.targetPreferredAuthSchemes = targetPreferredAuthSchemes;
100         this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes;
101         this.connectionRequestTimeout = connectionRequestTimeout;
102         this.connectTimeout = connectTimeout;
103         this.responseTimeout = responseTimeout;
104         this.connectionKeepAlive = connectionKeepAlive;
105         this.contentCompressionEnabled = contentCompressionEnabled;
106         this.hardCancellationEnabled = hardCancellationEnabled;
107     }
108 
109     /**
110      * @see Builder#setExpectContinueEnabled(boolean)
111      */
112     public boolean isExpectContinueEnabled() {
113         return expectContinueEnabled;
114     }
115 
116     /**
117      * @see Builder#setProxy(HttpHost)
118      */
119     public HttpHost getProxy() {
120         return proxy;
121     }
122 
123     /**
124      * @see Builder#setCookieSpec(String)
125      */
126     public String getCookieSpec() {
127         return cookieSpec;
128     }
129 
130     /**
131      * @see Builder#setRedirectsEnabled(boolean)
132      */
133     public boolean isRedirectsEnabled() {
134         return redirectsEnabled;
135     }
136 
137     /**
138      * @see Builder#setCircularRedirectsAllowed(boolean)
139      */
140     public boolean isCircularRedirectsAllowed() {
141         return circularRedirectsAllowed;
142     }
143 
144     /**
145      * @see Builder#setMaxRedirects(int)
146      */
147     public int getMaxRedirects() {
148         return maxRedirects;
149     }
150 
151     /**
152      * @see Builder#setAuthenticationEnabled(boolean)
153      */
154     public boolean isAuthenticationEnabled() {
155         return authenticationEnabled;
156     }
157 
158     /**
159      * @see Builder#setTargetPreferredAuthSchemes(Collection)
160      */
161     public Collection<String> getTargetPreferredAuthSchemes() {
162         return targetPreferredAuthSchemes;
163     }
164 
165     /**
166      * @see Builder#setProxyPreferredAuthSchemes(Collection)
167      */
168     public Collection<String> getProxyPreferredAuthSchemes() {
169         return proxyPreferredAuthSchemes;
170     }
171 
172     /**
173      * @see Builder#setConnectionRequestTimeout(Timeout)
174      */
175     public Timeout getConnectionRequestTimeout() {
176         return connectionRequestTimeout;
177     }
178 
179     /**
180      * @see Builder#setConnectTimeout(Timeout)
181      */
182     public Timeout getConnectTimeout() {
183         return connectTimeout;
184     }
185 
186     /**
187      * @see Builder#setResponseTimeout(Timeout)
188      */
189     public Timeout getResponseTimeout() {
190         return responseTimeout;
191     }
192 
193     /**
194      * @see Builder#setConnectionKeepAlive(TimeValue)
195      */
196     public TimeValue getConnectionKeepAlive() {
197         return connectionKeepAlive;
198     }
199 
200     /**
201      * @see Builder#setContentCompressionEnabled(boolean)
202      */
203     public boolean isContentCompressionEnabled() {
204         return contentCompressionEnabled;
205     }
206 
207     /**
208      * @see Builder#setHardCancellationEnabled(boolean)
209      */
210     public boolean isHardCancellationEnabled() {
211         return hardCancellationEnabled;
212     }
213 
214     @Override
215     protected RequestConfig clone() throws CloneNotSupportedException {
216         return (RequestConfig) super.clone();
217     }
218 
219     @Override
220     public String toString() {
221         final StringBuilder builder = new StringBuilder();
222         builder.append("[");
223         builder.append("expectContinueEnabled=").append(expectContinueEnabled);
224         builder.append(", proxy=").append(proxy);
225         builder.append(", cookieSpec=").append(cookieSpec);
226         builder.append(", redirectsEnabled=").append(redirectsEnabled);
227         builder.append(", maxRedirects=").append(maxRedirects);
228         builder.append(", circularRedirectsAllowed=").append(circularRedirectsAllowed);
229         builder.append(", authenticationEnabled=").append(authenticationEnabled);
230         builder.append(", targetPreferredAuthSchemes=").append(targetPreferredAuthSchemes);
231         builder.append(", proxyPreferredAuthSchemes=").append(proxyPreferredAuthSchemes);
232         builder.append(", connectionRequestTimeout=").append(connectionRequestTimeout);
233         builder.append(", connectTimeout=").append(connectTimeout);
234         builder.append(", responseTimeout=").append(responseTimeout);
235         builder.append(", connectionKeepAlive=").append(connectionKeepAlive);
236         builder.append(", contentCompressionEnabled=").append(contentCompressionEnabled);
237         builder.append(", hardCancellationEnabled=").append(hardCancellationEnabled);
238         builder.append("]");
239         return builder.toString();
240     }
241 
242     public static RequestConfig.Builder custom() {
243         return new Builder();
244     }
245 
246     public static RequestConfig.Builder copy(final RequestConfig config) {
247         return new Builder()
248             .setExpectContinueEnabled(config.isExpectContinueEnabled())
249             .setProxy(config.getProxy())
250             .setCookieSpec(config.getCookieSpec())
251             .setRedirectsEnabled(config.isRedirectsEnabled())
252             .setCircularRedirectsAllowed(config.isCircularRedirectsAllowed())
253             .setMaxRedirects(config.getMaxRedirects())
254             .setAuthenticationEnabled(config.isAuthenticationEnabled())
255             .setTargetPreferredAuthSchemes(config.getTargetPreferredAuthSchemes())
256             .setProxyPreferredAuthSchemes(config.getProxyPreferredAuthSchemes())
257             .setConnectionRequestTimeout(config.getConnectionRequestTimeout())
258             .setConnectTimeout(config.getConnectTimeout())
259             .setResponseTimeout(config.getResponseTimeout())
260             .setConnectionKeepAlive(config.getConnectionKeepAlive())
261             .setContentCompressionEnabled(config.isContentCompressionEnabled())
262             .setHardCancellationEnabled(config.isHardCancellationEnabled());
263     }
264 
265     public static class Builder {
266 
267         private boolean expectContinueEnabled;
268         private HttpHost proxy;
269         private String cookieSpec;
270         private boolean redirectsEnabled;
271         private boolean circularRedirectsAllowed;
272         private int maxRedirects;
273         private boolean authenticationEnabled;
274         private Collection<String> targetPreferredAuthSchemes;
275         private Collection<String> proxyPreferredAuthSchemes;
276         private Timeout connectionRequestTimeout;
277         private Timeout connectTimeout;
278         private Timeout responseTimeout;
279         private TimeValue connectionKeepAlive;
280         private boolean contentCompressionEnabled;
281         private boolean hardCancellationEnabled;
282 
283         Builder() {
284             super();
285             this.redirectsEnabled = true;
286             this.maxRedirects = 50;
287             this.authenticationEnabled = true;
288             this.connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
289             this.connectTimeout = DEFAULT_CONNECT_TIMEOUT;
290             this.contentCompressionEnabled = true;
291             this.hardCancellationEnabled = true;
292         }
293 
294         /**
295          * Determines whether the 'Expect: 100-Continue' handshake is enabled
296          * for entity enclosing methods. The purpose of the 'Expect: 100-Continue'
297          * handshake is to allow a client that is sending a request message with
298          * a request body to determine if the origin server is willing to
299          * accept the request (based on the request headers) before the client
300          * sends the request body.
301          * <p>
302          * The use of the 'Expect: 100-continue' handshake can result in
303          * a noticeable performance improvement for entity enclosing requests
304          * (such as POST and PUT) that require the target server's
305          * authentication.
306          * </p>
307          * <p>
308          * 'Expect: 100-continue' handshake should be used with caution, as it
309          * may cause problems with HTTP servers and proxies that do not support
310          * HTTP/1.1 protocol.
311          * </p>
312          * <p>
313          * Default: {@code false}
314          * </p>
315          */
316         public Builder setExpectContinueEnabled(final boolean expectContinueEnabled) {
317             this.expectContinueEnabled = expectContinueEnabled;
318             return this;
319         }
320 
321         /**
322          * Returns HTTP proxy to be used for request execution.
323          * <p>
324          * Default: {@code null}
325          * </p>
326          */
327         public Builder setProxy(final HttpHost proxy) {
328             this.proxy = proxy;
329             return this;
330         }
331 
332         /**
333          * Determines the name of the cookie specification to be used for HTTP state
334          * management.
335          * <p>
336          * Default: {@code null}
337          * </p>
338          */
339         public Builder setCookieSpec(final String cookieSpec) {
340             this.cookieSpec = cookieSpec;
341             return this;
342         }
343 
344         /**
345          * Determines whether redirects should be handled automatically.
346          * <p>
347          * Default: {@code true}
348          * </p>
349          */
350         public Builder setRedirectsEnabled(final boolean redirectsEnabled) {
351             this.redirectsEnabled = redirectsEnabled;
352             return this;
353         }
354 
355         /**
356          * Determines whether circular redirects (redirects to the same location) should
357          * be allowed. The HTTP spec is not sufficiently clear whether circular redirects
358          * are permitted, therefore optionally they can be enabled
359          * <p>
360          * Default: {@code false}
361          * </p>
362          */
363         public Builder setCircularRedirectsAllowed(final boolean circularRedirectsAllowed) {
364             this.circularRedirectsAllowed = circularRedirectsAllowed;
365             return this;
366         }
367 
368         /**
369          * Returns the maximum number of redirects to be followed. The limit on number
370          * of redirects is intended to prevent infinite loops.
371          * <p>
372          * Default: {@code 50}
373          * </p>
374          */
375         public Builder setMaxRedirects(final int maxRedirects) {
376             this.maxRedirects = maxRedirects;
377             return this;
378         }
379 
380         /**
381          * Determines whether authentication should be handled automatically.
382          * <p>
383          * Default: {@code true}
384          * </p>
385          */
386         public Builder setAuthenticationEnabled(final boolean authenticationEnabled) {
387             this.authenticationEnabled = authenticationEnabled;
388             return this;
389         }
390 
391         /**
392          * Determines the order of preference for supported authentication schemes
393          * by their names when authenticating with the target host.
394          * <p>
395          * Default: {@code null}
396          * </p>
397          */
398         public Builder setTargetPreferredAuthSchemes(final Collection<String> targetPreferredAuthSchemes) {
399             this.targetPreferredAuthSchemes = targetPreferredAuthSchemes;
400             return this;
401         }
402 
403         /**
404          * Determines the order of preference for supported authentication schemes
405          * by their names when authenticating with the proxy host.
406          * <p>
407          * Default: {@code null}
408          * </p>
409          */
410         public Builder setProxyPreferredAuthSchemes(final Collection<String> proxyPreferredAuthSchemes) {
411             this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes;
412             return this;
413         }
414         /**
415          * Returns the connection lease request timeout used when requesting
416          * a connection from the connection manager.
417          * <p>
418          * A timeout value of zero is interpreted as an infinite timeout.
419          * </p>
420          * <p>
421          * Default: 3 minutes.
422          * </p>
423          */
424         public Builder setConnectionRequestTimeout(final Timeout connectionRequestTimeout) {
425             this.connectionRequestTimeout = connectionRequestTimeout;
426             return this;
427         }
428 
429         /**
430          * @see #setConnectionRequestTimeout(Timeout)
431          */
432         public Builder setConnectionRequestTimeout(final long connectionRequestTimeout, final TimeUnit timeUnit) {
433             this.connectionRequestTimeout = Timeout.of(connectionRequestTimeout, timeUnit);
434             return this;
435         }
436 
437         /**
438          * Determines the timeout until a new connection is fully established.
439          * This may also include transport security negotiation exchanges
440          * such as {@code SSL} or {@code TLS} protocol negotiation).
441          * <p>
442          * A timeout value of zero is interpreted as an infinite timeout.
443          * </p>
444          * <p>
445          * Default: 3 minutes
446          * </p>
447          */
448         public Builder setConnectTimeout(final Timeout connectTimeout) {
449             this.connectTimeout = connectTimeout;
450             return this;
451         }
452 
453         /**
454          * @see #setConnectTimeout(Timeout)
455          */
456         public Builder setConnectTimeout(final long connectTimeout, final TimeUnit timeUnit) {
457             this.connectTimeout = Timeout.of(connectTimeout, timeUnit);
458             return this;
459         }
460 
461         /**
462          * Determines the timeout until arrival of a response from the opposite
463          * endpoint.
464          * <p>
465          * A timeout value of zero is interpreted as an infinite timeout.
466          * </p>
467          * <p>
468          * Please note that response timeout may be unsupported by
469          * HTTP transports with message multiplexing.
470          * </p>
471          * <p>
472          * Default: {@code null}
473          * </p>
474          *
475          * @since 5.0
476          */
477         public Builder setResponseTimeout(final Timeout responseTimeout) {
478             this.responseTimeout = responseTimeout;
479             return this;
480         }
481 
482         /**
483          * @see #setResponseTimeout(Timeout)
484          */
485         public Builder setResponseTimeout(final long responseTimeout, final TimeUnit timeUnit) {
486             this.responseTimeout = Timeout.of(responseTimeout, timeUnit);
487             return this;
488         }
489 
490         /**
491          * Determines the default of value of connection keep-alive time period when not
492          * explicitly communicated by the origin server with a {@code Keep-Alive} response
493          * header.
494          * <p>
495          * A negative value is interpreted as an infinite keep-alive period.
496          * </p>
497          * <p>
498          * Default: 3 minutes
499          * </p>
500          *
501          * @since 5.0
502          */
503         public Builder setConnectionKeepAlive(final TimeValue connectionKeepAlive) {
504             this.connectionKeepAlive = connectionKeepAlive;
505             return this;
506         }
507 
508         /**
509          * @see #setConnectionKeepAlive(TimeValue)
510          */
511         public Builder setDefaultKeepAlive(final long defaultKeepAlive, final TimeUnit timeUnit) {
512             this.connectionKeepAlive = TimeValue.of(defaultKeepAlive, timeUnit);
513             return this;
514         }
515 
516         /**
517          * Determines whether the target server is requested to compress content.
518          * <p>
519          * Default: {@code true}
520          * </p>
521          *
522          * @since 4.5
523          */
524         public Builder setContentCompressionEnabled(final boolean contentCompressionEnabled) {
525             this.contentCompressionEnabled = contentCompressionEnabled;
526             return this;
527         }
528 
529         /**
530          * Determines whether request cancellation, such as through {@code
531          * Future#cancel(boolean)}, should kill the underlying connection. If this
532          * option is set to false, the client will attempt to preserve the
533          * underlying connection by allowing the request to complete in the
534          * background, discarding the response.
535          * <p>
536          * Note that when this option is {@code true}, cancelling a request may
537          * cause other requests to fail, if they are waiting to use the same
538          * connection.
539          * </p>
540          * <p>
541          * On HTTP/2, this option has no effect. Request cancellation will always
542          * result in the stream being cancelled with a {@code RST_STREAM}. This
543          * has no effect on connection reuse.
544          * </p>
545          * <p>
546          * On non-asynchronous clients, this option has no effect. Request
547          * cancellation, such as through {@code HttpUriRequestBase#cancel()}, will
548          * always kill the underlying connection.
549          * </p>
550          * <p>
551          * Default: {@code true}
552          * </p>
553          *
554          * @since 5.0
555          */
556         public Builder setHardCancellationEnabled(final boolean hardCancellationEnabled) {
557             this.hardCancellationEnabled = hardCancellationEnabled;
558             return this;
559         }
560 
561         public RequestConfig build() {
562             return new RequestConfig(
563                     expectContinueEnabled,
564                     proxy,
565                     cookieSpec,
566                     redirectsEnabled,
567                     circularRedirectsAllowed,
568                     maxRedirects,
569                     authenticationEnabled,
570                     targetPreferredAuthSchemes,
571                     proxyPreferredAuthSchemes,
572                     connectionRequestTimeout != null ? connectionRequestTimeout : DEFAULT_CONNECTION_REQUEST_TIMEOUT,
573                     connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
574                     responseTimeout,
575                     connectionKeepAlive != null ? connectionKeepAlive : DEFAULT_CONN_KEEP_ALIVE,
576                     contentCompressionEnabled,
577                     hardCancellationEnabled);
578         }
579 
580     }
581 
582 }