View Javadoc
1   package org.eclipse.aether.transport.http;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   * 
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   * 
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.Closeable;
23  import java.util.Arrays;
24  import java.util.Iterator;
25  import java.util.Map;
26  import java.util.concurrent.ConcurrentHashMap;
27  import java.util.concurrent.ConcurrentMap;
28  
29  import org.apache.http.HttpHost;
30  import org.apache.http.config.RegistryBuilder;
31  import org.apache.http.conn.HttpClientConnectionManager;
32  import org.apache.http.conn.socket.ConnectionSocketFactory;
33  import org.apache.http.conn.socket.PlainConnectionSocketFactory;
34  import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
35  import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
36  import org.eclipse.aether.RepositoryCache;
37  import org.eclipse.aether.RepositorySystemSession;
38  import org.eclipse.aether.util.ConfigUtils;
39  
40  import javax.net.ssl.HostnameVerifier;
41  import javax.net.ssl.SSLSocketFactory;
42  
43  /**
44   * Container for HTTP-related state that can be shared across incarnations of the transporter to optimize the
45   * communication with servers.
46   */
47  final class GlobalState
48      implements Closeable
49  {
50  
51      static class CompoundKey
52      {
53  
54          private final Object[] keys;
55  
56          CompoundKey( Object... keys )
57          {
58              this.keys = keys;
59          }
60  
61          @Override
62          public boolean equals( Object obj )
63          {
64              if ( this == obj )
65              {
66                  return true;
67              }
68              if ( obj == null || !getClass().equals( obj.getClass() ) )
69              {
70                  return false;
71              }
72              CompoundKey that = (CompoundKey) obj;
73              return Arrays.equals( keys, that.keys );
74          }
75  
76          @Override
77          public int hashCode()
78          {
79              int hash = 17;
80              hash = hash * 31 + Arrays.hashCode( keys );
81              return hash;
82          }
83  
84          @Override
85          public String toString()
86          {
87              return Arrays.toString( keys );
88          }
89      }
90  
91      private static final String KEY = GlobalState.class.getName();
92  
93      private static final String CONFIG_PROP_CACHE_STATE = "aether.connector.http.cacheState";
94  
95      private final ConcurrentMap<SslConfig, HttpClientConnectionManager> connectionManagers;
96  
97      private final ConcurrentMap<CompoundKey, Object> userTokens;
98  
99      private final ConcurrentMap<HttpHost, AuthSchemePool> authSchemePools;
100 
101     private final ConcurrentMap<CompoundKey, Boolean> expectContinues;
102 
103     public static GlobalState get( RepositorySystemSession session )
104     {
105         GlobalState cache;
106         RepositoryCache repoCache = session.getCache();
107         if ( repoCache == null || !ConfigUtils.getBoolean( session, true, CONFIG_PROP_CACHE_STATE ) )
108         {
109             cache = null;
110         }
111         else
112         {
113             Object tmp = repoCache.get( session, KEY );
114             if ( tmp instanceof GlobalState )
115             {
116                 cache = (GlobalState) tmp;
117             }
118             else
119             {
120                 synchronized ( GlobalState.class )
121                 {
122                     tmp = repoCache.get( session, KEY );
123                     if ( tmp instanceof GlobalState )
124                     {
125                         cache = (GlobalState) tmp;
126                     }
127                     else
128                     {
129                         cache = new GlobalState();
130                         repoCache.put( session, KEY, cache );
131                     }
132                 }
133             }
134         }
135         return cache;
136     }
137 
138     private GlobalState()
139     {
140         connectionManagers = new ConcurrentHashMap<>();
141         userTokens = new ConcurrentHashMap<>();
142         authSchemePools = new ConcurrentHashMap<>();
143         expectContinues = new ConcurrentHashMap<>();
144     }
145 
146     @Override
147     public void close()
148     {
149         for ( Iterator<Map.Entry<SslConfig, HttpClientConnectionManager>> it = connectionManagers.entrySet().iterator();
150               it.hasNext(); )
151         {
152             HttpClientConnectionManager connMgr = it.next().getValue();
153             it.remove();
154             connMgr.shutdown();
155         }
156     }
157 
158     public HttpClientConnectionManager getConnectionManager( SslConfig config )
159     {
160         HttpClientConnectionManager manager = connectionManagers.get( config );
161         if ( manager == null )
162         {
163             HttpClientConnectionManager connMgr = newConnectionManager( config );
164             manager = connectionManagers.putIfAbsent( config, connMgr );
165             if ( manager != null )
166             {
167                 connMgr.shutdown();
168             }
169             else
170             {
171                 manager = connMgr;
172             }
173         }
174         return manager;
175     }
176 
177     @SuppressWarnings( "checkstyle:magicnumber" )
178     public static HttpClientConnectionManager newConnectionManager( SslConfig sslConfig )
179     {
180         RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create()
181                 .register( "http", PlainConnectionSocketFactory.getSocketFactory() );
182 
183         if ( sslConfig == null )
184         {
185             registryBuilder.register( "https", SSLConnectionSocketFactory.getSystemSocketFactory() );
186         }
187         else
188         {
189             SSLSocketFactory sslSocketFactory = ( sslConfig.context != null )
190                     ? sslConfig.context.getSocketFactory() : (SSLSocketFactory) SSLSocketFactory.getDefault();
191 
192             HostnameVerifier hostnameVerifier = ( sslConfig.verifier != null )
193                     ? sslConfig.verifier : SSLConnectionSocketFactory.getDefaultHostnameVerifier();
194 
195             registryBuilder.register( "https", new SSLConnectionSocketFactory(
196                     sslSocketFactory, sslConfig.protocols, sslConfig.cipherSuites, hostnameVerifier ) );
197         }
198         PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager( registryBuilder.build() );
199         connMgr.setMaxTotal( 100 );
200         connMgr.setDefaultMaxPerRoute( 50 );
201         return connMgr;
202     }
203 
204     public Object getUserToken( CompoundKey key )
205     {
206         return userTokens.get( key );
207     }
208 
209     public void setUserToken( CompoundKey key, Object userToken )
210     {
211         if ( userToken != null )
212         {
213             userTokens.put( key, userToken );
214         }
215         else
216         {
217             userTokens.remove( key );
218         }
219     }
220 
221     public ConcurrentMap<HttpHost, AuthSchemePool> getAuthSchemePools()
222     {
223         return authSchemePools;
224     }
225 
226     public Boolean getExpectContinue( CompoundKey key )
227     {
228         return expectContinues.get( key );
229     }
230 
231     public void setExpectContinue( CompoundKey key, boolean enabled )
232     {
233         expectContinues.put( key, enabled );
234     }
235 
236 }