1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider.http5;
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.vfs2.Capability;
38 import org.apache.commons.vfs2.FileName;
39 import org.apache.commons.vfs2.FileSystem;
40 import org.apache.commons.vfs2.FileSystemConfigBuilder;
41 import org.apache.commons.vfs2.FileSystemException;
42 import org.apache.commons.vfs2.FileSystemOptions;
43 import org.apache.commons.vfs2.UserAuthenticationData;
44 import org.apache.commons.vfs2.UserAuthenticator;
45 import org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider;
46 import org.apache.commons.vfs2.provider.GenericFileName;
47 import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
48 import org.apache.hc.client5.http.auth.AuthCache;
49 import org.apache.hc.client5.http.auth.AuthScope;
50 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
51 import org.apache.hc.client5.http.classic.HttpClient;
52 import org.apache.hc.client5.http.config.RequestConfig;
53 import org.apache.hc.client5.http.cookie.BasicCookieStore;
54 import org.apache.hc.client5.http.cookie.Cookie;
55 import org.apache.hc.client5.http.cookie.CookieStore;
56 import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
57 import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
58 import org.apache.hc.client5.http.impl.auth.BasicScheme;
59 import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
60 import org.apache.hc.client5.http.impl.classic.HttpClients;
61 import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
62 import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
63 import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
64 import org.apache.hc.client5.http.io.HttpClientConnectionManager;
65 import org.apache.hc.client5.http.protocol.HttpClientContext;
66 import org.apache.hc.client5.http.routing.HttpRoutePlanner;
67 import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
68 import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
69 import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
70 import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
71 import org.apache.hc.client5.http.ssl.TrustAllStrategy;
72 import org.apache.hc.core5.http.ConnectionReuseStrategy;
73 import org.apache.hc.core5.http.Header;
74 import org.apache.hc.core5.http.HttpHeaders;
75 import org.apache.hc.core5.http.HttpHost;
76 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
77 import org.apache.hc.core5.http.io.SocketConfig;
78 import org.apache.hc.core5.http.message.BasicHeader;
79 import org.apache.hc.core5.http.ssl.TLS;
80 import org.apache.hc.core5.ssl.SSLContextBuilder;
81 import org.apache.hc.core5.util.Timeout;
82
83
84
85
86
87
88 public class Http5FileProvider extends AbstractOriginatingFileProvider {
89
90
91 static final UserAuthenticationData.Type[] AUTHENTICATOR_TYPES =
92 new UserAuthenticationData.Type[] {
93 UserAuthenticationData.USERNAME,
94 UserAuthenticationData.PASSWORD
95 };
96
97
98 static final Collection<Capability> CAPABILITIES =
99 Collections.unmodifiableCollection(
100 Arrays.asList(
101 Capability.GET_TYPE,
102 Capability.READ_CONTENT,
103 Capability.URI,
104 Capability.GET_LAST_MODIFIED,
105 Capability.ATTRIBUTES,
106 Capability.RANDOM_ACCESS_READ,
107 Capability.DIRECTORY_READ_CONTENT
108 )
109 );
110
111
112
113
114 public Http5FileProvider() {
115 setFileNameParser(Http5FileNameParser.getInstance());
116 }
117
118 private HttpClientConnectionManager createConnectionManager(final Http5FileSystemConfigBuilder builder,
119 final FileSystemOptions fileSystemOptions) throws FileSystemException {
120
121 final SocketConfig socketConfig =
122 SocketConfig
123 .custom()
124 .setSoTimeout(Timeout.ofMilliseconds(builder.getSoTimeoutDuration(fileSystemOptions).toMillis()))
125 .build();
126
127 final String[] tlsVersions = builder.getTlsVersions(fileSystemOptions).split("\\s*,\\s*");
128
129 final TLS[] tlsArray = Stream.of(tlsVersions).map(TLS::valueOf).toArray(TLS[]::new);
130
131 final SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
132 .setSslContext(createSSLContext(builder, fileSystemOptions))
133 .setHostnameVerifier(createHostnameVerifier(builder, fileSystemOptions))
134 .setTlsVersions(tlsArray)
135 .build();
136
137 return PoolingHttpClientConnectionManagerBuilder.create()
138 .setSSLSocketFactory(sslSocketFactory)
139 .setMaxConnTotal(builder.getMaxTotalConnections(fileSystemOptions))
140 .setMaxConnPerRoute(builder.getMaxConnectionsPerHost(fileSystemOptions))
141 .setDefaultSocketConfig(socketConfig)
142 .build();
143 }
144
145 private CookieStore createDefaultCookieStore(final Http5FileSystemConfigBuilder builder,
146 final FileSystemOptions fileSystemOptions) {
147 final CookieStore cookieStore = new BasicCookieStore();
148 final Cookie[] cookies = builder.getCookies(fileSystemOptions);
149
150 if (cookies != null) {
151 Stream.of(cookies).forEach(cookieStore::addCookie);
152 }
153
154 return cookieStore;
155 }
156
157 private RequestConfig createDefaultRequestConfig(final Http5FileSystemConfigBuilder builder,
158 final FileSystemOptions fileSystemOptions) {
159 return RequestConfig.custom()
160 .setConnectTimeout(Timeout.ofMilliseconds(builder.getSoTimeoutDuration(fileSystemOptions).toMillis()))
161 .build();
162 }
163
164 private HostnameVerifier createHostnameVerifier(final Http5FileSystemConfigBuilder builder, final FileSystemOptions fileSystemOptions) {
165 if (!builder.isHostnameVerificationEnabled(fileSystemOptions)) {
166 return NoopHostnameVerifier.INSTANCE;
167 }
168 return new DefaultHostnameVerifier();
169 }
170
171
172
173
174
175
176
177
178
179
180 protected HttpClient createHttpClient(final Http5FileSystemConfigBuilder builder, final GenericFileName rootName,
181 final FileSystemOptions fileSystemOptions) throws FileSystemException {
182 return createHttpClientBuilder(builder, rootName, fileSystemOptions).build();
183 }
184
185
186
187
188
189
190
191
192
193
194 protected HttpClientBuilder createHttpClientBuilder(final Http5FileSystemConfigBuilder builder, final GenericFileName rootName,
195 final FileSystemOptions fileSystemOptions) throws FileSystemException {
196 final List<Header> defaultHeaders = new ArrayList<>();
197 defaultHeaders.add(new BasicHeader(HttpHeaders.USER_AGENT, builder.getUserAgent(fileSystemOptions)));
198
199 final ConnectionReuseStrategy connectionReuseStrategy = builder.isKeepAlive(fileSystemOptions)
200 ? DefaultConnectionReuseStrategy.INSTANCE
201 : (request, response, context) -> false;
202
203 final HttpClientBuilder httpClientBuilder =
204 HttpClients.custom()
205 .setRoutePlanner(createHttpRoutePlanner(builder, fileSystemOptions))
206 .setConnectionManager(createConnectionManager(builder, fileSystemOptions))
207 .setConnectionReuseStrategy(connectionReuseStrategy)
208 .setDefaultRequestConfig(createDefaultRequestConfig(builder, fileSystemOptions))
209 .setDefaultHeaders(defaultHeaders)
210 .setDefaultCookieStore(createDefaultCookieStore(builder, fileSystemOptions));
211
212 if (!builder.getFollowRedirect(fileSystemOptions)) {
213 httpClientBuilder.disableRedirectHandling();
214 }
215
216 return httpClientBuilder;
217 }
218
219
220
221
222
223
224
225
226
227
228 protected HttpClientContext createHttpClientContext(final Http5FileSystemConfigBuilder builder,
229 final GenericFileName rootName, final FileSystemOptions fileSystemOptions,
230 final UserAuthenticationData authData) {
231
232 final HttpClientContext clientContext = HttpClientContext.create();
233 final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
234 clientContext.setCredentialsProvider(credsProvider);
235
236 final String username = UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData,
237 UserAuthenticationData.USERNAME, UserAuthenticatorUtils.toChar(rootName.getUserName())));
238 final char[] password = UserAuthenticatorUtils.getData(authData,
239 UserAuthenticationData.PASSWORD, UserAuthenticatorUtils.toChar(rootName.getPassword()));
240
241 if (!StringUtils.isEmpty(username)) {
242
243 credsProvider.setCredentials(new AuthScope(rootName.getHostName(), rootName.getPort()),
244 new UsernamePasswordCredentials(username, password));
245 }
246
247 final HttpHost proxyHost = getProxyHttpHost(builder, fileSystemOptions);
248
249 if (proxyHost != null) {
250 final UserAuthenticator proxyAuth = builder.getProxyAuthenticator(fileSystemOptions);
251
252 if (proxyAuth != null) {
253 final UserAuthenticationData proxyAuthData = UserAuthenticatorUtils.authenticate(proxyAuth,
254 new UserAuthenticationData.Type[] { UserAuthenticationData.USERNAME,
255 UserAuthenticationData.PASSWORD });
256
257 if (proxyAuthData != null) {
258 final UsernamePasswordCredentials proxyCreds = new UsernamePasswordCredentials(
259 UserAuthenticatorUtils.toString(
260 UserAuthenticatorUtils.getData(proxyAuthData, UserAuthenticationData.USERNAME, null)),
261 UserAuthenticatorUtils.getData(proxyAuthData, UserAuthenticationData.PASSWORD, null));
262
263
264 credsProvider.setCredentials(new AuthScope(proxyHost.getHostName(), proxyHost.getPort()),
265 proxyCreds);
266 }
267
268 if (builder.isPreemptiveAuth(fileSystemOptions)) {
269 final AuthCache authCache = new BasicAuthCache();
270 final BasicScheme basicAuth = new BasicScheme();
271 authCache.put(proxyHost, basicAuth);
272 clientContext.setAuthCache(authCache);
273 }
274 }
275 }
276
277 return clientContext;
278 }
279
280 private HttpRoutePlanner createHttpRoutePlanner(final Http5FileSystemConfigBuilder builder,
281 final FileSystemOptions fileSystemOptions) {
282 final HttpHost proxyHost = getProxyHttpHost(builder, fileSystemOptions);
283
284 if (proxyHost != null) {
285 return new DefaultProxyRoutePlanner(proxyHost);
286 }
287
288 return new SystemDefaultRoutePlanner(ProxySelector.getDefault());
289 }
290
291
292
293
294
295
296
297
298
299 protected SSLContext createSSLContext(final Http5FileSystemConfigBuilder builder,
300 final FileSystemOptions fileSystemOptions) throws FileSystemException {
301 try {
302 final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
303 sslContextBuilder.setKeyStoreType(builder.getKeyStoreType(fileSystemOptions));
304
305 File keystoreFileObject = null;
306 final String keystoreFile = builder.getKeyStoreFile(fileSystemOptions);
307
308 if (!StringUtils.isEmpty(keystoreFile)) {
309 keystoreFileObject = new File(keystoreFile);
310 }
311
312 if (keystoreFileObject != null && keystoreFileObject.exists()) {
313 final String keystorePass = builder.getKeyStorePass(fileSystemOptions);
314 final char[] keystorePassChars = (keystorePass != null) ? keystorePass.toCharArray() : null;
315 sslContextBuilder.loadTrustMaterial(keystoreFileObject, keystorePassChars, TrustAllStrategy.INSTANCE);
316 } else {
317 sslContextBuilder.loadTrustMaterial(TrustAllStrategy.INSTANCE);
318 }
319
320 return sslContextBuilder.build();
321 } catch (final KeyStoreException e) {
322 throw new FileSystemException("Keystore error. " + e.getMessage(), e);
323 } catch (final KeyManagementException e) {
324 throw new FileSystemException("Cannot retrieve keys. " + e.getMessage(), e);
325 } catch (final NoSuchAlgorithmException e) {
326 throw new FileSystemException("Algorithm error. " + e.getMessage(), e);
327 } catch (final CertificateException e) {
328 throw new FileSystemException("Certificate error. " + e.getMessage(), e);
329 } catch (final IOException e) {
330 throw new FileSystemException("Cannot open key file. " + e.getMessage(), e);
331 }
332 }
333
334 @Override
335 protected FileSystem doCreateFileSystem(final FileName name, final FileSystemOptions fileSystemOptions)
336 throws FileSystemException {
337 final GenericFileName/../../org/apache/commons/vfs2/provider/GenericFileName.html#GenericFileName">GenericFileName rootName = (GenericFileName) name;
338
339 UserAuthenticationData authData = null;
340 HttpClient httpClient;
341 HttpClientContext httpClientContext;
342
343 try {
344 final Http5FileSystemConfigBuilder builder = Http5FileSystemConfigBuilder.getInstance();
345 authData = UserAuthenticatorUtils.authenticate(fileSystemOptions, AUTHENTICATOR_TYPES);
346 httpClientContext = createHttpClientContext(builder, rootName, fileSystemOptions, authData);
347 httpClient = createHttpClient(builder, rootName, fileSystemOptions);
348 } finally {
349 UserAuthenticatorUtils.cleanup(authData);
350 }
351
352 return new Http5FileSystem(rootName, fileSystemOptions, httpClient, httpClientContext);
353 }
354
355 @Override
356 public Collection<Capability> getCapabilities() {
357 return CAPABILITIES;
358 }
359
360 @Override
361 public FileSystemConfigBuilder getConfigBuilder() {
362 return Http5FileSystemConfigBuilder.getInstance();
363 }
364
365 private HttpHost getProxyHttpHost(final Http5FileSystemConfigBuilder builder,
366 final FileSystemOptions fileSystemOptions) {
367 final String proxyScheme = builder.getProxyScheme(fileSystemOptions);
368 final String proxyHost = builder.getProxyHost(fileSystemOptions);
369 final int proxyPort = builder.getProxyPort(fileSystemOptions);
370
371 if (!StringUtils.isEmpty(proxyHost) && proxyPort > 0) {
372 return new HttpHost(proxyScheme, proxyHost, proxyPort);
373 }
374
375 return null;
376 }
377
378 }