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  package org.apache.hc.client5.http.fluent;
28  
29  import java.io.IOException;
30  import java.net.URISyntaxException;
31  
32  import org.apache.hc.client5.http.auth.AuthCache;
33  import org.apache.hc.client5.http.auth.AuthScope;
34  import org.apache.hc.client5.http.auth.Credentials;
35  import org.apache.hc.client5.http.auth.CredentialsStore;
36  import org.apache.hc.client5.http.auth.NTCredentials;
37  import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
38  import org.apache.hc.client5.http.config.ConnectionConfig;
39  import org.apache.hc.client5.http.cookie.CookieStore;
40  import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
41  import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
42  import org.apache.hc.client5.http.impl.auth.BasicScheme;
43  import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
44  import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
45  import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
46  import org.apache.hc.client5.http.protocol.HttpClientContext;
47  import org.apache.hc.core5.http.HttpHost;
48  import org.apache.hc.core5.util.TimeValue;
49  
50  /**
51   * Executor for {@link Request}s.
52   * <p>
53   * A connection pool with maximum 100 connections per route and
54   * a total maximum of 200 connections is used internally.
55   *
56   * @since 4.2
57   */
58  public class Executor {
59  
60      final static CloseableHttpClient CLIENT;
61  
62      static {
63          CLIENT = HttpClientBuilder.create()
64                  .setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
65                          .useSystemProperties()
66                          .setMaxConnPerRoute(100)
67                          .setMaxConnTotal(200)
68                          .setDefaultConnectionConfig(ConnectionConfig.custom()
69                                  .setValidateAfterInactivity(TimeValue.ofSeconds(10))
70                                  .build())
71                          .build())
72                  .useSystemProperties()
73                  .evictExpiredConnections()
74                  .evictIdleConnections(TimeValue.ofMinutes(1))
75                  .build();
76      }
77  
78      public static Executor newInstance() {
79          return new Executor(CLIENT);
80      }
81  
82      public static Executor newInstance(final CloseableHttpClient httpclient) {
83          return new Executor(httpclient != null ? httpclient : CLIENT);
84      }
85  
86      private final CloseableHttpClient httpclient;
87      private final AuthCache authCache;
88      private volatile CredentialsStore credentialsStore;
89      private volatile CookieStore cookieStore;
90  
91      Executor(final CloseableHttpClient httpclient) {
92          super();
93          this.httpclient = httpclient;
94          this.authCache = new BasicAuthCache();
95      }
96  
97      /**
98       * @since 4.5
99       */
100     public Executor use(final CredentialsStore credentialsStore) {
101         this.credentialsStore = credentialsStore;
102         return this;
103     }
104 
105     public Executor auth(final AuthScope authScope, final Credentials credentials) {
106         CredentialsStore credentialsStoreSnapshot = credentialsStore;
107         if (credentialsStoreSnapshot == null) {
108             credentialsStoreSnapshot = new BasicCredentialsProvider();
109             this.credentialsStore = credentialsStoreSnapshot;
110         }
111         credentialsStoreSnapshot.setCredentials(authScope, credentials);
112         return this;
113     }
114 
115     public Executor auth(final HttpHost host, final Credentials credentials) {
116         return auth(new AuthScope(host), credentials);
117     }
118 
119     /**
120      * @since 4.4
121      */
122     public Executor auth(final String host, final Credentials credentials) {
123         final HttpHost httpHost;
124         try {
125             httpHost = HttpHost.create(host);
126         } catch (final URISyntaxException ex) {
127             throw new IllegalArgumentException("Invalid host: " + host);
128         }
129         return auth(httpHost, credentials);
130     }
131 
132     public Executor authPreemptive(final HttpHost host) {
133         final CredentialsStore credentialsStoreSnapshot = credentialsStore;
134         if (credentialsStoreSnapshot != null) {
135             final Credentials credentials = credentialsStoreSnapshot.getCredentials(new AuthScope(host), null);
136             if (credentials != null) {
137                 final BasicScheme basicScheme = new BasicScheme();
138                 basicScheme.initPreemptive(credentials);
139                 this.authCache.put(host, basicScheme);
140             }
141         }
142         return this;
143     }
144 
145     /**
146      * @since 4.4
147      */
148     public Executor authPreemptive(final String host) {
149         final HttpHost httpHost;
150         try {
151             httpHost = HttpHost.create(host);
152         } catch (final URISyntaxException ex) {
153             throw new IllegalArgumentException("Invalid host: " + host);
154         }
155         return authPreemptive(httpHost);
156     }
157 
158     public Executor authPreemptiveProxy(final HttpHost proxy) {
159         final CredentialsStore credentialsStoreSnapshot = credentialsStore;
160         if (credentialsStoreSnapshot != null) {
161             final Credentials credentials = credentialsStoreSnapshot.getCredentials(new AuthScope(proxy), null);
162             if (credentials != null) {
163                 final BasicScheme basicScheme = new BasicScheme();
164                 basicScheme.initPreemptive(credentials);
165                 this.authCache.put(proxy, basicScheme);
166             }
167         }
168         return this;
169     }
170 
171     /**
172      * @since 4.4
173      */
174     public Executor authPreemptiveProxy(final String proxy) {
175         final HttpHost httpHost;
176         try {
177             httpHost = HttpHost.create(proxy);
178         } catch (final URISyntaxException ex) {
179             throw new IllegalArgumentException("Invalid host: " + proxy);
180         }
181         return authPreemptiveProxy(httpHost);
182     }
183 
184     public Executor auth(final HttpHost host,
185             final String username, final char[] password) {
186         return auth(host, new UsernamePasswordCredentials(username, password));
187     }
188 
189     public Executor auth(final HttpHost host,
190             final String username, final char[] password,
191             final String workstation, final String domain) {
192         return auth(host, new NTCredentials(username, password, workstation, domain));
193     }
194 
195     public Executor clearAuth() {
196         final CredentialsStore credentialsStoreSnapshot = credentialsStore;
197         if (credentialsStoreSnapshot != null) {
198             credentialsStoreSnapshot.clear();
199         }
200         return this;
201     }
202 
203     /**
204      * @since 4.5
205      */
206     public Executor use(final CookieStore cookieStore) {
207         this.cookieStore = cookieStore;
208         return this;
209     }
210 
211     public Executor clearCookies() {
212         final CookieStore cookieStoreSnapshot = cookieStore;
213         if (cookieStoreSnapshot != null) {
214             cookieStoreSnapshot.clear();
215         }
216         return this;
217     }
218 
219     /**
220      * Executes the request. Please Note that response content must be processed
221      * or discarded using {@link Response#discardContent()}, otherwise the
222      * connection used for the request might not be released to the pool.
223      *
224      * @see Response#handleResponse(org.apache.hc.core5.http.io.HttpClientResponseHandler)
225      * @see Response#discardContent()
226      */
227     public Response execute(
228             final Request request) throws IOException {
229         final HttpClientContext localContext = HttpClientContext.create();
230         final CredentialsStore credentialsStoreSnapshot = credentialsStore;
231         if (credentialsStoreSnapshot != null) {
232             localContext.setAttribute(HttpClientContext.CREDS_PROVIDER, credentialsStoreSnapshot);
233         }
234         if (this.authCache != null) {
235             localContext.setAttribute(HttpClientContext.AUTH_CACHE, this.authCache);
236         }
237         final CookieStore cookieStoreSnapshot = cookieStore;
238         if (cookieStoreSnapshot != null) {
239             localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStoreSnapshot);
240         }
241         return new Response(request.internalExecute(this.httpclient, localContext));
242     }
243 
244 }