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