PageRenderTime 68ms CodeModel.GetById 44ms RepoModel.GetById 1ms app.codeStats 0ms

/core/src/main/java/ch/cyberduck/core/http/HttpConnectionPoolBuilder.java

https://gitlab.com/vincent.hsu/cyberduck
Java | 253 lines | 211 code | 19 blank | 23 comment | 14 complexity | 0b8df78905881c70e9c86aa92b678609 MD5 | raw file
  1. package ch.cyberduck.core.http;
  2. /*
  3. * Copyright (c) 2002-2015 David Kocher. All rights reserved.
  4. * http://cyberduck.ch/
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch
  17. */
  18. import ch.cyberduck.core.Host;
  19. import ch.cyberduck.core.PreferencesUseragentProvider;
  20. import ch.cyberduck.core.Scheme;
  21. import ch.cyberduck.core.TranscriptListener;
  22. import ch.cyberduck.core.preferences.Preferences;
  23. import ch.cyberduck.core.preferences.PreferencesFactory;
  24. import ch.cyberduck.core.proxy.Proxy;
  25. import ch.cyberduck.core.proxy.ProxyFinder;
  26. import ch.cyberduck.core.proxy.ProxySocketFactory;
  27. import ch.cyberduck.core.ssl.CustomTrustSSLProtocolSocketFactory;
  28. import ch.cyberduck.core.ssl.ThreadLocalHostnameDelegatingTrustManager;
  29. import ch.cyberduck.core.ssl.TrustManagerHostnameCallback;
  30. import ch.cyberduck.core.ssl.X509KeyManager;
  31. import ch.cyberduck.core.ssl.X509TrustManager;
  32. import org.apache.http.HttpHost;
  33. import org.apache.http.auth.AuthSchemeProvider;
  34. import org.apache.http.client.config.AuthSchemes;
  35. import org.apache.http.client.config.RequestConfig;
  36. import org.apache.http.config.ConnectionConfig;
  37. import org.apache.http.config.Registry;
  38. import org.apache.http.config.RegistryBuilder;
  39. import org.apache.http.config.SocketConfig;
  40. import org.apache.http.conn.socket.ConnectionSocketFactory;
  41. import org.apache.http.conn.socket.PlainConnectionSocketFactory;
  42. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  43. import org.apache.http.impl.DefaultConnectionReuseStrategy;
  44. import org.apache.http.impl.NoConnectionReuseStrategy;
  45. import org.apache.http.impl.auth.BasicSchemeFactory;
  46. import org.apache.http.impl.auth.DigestSchemeFactory;
  47. import org.apache.http.impl.auth.KerberosSchemeFactory;
  48. import org.apache.http.impl.auth.NTLMSchemeFactory;
  49. import org.apache.http.impl.auth.SPNegoSchemeFactory;
  50. import org.apache.http.impl.client.HttpClientBuilder;
  51. import org.apache.http.impl.client.HttpClients;
  52. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  53. import org.apache.http.protocol.HttpContext;
  54. import org.apache.http.protocol.HttpCoreContext;
  55. import org.apache.log4j.Logger;
  56. import javax.net.SocketFactory;
  57. import java.io.IOException;
  58. import java.net.InetSocketAddress;
  59. import java.net.Socket;
  60. import java.nio.charset.Charset;
  61. public class HttpConnectionPoolBuilder {
  62. private static final Logger log = Logger.getLogger(HttpConnectionPoolBuilder.class);
  63. private final Preferences preferences
  64. = PreferencesFactory.get();
  65. private final ConnectionSocketFactory socketFactory;
  66. private final ConnectionSocketFactory sslSocketFactory;
  67. private final HttpClientBuilder builder
  68. = HttpClients.custom();
  69. private final ProxyFinder proxyFinder;
  70. private final Host host;
  71. public HttpConnectionPoolBuilder(final Host host,
  72. final ThreadLocalHostnameDelegatingTrustManager trust,
  73. final X509KeyManager key,
  74. final ProxyFinder proxy) {
  75. this(host, new PlainConnectionSocketFactory() {
  76. @Override
  77. public Socket createSocket(final HttpContext context) throws IOException {
  78. // Return socket factory with disabled support for HTTP tunneling as provided internally
  79. return new ProxySocketFactory(host.getProtocol(), new TrustManagerHostnameCallback() {
  80. @Override
  81. public String getTarget() {
  82. return context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST).toString();
  83. }
  84. }, proxy).disable(Proxy.Type.HTTP).disable(Proxy.Type.HTTPS).createSocket();
  85. }
  86. }, new SSLConnectionSocketFactory(
  87. new CustomTrustSSLProtocolSocketFactory(trust, key),
  88. new DisabledX509HostnameVerifier()
  89. ) {
  90. @Override
  91. public Socket createSocket(final HttpContext context) throws IOException {
  92. return new ProxySocketFactory(host.getProtocol(), new TrustManagerHostnameCallback() {
  93. @Override
  94. public String getTarget() {
  95. return context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST).toString();
  96. }
  97. }, proxy).disable(Proxy.Type.HTTP).disable(Proxy.Type.HTTPS).createSocket();
  98. }
  99. @Override
  100. public Socket connectSocket(final int connectTimeout,
  101. final Socket socket,
  102. final HttpHost host,
  103. final InetSocketAddress remoteAddress,
  104. final InetSocketAddress localAddress,
  105. final HttpContext context) throws IOException {
  106. trust.setTarget(remoteAddress.getHostName());
  107. return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
  108. }
  109. }, proxy);
  110. }
  111. protected HttpConnectionPoolBuilder(final Host host, final X509TrustManager trust, final X509KeyManager key,
  112. final ProxyFinder proxy,
  113. final SocketFactory socketFactory) {
  114. this(host, new PlainConnectionSocketFactory() {
  115. @Override
  116. public Socket createSocket(final HttpContext context) throws IOException {
  117. return socketFactory.createSocket();
  118. }
  119. }, new SSLConnectionSocketFactory(
  120. new CustomTrustSSLProtocolSocketFactory(trust, key),
  121. new DisabledX509HostnameVerifier()
  122. ) {
  123. @Override
  124. public Socket createSocket(final HttpContext context) throws IOException {
  125. return socketFactory.createSocket();
  126. }
  127. @Override
  128. public Socket connectSocket(final int connectTimeout,
  129. final Socket socket,
  130. final HttpHost host,
  131. final InetSocketAddress remoteAddress,
  132. final InetSocketAddress localAddress,
  133. final HttpContext context) throws IOException {
  134. if(trust instanceof ThreadLocalHostnameDelegatingTrustManager) {
  135. ((ThreadLocalHostnameDelegatingTrustManager) trust).setTarget(remoteAddress.getHostName());
  136. }
  137. return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
  138. }
  139. }, proxy);
  140. }
  141. public HttpConnectionPoolBuilder(final Host host,
  142. final ConnectionSocketFactory socketFactory,
  143. final ConnectionSocketFactory sslSocketFactory,
  144. final ProxyFinder proxyFinder) {
  145. this.host = host;
  146. this.socketFactory = socketFactory;
  147. this.sslSocketFactory = sslSocketFactory;
  148. this.proxyFinder = proxyFinder;
  149. }
  150. public HttpClientBuilder build(final TranscriptListener listener) {
  151. // Use HTTP Connect proxy implementation provided here instead of
  152. // relying on internal proxy support in socket factory
  153. final Proxy proxy = proxyFinder.find(host);
  154. if(proxy.getType() == Proxy.Type.HTTP) {
  155. final HttpHost h = new HttpHost(proxy.getHostname(), proxy.getPort(), Scheme.http.name());
  156. if(log.isInfoEnabled()) {
  157. log.info(String.format("Setup proxy %s", h));
  158. }
  159. builder.setProxy(h);
  160. }
  161. if(proxy.getType() == Proxy.Type.HTTPS) {
  162. final HttpHost h = new HttpHost(proxy.getHostname(), proxy.getPort(), Scheme.https.name());
  163. if(log.isInfoEnabled()) {
  164. log.info(String.format("Setup proxy %s", h));
  165. }
  166. builder.setProxy(h);
  167. }
  168. builder.setUserAgent(new PreferencesUseragentProvider().get());
  169. final int timeout = preferences.getInteger("connection.timeout.seconds") * 1000;
  170. builder.setDefaultSocketConfig(SocketConfig.custom()
  171. .setTcpNoDelay(true)
  172. .setSoTimeout(timeout)
  173. .build());
  174. builder.setDefaultRequestConfig(RequestConfig.custom()
  175. .setRedirectsEnabled(true)
  176. // Disable use of Expect: Continue by default for all methods
  177. .setExpectContinueEnabled(false)
  178. .setAuthenticationEnabled(true)
  179. .setConnectTimeout(timeout)
  180. // Sets the timeout in milliseconds used when retrieving a connection from the ClientConnectionManager
  181. .setConnectionRequestTimeout(preferences.getInteger("http.manager.timeout"))
  182. .setSocketTimeout(timeout)
  183. .build());
  184. final String encoding;
  185. if(null == host.getEncoding()) {
  186. encoding = preferences.getProperty("browser.charset.encoding");
  187. }
  188. else {
  189. encoding = host.getEncoding();
  190. }
  191. builder.setDefaultConnectionConfig(ConnectionConfig.custom()
  192. .setBufferSize(preferences.getInteger("http.socket.buffer"))
  193. .setCharset(Charset.forName(encoding))
  194. .build());
  195. if(preferences.getBoolean("http.connections.reuse")) {
  196. builder.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy());
  197. }
  198. else {
  199. builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
  200. }
  201. builder.setRetryHandler(new ExtendedHttpRequestRetryHandler(preferences.getInteger("http.connections.retry")));
  202. if(!preferences.getBoolean("http.compression.enable")) {
  203. builder.disableContentCompression();
  204. }
  205. builder.setRequestExecutor(new LoggingHttpRequestExecutor(listener));
  206. // Always register HTTP for possible use with proxy. Contains a number of protocol properties such as the
  207. // default port and the socket factory to be used to create the java.net.Socket instances for the given protocol
  208. builder.setConnectionManager(this.pool(this.registry().build()));
  209. builder.setDefaultAuthSchemeRegistry(RegistryBuilder.<AuthSchemeProvider>create()
  210. .register(AuthSchemes.BASIC, new BasicSchemeFactory(
  211. Charset.forName(preferences.getProperty("http.credentials.charset"))))
  212. .register(AuthSchemes.DIGEST, new DigestSchemeFactory(
  213. Charset.forName(preferences.getProperty("http.credentials.charset"))))
  214. .register(AuthSchemes.NTLM, new NTLMSchemeFactory())
  215. .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
  216. .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory()).build());
  217. return builder;
  218. }
  219. protected RegistryBuilder<ConnectionSocketFactory> registry() {
  220. return RegistryBuilder.<ConnectionSocketFactory>create()
  221. .register(Scheme.http.toString(), socketFactory)
  222. .register(Scheme.https.toString(), sslSocketFactory);
  223. }
  224. protected PoolingHttpClientConnectionManager pool(final Registry<ConnectionSocketFactory> registry) {
  225. if(log.isDebugEnabled()) {
  226. log.debug(String.format("Setup connection pool with registry %s", registry));
  227. }
  228. final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry);
  229. manager.setMaxTotal(preferences.getInteger("http.connections.total"));
  230. manager.setDefaultMaxPerRoute(preferences.getInteger("http.connections.route"));
  231. manager.setValidateAfterInactivity(5000);
  232. return manager;
  233. }
  234. }