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