• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 package software.amazon.awssdk.crt.http;
7 
8 import java.util.concurrent.CompletableFuture;
9 import java.util.Map;
10 import java.util.HashMap;
11 
12 import software.amazon.awssdk.crt.CRT;
13 import software.amazon.awssdk.crt.CrtResource;
14 import software.amazon.awssdk.crt.CrtRuntimeException;
15 import software.amazon.awssdk.crt.http.HttpStreamResponseHandler;
16 import software.amazon.awssdk.crt.http.HttpStream;
17 
18 import static software.amazon.awssdk.crt.CRT.awsLastError;
19 
20 /**
21  * This class wraps aws-c-http to provide the basic HTTP request/response functionality via the AWS Common Runtime.
22  *
23  * HttpClientConnection represents a single connection to a HTTP service endpoint.
24  *
25  * This class is not thread safe and should not be called from different threads.
26  */
27 public class HttpClientConnection extends CrtResource {
28 
HttpClientConnection(long connectionBinding)29     protected HttpClientConnection(long connectionBinding) {
30         acquireNativeHandle(connectionBinding);
31     }
32 
33     /**
34      * Schedules an HttpRequest on the Native EventLoop for this HttpClientConnection specific to HTTP/1.1 connection.
35      *
36      * @param request The Request to make to the Server.
37      * @param streamHandler The Stream Handler to be called from the Native EventLoop
38      * @throws CrtRuntimeException if stream creation fails
39      * @return The HttpStream that represents this Request/Response Pair. It can be closed at any time during the
40      *          request/response, but must be closed by the user thread making this request when it's done.
41      */
makeRequest(HttpRequest request, HttpStreamResponseHandler streamHandler)42     public HttpStream makeRequest(HttpRequest request, HttpStreamResponseHandler streamHandler)
43             throws CrtRuntimeException {
44         if (isNull()) {
45             throw new IllegalStateException("HttpClientConnection has been closed, can't make requests on it.");
46         }
47         if (getVersion() == HttpVersion.HTTP_2) {
48             throw new IllegalArgumentException("HTTP/1 only method called on an HTTP/2 connection.");
49         }
50         HttpStreamBase stream = httpClientConnectionMakeRequest(getNativeHandle(),
51                 request.marshalForJni(),
52                 request.getBodyStream(),
53                 new HttpStreamResponseHandlerNativeAdapter(streamHandler));
54 
55         return (HttpStream)stream;
56     }
57 
58     /**
59      * Schedules an HttpRequestBase on the Native EventLoop for this HttpClientConnection applies to both HTTP/2 and HTTP/1.1 connection.
60      *
61      * @param request The Request to make to the Server.
62      * @param streamHandler The Stream Handler to be called from the Native EventLoop
63      * @throws CrtRuntimeException if stream creation fails
64      * @return The HttpStream that represents this Request/Response Pair. It can be closed at any time during the
65      *          request/response, but must be closed by the user thread making this request when it's done.
66      */
makeRequest(HttpRequestBase request, HttpStreamBaseResponseHandler streamHandler)67     public HttpStreamBase makeRequest(HttpRequestBase request, HttpStreamBaseResponseHandler streamHandler) throws CrtRuntimeException {
68         if (isNull()) {
69             throw new IllegalStateException("HttpClientConnection has been closed, can't make requests on it.");
70         }
71         HttpStreamBase stream = httpClientConnectionMakeRequest(getNativeHandle(),
72                 request.marshalForJni(),
73                 request.getBodyStream(),
74                 new HttpStreamResponseHandlerNativeAdapter(streamHandler));
75 
76         return stream;
77     }
78 
79     /**
80      * Determines whether a resource releases its dependencies at the same time the native handle is released or if it waits.
81      * Resources that wait are responsible for calling releaseReferences() manually.
82      */
83     @Override
canReleaseReferencesImmediately()84     protected boolean canReleaseReferencesImmediately() { return true; }
85 
86     /**
87      * Releases this HttpClientConnection back into the Connection Pool, and allows another Request to acquire this connection.
88      */
89     @Override
releaseNativeHandle()90     protected void releaseNativeHandle() {
91         if (!isNull()){
92             httpClientConnectionReleaseManaged(getNativeHandle());
93         }
94     }
95 
96     /**
97      * Shuts down the underlying http connection.  Even if this function is called, you still need to properly close
98      * the connection as well in order to release the native resources.
99      */
shutdown()100     public void shutdown() {
101         if (isNull()) {
102             throw new IllegalStateException("HttpClientConnection has been closed and released back to the pool, cannot shutdown the connection.");
103         }
104         httpClientConnectionShutdown(getNativeHandle());
105     }
106 
107     /**
108      * Check the underlying http connection is still open or not.
109      *
110      * @return true unless the underlying http connection is shutting down, or has been shutdown.
111      */
isOpen()112     public boolean isOpen() {
113         if (isNull()) {
114             throw new IllegalStateException("HttpClientConnection has been closed.");
115         }
116         return httpClientConnectionIsOpen(getNativeHandle());
117     }
118 
getVersion()119     public HttpVersion getVersion() {
120         if (isNull()) {
121             throw new IllegalStateException("HttpClientConnection has been closed.");
122         }
123         short version = httpClientConnectionGetVersion(getNativeHandle());
124         return HttpVersion.getEnumValueFromInteger((int) version);
125     };
126 
127     /** Called from Native when a new connection is acquired **/
onConnectionAcquired(CompletableFuture<HttpClientConnection> acquireFuture, long nativeConnectionBinding, int errorCode)128     private static void onConnectionAcquired(CompletableFuture<HttpClientConnection> acquireFuture, long nativeConnectionBinding, int errorCode) {
129         if (errorCode != CRT.AWS_CRT_SUCCESS) {
130             acquireFuture.completeExceptionally(new HttpException(errorCode));
131             return;
132         }
133         if (HttpVersion.getEnumValueFromInteger(
134                 (int) httpClientConnectionGetVersion(nativeConnectionBinding)) == HttpVersion.HTTP_2) {
135             HttpClientConnection h2Conn = new Http2ClientConnection(nativeConnectionBinding);
136             if (!acquireFuture.complete(h2Conn)) {
137                 // future was already completed/cancelled, return it immediately to the pool to not leak it
138                 h2Conn.close();
139             }
140         } else {
141             HttpClientConnection conn = new HttpClientConnection(nativeConnectionBinding);
142             if (!acquireFuture.complete(conn)) {
143                 // future was already completed/cancelled, return it immediately to the pool to not leak it
144                 conn.close();
145             }
146         }
147     }
148 
149     /**
150      * Certain exceptions thrown by this HTTP API are from invalid boundary conditions
151      * that, if the request isn't altered, will never succeed. This function returns
152      * false if the exception is caused by such a condition.
153      * <p>
154      * It does not mean the request that generated the error SHOULD be retried:
155      * only that as far as this client is concerned, the request might,
156      * possibly succeed with a subsequent attempt.
157      *
158      * @param exception, an exception thrown by the CRT HTTP API--for any reason.
159      * @return true if the error that generated the exception makes sense for a retry, and
160      * false otherwise.
161      */
isErrorRetryable(HttpException exception)162     public static boolean isErrorRetryable(HttpException exception) {
163         // why take an exception rather than an error code directly?
164         // to give us breathing room for changing our mind later about how we convey
165         // retry information on the exceptions we throw.
166         return isErrorRetryable(exception.getErrorCode());
167     }
168 
169     /*******************************************************************************
170      * Native methods
171      ******************************************************************************/
httpClientConnectionMakeRequest(long connectionBinding, byte[] marshalledRequest, HttpRequestBodyStream bodyStream, HttpStreamResponseHandlerNativeAdapter responseHandler)172     private static native HttpStreamBase httpClientConnectionMakeRequest(long connectionBinding,
173                                                                      byte[] marshalledRequest,
174                                                                      HttpRequestBodyStream bodyStream,
175                                                                      HttpStreamResponseHandlerNativeAdapter responseHandler) throws CrtRuntimeException;
176 
httpClientConnectionShutdown(long connectionBinding)177     private static native void httpClientConnectionShutdown(long connectionBinding) throws CrtRuntimeException;
httpClientConnectionIsOpen(long connectionBinding)178     private static native boolean httpClientConnectionIsOpen(long connectionBinding) throws CrtRuntimeException;
179 
httpClientConnectionReleaseManaged(long connectionBinding)180     private static native void httpClientConnectionReleaseManaged(long connectionBinding) throws CrtRuntimeException;
httpClientConnectionGetVersion(long connectionBinding)181     private static native short httpClientConnectionGetVersion(long connectionBinding) throws CrtRuntimeException;
182 
isErrorRetryable(int errorCode)183     private static native boolean isErrorRetryable(int errorCode);
184 }
185