View Javadoc

1   package com.atlassian.marketplace.client.http;
2   
3   import com.atlassian.fugue.Option;
4   
5   import org.apache.http.impl.client.ProxyAuthenticationStrategy;
6   
7   import static com.atlassian.fugue.Option.none;
8   import static com.atlassian.fugue.Option.some;
9   import static com.google.common.base.Preconditions.checkNotNull;
10  
11  /**
12   * Encapsulates all the properties that control the client's HTTP behavior, except for the
13   * base URI of the server.
14   * @since 2.0.0
15   */
16  public class HttpConfiguration
17  {
18      public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 15000;
19      public static final int DEFAULT_READ_TIMEOUT_MILLIS = 15000;
20      public static final int DEFAULT_MAX_CONNECTIONS = 10;
21      public static final int DEFAULT_MAX_CACHE_ENTRIES = 100;
22      public static final long DEFAULT_MAX_CACHE_OBJECT_SIZE = 60000;
23      
24      private final int connectTimeoutMillis;
25      private final int readTimeoutMillis;
26      private final int maxConnections;
27      private final int maxCacheEntries;
28      private final long maxCacheObjectSize;
29      private final Option<Credentials> credentials;
30      private final Option<Integer> maxRedirects;
31      private final Option<ProxyConfiguration> proxy;
32      private final Option<RequestDecorator> requestDecorator;
33   
34      /**
35       * A username-password pair for client authentication or proxy authentication.
36       */
37      public static class Credentials
38      {
39          private final String username;
40          private final String password;
41          
42          public Credentials(String username, String password)
43          {
44              this.username = checkNotNull(username, "username");
45              this.password = checkNotNull(password, "password");
46          }
47          
48          public String getUsername()
49          {
50              return username;
51          }
52          
53          public String getPassword()
54          {
55              return password;
56          }
57          
58          @Override
59          public boolean equals(Object other)
60          {
61              if (other instanceof Credentials)
62              {
63                  Credentials o = (Credentials) other;
64                  return o.username.equals(this.username) && o.password.equals(this.password);
65              }
66              return false;
67          }
68          
69          @Override
70          public int hashCode()
71          {
72              return username.hashCode() + password.hashCode();
73          }
74          
75          @Override
76          public String toString()
77          {
78              return "Credentials(" + username + ", " + password + ")";
79          }
80      }
81      
82      /**
83       * Returns a new {@link Builder} for constructing an HttpConfiguration instance.
84       */
85      public static Builder builder()
86      {
87          return new Builder();
88      }
89      
90      public static HttpConfiguration defaults()
91      {
92          return builder().build();
93      }
94      
95      private HttpConfiguration(Builder builder)
96      {
97          this.connectTimeoutMillis = builder.connectTimeoutMillis;
98          this.readTimeoutMillis = builder.readTimeoutMillis;
99          this.maxConnections = builder.maxConnections;
100         this.maxCacheEntries = builder.maxCacheEntries;
101         this.maxCacheObjectSize = builder.maxCacheObjectSize;
102         this.credentials = builder.credentials;
103         this.maxRedirects = builder.maxRedirects;
104         this.proxy = builder.proxy;
105         this.requestDecorator = builder.requestDecorator;
106     }
107  
108     public int getConnectTimeoutMillis()
109     {
110         return connectTimeoutMillis;
111     }
112     
113     public int getReadTimeoutMillis()
114     {
115         return readTimeoutMillis;
116     }
117     
118     public int getMaxConnections()
119     {
120         return maxConnections;
121     }
122     
123     public int getMaxCacheEntries()
124     {
125         return maxCacheEntries;
126     }
127     
128     public long getMaxCacheObjectSize()
129     {
130         return maxCacheObjectSize;
131     }
132     
133     public Option<Integer> getMaxRedirects()
134     {
135         return maxRedirects;
136     }
137     
138     public Option<Credentials> getCredentials()
139     {
140         return credentials;
141     }
142     
143     public boolean hasCredentials()
144     {
145         return credentials.isDefined();
146     }
147     
148     public Option<ProxyConfiguration> getProxyConfiguration()
149     {
150         return proxy;
151     }
152     
153     public boolean hasProxy()
154     {
155         return proxy.isDefined();
156     }
157 
158     public Option<RequestDecorator> getRequestDecorator()
159     {
160         return requestDecorator;
161     }
162     
163     @Override
164     public boolean equals(Object other)
165     {
166         if (other instanceof HttpConfiguration)
167         {
168             HttpConfiguration o = (HttpConfiguration) other;
169             return o.credentials.equals(this.credentials) && o.proxy.equals(this.proxy) &&
170                     o.requestDecorator.equals(this.requestDecorator) &&
171                     o.connectTimeoutMillis == this.connectTimeoutMillis &&
172                     o.readTimeoutMillis == this.readTimeoutMillis &&
173                     o.maxCacheEntries == this.maxCacheEntries &&
174                     o.maxCacheObjectSize == this.maxCacheObjectSize &&
175                     o.maxConnections == this.maxConnections;
176         }
177         return false;
178     }
179     
180     @Override
181     public int hashCode()
182     {
183         return (int) (credentials.hashCode() + proxy.hashCode() + requestDecorator.hashCode() +
184                 connectTimeoutMillis + (int) readTimeoutMillis + maxCacheEntries + maxCacheObjectSize + maxConnections);
185     }
186     
187     @Override
188     public String toString()
189     {
190         return "HttpConfiguration(" + credentials + ", " + proxy + ", " + requestDecorator + ", " +
191                 connectTimeoutMillis + ", " + readTimeoutMillis + ", " + maxCacheEntries + ", " +
192                 maxCacheObjectSize + ", " + maxConnections + ")";
193     }
194     
195     /**
196      * Builder class for {@link HttpConfiguration}.  Use {@link HttpConfiguration#builder()} to create an instance. 
197      */
198     public static class Builder
199     {
200         private int connectTimeoutMillis = DEFAULT_CONNECT_TIMEOUT_MILLIS;
201         private int readTimeoutMillis = DEFAULT_READ_TIMEOUT_MILLIS;
202         private int maxConnections = DEFAULT_MAX_CONNECTIONS;
203         private int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
204         private long maxCacheObjectSize = DEFAULT_MAX_CACHE_OBJECT_SIZE;
205         private Option<Credentials> credentials = none();
206         private Option<Integer> maxRedirects = none();
207         private Option<ProxyConfiguration> proxy = none();
208         private Option<RequestDecorator> requestDecorator = none();
209         
210         public HttpConfiguration build()
211         {
212             return new HttpConfiguration(this);
213         }
214         
215         /**
216          * Sets the length of time to wait for a connection before timing out.
217          * @param connectTimeoutMillis  a number of milliseconds; null to use
218          * {@link HttpConfiguration#DEFAULT_CONNECT_TIMEOUT_MILLIS}
219          * @return  the same Builder
220          */
221         public Builder connectTimeoutMillis(Integer connectTimeoutMillis)
222         {
223             this.connectTimeoutMillis = (connectTimeoutMillis == null) ? DEFAULT_CONNECT_TIMEOUT_MILLIS :
224                 connectTimeoutMillis.intValue();
225             return this;
226         }
227         
228         /**
229          * Sets the length of time to wait for a server response before timing out.
230          * @param readTimeoutMillis  a number of milliseconds; null to use
231          * {@link HttpConfiguration#DEFAULT_READ_TIMEOUT_MILLIS}
232          * @return  the same Builder
233          */
234         public Builder readTimeoutMillis(Integer readTimeoutMillis)
235         {
236             this.readTimeoutMillis = (readTimeoutMillis == null) ? DEFAULT_READ_TIMEOUT_MILLIS :
237                 readTimeoutMillis.intValue();
238             return this;
239         }
240         
241         /**
242          * Sets the maximum number of simultaneous HTTP connections the client can make to the server.
243          * @param maxConnections  the maximum number of simultaneous connections; default is
244          * {@link HttpConfiguration#DEFAULT_MAX_CONNECTIONS}
245          * @return  the same Builder
246          */
247         public Builder maxConnections(int maxConnections)
248         {
249             this.maxConnections = maxConnections;
250             return this;
251         }
252         
253         /**
254          * Sets the maximum number of HTTP responses that will be stored in the HTTP cache at a time.
255          * @param maxCacheEntries  maximum number of cache entries; default is
256          * {@link HttpConfiguration#DEFAULT_MAX_CACHE_ENTRIES}
257          * @return  the same Builder
258          */
259         public Builder maxCacheEntries(int maxCacheEntries)
260         {
261             this.maxCacheEntries = maxCacheEntries;
262             return this;
263         }
264         
265         /**
266          * Sets the maximum size of HTTP responses that can be stored in the HTTP cache.
267          * @param maxCacheObjectSize  maximum cacheable object size in bytes; default is
268          * {@link HttpConfiguration#DEFAULT_MAX_CACHE_OBJECT_SIZE}
269          * @return  the same Builder
270          */
271         public Builder maxCacheObjectSize(long maxCacheObjectSize)
272         {
273             this.maxCacheObjectSize = maxCacheObjectSize;
274             return this;
275         }
276         
277         /**
278          * Sets the maximum number of redirects that will be automatically followed.
279          * @param maxRedirects  maximum number of redirects, if any
280          * @return  the same Builder
281          */
282         public Builder maxRedirects(Option<Integer> maxRedirects)
283         {
284             this.maxRedirects = checkNotNull(maxRedirects);
285             return this;
286         }
287         
288         /**
289          * Sets a username and password for basic authentication.  Authentication is not required for standard use of
290          * the client; authenticating with a valid marketplace.atlassian.com account will allow you to see
291          * unpublished versions of plugins you have permission to edit.
292          * @param credentials  a {@link Credentials} object wrapped in {@link Option#some}; or {@link Option#none()}
293          *   to turn off authentication
294          * @return  the same Builder
295          */
296         public Builder credentials(Option<Credentials> credentials)
297         {
298             this.credentials = checkNotNull(credentials);
299             return this;
300         }
301         
302         /**
303          * Sets HTTP proxy server parameters.  You do not have to do this if a proxy was already specified in standard
304          * system properties supported by the JRE (<tt>http.proxyHost</tt>, etc.).  However, if you want to use proxy
305          * authentication which the JRE does not support, you must supply a ProxyConfiguration.
306          * @param proxy  a {@link ProxyConfiguration} object wrapped in {@link Option#some}, or {@link Option#none()} to
307          *   use no proxy (unless one was already specified in system properties)
308          * @return  the same Builder
309          */
310         public Builder proxyConfiguration(Option<ProxyConfiguration> proxy)
311         {
312             this.proxy = checkNotNull(proxy);
313             return this;
314         }
315         
316         /**
317          * Specifies an object that can provide custom headers for HTTP requests, e.g. to set the User-Agent header.
318          * @param requestDecorator  a {@link RequestDecorator} object wrapped in {@link Option#some}, or {@link Option#none()} for no decorator
319          * @return  the same Builder
320          */
321         public Builder requestDecorator(Option<RequestDecorator> requestDecorator)
322         {
323             this.requestDecorator = checkNotNull(requestDecorator);
324             return this;
325         }
326     }
327     
328     /**
329      * Parameters for specifying an HTTP proxy.
330      */
331     public static class ProxyConfiguration
332     {   
333         private final Option<ProxyHost> proxyHost;
334         private final Option<ProxyAuthParams> authParams;
335 
336         public static Builder builder()
337         {
338             return new Builder();
339         }
340 
341         private ProxyConfiguration(Builder builder)
342         {
343             this.proxyHost = builder.proxyHost;
344             this.authParams = builder.authParams;
345         }
346 
347         public Option<ProxyHost> getProxyHost()
348         {
349             return proxyHost;
350         }
351         
352         public boolean hasAuth()
353         {
354             return authParams.isDefined();
355         }
356 
357         public Option<ProxyAuthParams> getAuthParams()
358         {
359             return authParams;
360         }
361                 
362         @Override
363         public boolean equals(Object other)
364         {
365             if (other instanceof ProxyConfiguration)
366             {
367                 ProxyConfiguration o = (ProxyConfiguration) other;
368                 return o.proxyHost.equals(this.proxyHost) && o.authParams.equals(this.authParams);
369             }
370             return false;
371         }
372         
373         @Override
374         public int hashCode()
375         {
376             return proxyHost.hashCode() + authParams.hashCode();
377         }
378         
379         @Override
380         public String toString()
381         {
382             return "ProxyConfiguration(" + proxyHost + ", " + authParams + ")";
383         }
384         
385         /**
386          * Builder class for {@link ProxyConfiguration}.
387          */
388         public static class Builder
389         {
390             private Option<ProxyHost> proxyHost = none();
391             private Option<ProxyAuthParams> authParams = none();
392             
393             public ProxyConfiguration build()
394             {
395                 return new ProxyConfiguration(this);
396             }
397 
398             public Builder proxyHost(Option<ProxyHost> proxyHost)
399             {
400                 this.proxyHost = checkNotNull(proxyHost);
401                 return this;
402             }
403             
404             public Builder authParams(Option<ProxyAuthParams> authParams)
405             {
406                 this.authParams = checkNotNull(authParams);
407                 return this;
408             }            
409         }
410     }
411     
412     /**
413      * Specifies the proxy hostname and port.  These are encapsulated in their own object, rather than being
414      * required properties of {@link ProxyConfiguration}, because it is possible to specify other proxy
415      * parameters explicitly ({@link ProxyAuthenticationStrategy
416      */
417     public static class ProxyHost
418     {
419         public static final int DEFAULT_PORT = 80;
420         
421         private final String hostname;
422         private final int port;
423 
424         public ProxyHost(String hostname, int port)
425         {
426             this.hostname = checkNotNull(hostname);
427             this.port = port;
428         }
429         
430         public ProxyHost(String hostname)
431         {
432             this(hostname, 80);
433         }
434         
435         public String getHostname()
436         {
437             return hostname;
438         }
439         
440         public int getPort()
441         {
442             return port;
443         }
444         
445         @Override
446         public boolean equals(Object other)
447         {
448             if (other instanceof ProxyHost)
449             {
450                 ProxyHost o = (ProxyHost) other;
451                 return o.hostname.equals(this.hostname) && (o.port == this.port);
452             }
453             return false;
454         }
455         
456         @Override
457         public int hashCode()
458         {
459             return hostname.hashCode() + port;
460         }
461         
462         @Override
463         public String toString()
464         {
465             return hostname + ":" + port;
466         }
467     }
468 
469     /**
470      * Parameters for proxy authentication, instantiated only if you are using an authenticated proxy.
471      */
472     public static class ProxyAuthParams
473     {
474         private final Credentials credentials;
475         private final ProxyAuthMethod authMethod;
476         private final Option<String> ntlmDomain;
477         private final Option<String> ntlmWorkstation;
478 
479         public ProxyAuthParams(Credentials credentials, ProxyAuthMethod authMethod, Option<String> ntlmDomain, Option<String> ntlmWorkstation)
480         {
481             this.credentials = checkNotNull(credentials, "credentials");
482             this.authMethod = checkNotNull(authMethod, "authMethod");
483             this.ntlmDomain = checkNotNull(ntlmDomain, "ntlmDomain");
484             this.ntlmWorkstation = checkNotNull(ntlmWorkstation, "ntlmWorkstation");
485         }
486         
487         public ProxyAuthParams(Credentials credentials, ProxyAuthMethod authMethod)
488         {
489             this(credentials, authMethod, none(String.class), none(String.class));
490         }
491         
492         public Credentials getCredentials()
493         {
494             return credentials;
495         }
496         
497         public ProxyAuthMethod getAuthMethod()
498         {
499             return authMethod;
500         }
501         
502         public Option<String> getNtlmDomain()
503         {
504             return ntlmDomain;
505         }
506         
507         public Option<String> getNtlmWorkstation()
508         {
509             return ntlmWorkstation;
510         }
511         
512         @Override
513         public boolean equals(Object other)
514         {
515             if (other instanceof ProxyAuthParams)
516             {
517                 ProxyAuthParams o = (ProxyAuthParams) other;
518                 return o.credentials.equals(this.credentials) && o.authMethod.equals(this.authMethod)
519                         && o.ntlmDomain.equals(this.ntlmDomain) && o.ntlmWorkstation.equals(this.ntlmWorkstation);
520             }
521             return false;
522         }
523         
524         @Override
525         public int hashCode()
526         {
527             return credentials.hashCode() + authMethod.hashCode() + ntlmDomain.hashCode() + ntlmWorkstation.hashCode();
528         }
529         
530         @Override
531         public String toString()
532         {
533             return "AuthParams(" + credentials + ", " + authMethod + ", " + ntlmDomain + ", " + ntlmWorkstation + ")";
534         }
535     }
536 
537     /**
538      * Constants for the supported proxy authentication methods.
539      */
540     public enum ProxyAuthMethod
541     {
542         BASIC,
543         DIGEST,
544         NTLM;
545         
546         public static Option<ProxyAuthMethod> fromKey(String key)
547         {
548             for (ProxyAuthMethod a: values())
549             {
550                 if (a.name().equalsIgnoreCase(key))
551                 {
552                     return some(a);
553                 }
554             }
555             return none();
556         }
557     };
558 }