• 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 package software.amazon.awssdk.crt.test;
6 
7 import org.junit.Assert;
8 import software.amazon.awssdk.crt.CRT;
9 import software.amazon.awssdk.crt.CrtResource;
10 import software.amazon.awssdk.crt.http.HttpClientConnection;
11 import software.amazon.awssdk.crt.http.HttpClientConnectionManager;
12 import software.amazon.awssdk.crt.http.HttpHeader;
13 import software.amazon.awssdk.crt.http.HttpRequestBase;
14 import software.amazon.awssdk.crt.http.HttpStream;
15 import software.amazon.awssdk.crt.http.HttpStreamBase;
16 import software.amazon.awssdk.crt.http.HttpStreamBaseResponseHandler;
17 import software.amazon.awssdk.crt.http.HttpStreamMetrics;
18 import software.amazon.awssdk.crt.http.HttpVersion;
19 
20 import java.net.URI;
21 import java.nio.ByteBuffer;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25 import java.nio.charset.Charset;
26 import java.nio.charset.StandardCharsets;
27 import java.security.MessageDigest;
28 import java.security.NoSuchAlgorithmException;
29 import java.util.concurrent.CompletableFuture;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.atomic.AtomicReference;
32 
33 public class HttpRequestResponseFixture extends HttpClientTestFixture {
34 
35     protected final static Charset UTF8 = StandardCharsets.UTF_8;
36     protected final String EMPTY_BODY = "";
37     protected final static String TEST_DOC_LINE = "This is a sample to prove that http downloads and uploads work. It doesn't really matter what's in here, we mainly just need to verify the downloads and uploads work.";
38     protected final static String TEST_DOC_SHA256 = "C7FDB5314B9742467B16BD5EA2F8012190B5E2C44A005F7984F89AAB58219534";
39 
40     protected class TestHttpResponse {
41         int statusCode = -1;
42         int blockType = -1;
43         List<HttpHeader> headers = new ArrayList<>();
44         ByteBuffer bodyBuffer = ByteBuffer.wrap(new byte[16 * 1024 * 1024]); // Allow up to 16 MB Responses
45         int onCompleteErrorCode = -1;
46 
getBody()47         public String getBody() {
48             bodyBuffer.flip();
49             return UTF8.decode(bodyBuffer).toString();
50         }
51 
52         @Override
toString()53         public String toString() {
54             StringBuilder builder = new StringBuilder();
55             builder.append("Status: " + statusCode);
56             int i = 0;
57             for (HttpHeader h : headers) {
58                 builder.append("\nHeader[" + i + "]: " + h.toString());
59             }
60 
61             builder.append("\nBody:\n");
62             builder.append(getBody());
63 
64             return builder.toString();
65         }
66     }
67 
shouldRetry(TestHttpResponse response)68     protected boolean shouldRetry(TestHttpResponse response) {
69         // Retry if we couldn't connect or if we got flaky 5xx server errors
70         if (response.onCompleteErrorCode != CRT.AWS_CRT_SUCCESS || response.statusCode == 503 || response.statusCode == 504) {
71             return true;
72         }
73         return false;
74     }
75 
byteArrayToHex(byte[] input)76     public static String byteArrayToHex(byte[] input) {
77         StringBuilder output = new StringBuilder(input.length * 2);
78         for (byte b : input) {
79             output.append(String.format("%02X", b));
80         }
81         return output.toString();
82     }
83 
calculateBodyHash(ByteBuffer bodyBuffer)84     public String calculateBodyHash(ByteBuffer bodyBuffer) throws NoSuchAlgorithmException {
85         MessageDigest digest = MessageDigest.getInstance("SHA-256");
86         digest.update(bodyBuffer);
87         return byteArrayToHex(digest.digest());
88     }
89 
getResponse(URI uri, HttpRequestBase request, byte[] chunkedData, HttpVersion expectedVersion)90     public TestHttpResponse getResponse(URI uri, HttpRequestBase request, byte[] chunkedData,
91             HttpVersion expectedVersion) throws Exception {
92         boolean actuallyConnected = false;
93 
94         final CompletableFuture<Void> reqCompleted = new CompletableFuture<>();
95         final AtomicReference<HttpStreamMetrics> metricsRef = new AtomicReference<>(null);
96 
97         final TestHttpResponse response = new TestHttpResponse();
98 
99         CompletableFuture<Void> shutdownComplete = null;
100 
101         try (HttpClientConnectionManager connPool = createConnectionPoolManager(uri, expectedVersion)) {
102             shutdownComplete = connPool.getShutdownCompleteFuture();
103             try (HttpClientConnection conn = connPool.acquireConnection().get(60, TimeUnit.SECONDS)) {
104                 actuallyConnected = true;
105                 HttpStreamBaseResponseHandler streamHandler = new HttpStreamBaseResponseHandler() {
106                     @Override
107                     public void onResponseHeaders(HttpStreamBase stream, int responseStatusCode, int blockType,
108                             HttpHeader[] nextHeaders) {
109                         response.statusCode = responseStatusCode;
110                         Assert.assertEquals(responseStatusCode, stream.getResponseStatusCode());
111                         response.headers.addAll(Arrays.asList(nextHeaders));
112                     }
113 
114                     @Override
115                     public void onResponseHeadersDone(HttpStreamBase stream, int blockType) {
116                         response.blockType = blockType;
117                     }
118 
119                     @Override
120                     public int onResponseBody(HttpStreamBase stream, byte[] bodyBytesIn) {
121                         response.bodyBuffer.put(bodyBytesIn);
122                         int amountRead = bodyBytesIn.length;
123 
124                         // Slide the window open by the number of bytes just read
125                         return amountRead;
126                     }
127 
128                     @Override
129                     public void onMetrics(HttpStreamBase stream, HttpStreamMetrics metrics) {
130                         Assert.assertTrue(metricsRef.compareAndSet(null, metrics));
131                     }
132 
133                     @Override
134                     public void onResponseComplete(HttpStreamBase stream, int errorCode) {
135                         response.onCompleteErrorCode = errorCode;
136                         reqCompleted.complete(null);
137                     }
138                 };
139                 HttpStreamBase stream = conn.makeRequest(request, streamHandler);
140                 stream.activate();
141                 if (chunkedData != null) {
142                     HttpStream h1_stream = (HttpStream)stream;
143                     h1_stream.writeChunk(chunkedData, true).get(5, TimeUnit.SECONDS);
144                 }
145 
146                 // Give the request up to 60 seconds to complete, otherwise throw a
147                 // TimeoutException
148                 reqCompleted.get(60, TimeUnit.SECONDS);
149                 stream.close();
150             }
151         } catch (Exception e) {
152             throw new RuntimeException(e);
153         }
154 
155         Assert.assertTrue(actuallyConnected);
156         Assert.assertNotNull(metricsRef.get());
157 
158         shutdownComplete.get(60, TimeUnit.SECONDS);
159 
160         CrtResource.waitForNoResources();
161 
162         return response;
163 
164     }
165 
166 }
167