• 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 software.amazon.awssdk.crt.AsyncCallback;
9 import software.amazon.awssdk.crt.CrtRuntimeException;
10 
11 import java.util.concurrent.CompletableFuture;
12 import java.util.List;
13 
14 /**
15  * This class wraps aws-c-http to provide the basic HTTP/2 request/response
16  * functionality via the AWS Common Runtime.
17  *
18  * Http2ClientConnection represents a single connection to a HTTP/2 service
19  * endpoint.
20  *
21  * This class is not thread safe and should not be called from different
22  * threads.
23  */
24 public class Http2ClientConnection extends HttpClientConnection {
25 
26     /*
27      * Error codes that may be present in HTTP/2 RST_STREAM and GOAWAY frames
28      * (RFC-7540 7).
29      */
30     public enum Http2ErrorCode {
31         PROTOCOL_ERROR(1), INTERNAL_ERROR(2), FLOW_CONTROL_ERROR(3), SETTINGS_TIMEOUT(4), STREAM_CLOSED(5),
32         FRAME_SIZE_ERROR(6), REFUSED_STREAM(7), CANCEL(8), COMPRESSION_ERROR(9), CONNECT_ERROR(10),
33         ENHANCE_YOUR_CALM(11), INADEQUATE_SECURITY(12), HTTP_1_1_REQUIRED(13);
34 
35         private int errorCode;
36 
Http2ErrorCode(int value)37         Http2ErrorCode(int value) {
38             errorCode = value;
39         }
40 
getValue()41         public int getValue() {
42             return errorCode;
43         }
44     }
45 
Http2ClientConnection(long connectionBinding)46     public Http2ClientConnection(long connectionBinding) {
47         super(connectionBinding);
48     }
49 
50     /**
51      * Send a SETTINGS frame. SETTINGS will be applied locally when SETTINGS ACK is
52      * received from peer.
53      *
54      * @param settings The array of settings to change. Note: each setting has its
55      *                 boundary.
56      *
57      * @return When this future completes without exception, the peer has
58      *         acknowledged the settings and the change has been applied.
59      */
updateSettings(final List<Http2ConnectionSetting> settings)60     public CompletableFuture<Void> updateSettings(final List<Http2ConnectionSetting> settings) {
61         CompletableFuture<Void> future = new CompletableFuture<>();
62         if (isNull()) {
63             future.completeExceptionally(
64                     new IllegalStateException("Http2ClientConnection has been closed, can't change settings on it."));
65             return future;
66         }
67         AsyncCallback updateSettingsCompleted = AsyncCallback.wrapFuture(future, null);
68         try {
69             http2ClientConnectionUpdateSettings(getNativeHandle(), updateSettingsCompleted,
70                     Http2ConnectionSetting.marshallSettingsForJNI(settings));
71         } catch (CrtRuntimeException ex) {
72             future.completeExceptionally(ex);
73         }
74         return future;
75     }
76 
77     /**
78      * Send a PING frame. Round-trip-time is calculated when PING ACK is received
79      * from peer.
80      *
81      * @param pingData 8 Bytes data with the PING frame or null for not include data
82      *                 in ping
83      *
84      * @return When this future completes without exception, the peer has
85      *         acknowledged the PING and future will be completed with the round
86      *         trip time in nano seconds for the connection.
87      */
sendPing(final byte[] pingData)88     public CompletableFuture<Long> sendPing(final byte[] pingData) {
89         CompletableFuture<Long> completionFuture = new CompletableFuture<>();
90         if (isNull()) {
91             completionFuture.completeExceptionally(
92                     new IllegalStateException("Http2ClientConnection has been closed, can't send ping on it."));
93             return completionFuture;
94         }
95         AsyncCallback pingCompleted = AsyncCallback.wrapFuture(completionFuture, 0L);
96         try {
97             http2ClientConnectionSendPing(getNativeHandle(), pingCompleted, pingData);
98         } catch (CrtRuntimeException ex) {
99             completionFuture.completeExceptionally(ex);
100         }
101         return completionFuture;
102     }
103 
sendPing()104     public CompletableFuture<Long> sendPing() {
105         return this.sendPing(null);
106     }
107 
108     /**
109      * Send a custom GOAWAY frame.
110      *
111      * Note that the connection automatically attempts to send a GOAWAY during
112      * shutdown (unless a GOAWAY with a valid Last-Stream-ID has already been sent).
113      *
114      * This call can be used to gracefully warn the peer of an impending shutdown
115      * (http2_error=0, allow_more_streams=true), or to customize the final GOAWAY
116      * frame that is sent by this connection.
117      *
118      * The other end may not receive the goaway, if the connection already closed.
119      *
120      * @param Http2ErrorCode   The HTTP/2 error code (RFC-7540 section 7) to send.
121      *                         `enum Http2ErrorCode` lists official codes.
122      * @param allowMoreStreams If true, new peer-initiated streams will continue to
123      *                         be acknowledged and the GOAWAY's Last-Stream-ID will
124      *                         be set to a max value. If false, new peer-initiated
125      *                         streams will be ignored and the GOAWAY's
126      *                         Last-Stream-ID will be set to the latest acknowledged
127      *                         stream.
128      * @param debugData        Optional debug data to send. Size must not exceed
129      *                         16KB. null is acceptable to not include debug data.
130      */
sendGoAway(final Http2ErrorCode Http2ErrorCode, final boolean allowMoreStreams, final byte[] debugData)131     public void sendGoAway(final Http2ErrorCode Http2ErrorCode, final boolean allowMoreStreams,
132             final byte[] debugData) {
133         if (isNull()) {
134             throw new IllegalStateException("Http2ClientConnection has been closed.");
135         }
136         http2ClientConnectionSendGoAway(getNativeHandle(), (long) Http2ErrorCode.getValue(), allowMoreStreams,
137                 debugData);
138     }
139 
sendGoAway(final Http2ErrorCode Http2ErrorCode, final boolean allowMoreStreams)140     public void sendGoAway(final Http2ErrorCode Http2ErrorCode, final boolean allowMoreStreams) {
141         this.sendGoAway(Http2ErrorCode, allowMoreStreams, null);
142     }
143 
144     /**
145      * Increment the connection's flow-control window to keep data flowing.
146      *
147      * If the connection was created with `manualWindowManagement` set true, the
148      * flow-control window of the connection will shrink as body data is received
149      * for all the streams created on it. (headers, padding, and other metadata do
150      * not affect the window). The initial connection flow-control window is 65,535.
151      * Once the connection's flow-control window reaches to 0, all the streams on
152      * the connection stop receiving any further data.
153      *
154      * If `manualWindowManagement` is false, this call will have no effect. The
155      * connection maintains its flow-control windows such that no back-pressure is
156      * applied and data arrives as fast as possible.
157      *
158      * If you are not connected, this call will have no effect.
159      *
160      * Crashes when the connection is not http2 connection. The limit of the Maximum
161      * Size is 2**31 - 1. If the increment size cause the connection flow window
162      * exceeds the Maximum size, this call will result in the connection lost.
163      *
164      * @param incrementSize The size to increment for the connection's flow control
165      *                      window
166      */
updateConnectionWindow(long incrementSize)167     public void updateConnectionWindow(long incrementSize) {
168         if (incrementSize > 4294967296L || incrementSize < 0) {
169             throw new IllegalArgumentException("increment size cannot exceed 4294967296");
170         }
171         http2ClientConnectionUpdateConnectionWindow(getNativeHandle(), incrementSize);
172     }
173 
174     /**
175      * Schedules an HttpRequest on the Native EventLoop for this
176      * HttpClientConnection. The HTTP/1.1 request will be transformed to HTTP/2
177      * request under the hood.
178      *
179      * @param request       The Request to make to the Server.
180      * @param streamHandler The Stream Handler to be called from the Native
181      *                      EventLoop
182      * @throws CrtRuntimeException if stream creation fails
183      * @return The Http2Stream that represents this Request/Response Pair. It can be
184      *         closed at any time during the request/response, but must be closed by
185      *         the user thread making this request when it's done.
186      */
187     @Override
makeRequest(HttpRequestBase request, HttpStreamBaseResponseHandler streamHandler)188     public Http2Stream makeRequest(HttpRequestBase request, HttpStreamBaseResponseHandler streamHandler)
189             throws CrtRuntimeException {
190         if (isNull()) {
191             throw new IllegalStateException("Http2ClientConnection has been closed, can't make requests on it.");
192         }
193 
194         Http2Stream stream = http2ClientConnectionMakeRequest(getNativeHandle(), request.marshalForJni(),
195                 request.getBodyStream(), new HttpStreamResponseHandlerNativeAdapter(streamHandler));
196         return stream;
197     }
198 
199     /**
200      * @TODO: bindings for getters of local/remote setting and goaway.
201      */
202     /*******************************************************************************
203      * Native methods
204      ******************************************************************************/
205 
http2ClientConnectionMakeRequest(long connectionBinding, byte[] marshalledRequest, HttpRequestBodyStream bodyStream, HttpStreamResponseHandlerNativeAdapter responseHandler)206     private static native Http2Stream http2ClientConnectionMakeRequest(long connectionBinding, byte[] marshalledRequest,
207             HttpRequestBodyStream bodyStream, HttpStreamResponseHandlerNativeAdapter responseHandler)
208             throws CrtRuntimeException;
209 
http2ClientConnectionUpdateSettings(long connectionBinding, AsyncCallback completedCallback, long[] marshalledSettings)210     private static native void http2ClientConnectionUpdateSettings(long connectionBinding,
211             AsyncCallback completedCallback, long[] marshalledSettings) throws CrtRuntimeException;
212 
http2ClientConnectionSendPing(long connectionBinding, AsyncCallback completedCallback, byte[] pingData)213     private static native void http2ClientConnectionSendPing(long connectionBinding, AsyncCallback completedCallback,
214             byte[] pingData) throws CrtRuntimeException;
215 
http2ClientConnectionSendGoAway(long connectionBinding, long h2ErrorCode, boolean allowMoreStreams, byte[] debugData)216     private static native void http2ClientConnectionSendGoAway(long connectionBinding, long h2ErrorCode,
217             boolean allowMoreStreams, byte[] debugData) throws CrtRuntimeException;
218 
http2ClientConnectionUpdateConnectionWindow(long connectionBinding, long incrementSize)219     private static native void http2ClientConnectionUpdateConnectionWindow(long connectionBinding, long incrementSize)
220             throws CrtRuntimeException;
221 }
222