001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.vfs2.provider.http4; 018 019import java.io.File; 020import java.io.IOException; 021import java.net.ProxySelector; 022import java.security.KeyManagementException; 023import java.security.KeyStoreException; 024import java.security.NoSuchAlgorithmException; 025import java.security.cert.CertificateException; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.List; 031import java.util.stream.Stream; 032 033import javax.net.ssl.HostnameVerifier; 034import javax.net.ssl.SSLContext; 035 036import org.apache.commons.lang3.StringUtils; 037import org.apache.commons.lang3.time.DurationUtils; 038import org.apache.commons.vfs2.Capability; 039import org.apache.commons.vfs2.FileName; 040import org.apache.commons.vfs2.FileSystem; 041import org.apache.commons.vfs2.FileSystemConfigBuilder; 042import org.apache.commons.vfs2.FileSystemException; 043import org.apache.commons.vfs2.FileSystemOptions; 044import org.apache.commons.vfs2.UserAuthenticationData; 045import org.apache.commons.vfs2.UserAuthenticator; 046import org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider; 047import org.apache.commons.vfs2.provider.GenericFileName; 048import org.apache.commons.vfs2.util.UserAuthenticatorUtils; 049import org.apache.http.ConnectionReuseStrategy; 050import org.apache.http.Header; 051import org.apache.http.HttpHost; 052import org.apache.http.auth.AuthScope; 053import org.apache.http.auth.UsernamePasswordCredentials; 054import org.apache.http.client.AuthCache; 055import org.apache.http.client.CookieStore; 056import org.apache.http.client.CredentialsProvider; 057import org.apache.http.client.HttpClient; 058import org.apache.http.client.config.RequestConfig; 059import org.apache.http.client.protocol.HttpClientContext; 060import org.apache.http.config.Registry; 061import org.apache.http.config.RegistryBuilder; 062import org.apache.http.config.SocketConfig; 063import org.apache.http.conn.HttpClientConnectionManager; 064import org.apache.http.conn.routing.HttpRoutePlanner; 065import org.apache.http.conn.socket.ConnectionSocketFactory; 066import org.apache.http.conn.socket.PlainConnectionSocketFactory; 067import org.apache.http.conn.ssl.DefaultHostnameVerifier; 068import org.apache.http.conn.ssl.NoopHostnameVerifier; 069import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 070import org.apache.http.conn.ssl.TrustAllStrategy; 071import org.apache.http.cookie.Cookie; 072import org.apache.http.impl.DefaultConnectionReuseStrategy; 073import org.apache.http.impl.NoConnectionReuseStrategy; 074import org.apache.http.impl.auth.BasicScheme; 075import org.apache.http.impl.client.BasicAuthCache; 076import org.apache.http.impl.client.BasicCookieStore; 077import org.apache.http.impl.client.BasicCredentialsProvider; 078import org.apache.http.impl.client.HttpClientBuilder; 079import org.apache.http.impl.client.HttpClients; 080import org.apache.http.impl.conn.DefaultProxyRoutePlanner; 081import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 082import org.apache.http.impl.conn.SystemDefaultRoutePlanner; 083import org.apache.http.message.BasicHeader; 084import org.apache.http.protocol.HTTP; 085import org.apache.http.ssl.SSLContextBuilder; 086 087/** 088 * {@code FileProvider} implementation using HttpComponents HttpClient library. 089 * 090 * @since 2.3 091 * @deprecated Use {@link org.apache.commons.vfs2.provider.http5}. 092 */ 093@Deprecated 094public class Http4FileProvider extends AbstractOriginatingFileProvider { 095 096 /** Authenticator information. */ 097 static final UserAuthenticationData.Type[] AUTHENTICATOR_TYPES = 098 { 099 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}