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