1 /** 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * SPDX-License-Identifier: Apache-2.0. 4 */ 5 package software.amazon.awssdk.crt.http; 6 7 import java.net.URI; 8 import java.nio.charset.Charset; 9 import java.util.Queue; 10 import java.util.concurrent.CompletableFuture; 11 import java.util.concurrent.ConcurrentLinkedQueue; 12 13 import software.amazon.awssdk.crt.CRT; 14 import software.amazon.awssdk.crt.CrtResource; 15 import software.amazon.awssdk.crt.CrtRuntimeException; 16 import software.amazon.awssdk.crt.io.ClientBootstrap; 17 import software.amazon.awssdk.crt.io.SocketOptions; 18 import software.amazon.awssdk.crt.io.TlsConnectionOptions; 19 import software.amazon.awssdk.crt.io.TlsContext; 20 21 /** 22 * Manages a Pool of Http Connections 23 */ 24 public class HttpClientConnectionManager extends CrtResource { 25 private static final String HTTP = "http"; 26 private static final String HTTPS = "https"; 27 private static final int DEFAULT_HTTP_PORT = 80; 28 private static final int DEFAULT_HTTPS_PORT = 443; 29 private final static Charset UTF8 = java.nio.charset.StandardCharsets.UTF_8; 30 31 private final long windowSize; 32 private final URI uri; 33 private final int port; 34 private final int maxConnections; 35 private final CompletableFuture<Void> shutdownComplete = new CompletableFuture<>(); 36 private final HttpVersion expectedHttpVersion; 37 38 /** 39 * Factory function for HttpClientConnectionManager instances 40 * 41 * @param options configuration options 42 * @return a new instance of an HttpClientConnectionManager 43 */ create(HttpClientConnectionManagerOptions options)44 public static HttpClientConnectionManager create(HttpClientConnectionManagerOptions options) { 45 return new HttpClientConnectionManager(options); 46 } 47 HttpClientConnectionManager(HttpClientConnectionManagerOptions options)48 private HttpClientConnectionManager(HttpClientConnectionManagerOptions options) { 49 50 options.validateOptions(); 51 URI uri = options.getUri(); 52 ClientBootstrap clientBootstrap = options.getClientBootstrap(); 53 SocketOptions socketOptions = options.getSocketOptions(); 54 boolean useTls = HTTPS.equals(uri.getScheme()); 55 TlsContext tlsContext = options.getTlsContext(); 56 TlsConnectionOptions tlsConnectionOptions = options.getTlsConnectionOptions(); 57 58 long windowSize = options.getWindowSize(); 59 int maxConnections = options.getMaxConnections(); 60 int port = options.getPort(); 61 if (port == -1) { 62 port = uri.getPort(); 63 /* Pick a default port based on the scheme if one wasn't set */ 64 if (port == -1) { 65 if (HTTP.equals(uri.getScheme())) { port = DEFAULT_HTTP_PORT; } 66 if (HTTPS.equals(uri.getScheme())) { port = DEFAULT_HTTPS_PORT; } 67 } 68 } 69 70 HttpProxyOptions proxyOptions = options.getProxyOptions(); 71 72 this.windowSize = windowSize; 73 this.uri = uri; 74 this.port = port; 75 this.maxConnections = maxConnections; 76 this.expectedHttpVersion = options.getExpectedHttpVersion(); 77 78 int proxyConnectionType = 0; 79 String proxyHost = null; 80 int proxyPort = 0; 81 TlsContext proxyTlsContext = null; 82 int proxyAuthorizationType = 0; 83 String proxyAuthorizationUsername = null; 84 String proxyAuthorizationPassword = null; 85 86 if (proxyOptions != null) { 87 proxyConnectionType = proxyOptions.getConnectionType().getValue(); 88 proxyHost = proxyOptions.getHost(); 89 proxyPort = proxyOptions.getPort(); 90 proxyTlsContext = proxyOptions.getTlsContext(); 91 proxyAuthorizationType = proxyOptions.getAuthorizationType().getValue(); 92 proxyAuthorizationUsername = proxyOptions.getAuthorizationUsername(); 93 proxyAuthorizationPassword = proxyOptions.getAuthorizationPassword(); 94 } 95 96 int environmentVariableProxyConnectionType = 0; 97 TlsConnectionOptions environmentVariableProxyTlsConnectionOptions = null; 98 int environmentVariableType = 0; 99 HttpProxyEnvironmentVariableSetting environmentVariableSetting = options.getHttpProxyEnvironmentVariableSetting(); 100 if (environmentVariableSetting != null) { 101 environmentVariableProxyConnectionType = environmentVariableSetting.getConnectionType().getValue(); 102 environmentVariableProxyTlsConnectionOptions = environmentVariableSetting.getTlsConnectionOptions(); 103 environmentVariableType = environmentVariableSetting.getEnvironmentVariableType().getValue(); 104 } 105 106 HttpMonitoringOptions monitoringOptions = options.getMonitoringOptions(); 107 long monitoringThroughputThresholdInBytesPerSecond = 0; 108 int monitoringFailureIntervalInSeconds = 0; 109 if (monitoringOptions != null) { 110 monitoringThroughputThresholdInBytesPerSecond = monitoringOptions.getMinThroughputBytesPerSecond(); 111 monitoringFailureIntervalInSeconds = monitoringOptions.getAllowableThroughputFailureIntervalSeconds(); 112 } 113 114 acquireNativeHandle(httpClientConnectionManagerNew(this, 115 clientBootstrap.getNativeHandle(), 116 socketOptions.getNativeHandle(), 117 useTls && tlsContext!=null ? tlsContext.getNativeHandle() : 0, 118 useTls && tlsConnectionOptions!=null ? tlsConnectionOptions.getNativeHandle() : 0, 119 windowSize, 120 uri.getHost().getBytes(UTF8), 121 port, 122 maxConnections, 123 proxyConnectionType, 124 proxyHost != null ? proxyHost.getBytes(UTF8) : null, 125 proxyPort, 126 proxyTlsContext != null ? proxyTlsContext.getNativeHandle() : 0, 127 proxyAuthorizationType, 128 proxyAuthorizationUsername != null ? proxyAuthorizationUsername.getBytes(UTF8) : null, 129 proxyAuthorizationPassword != null ? proxyAuthorizationPassword.getBytes(UTF8) : null, 130 environmentVariableProxyConnectionType, 131 environmentVariableProxyTlsConnectionOptions != null 132 ? environmentVariableProxyTlsConnectionOptions.getNativeHandle() 133 : 0, 134 environmentVariableType, 135 options.isManualWindowManagement(), 136 options.getMaxConnectionIdleInMilliseconds(), 137 monitoringThroughputThresholdInBytesPerSecond, 138 monitoringFailureIntervalInSeconds, 139 expectedHttpVersion.getValue())); 140 141 /* we don't need to add a reference to socketOptions since it's copied during connection manager construction */ 142 addReferenceTo(clientBootstrap); 143 if (useTls) { 144 if (tlsContext != null) { 145 addReferenceTo(tlsContext); 146 } 147 if (tlsConnectionOptions != null) { 148 addReferenceTo(tlsConnectionOptions); 149 } 150 } 151 } 152 153 /** 154 * Request a HttpClientConnection from the Connection Pool. 155 * @return A Future for a HttpClientConnection that will be completed when a connection is acquired. 156 */ acquireConnection()157 public CompletableFuture<HttpClientConnection> acquireConnection() { 158 if (isNull()) { 159 throw new IllegalStateException("HttpClientConnectionManager has been closed, can't acquire new connections"); 160 } 161 162 CompletableFuture<HttpClientConnection> returnedFuture = new CompletableFuture<>(); 163 httpClientConnectionManagerAcquireConnection(this.getNativeHandle(), returnedFuture); 164 return returnedFuture; 165 } 166 167 /** 168 * Releases this HttpClientConnection back into the Connection Pool, and allows another Request to acquire this connection. 169 * @param conn Connection to release 170 */ releaseConnection(HttpClientConnection conn)171 public void releaseConnection(HttpClientConnection conn) { 172 conn.close(); 173 } 174 175 /** 176 * Called from Native when all Connections in this Connection Pool have finished shutting down and it is safe to 177 * begin releasing Native Resources that HttpClientConnectionManager depends on. 178 */ onShutdownComplete()179 private void onShutdownComplete() { 180 releaseReferences(); 181 182 this.shutdownComplete.complete(null); 183 } 184 185 /** 186 * Determines whether a resource releases its dependencies at the same time the native handle is released or if it waits. 187 * Resources that wait are responsible for calling releaseReferences() manually. 188 */ 189 @Override canReleaseReferencesImmediately()190 protected boolean canReleaseReferencesImmediately() { return false; } 191 192 /** 193 * Closes this Connection Pool and any pending Connection Acquisitions 194 */ 195 @Override releaseNativeHandle()196 protected void releaseNativeHandle() { 197 if (!isNull()) { 198 /* 199 * Release our Native pointer and schedule tasks on the Native Event Loop to start sending HTTP/TLS/TCP 200 * connection shutdown messages to peers for any open Connections. 201 */ 202 httpClientConnectionManagerRelease(getNativeHandle()); 203 } 204 } 205 getShutdownCompleteFuture()206 public CompletableFuture<Void> getShutdownCompleteFuture() { return shutdownComplete; } 207 208 /******************************************************************************* 209 * Getter methods 210 ******************************************************************************/ 211 212 /** 213 * @return maximum number of connections this connection manager will pool 214 */ getMaxConnections()215 public int getMaxConnections() { 216 return maxConnections; 217 } 218 219 /** 220 * @return concurrency metrics for the current manager 221 */ getManagerMetrics()222 public HttpManagerMetrics getManagerMetrics() { 223 if (isNull()) { 224 throw new IllegalStateException("HttpClientConnectionManager has been closed, can't fetch metrics"); 225 } 226 return httpConnectionManagerFetchMetrics(getNativeHandle()); 227 } 228 229 /** 230 * @return size of the per-connection streaming read window for response handling 231 */ getWindowSize()232 public long getWindowSize() { 233 return windowSize; 234 } 235 236 /** 237 * @return uri the connection manager is making connections to 238 */ getUri()239 public URI getUri() { 240 return uri; 241 } 242 243 /******************************************************************************* 244 * Native methods 245 ******************************************************************************/ 246 httpClientConnectionManagerNew(HttpClientConnectionManager thisObj, long client_bootstrap, long socketOptions, long tlsContext, long tlsConnectionOptions, long windowSize, byte[] endpoint, int port, int maxConns, int proxyConnectionType, byte[] proxyHost, int proxyPort, long proxyTlsContext, int proxyAuthorizationType, byte[] proxyAuthorizationUsername, byte[] proxyAuthorizationPassword, int environmentVariableProxyConnectionType, long environmentVariableProxyTlsConnectionOptions, int environmentVariableSetting, boolean isManualWindowManagement, long maxConnectionIdleInMilliseconds, long monitoringThroughputThresholdInBytesPerSecond, int monitoringFailureIntervalInSeconds, int expectedProtocol)247 private static native long httpClientConnectionManagerNew(HttpClientConnectionManager thisObj, 248 long client_bootstrap, 249 long socketOptions, 250 long tlsContext, 251 long tlsConnectionOptions, 252 long windowSize, 253 byte[] endpoint, 254 int port, 255 int maxConns, 256 int proxyConnectionType, 257 byte[] proxyHost, 258 int proxyPort, 259 long proxyTlsContext, 260 int proxyAuthorizationType, 261 byte[] proxyAuthorizationUsername, 262 byte[] proxyAuthorizationPassword, 263 int environmentVariableProxyConnectionType, 264 long environmentVariableProxyTlsConnectionOptions, 265 int environmentVariableSetting, 266 boolean isManualWindowManagement, 267 long maxConnectionIdleInMilliseconds, 268 long monitoringThroughputThresholdInBytesPerSecond, 269 int monitoringFailureIntervalInSeconds, 270 int expectedProtocol) throws CrtRuntimeException; 271 httpClientConnectionManagerRelease(long conn_manager)272 private static native void httpClientConnectionManagerRelease(long conn_manager) throws CrtRuntimeException; 273 httpClientConnectionManagerAcquireConnection(long conn_manager, CompletableFuture<HttpClientConnection> acquireFuture)274 private static native void httpClientConnectionManagerAcquireConnection(long conn_manager, CompletableFuture<HttpClientConnection> acquireFuture) throws CrtRuntimeException; 275 httpConnectionManagerFetchMetrics(long conn_manager)276 private static native HttpManagerMetrics httpConnectionManagerFetchMetrics(long conn_manager) throws CrtRuntimeException; 277 278 } 279