View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.provider.http4;
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.net.ProxySelector;
22  import java.security.KeyManagementException;
23  import java.security.KeyStoreException;
24  import java.security.NoSuchAlgorithmException;
25  import java.security.cert.CertificateException;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.stream.Stream;
32  
33  import javax.net.ssl.HostnameVerifier;
34  import javax.net.ssl.SSLContext;
35  
36  import org.apache.commons.lang3.StringUtils;
37  import org.apache.commons.lang3.time.DurationUtils;
38  import org.apache.commons.vfs2.Capability;
39  import org.apache.commons.vfs2.FileName;
40  import org.apache.commons.vfs2.FileSystem;
41  import org.apache.commons.vfs2.FileSystemConfigBuilder;
42  import org.apache.commons.vfs2.FileSystemException;
43  import org.apache.commons.vfs2.FileSystemOptions;
44  import org.apache.commons.vfs2.UserAuthenticationData;
45  import org.apache.commons.vfs2.UserAuthenticator;
46  import org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider;
47  import org.apache.commons.vfs2.provider.GenericFileName;
48  import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
49  import org.apache.http.ConnectionReuseStrategy;
50  import org.apache.http.Header;
51  import org.apache.http.HttpHost;
52  import org.apache.http.auth.AuthScope;
53  import org.apache.http.auth.UsernamePasswordCredentials;
54  import org.apache.http.client.AuthCache;
55  import org.apache.http.client.CookieStore;
56  import org.apache.http.client.CredentialsProvider;
57  import org.apache.http.client.HttpClient;
58  import org.apache.http.client.config.RequestConfig;
59  import org.apache.http.client.protocol.HttpClientContext;
60  import org.apache.http.config.Registry;
61  import org.apache.http.config.RegistryBuilder;
62  import org.apache.http.config.SocketConfig;
63  import org.apache.http.conn.HttpClientConnectionManager;
64  import org.apache.http.conn.routing.HttpRoutePlanner;
65  import org.apache.http.conn.socket.ConnectionSocketFactory;
66  import org.apache.http.conn.socket.PlainConnectionSocketFactory;
67  import org.apache.http.conn.ssl.DefaultHostnameVerifier;
68  import org.apache.http.conn.ssl.NoopHostnameVerifier;
69  import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
70  import org.apache.http.conn.ssl.TrustAllStrategy;
71  import org.apache.http.cookie.Cookie;
72  import org.apache.http.impl.DefaultConnectionReuseStrategy;
73  import org.apache.http.impl.NoConnectionReuseStrategy;
74  import org.apache.http.impl.auth.BasicScheme;
75  import org.apache.http.impl.client.BasicAuthCache;
76  import org.apache.http.impl.client.BasicCookieStore;
77  import org.apache.http.impl.client.BasicCredentialsProvider;
78  import org.apache.http.impl.client.HttpClientBuilder;
79  import org.apache.http.impl.client.HttpClients;
80  import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
81  import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
82  import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
83  import org.apache.http.message.BasicHeader;
84  import org.apache.http.protocol.HTTP;
85  import org.apache.http.ssl.SSLContextBuilder;
86  
87  /**
88   * {@code FileProvider} implementation using HttpComponents HttpClient library.
89   *
90   * @since 2.3
91   * @deprecated Use {@link org.apache.commons.vfs2.provider.http5}.
92   */
93  @Deprecated
94  public class Http4FileProvider extends AbstractOriginatingFileProvider {
95  
96      /** Authenticator information. */
97      static final UserAuthenticationData.Type[] AUTHENTICATOR_TYPES =
98              {
99              UserAuthenticationData.USERNAME,
100             UserAuthenticationData.PASSWORD
101             };
102 
103     /** FileProvider capabilities */
104     static final Collection<Capability> CAPABILITIES =
105             Collections.unmodifiableCollection(
106                     Arrays.asList(
107                             Capability.GET_TYPE,
108                             Capability.READ_CONTENT,
109                             Capability.URI,
110                             Capability.GET_LAST_MODIFIED,
111                             Capability.ATTRIBUTES,
112                             Capability.RANDOM_ACCESS_READ,
113                             Capability.DIRECTORY_READ_CONTENT
114                             )
115                     );
116 
117     /**
118      * Constructs a new provider.
119      */
120     public Http4FileProvider() {
121         setFileNameParser(Http4FileNameParser.getInstance());
122     }
123 
124     private HttpClientConnectionManager createConnectionManager(final Http4FileSystemConfigBuilder builder,
125         final FileSystemOptions fileSystemOptions, final SSLContext sslContext, final HostnameVerifier verifier) {
126         final SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext, verifier);
127         // @formatter:off
128         final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
129             .register("https", sslFactory)
130             .register("http", new PlainConnectionSocketFactory())
131             .build();
132         // @formatter:on
133 
134         final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
135         connManager.setMaxTotal(builder.getMaxTotalConnections(fileSystemOptions));
136         connManager.setDefaultMaxPerRoute(builder.getMaxConnectionsPerHost(fileSystemOptions));
137 
138         // @formatter:off
139         final SocketConfig socketConfig = SocketConfig
140             .custom()
141             .setSoTimeout(DurationUtils.toMillisInt(builder.getSoTimeoutDuration(fileSystemOptions)))
142             .build();
143         // @formatter:on
144 
145         connManager.setDefaultSocketConfig(socketConfig);
146 
147         return connManager;
148     }
149 
150     private CookieStore createDefaultCookieStore(final Http4FileSystemConfigBuilder builder,
151             final FileSystemOptions fileSystemOptions) {
152         final CookieStore cookieStore = new BasicCookieStore();
153         final Cookie[] cookies = builder.getCookies(fileSystemOptions);
154 
155         if (cookies != null) {
156             Stream.of(cookies).forEach(cookieStore::addCookie);
157         }
158 
159         return cookieStore;
160     }
161 
162     private RequestConfig createDefaultRequestConfig(final Http4FileSystemConfigBuilder builder,
163         final FileSystemOptions fileSystemOptions) {
164         return RequestConfig.custom()
165             .setConnectTimeout(DurationUtils.toMillisInt(builder.getConnectionTimeoutDuration(fileSystemOptions)))
166             .build();
167     }
168 
169     private HostnameVerifier createHostnameVerifier(final Http4FileSystemConfigBuilder builder,
170         final FileSystemOptions fileSystemOptions) {
171         return builder.isHostnameVerificationEnabled(fileSystemOptions) ? new DefaultHostnameVerifier()
172             : NoopHostnameVerifier.INSTANCE;
173     }
174 
175     /**
176      * Create an {@link HttpClient} object for an http4 file system.
177      *
178      * @param builder Configuration options builder for http4 provider
179      * @param rootName The root path
180      * @param fileSystemOptions The file system options
181      * @return an {@link HttpClient} object
182      * @throws FileSystemException if an error occurs.
183      */
184     protected HttpClient createHttpClient(final Http4FileSystemConfigBuilder builder, final GenericFileName rootName,
185             final FileSystemOptions fileSystemOptions) throws FileSystemException {
186         return createHttpClientBuilder(builder, rootName, fileSystemOptions).build();
187     }
188 
189     /**
190      * Create an {@link HttpClientBuilder} object. Invoked by {@link #createHttpClient(Http4FileSystemConfigBuilder, GenericFileName, FileSystemOptions)}.
191      *
192      * @param builder Configuration options builder for HTTP4 provider
193      * @param rootName The root path
194      * @param fileSystemOptions The FileSystem options
195      * @return an {@link HttpClientBuilder} object
196      * @throws FileSystemException if an error occurs
197      */
198     protected HttpClientBuilder createHttpClientBuilder(final Http4FileSystemConfigBuilder builder, final GenericFileName rootName,
199             final FileSystemOptions fileSystemOptions) throws FileSystemException {
200         final List<Header> defaultHeaders = new ArrayList<>();
201         defaultHeaders.add(new BasicHeader(HTTP.USER_AGENT, builder.getUserAgent(fileSystemOptions)));
202 
203         final ConnectionReuseStrategy connectionReuseStrategy = builder.isKeepAlive(fileSystemOptions)
204                 ? DefaultConnectionReuseStrategy.INSTANCE
205                 : NoConnectionReuseStrategy.INSTANCE;
206         final SSLContext sslContext = createSSLContext(builder, fileSystemOptions);
207         final HostnameVerifier hostNameVerifier = createHostnameVerifier(builder, fileSystemOptions);
208         final HttpClientBuilder httpClientBuilder =
209                 HttpClients.custom()
210                 .setRoutePlanner(createHttpRoutePlanner(builder, fileSystemOptions))
211                 .setConnectionManager(createConnectionManager(builder, fileSystemOptions, sslContext, hostNameVerifier))
212                 .setSSLContext(sslContext)
213                 .setSSLHostnameVerifier(hostNameVerifier)
214                 .setConnectionReuseStrategy(connectionReuseStrategy)
215                 .setDefaultRequestConfig(createDefaultRequestConfig(builder, fileSystemOptions))
216                 .setDefaultHeaders(defaultHeaders)
217                 .setDefaultCookieStore(createDefaultCookieStore(builder, fileSystemOptions));
218 
219         if (!builder.getFollowRedirect(fileSystemOptions)) {
220             httpClientBuilder.disableRedirectHandling();
221         }
222 
223         return httpClientBuilder;
224     }
225 
226     /**
227      * Create an {@link HttpClientContext} object for an http4 file system.
228      *
229      * @param builder Configuration options builder for http4 provider
230      * @param rootName The root path
231      * @param fileSystemOptions The FileSystem options
232      * @param authData The {@code UserAuthenticationData} object
233      * @return an {@link HttpClientContext} object
234      */
235     protected HttpClientContext createHttpClientContext(final Http4FileSystemConfigBuilder builder,
236             final GenericFileName rootName, final FileSystemOptions fileSystemOptions,
237             final UserAuthenticationData authData) {
238 
239         final HttpClientContext clientContext = HttpClientContext.create();
240         final CredentialsProvider credsProvider = new BasicCredentialsProvider();
241         clientContext.setCredentialsProvider(credsProvider);
242 
243         final String username = UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData,
244                 UserAuthenticationData.USERNAME, UserAuthenticatorUtils.toChar(rootName.getUserName())));
245         final String password = UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData,
246                 UserAuthenticationData.PASSWORD, UserAuthenticatorUtils.toChar(rootName.getPassword())));
247 
248         if (!StringUtils.isEmpty(username)) {
249             credsProvider.setCredentials(new AuthScope(rootName.getHostName(), rootName.getPort()),
250                     new UsernamePasswordCredentials(username, password));
251         }
252 
253         final HttpHost proxyHost = getProxyHttpHost(builder, fileSystemOptions);
254 
255         if (proxyHost != null) {
256             final UserAuthenticator proxyAuth = builder.getProxyAuthenticator(fileSystemOptions);
257 
258             if (proxyAuth != null) {
259                 final UserAuthenticationData proxyAuthData = UserAuthenticatorUtils.authenticate(proxyAuth,
260                     new UserAuthenticationData.Type[] {UserAuthenticationData.USERNAME, UserAuthenticationData.PASSWORD});
261 
262                 if (proxyAuthData != null) {
263                     final UsernamePasswordCredentials proxyCreds = new UsernamePasswordCredentials(
264                             UserAuthenticatorUtils.toString(
265                                     UserAuthenticatorUtils.getData(proxyAuthData, UserAuthenticationData.USERNAME, null)),
266                             UserAuthenticatorUtils.toString(
267                                     UserAuthenticatorUtils.getData(proxyAuthData, UserAuthenticationData.PASSWORD, null)));
268 
269                     credsProvider.setCredentials(new AuthScope(proxyHost.getHostName(), proxyHost.getPort()),
270                             proxyCreds);
271                 }
272 
273                 if (builder.isPreemptiveAuth(fileSystemOptions)) {
274                     final AuthCache authCache = new BasicAuthCache();
275                     final BasicScheme basicAuth = new BasicScheme();
276                     authCache.put(proxyHost, basicAuth);
277                     clientContext.setAuthCache(authCache);
278                 }
279             }
280         }
281 
282         return clientContext;
283     }
284 
285     private HttpRoutePlanner createHttpRoutePlanner(final Http4FileSystemConfigBuilder builder,
286             final FileSystemOptions fileSystemOptions) {
287         final HttpHost proxyHost = getProxyHttpHost(builder, fileSystemOptions);
288 
289         if (proxyHost != null) {
290             return new DefaultProxyRoutePlanner(proxyHost);
291         }
292 
293         return new SystemDefaultRoutePlanner(ProxySelector.getDefault());
294     }
295 
296     /**
297      * Create {@link SSLContext} for HttpClient. Invoked by {@link #createHttpClientBuilder(Http4FileSystemConfigBuilder, GenericFileName, FileSystemOptions)}.
298      *
299      * @param builder Configuration options builder for HTTP4 provider
300      * @param fileSystemOptions The FileSystem options
301      * @return a {@link SSLContext} for HttpClient
302      * @throws FileSystemException if an error occurs
303      */
304     protected SSLContext createSSLContext(final Http4FileSystemConfigBuilder builder,
305             final FileSystemOptions fileSystemOptions) throws FileSystemException {
306         try {
307             final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
308             sslContextBuilder.setKeyStoreType(builder.getKeyStoreType(fileSystemOptions));
309 
310             File keystoreFileObject = null;
311             final String keystoreFile = builder.getKeyStoreFile(fileSystemOptions);
312 
313             if (!StringUtils.isEmpty(keystoreFile)) {
314                 keystoreFileObject = new File(keystoreFile);
315             }
316 
317             if (keystoreFileObject != null && keystoreFileObject.exists()) {
318                 final String keystorePass = builder.getKeyStorePass(fileSystemOptions);
319                 final char[] keystorePassChars = keystorePass != null ? keystorePass.toCharArray() : null;
320                 sslContextBuilder.loadTrustMaterial(keystoreFileObject, keystorePassChars, TrustAllStrategy.INSTANCE);
321             } else {
322                 sslContextBuilder.loadTrustMaterial(TrustAllStrategy.INSTANCE);
323             }
324 
325             return sslContextBuilder.build();
326         } catch (final KeyStoreException e) {
327             throw new FileSystemException("Keystore error. " + e.getMessage(), e);
328         } catch (final KeyManagementException e) {
329             throw new FileSystemException("Cannot retrieve keys. " + e.getMessage(), e);
330         } catch (final NoSuchAlgorithmException e) {
331             throw new FileSystemException("Algorithm error. " + e.getMessage(), e);
332         } catch (final CertificateException e) {
333             throw new FileSystemException("Certificate error. " + e.getMessage(), e);
334         } catch (final IOException e) {
335             throw new FileSystemException("Cannot open key file. " + e.getMessage(), e);
336         }
337     }
338 
339     @Override
340     protected FileSystem doCreateFileSystem(final FileName name, final FileSystemOptions fileSystemOptions)
341             throws FileSystemException {
342         final GenericFileName rootName = (GenericFileName) name;
343         UserAuthenticationData authData = null;
344         HttpClient httpClient;
345         HttpClientContext httpClientContext;
346         try {
347             final Http4FileSystemConfigBuilder builder = Http4FileSystemConfigBuilder.getInstance();
348             authData = UserAuthenticatorUtils.authenticate(fileSystemOptions, AUTHENTICATOR_TYPES);
349             httpClientContext = createHttpClientContext(builder, rootName, fileSystemOptions, authData);
350             httpClient = createHttpClient(builder, rootName, fileSystemOptions);
351         } finally {
352             UserAuthenticatorUtils.cleanup(authData);
353         }
354         return new Http4FileSystem(rootName, fileSystemOptions, httpClient, httpClientContext);
355     }
356 
357     @Override
358     public Collection<Capability> getCapabilities() {
359         return CAPABILITIES;
360     }
361 
362     @Override
363     public FileSystemConfigBuilder getConfigBuilder() {
364         return Http4FileSystemConfigBuilder.getInstance();
365     }
366 
367     private HttpHost getProxyHttpHost(final Http4FileSystemConfigBuilder builder,
368             final FileSystemOptions fileSystemOptions) {
369         final String proxyHost = builder.getProxyHost(fileSystemOptions);
370         final int proxyPort = builder.getProxyPort(fileSystemOptions);
371         final String proxyScheme = builder.getProxyScheme(fileSystemOptions);
372 
373         if (!StringUtils.isEmpty(proxyHost) && proxyPort > 0) {
374             return new HttpHost(proxyHost, proxyPort, proxyScheme);
375         }
376 
377         return null;
378     }
379 
380 }