2828import java .util .concurrent .ConcurrentHashMap ;
2929import software .amazon .awssdk .annotations .SdkProtectedApi ;
3030import software .amazon .awssdk .crt .CrtResource ;
31- import software .amazon .awssdk .crt .http .HttpClientConnectionManager ;
31+ import software .amazon .awssdk .crt .http .Http2StreamManagerOptions ;
3232import software .amazon .awssdk .crt .http .HttpClientConnectionManagerOptions ;
3333import software .amazon .awssdk .crt .http .HttpMonitoringOptions ;
3434import software .amazon .awssdk .crt .http .HttpProxyOptions ;
35+ import software .amazon .awssdk .crt .http .HttpStreamManager ;
36+ import software .amazon .awssdk .crt .http .HttpStreamManagerOptions ;
37+ import software .amazon .awssdk .crt .http .HttpVersion ;
3538import software .amazon .awssdk .crt .io .ClientBootstrap ;
3639import software .amazon .awssdk .crt .io .SocketOptions ;
3740import software .amazon .awssdk .crt .io .TlsContext ;
@@ -57,7 +60,8 @@ abstract class AwsCrtHttpClientBase implements SdkAutoCloseable {
5760 private static final long DEFAULT_STREAM_WINDOW_SIZE = 16L * 1024L * 1024L ; // 16 MB
5861
5962 protected final long readBufferSize ;
60- private final Map <URI , HttpClientConnectionManager > connectionPools = new ConcurrentHashMap <>();
63+ protected final Protocol protocol ;
64+ private final Map <URI , HttpStreamManager > connectionPools = new ConcurrentHashMap <>();
6165 private final LinkedList <CrtResource > ownedSubResources = new LinkedList <>();
6266 private final ClientBootstrap bootstrap ;
6367 private final SocketOptions socketOptions ;
@@ -70,31 +74,30 @@ abstract class AwsCrtHttpClientBase implements SdkAutoCloseable {
7074 private boolean isClosed = false ;
7175
7276 AwsCrtHttpClientBase (AwsCrtClientBuilderBase builder , AttributeMap config ) {
73- if (config .get (PROTOCOL ) == Protocol .HTTP2 ) {
74- throw new UnsupportedOperationException ("HTTP/2 is not supported in AwsCrtHttpClient yet. Use "
75- + "NettyNioAsyncHttpClient instead." );
77+ ClientBootstrap clientBootstrap = new ClientBootstrap (null , null );
78+ SocketOptions clientSocketOptions = buildSocketOptions (builder .getTcpKeepAliveConfiguration (),
79+ config .get (SdkHttpConfigurationOption .CONNECTION_TIMEOUT ));
80+ TlsContextOptions clientTlsContextOptions =
81+ TlsContextOptions .createDefaultClient ()
82+ .withCipherPreference (resolveCipherPreference (builder .getPostQuantumTlsEnabled ()))
83+ .withVerifyPeer (!config .get (SdkHttpConfigurationOption .TRUST_ALL_CERTIFICATES ));
84+ this .protocol = config .get (PROTOCOL );
85+ if (protocol == Protocol .HTTP2 ) {
86+ clientTlsContextOptions = clientTlsContextOptions .withAlpnList ("h2" );
7687 }
7788
78- try (ClientBootstrap clientBootstrap = new ClientBootstrap (null , null );
79- SocketOptions clientSocketOptions = buildSocketOptions (builder .getTcpKeepAliveConfiguration (),
80- config .get (SdkHttpConfigurationOption .CONNECTION_TIMEOUT ));
81- TlsContextOptions clientTlsContextOptions =
82- TlsContextOptions .createDefaultClient ()
83- .withCipherPreference (resolveCipherPreference (builder .getPostQuantumTlsEnabled ()))
84- .withVerifyPeer (!config .get (SdkHttpConfigurationOption .TRUST_ALL_CERTIFICATES ));
85- TlsContext clientTlsContext = new TlsContext (clientTlsContextOptions )) {
86-
87- this .bootstrap = registerOwnedResource (clientBootstrap );
88- this .socketOptions = registerOwnedResource (clientSocketOptions );
89- this .tlsContext = registerOwnedResource (clientTlsContext );
90- this .readBufferSize = builder .getReadBufferSizeInBytes () == null ?
91- DEFAULT_STREAM_WINDOW_SIZE : builder .getReadBufferSizeInBytes ();
92- this .maxConnectionsPerEndpoint = config .get (SdkHttpConfigurationOption .MAX_CONNECTIONS );
93- this .monitoringOptions = resolveHttpMonitoringOptions (builder .getConnectionHealthConfiguration ()).orElse (null );
94- this .maxConnectionIdleInMilliseconds = config .get (SdkHttpConfigurationOption .CONNECTION_MAX_IDLE_TIMEOUT ).toMillis ();
95- this .connectionAcquisitionTimeout = config .get (SdkHttpConfigurationOption .CONNECTION_ACQUIRE_TIMEOUT ).toMillis ();
96- this .proxyOptions = resolveProxy (builder .getProxyConfiguration (), tlsContext ).orElse (null );
97- }
89+ TlsContext clientTlsContext = new TlsContext (clientTlsContextOptions );
90+
91+ this .bootstrap = registerOwnedResource (clientBootstrap );
92+ this .socketOptions = registerOwnedResource (clientSocketOptions );
93+ this .tlsContext = registerOwnedResource (clientTlsContext );
94+ this .readBufferSize = builder .getReadBufferSizeInBytes () == null ?
95+ DEFAULT_STREAM_WINDOW_SIZE : builder .getReadBufferSizeInBytes ();
96+ this .maxConnectionsPerEndpoint = config .get (SdkHttpConfigurationOption .MAX_CONNECTIONS );
97+ this .monitoringOptions = resolveHttpMonitoringOptions (builder .getConnectionHealthConfiguration ()).orElse (null );
98+ this .maxConnectionIdleInMilliseconds = config .get (SdkHttpConfigurationOption .CONNECTION_MAX_IDLE_TIMEOUT ).toMillis ();
99+ this .connectionAcquisitionTimeout = config .get (SdkHttpConfigurationOption .CONNECTION_ACQUIRE_TIMEOUT ).toMillis ();
100+ this .proxyOptions = resolveProxy (builder .getProxyConfiguration (), tlsContext ).orElse (null );
98101 }
99102
100103 /**
@@ -106,7 +109,6 @@ abstract class AwsCrtHttpClientBase implements SdkAutoCloseable {
106109 */
107110 private <T extends CrtResource > T registerOwnedResource (T subresource ) {
108111 if (subresource != null ) {
109- subresource .addRef ();
110112 ownedSubResources .push (subresource );
111113 }
112114 return subresource ;
@@ -116,13 +118,16 @@ String clientName() {
116118 return AWS_COMMON_RUNTIME ;
117119 }
118120
119- private HttpClientConnectionManager createConnectionPool (URI uri ) {
121+ private HttpStreamManager createConnectionPool (URI uri ) {
120122 log .debug (() -> "Creating ConnectionPool for: URI:" + uri + ", MaxConns: " + maxConnectionsPerEndpoint );
121123
122- HttpClientConnectionManagerOptions options = new HttpClientConnectionManagerOptions ()
124+ boolean isHttps = "https" .equalsIgnoreCase (uri .getScheme ());
125+ TlsContext poolTlsContext = isHttps ? tlsContext : null ;
126+
127+ HttpClientConnectionManagerOptions h1Options = new HttpClientConnectionManagerOptions ()
123128 .withClientBootstrap (bootstrap )
124129 .withSocketOptions (socketOptions )
125- .withTlsContext (tlsContext )
130+ .withTlsContext (poolTlsContext )
126131 .withUri (uri )
127132 .withWindowSize (readBufferSize )
128133 .withMaxConnections (maxConnectionsPerEndpoint )
@@ -132,7 +137,24 @@ private HttpClientConnectionManager createConnectionPool(URI uri) {
132137 .withMaxConnectionIdleInMilliseconds (maxConnectionIdleInMilliseconds )
133138 .withConnectionAcquisitionTimeoutInMilliseconds (connectionAcquisitionTimeout );
134139
135- return HttpClientConnectionManager .create (options );
140+ HttpStreamManagerOptions options = new HttpStreamManagerOptions ()
141+ .withHTTP1ConnectionManagerOptions (h1Options );
142+
143+ if (protocol == Protocol .HTTP2 ) {
144+ Http2StreamManagerOptions h2Options = new Http2StreamManagerOptions ()
145+ .withConnectionManagerOptions (h1Options );
146+
147+ if (!isHttps ) {
148+ h2Options .withPriorKnowledge (true );
149+ }
150+
151+ options .withHTTP2StreamManagerOptions (h2Options );
152+ options .withExpectedProtocol (HttpVersion .HTTP_2 );
153+ } else {
154+ options .withExpectedProtocol (HttpVersion .HTTP_1_1 );
155+ }
156+
157+ return HttpStreamManager .create (options );
136158 }
137159
138160 /*
@@ -150,14 +172,13 @@ private HttpClientConnectionManager createConnectionPool(URI uri) {
150172 * existing pool. If we add all of execute() to the scope, we include, at minimum a JNI call to the native
151173 * pool implementation.
152174 */
153- HttpClientConnectionManager getOrCreateConnectionPool (URI uri ) {
175+ HttpStreamManager getOrCreateConnectionPool (URI uri ) {
154176 synchronized (this ) {
155177 if (isClosed ) {
156178 throw new IllegalStateException ("Client is closed. No more requests can be made with this client." );
157179 }
158180
159- HttpClientConnectionManager connPool = connectionPools .computeIfAbsent (uri , this ::createConnectionPool );
160- connPool .addRef ();
181+ HttpStreamManager connPool = connectionPools .computeIfAbsent (uri , this ::createConnectionPool );
161182 return connPool ;
162183 }
163184 }
0 commit comments