View Javadoc
1   package com.atlassian.sal.core.net;
2   
3   import com.atlassian.sal.api.net.NonMarshallingRequestFactory;
4   import com.atlassian.sal.api.net.Request.MethodType;
5   import com.google.common.base.Supplier;
6   import com.google.common.base.Suppliers;
7   import org.apache.http.HttpHeaders;
8   import org.apache.http.HttpHost;
9   import org.apache.http.auth.AuthScope;
10  import org.apache.http.auth.Credentials;
11  import org.apache.http.auth.MalformedChallengeException;
12  import org.apache.http.auth.UsernamePasswordCredentials;
13  import org.apache.http.client.AuthCache;
14  import org.apache.http.client.CredentialsProvider;
15  import org.apache.http.client.config.CookieSpecs;
16  import org.apache.http.client.config.RequestConfig;
17  import org.apache.http.client.protocol.HttpClientContext;
18  import org.apache.http.conn.HttpClientConnectionManager;
19  import org.apache.http.conn.routing.HttpRoutePlanner;
20  import org.apache.http.impl.auth.BasicScheme;
21  import org.apache.http.impl.client.BasicCredentialsProvider;
22  import org.apache.http.impl.client.CloseableHttpClient;
23  import org.apache.http.impl.client.HttpClients;
24  import org.apache.http.message.BasicHeader;
25  import org.apache.http.protocol.HttpRequestExecutor;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  /**
30   * Does NOT support json/xml object marshalling. Use the atlassian-rest implementation of {@link
31   * com.atlassian.sal.api.net.RequestFactory} instead.
32   */
33  public class HttpClientRequestFactory implements NonMarshallingRequestFactory<HttpClientRequest<?, ?>> {
34      private static final Logger log = LoggerFactory.getLogger(HttpClientRequestFactory.class);
35  
36      private final Supplier<ProxyConfig> proxyConfigSupplier;
37  
38      public HttpClientRequestFactory() {
39          // must be initialised not earlier than the fisrt use, because
40          // system properties for the proxy config could be defined later in the startup
41          proxyConfigSupplier = Suppliers.memoize(SystemPropertiesProxyConfig::new);
42      }
43  
44      public HttpClientRequestFactory(final ProxyConfig proxyConfig) {
45          this.proxyConfigSupplier = () -> proxyConfig;
46      }
47  
48      /* (non-Javadoc)
49       * @see com.atlassian.sal.api.net.RequestFactory#createMethod(com.atlassian.sal.api.net.Request.MethodType, java.lang.String)
50       */
51      public HttpClientRequest createRequest(final MethodType methodType, final String url) {
52          log.debug("Creating HttpClientRequest with proxy config:", proxyConfigSupplier.get());
53  
54          final CloseableHttpClient httpClient = createHttpClient();
55          final boolean requiresAuthentication = ProxyUtil.requiresAuthentication(proxyConfigSupplier.get(), url);
56          final HttpClientContext clientContext = createClientContext(requiresAuthentication);
57          return new HttpClientRequest(httpClient, clientContext, methodType, url);
58      }
59  
60      protected CloseableHttpClient createHttpClient() {
61          return HttpClients.custom()
62                  .useSystemProperties()
63                  .setRoutePlanner(getRoutePlanner())
64                  .setRequestExecutor(getRequestExecutor())
65                  .setConnectionManager(getConnectionManager())
66                  .setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())
67                  .build();
68      }
69  
70      protected HttpClientContext createClientContext() {
71          return createClientContext(proxyConfigSupplier.get().requiresAuthentication());
72      }
73  
74      protected HttpClientContext createClientContext(boolean requiresAuthentication) {
75          final HttpClientContext httpClientContext = HttpClientContext.create();
76          final AuthCache authCache = new AllPortsAuthCache();
77          final CredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
78          final ProxyConfig proxyConfig = this.proxyConfigSupplier.get();
79  
80          if (requiresAuthentication) {
81              HttpHost proxyHost = new HttpHost(proxyConfig.getHost(), proxyConfig.getPort());
82              final AuthScope proxyAuthScope = new AuthScope(proxyHost);
83              final Credentials proxyCredentials = new UsernamePasswordCredentials(proxyConfig.getUser(),
84                      proxyConfig.getPassword());
85              basicCredentialsProvider.setCredentials(proxyAuthScope, proxyCredentials);
86  
87              // This ensures that proxy authentication is preemptive.
88              BasicScheme proxyScheme = new BasicScheme();
89              try {
90                  proxyScheme.processChallenge(
91                          new BasicHeader(HttpHeaders.PROXY_AUTHENTICATE, "Basic "));
92              } catch (MalformedChallengeException e) {
93                  throw new IllegalStateException(e);
94              }
95              authCache.put(proxyHost, proxyScheme);
96          }
97  
98          httpClientContext.setCredentialsProvider(basicCredentialsProvider);
99          httpClientContext.setAuthCache(authCache);
100         return httpClientContext;
101     }
102 
103     public boolean supportsHeader() {
104         return true;
105     }
106 
107     protected HttpRoutePlanner getRoutePlanner() {
108         return proxyConfigSupplier.get().isSet() ? new ProxyRoutePlanner(proxyConfigSupplier.get()) : null;
109     }
110 
111     /**
112      * We can override the to override the request execution behaviour. This is useful for testing, but potentially
113      * also useful in other scenarious.
114      *
115      * @return HttpRequestExecutor
116      */
117     protected HttpRequestExecutor getRequestExecutor() {
118         return null;
119     }
120 
121     protected HttpClientConnectionManager getConnectionManager() {
122         return null;
123     }
124 
125 }