View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.impl.async;
28  
29  import java.net.InetSocketAddress;
30  import java.util.concurrent.Future;
31  
32  import org.apache.hc.client5.http.HttpRoute;
33  import org.apache.hc.client5.http.config.ConnectionConfig;
34  import org.apache.hc.core5.concurrent.CallbackContribution;
35  import org.apache.hc.core5.concurrent.FutureCallback;
36  import org.apache.hc.core5.function.Callback;
37  import org.apache.hc.core5.function.Resolver;
38  import org.apache.hc.core5.http.HttpHost;
39  import org.apache.hc.core5.http.URIScheme;
40  import org.apache.hc.core5.http.nio.command.ShutdownCommand;
41  import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
42  import org.apache.hc.core5.http2.nio.command.PingCommand;
43  import org.apache.hc.core5.http2.nio.support.BasicPingHandler;
44  import org.apache.hc.core5.io.CloseMode;
45  import org.apache.hc.core5.io.ModalCloseable;
46  import org.apache.hc.core5.net.NamedEndpoint;
47  import org.apache.hc.core5.reactor.AbstractIOSessionPool;
48  import org.apache.hc.core5.reactor.Command;
49  import org.apache.hc.core5.reactor.ConnectionInitiator;
50  import org.apache.hc.core5.reactor.IOSession;
51  import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
52  import org.apache.hc.core5.util.TimeValue;
53  import org.apache.hc.core5.util.Timeout;
54  
55  class InternalH2ConnPool implements ModalCloseable {
56  
57      private final SessionPool sessionPool;
58  
59      private volatile Resolver<HttpHost, ConnectionConfig> connectionConfigResolver;
60  
61      InternalH2ConnPool(final ConnectionInitiator connectionInitiator,
62                         final Resolver<HttpHost, InetSocketAddress> addressResolver,
63                         final TlsStrategy tlsStrategy) {
64          this.sessionPool = new SessionPool(connectionInitiator, addressResolver, tlsStrategy);
65      }
66  
67      @Override
68      public void close(final CloseMode closeMode) {
69          sessionPool.close(closeMode);
70      }
71  
72      @Override
73      public void close() {
74          sessionPool.close();
75      }
76  
77      private ConnectionConfig resolveConnectionConfig(final HttpHost httpHost) {
78          final Resolver<HttpHost, ConnectionConfig> resolver = this.connectionConfigResolver;
79          final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(httpHost) : null;
80          return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
81      }
82  
83      public Future<IOSession> getSession(
84              final HttpRoute route,
85              final Timeout connectTimeout,
86              final FutureCallback<IOSession> callback) {
87          final ConnectionConfig connectionConfig = resolveConnectionConfig(route.getTargetHost());
88          return sessionPool.getSession(
89                  route,
90                  connectTimeout != null ? connectTimeout : connectionConfig.getConnectTimeout(),
91                  new CallbackContribution<IOSession>(callback) {
92  
93                      @Override
94                      public void completed(final IOSession ioSession) {
95                          final Timeout socketTimeout = connectionConfig.getSocketTimeout();
96                          if (socketTimeout != null) {
97                              ioSession.setSocketTimeout(socketTimeout);
98                          }
99                          callback.completed(ioSession);
100                     }
101 
102                 });
103     }
104 
105     public void closeIdle(final TimeValue idleTime) {
106         sessionPool.closeIdle(idleTime);
107     }
108 
109     public void setConnectionConfigResolver(final Resolver<HttpHost, ConnectionConfig> connectionConfigResolver) {
110         this.connectionConfigResolver = connectionConfigResolver;
111     }
112 
113     public TimeValue getValidateAfterInactivity() {
114         return sessionPool.validateAfterInactivity;
115     }
116 
117     public void setValidateAfterInactivity(final TimeValue timeValue) {
118         sessionPool.validateAfterInactivity = timeValue;
119     }
120 
121 
122     static class SessionPool extends AbstractIOSessionPool<HttpRoute> {
123 
124         private final ConnectionInitiator connectionInitiator;
125         private final Resolver<HttpHost, InetSocketAddress> addressResolver;
126         private final TlsStrategy tlsStrategy;
127 
128         private volatile TimeValue validateAfterInactivity = TimeValue.NEG_ONE_MILLISECOND;
129 
130         SessionPool(final ConnectionInitiator connectionInitiator,
131                     final Resolver<HttpHost, InetSocketAddress> addressResolver,
132                     final TlsStrategy tlsStrategy) {
133             this.connectionInitiator = connectionInitiator;
134             this.addressResolver = addressResolver;
135             this.tlsStrategy = tlsStrategy;
136         }
137 
138         @Override
139         protected Future<IOSession> connectSession(final HttpRoute route,
140                                                    final Timeout connectTimeout,
141                                                    final FutureCallback<IOSession> callback) {
142             final HttpHost target = route.getTargetHost();
143             final InetSocketAddress localAddress = route.getLocalSocketAddress();
144             final InetSocketAddress remoteAddress = addressResolver.resolve(target);
145             return connectionInitiator.connect(
146                     target,
147                     remoteAddress,
148                     localAddress,
149                     connectTimeout,
150                     null,
151                     new CallbackContribution<IOSession>(callback) {
152 
153                         @Override
154                         public void completed(final IOSession ioSession) {
155                             if (tlsStrategy != null
156                                     && URIScheme.HTTPS.same(target.getSchemeName())
157                                     && ioSession instanceof TransportSecurityLayer) {
158                                 final NamedEndpoint tlsName = route.getTargetName() != null ? route.getTargetName() : target;
159                                 tlsStrategy.upgrade(
160                                         (TransportSecurityLayer) ioSession,
161                                         tlsName,
162                                         null,
163                                         connectTimeout,
164                                         new CallbackContribution<TransportSecurityLayer>(callback) {
165 
166                                             @Override
167                                             public void completed(final TransportSecurityLayer transportSecurityLayer) {
168                                                 callback.completed(ioSession);
169                                             }
170 
171                                         });
172                                 ioSession.setSocketTimeout(connectTimeout);
173                             } else {
174                                 callback.completed(ioSession);
175                             }
176                         }
177 
178                     });
179         }
180 
181         @Override
182         protected void validateSession(final IOSession ioSession,
183                                        final Callback<Boolean> callback) {
184             if (ioSession.isOpen()) {
185                 final TimeValue timeValue = validateAfterInactivity;
186                 if (TimeValue.isNonNegative(timeValue)) {
187                     final long lastAccessTime = Math.min(ioSession.getLastReadTime(), ioSession.getLastWriteTime());
188                     final long deadline = lastAccessTime + timeValue.toMilliseconds();
189                     if (deadline <= System.currentTimeMillis()) {
190                         final Timeout socketTimeoutMillis = ioSession.getSocketTimeout();
191                         ioSession.enqueue(new PingCommand(new BasicPingHandler(result -> {
192                             ioSession.setSocketTimeout(socketTimeoutMillis);
193                             callback.execute(result);
194                         })), Command.Priority.NORMAL);
195                         return;
196                     }
197                 }
198                 callback.execute(true);
199             } else {
200                 callback.execute(false);
201             }
202         }
203 
204         @Override
205         protected void closeSession(final IOSession ioSession,
206                                     final CloseMode closeMode) {
207             if (closeMode == CloseMode.GRACEFUL) {
208                 ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.NORMAL);
209             } else {
210                 ioSession.close(closeMode);
211             }
212         }
213     }
214 
215 }