1
15 package com.amazonaws.http;
16
17 import static com.amazonaws.SDKGlobalConfiguration.PROFILING_SYSTEM_PROPERTY;
18 import static com.amazonaws.event.SDKProgressPublisher.publishProgress;
19 import static com.amazonaws.event.SDKProgressPublisher.publishRequestContentLength;
20 import static com.amazonaws.event.SDKProgressPublisher.publishResponseContentLength;
21 import static com.amazonaws.util.AWSRequestMetrics.Field.HttpClientPoolAvailableCount;
22 import static com.amazonaws.util.AWSRequestMetrics.Field.HttpClientPoolLeasedCount;
23 import static com.amazonaws.util.AWSRequestMetrics.Field.HttpClientPoolPendingCount;
24 import static com.amazonaws.util.AWSRequestMetrics.Field.ThrottledRetryCount;
25 import static com.amazonaws.util.AwsClientSideMonitoringMetrics.MaxRetriesExceeded;
26 import static com.amazonaws.util.IOUtils.closeQuietly;
27
28 import com.amazonaws.AbortedException;
29 import com.amazonaws.AmazonClientException;
30 import com.amazonaws.AmazonServiceException;
31 import com.amazonaws.AmazonWebServiceRequest;
32 import com.amazonaws.AmazonWebServiceResponse;
33 import com.amazonaws.ClientConfiguration;
34 import com.amazonaws.Request;
35 import com.amazonaws.RequestClientOptions;
36 import com.amazonaws.RequestClientOptions.Marker;
37 import com.amazonaws.RequestConfig;
38 import com.amazonaws.ResetException;
39 import com.amazonaws.Response;
40 import com.amazonaws.ResponseMetadata;
41 import com.amazonaws.SDKGlobalTime;
42 import com.amazonaws.SdkBaseException;
43 import com.amazonaws.SdkClientException;
44 import com.amazonaws.annotation.SdkInternalApi;
45 import com.amazonaws.annotation.SdkTestInternalApi;
46 import com.amazonaws.annotation.ThreadSafe;
47 import com.amazonaws.auth.AWS4Signer;
48 import com.amazonaws.auth.AWSCredentials;
49 import com.amazonaws.auth.AWSCredentialsProvider;
50 import com.amazonaws.auth.CanHandleNullCredentials;
51 import com.amazonaws.auth.Signer;
52 import com.amazonaws.event.ProgressEventType;
53 import com.amazonaws.event.ProgressInputStream;
54 import com.amazonaws.event.ProgressListener;
55 import com.amazonaws.handlers.CredentialsRequestHandler;
56 import com.amazonaws.handlers.HandlerAfterAttemptContext;
57 import com.amazonaws.handlers.HandlerBeforeAttemptContext;
58 import com.amazonaws.handlers.HandlerContextKey;
59 import com.amazonaws.handlers.RequestHandler2;
60 import com.amazonaws.http.apache.client.impl.ApacheHttpClientFactory;
61 import com.amazonaws.http.apache.client.impl.ConnectionManagerAwareHttpClient;
62 import com.amazonaws.http.apache.request.impl.ApacheHttpRequestFactory;
63 import com.amazonaws.http.apache.utils.ApacheUtils;
64 import com.amazonaws.http.client.HttpClientFactory;
65 import com.amazonaws.http.exception.HttpRequestTimeoutException;
66 import com.amazonaws.http.request.HttpRequestFactory;
67 import com.amazonaws.http.response.AwsResponseHandlerAdapter;
68 import com.amazonaws.http.settings.HttpClientSettings;
69 import com.amazonaws.http.timers.client.ClientExecutionAbortTrackerTask;
70 import com.amazonaws.http.timers.client.ClientExecutionTimeoutException;
71 import com.amazonaws.http.timers.client.ClientExecutionTimer;
72 import com.amazonaws.http.timers.client.SdkInterruptedException;
73 import com.amazonaws.http.timers.request.HttpRequestAbortTaskTracker;
74 import com.amazonaws.http.timers.request.HttpRequestTimer;
75 import com.amazonaws.internal.AmazonWebServiceRequestAdapter;
76 import com.amazonaws.internal.CRC32MismatchException;
77 import com.amazonaws.internal.ReleasableInputStream;
78 import com.amazonaws.internal.ResettableInputStream;
79 import com.amazonaws.internal.SdkBufferedInputStream;
80 import com.amazonaws.internal.auth.SignerProviderContext;
81 import com.amazonaws.metrics.AwsSdkMetrics;
82 import com.amazonaws.metrics.RequestMetricCollector;
83 import com.amazonaws.monitoring.internal.ClientSideMonitoringRequestHandler;
84 import com.amazonaws.retry.ClockSkewAdjuster;
85 import com.amazonaws.retry.ClockSkewAdjuster.AdjustmentRequest;
86 import com.amazonaws.retry.ClockSkewAdjuster.ClockSkewAdjustment;
87 import com.amazonaws.retry.RetryMode;
88 import com.amazonaws.retry.RetryPolicyAdapter;
89 import com.amazonaws.retry.RetryUtils;
90 import com.amazonaws.internal.SdkRequestRetryHeaderProvider;
91 import com.amazonaws.retry.internal.AuthErrorRetryStrategy;
92 import com.amazonaws.retry.internal.AuthRetryParameters;
93 import com.amazonaws.retry.v2.RetryPolicy;
94 import com.amazonaws.retry.v2.RetryPolicyContext;
95 import com.amazonaws.util.AWSRequestMetrics;
96 import com.amazonaws.util.AWSRequestMetrics.Field;
97 import com.amazonaws.util.AwsClientSideMonitoringMetrics;
98 import com.amazonaws.util.CapacityManager;
99 import com.amazonaws.util.CollectionUtils;
100 import com.amazonaws.util.CountingInputStream;
101 import com.amazonaws.util.FakeIOException;
102 import com.amazonaws.util.ImmutableMapParameter;
103 import com.amazonaws.util.MetadataCache;
104 import com.amazonaws.util.NullResponseMetadataCache;
105 import com.amazonaws.util.ResponseMetadataCache;
106 import com.amazonaws.util.RuntimeHttpUtils;
107 import com.amazonaws.util.SdkHttpUtils;
108 import com.amazonaws.util.UnreliableFilterInputStream;
109 import java.io.BufferedInputStream;
110 import java.io.Closeable;
111 import java.io.FileInputStream;
112 import java.io.IOException;
113 import java.io.InputStream;
114 import java.net.SocketTimeoutException;
115 import java.net.URI;
116 import java.util.Arrays;
117 import java.util.Collections;
118 import java.util.HashMap;
119 import java.util.LinkedHashMap;
120 import java.util.List;
121 import java.util.Map;
122 import java.util.Map.Entry;
123 import java.util.Random;
124 import java.util.UUID;
125 import org.apache.commons.logging.Log;
126 import org.apache.commons.logging.LogFactory;
127 import org.apache.http.Header;
128 import org.apache.http.HttpEntity;
129 import org.apache.http.HttpEntityEnclosingRequest;
130 import org.apache.http.HttpStatus;
131 import org.apache.http.StatusLine;
132 import org.apache.http.client.methods.HttpRequestBase;
133 import org.apache.http.client.protocol.HttpClientContext;
134 import org.apache.http.conn.ConnectTimeoutException;
135 import org.apache.http.entity.BufferedHttpEntity;
136 import org.apache.http.impl.execchain.RequestAbortedException;
137 import org.apache.http.pool.ConnPoolControl;
138 import org.apache.http.pool.PoolStats;
139 import org.apache.http.protocol.HttpContext;
140
141 @ThreadSafe
142 public class AmazonHttpClient {
143
144 public static final String HEADER_USER_AGENT = "User-Agent";
145 public static final String HEADER_SDK_TRANSACTION_ID = "amz-sdk-invocation-id";
146 public static final String HEADER_SDK_RETRY_INFO = "amz-sdk-retry";
147
148
152 static final Log log = LogFactory.getLog(AmazonHttpClient.class);
153
154
159 @SdkInternalApi
160 public static final Log requestLog = LogFactory.getLog("com.amazonaws.request");
161
162 private static final HttpClientFactory<ConnectionManagerAwareHttpClient> httpClientFactory = new
163 ApacheHttpClientFactory();
164
167 private static UnreliableTestConfig unreliableTestConfig;
168
169
175 private static final int THROTTLED_RETRY_COST = 5;
176
177
180 private static final int TIMEOUT_RETRY_COST = 10;
181
182 static {
183
184
185
186
187 List<String> problematicJvmVersions = Arrays
188 .asList("1.6.0_06", "1.6.0_13", "1.6.0_17", "1.6.0_65", "1.7.0_45");
189 String jvmVersion = System.getProperty("java.version");
190 if (problematicJvmVersions.contains(jvmVersion)) {
191 log.warn("Detected a possible problem with the current JVM version (" + jvmVersion +
192 "). " +
193 "If you experience XML parsing problems using the SDK, try upgrading to a more recent JVM update.");
194 }
195 }
196
197 private final ClockSkewAdjuster clockSkewAdjuster = new ClockSkewAdjuster();
198
199 private final HttpRequestFactory<HttpRequestBase> httpRequestFactory =
200 new ApacheHttpRequestFactory();
201
204 private ConnectionManagerAwareHttpClient httpClient;
205
208 private final ClientConfiguration config;
209
210 private final RetryPolicy retryPolicy;
211
212
215 private final HttpClientSettings httpClientSettings;
216
219 private final MetadataCache responseMetadataCache;
220
223 private final HttpRequestTimer httpRequestTimer;
224
225
228 private final CapacityManager retryCapacity;
229
230
234 private final ClientExecutionTimer clientExecutionTimer;
235
242 private final RequestMetricCollector requestMetricCollector;
243
244
249 private final Random random = new Random();
250
251
254 private volatile int timeOffset = SDKGlobalTime.getGlobalTimeOffset();
255
256 private final RetryMode retryMode;
257
258 private final SdkRequestRetryHeaderProvider sdkRequestHeaderProvider;
259
260
267 public AmazonHttpClient(ClientConfiguration config) {
268 this(config, null);
269 }
270
271
282 public AmazonHttpClient(ClientConfiguration config,
283 RequestMetricCollector requestMetricCollector) {
284 this(config, requestMetricCollector, false);
285 }
286
287
298 public AmazonHttpClient(ClientConfiguration config,
299 RequestMetricCollector requestMetricCollector,
300 boolean useBrowserCompatibleHostNameVerifier) {
301 this(config, requestMetricCollector, useBrowserCompatibleHostNameVerifier, false);
302 }
303
304
319 public AmazonHttpClient(ClientConfiguration config,
320 RequestMetricCollector requestMetricCollector,
321 boolean useBrowserCompatibleHostNameVerifier,
322 boolean calculateCRC32FromCompressedData) {
323 this(config,
324 null,
325 requestMetricCollector,
326 useBrowserCompatibleHostNameVerifier,
327 calculateCRC32FromCompressedData);
328 }
329
330 private AmazonHttpClient(ClientConfiguration config,
331 RetryPolicy retryPolicy,
332 RequestMetricCollector requestMetricCollector,
333 boolean useBrowserCompatibleHostNameVerifier,
334 boolean calculateCRC32FromCompressedData) {
335 this(config,
336 retryPolicy,
337 requestMetricCollector,
338 HttpClientSettings.adapt(config, useBrowserCompatibleHostNameVerifier, calculateCRC32FromCompressedData));
339 this.httpClient = httpClientFactory.create(this.httpClientSettings);
340 }
341
342
345 @SdkTestInternalApi
346 public AmazonHttpClient(ClientConfiguration clientConfig,
347 ConnectionManagerAwareHttpClient httpClient,
348 RequestMetricCollector requestMetricCollector) {
349 this(clientConfig,
350 null,
351 requestMetricCollector,
352 HttpClientSettings.adapt(clientConfig, false));
353 this.httpClient = httpClient;
354 }
355
356 private AmazonHttpClient(ClientConfiguration clientConfig,
357 RetryPolicy retryPolicy,
358 RequestMetricCollector requestMetricCollector,
359 HttpClientSettings httpClientSettings) {
360 this.config = clientConfig;
361 this.retryPolicy =
362 retryPolicy == null ? new RetryPolicyAdapter(clientConfig.getRetryPolicy(), clientConfig) : retryPolicy;
363 this.retryMode =
364 clientConfig.getRetryMode() == null ? clientConfig.getRetryPolicy().getRetryMode() : clientConfig.getRetryMode();
365 this.httpClientSettings = httpClientSettings;
366 this.requestMetricCollector = requestMetricCollector;
367 this.responseMetadataCache =
368 clientConfig.getCacheResponseMetadata() ?
369 new ResponseMetadataCache(clientConfig.getResponseMetadataCacheSize()) :
370 new NullResponseMetadataCache();
371 this.httpRequestTimer = new HttpRequestTimer();
372 this.clientExecutionTimer = new ClientExecutionTimer();
373
374
375
376 int throttledRetryMaxCapacity = clientConfig.useThrottledRetries()
377 ? THROTTLED_RETRY_COST * config.getMaxConsecutiveRetriesBeforeThrottling() : -1;
378 this.retryCapacity = new CapacityManager(throttledRetryMaxCapacity);
379 this.sdkRequestHeaderProvider = new SdkRequestRetryHeaderProvider(config, this.retryPolicy, clockSkewAdjuster);
380 }
381
382 public static Builder builder() {
383 return new Builder();
384 }
385
386 public static class Builder {
387
388 private ClientConfiguration clientConfig;
389 private RetryPolicy retryPolicy;
390 private RequestMetricCollector requestMetricCollector;
391 private boolean useBrowserCompatibleHostNameVerifier;
392 private boolean calculateCRC32FromCompressedData;
393
394 private Builder() {
395 }
396
397 public Builder clientConfiguration(ClientConfiguration clientConfig) {
398 this.clientConfig = clientConfig;
399 return this;
400 }
401
402 public Builder retryPolicy(RetryPolicy retryPolicy) {
403 this.retryPolicy = retryPolicy;
404 return this;
405 }
406
407 public Builder requestMetricCollector(RequestMetricCollector requestMetricCollector) {
408 this.requestMetricCollector = requestMetricCollector;
409 return this;
410 }
411
412 public Builder useBrowserCompatibleHostNameVerifier(boolean useBrowserCompatibleHostNameVerifier) {
413 this.useBrowserCompatibleHostNameVerifier = useBrowserCompatibleHostNameVerifier;
414 return this;
415 }
416
417 public Builder calculateCRC32FromCompressedData(boolean calculateCRC32FromCompressedData) {
418 this.calculateCRC32FromCompressedData = calculateCRC32FromCompressedData;
419 return this;
420 }
421
422 public AmazonHttpClient build() {
423 return new AmazonHttpClient(clientConfig,
424 retryPolicy,
425 requestMetricCollector,
426 useBrowserCompatibleHostNameVerifier,
427 calculateCRC32FromCompressedData);
428 }
429 }
430
431 private static boolean isTemporaryRedirect(org.apache.http.HttpResponse response) {
432 int status = response.getStatusLine().getStatusCode();
433 return status == HttpStatus.SC_TEMPORARY_REDIRECT && response.getHeaders("Location") != null
434 && response.getHeaders("Location").length > 0;
435 }
436
437 @Override
438 protected void finalize() throws Throwable {
439 this.shutdown();
440 super.finalize();
441 }
442
443
449 public void shutdown() {
450 clientExecutionTimer.shutdown();
451 httpRequestTimer.shutdown();
452 IdleConnectionReaper.removeConnectionManager(httpClient.getHttpClientConnectionManager());
453 httpClient.getHttpClientConnectionManager().shutdown();
454 }
455
456
463 static void configUnreliableTestConditions(UnreliableTestConfig config) {
464 unreliableTestConfig = config;
465 }
466
467
470 @SdkTestInternalApi
471 public HttpRequestTimer getHttpRequestTimer() {
472 return this.httpRequestTimer;
473 }
474
475
478 @SdkTestInternalApi
479 public ClientExecutionTimer getClientExecutionTimer() {
480 return this.clientExecutionTimer;
481 }
482
483
494 public ResponseMetadata getResponseMetadataForRequest(AmazonWebServiceRequest request) {
495 return responseMetadataCache.get(request);
496 }
497
498
502 public RequestMetricCollector getRequestMetricCollector() {
503 return requestMetricCollector;
504 }
505
506
509 public int getTimeOffset() {
510 return timeOffset;
511 }
512
513
525 @Deprecated
526 public <T> Response<T> execute(Request<?> request,
527 HttpResponseHandler<AmazonWebServiceResponse<T>> responseHandler,
528 HttpResponseHandler<AmazonServiceException> errorResponseHandler,
529 ExecutionContext executionContext) {
530 return execute(request, responseHandler, errorResponseHandler, executionContext,
531 new AmazonWebServiceRequestAdapter(request.getOriginalRequest()));
532 }
533
534 @SdkInternalApi
535 public <T> Response<T> execute(Request<?> request,
536 HttpResponseHandler<AmazonWebServiceResponse<T>> responseHandler,
537 HttpResponseHandler<AmazonServiceException> errorResponseHandler,
538 ExecutionContext executionContext,
539 RequestConfig requestConfig) {
540 HttpResponseHandler<T> adaptedRespHandler = new AwsResponseHandlerAdapter<T>(
541 getNonNullResponseHandler(responseHandler),
542 request,
543 executionContext.getAwsRequestMetrics(),
544 responseMetadataCache);
545 return requestExecutionBuilder()
546 .request(request)
547 .requestConfig(requestConfig)
548 .errorResponseHandler(new AwsErrorResponseHandler(errorResponseHandler, executionContext.getAwsRequestMetrics(), config))
549 .executionContext(executionContext)
550 .execute(adaptedRespHandler);
551 }
552
553
559 private <T> HttpResponseHandler<T> getNonNullResponseHandler(
560 HttpResponseHandler<T> responseHandler) {
561 if (responseHandler != null) {
562 return responseHandler;
563 } else {
564
565 return new HttpResponseHandler<T>() {
566
567 @Override
568 public T handle(HttpResponse response) throws Exception {
569 return null;
570 }
571
572 @Override
573 public boolean needsConnectionLeftOpen() {
574 return false;
575 }
576 };
577 }
578 }
579
580
583 public RequestExecutionBuilder requestExecutionBuilder() {
584 return new RequestExecutionBuilderImpl();
585 }
586
587
590 public interface RequestExecutionBuilder {
591
592
598 RequestExecutionBuilder request(Request<?> request);
599
600
606 RequestExecutionBuilder errorResponseHandler(
607 HttpResponseHandler<? extends SdkBaseException> errorResponseHandler);
608
609
615 RequestExecutionBuilder executionContext(ExecutionContext executionContext);
616
617
623 RequestExecutionBuilder requestConfig(RequestConfig requestConfig);
624
625
633 <Output> Response<Output> execute(HttpResponseHandler<Output> responseHandler);
634
635
640 Response<Void> execute();
641
642 }
643
644 private class RequestExecutionBuilderImpl implements RequestExecutionBuilder {
645
646 private Request<?> request;
647 private RequestConfig requestConfig;
648 private HttpResponseHandler<? extends SdkBaseException> errorResponseHandler;
649 private ExecutionContext executionContext = new ExecutionContext();
650
651 @Override
652 public RequestExecutionBuilder request(Request<?> request) {
653 this.request = request;
654 return this;
655 }
656
657 @Override
658 public RequestExecutionBuilder errorResponseHandler(
659 HttpResponseHandler<? extends SdkBaseException> errorResponseHandler) {
660 this.errorResponseHandler = errorResponseHandler;
661 return this;
662 }
663
664 @Override
665 public RequestExecutionBuilder executionContext(
666 ExecutionContext executionContext) {
667 this.executionContext = executionContext;
668 return this;
669 }
670
671 @Override
672 public RequestExecutionBuilder requestConfig(RequestConfig requestConfig) {
673 this.requestConfig = requestConfig;
674 return this;
675 }
676
677 @Override
678 public <Output> Response<Output> execute(HttpResponseHandler<Output> responseHandler) {
679 RequestConfig config = requestConfig != null ? requestConfig : new AmazonWebServiceRequestAdapter(request.getOriginalRequest());
680 return new RequestExecutor<Output>(request,
681 config,
682 getNonNullResponseHandler(errorResponseHandler),
683 getNonNullResponseHandler(responseHandler),
684 executionContext,
685 getRequestHandlers()
686 ).execute();
687 }
688
689 @Override
690 public Response<Void> execute() {
691 return execute(null);
692 }
693
694 private List<RequestHandler2> getRequestHandlers() {
695 List<RequestHandler2> requestHandler2s = executionContext.getRequestHandler2s();
696 if (requestHandler2s == null) {
697 return Collections.emptyList();
698 }
699 return requestHandler2s;
700 }
701
702 }
703
704 private class RequestExecutor<Output> {
705 private final Request<?> request;
706 private final RequestConfig requestConfig;
707 private final HttpResponseHandler<? extends SdkBaseException> errorResponseHandler;
708 private final HttpResponseHandler<Output> responseHandler;
709 private final ExecutionContext executionContext;
710 private final List<RequestHandler2> requestHandler2s;
711 private final AWSRequestMetrics awsRequestMetrics;
712
713 private RequestHandler2 csmRequestHandler;
714
715 private RequestExecutor(Request<?> request, RequestConfig requestConfig,
716 HttpResponseHandler<? extends SdkBaseException> errorResponseHandler,
717 HttpResponseHandler<Output> responseHandler,
718 ExecutionContext executionContext,
719 List<RequestHandler2> requestHandler2s) {
720 this.request = request;
721 this.requestConfig = requestConfig;
722 this.errorResponseHandler = errorResponseHandler;
723 this.responseHandler = responseHandler;
724 this.executionContext = executionContext;
725 this.requestHandler2s = requestHandler2s;
726 this.awsRequestMetrics = executionContext.getAwsRequestMetrics();
727 for (RequestHandler2 requestHandler2 : requestHandler2s) {
728 if (requestHandler2 instanceof ClientSideMonitoringRequestHandler) {
729 csmRequestHandler = requestHandler2;
730 break;
731 }
732 }
733 }
734
735
738 private Response<Output> execute() {
739 if (executionContext == null) {
740 throw new SdkClientException(
741 "Internal SDK Error: No execution context parameter specified.");
742 }
743 try {
744 return executeWithTimer();
745 } catch (InterruptedException ie) {
746 throw handleInterruptedException(ie);
747 } catch (AbortedException ae) {
748 throw handleAbortedException(ae);
749 } finally {
750 if (executionContext.getClientExecutionTrackerTask().hasTimeoutExpired()) {
751
752
753 Thread.interrupted();
754 }
755 }
756 }
757
758
763 private Response<Output> executeWithTimer() throws InterruptedException {
764 ClientExecutionAbortTrackerTask clientExecutionTrackerTask =
765 clientExecutionTimer.startTimer(getClientExecutionTimeout(requestConfig));
766 Response<Output> outputResponse;
767
768 try {
769 executionContext.setClientExecutionTrackerTask(clientExecutionTrackerTask);
770 outputResponse = doExecute();
771
772 } finally {
773
774
775
776 executionContext.getClientExecutionTrackerTask().cancelTask();
777 }
778
779 return outputResponse;
780 }
781
782 private Response<Output> doExecute() throws InterruptedException {
783 runBeforeRequestHandlers();
784 setSdkTransactionId(request);
785 setUserAgent(request);
786
787 ProgressListener listener = requestConfig.getProgressListener();
788
789 request.getHeaders().putAll(config.getHeaders());
790 request.getHeaders().putAll(requestConfig.getCustomRequestHeaders());
791
792 mergeQueryParameters(requestConfig.getCustomQueryParameters());
793 Response<Output> response = null;
794 final InputStream origContent = request.getContent();
795 final InputStream toBeClosed = beforeRequest();
796
797 final InputStream notCloseable = (toBeClosed == null) ? null
798 : ReleasableInputStream.wrap(toBeClosed).disableClose();
799 request.setContent(notCloseable);
800 try {
801 publishProgress(listener, ProgressEventType.CLIENT_REQUEST_STARTED_EVENT);
802 response = executeHelper();
803 publishProgress(listener, ProgressEventType.CLIENT_REQUEST_SUCCESS_EVENT);
804 awsRequestMetrics.endEvent(AwsClientSideMonitoringMetrics.ApiCallLatency);
805 awsRequestMetrics.getTimingInfo().endTiming();
806 afterResponse(response);
807 return response;
808 } catch (AmazonClientException e) {
809 publishProgress(listener, ProgressEventType.CLIENT_REQUEST_FAILED_EVENT);
810
811 awsRequestMetrics.endEvent(AwsClientSideMonitoringMetrics.ApiCallLatency);
812
813 afterError(response, e);
814 throw e;
815 } finally {
816
817 closeQuietlyForRuntimeExceptions(toBeClosed, log);
818 request.setContent(origContent);
819 }
820 }
821
822 private void closeQuietlyForRuntimeExceptions(Closeable c, Log log) {
823 try {
824 closeQuietly(c, log);
825 } catch (RuntimeException e) {
826 if (log.isDebugEnabled()) {
827 log.debug("Unable to close closeable", e);
828 }
829 }
830 }
831
832 private void runBeforeRequestHandlers() {
833 AWSCredentials credentials = getCredentialsFromContext();
834 request.addHandlerContext(HandlerContextKey.AWS_CREDENTIALS, credentials);
835
836 for (RequestHandler2 requestHandler2 : requestHandler2s) {
837
838 if (requestHandler2 instanceof CredentialsRequestHandler) {
839 ((CredentialsRequestHandler) requestHandler2).setCredentials(credentials);
840 }
841 requestHandler2.beforeRequest(request);
842 }
843 }
844
845
854 private RuntimeException handleInterruptedException(InterruptedException e) {
855 if (e instanceof SdkInterruptedException) {
856 if (((SdkInterruptedException) e).getResponse() != null) {
857 ((SdkInterruptedException) e).getResponse().getHttpResponse().getHttpRequest().abort();
858 }
859 }
860 if (executionContext.getClientExecutionTrackerTask().hasTimeoutExpired()) {
861
862 Thread.interrupted();
863 ClientExecutionTimeoutException exception = new ClientExecutionTimeoutException();
864 reportClientExecutionTimeout(exception);
865 return exception;
866 } else {
867 Thread.currentThread().interrupt();
868 return new AbortedException(e);
869 }
870 }
871
872
882 private RuntimeException handleAbortedException(final AbortedException ae) {
883 if (executionContext.getClientExecutionTrackerTask().hasTimeoutExpired()) {
884
885 Thread.interrupted();
886 ClientExecutionTimeoutException exception = new ClientExecutionTimeoutException();
887 reportClientExecutionTimeout(exception);
888 return exception;
889 } else {
890 Thread.currentThread().interrupt();
891 return ae;
892 }
893 }
894
895 private void reportClientExecutionTimeout(ClientExecutionTimeoutException exception) {
896 if (csmRequestHandler != null) {
897 csmRequestHandler.afterError(request, null, exception);
898 }
899 }
900
901
908 private void checkInterrupted() throws InterruptedException {
909 checkInterrupted(null);
910 }
911
912
921 private void checkInterrupted(Response<?> response) throws InterruptedException {
922 if (Thread.interrupted()) {
923 throw new SdkInterruptedException(response);
924 }
925 }
926
927
930 private void mergeQueryParameters(Map<String, List<String>> params) {
931 Map<String, List<String>> existingParams = request.getParameters();
932 for (Entry<String, List<String>> param : params.entrySet()) {
933 String pName = param.getKey();
934 List<String> pValues = param.getValue();
935 existingParams.put(pName, CollectionUtils.mergeLists(existingParams.get(pName), pValues));
936 }
937 }
938
939
946 private InputStream beforeRequest() {
947 ProgressListener listener = requestConfig.getProgressListener();
948 reportContentLength(listener);
949 if (request.getContent() == null) {
950 return null;
951 }
952 final InputStream content = monitorStreamProgress(listener,
953 buffer(
954 makeResettable(
955 request.getContent())));
956 if (AmazonHttpClient.unreliableTestConfig == null) {
957 return content;
958 }
959 return wrapWithUnreliableStream(content);
960 }
961
962
967 private void reportContentLength(ProgressListener listener) {
968 Map<String, String> headers = request.getHeaders();
969 String contentLengthStr = headers.get("Content-Length");
970 if (contentLengthStr != null) {
971 try {
972 long contentLength = Long.parseLong(contentLengthStr);
973 publishRequestContentLength(listener, contentLength);
974 } catch (NumberFormatException e) {
975 log.warn("Cannot parse the Content-Length header of the request.");
976 }
977 }
978 }
979
980
986 private InputStream makeResettable(InputStream content) {
987 if (!content.markSupported()) {
988
989
990 if (content instanceof FileInputStream) {
991 try {
992
993
994 return new ResettableInputStream((FileInputStream) content);
995 } catch (IOException e) {
996 if (log.isDebugEnabled()) {
997 log.debug("For the record; ignore otherwise", e);
998 }
999 }
1000 }
1001 }
1002 return content;
1003 }
1004
1005
1011 private InputStream buffer(InputStream content) {
1012 if (!content.markSupported()) {
1013 content = new SdkBufferedInputStream(content);
1014 }
1015 return content;
1016 }
1017
1018
1025 private InputStream monitorStreamProgress(ProgressListener listener,
1026 InputStream content) {
1027 return ProgressInputStream.inputStreamForRequest(content, listener);
1028 }
1029
1030
1037 private InputStream wrapWithUnreliableStream(InputStream content) {
1038 return new UnreliableFilterInputStream(content,
1039 unreliableTestConfig.isFakeIOException())
1040 .withBytesReadBeforeException(
1041 unreliableTestConfig.getBytesReadBeforeException())
1042 .withMaxNumErrors(unreliableTestConfig.getMaxNumErrors())
1043 .withResetIntervalBeforeException(
1044 unreliableTestConfig.getResetIntervalBeforeException());
1045 }
1046
1047
1048 private void afterError(Response<?> response,
1049 AmazonClientException e) throws InterruptedException {
1050 for (RequestHandler2 handler2 : requestHandler2s) {
1051 handler2.afterError(request, response, e);
1052 checkInterrupted(response);
1053 }
1054 }
1055
1056 private <T> void afterResponse(Response<T> response) throws InterruptedException {
1057 for (RequestHandler2 handler2 : requestHandler2s) {
1058 handler2.afterResponse(request, response);
1059 checkInterrupted(response);
1060 }
1061 }
1062
1063 private <T> void beforeAttempt(HandlerBeforeAttemptContext context) throws InterruptedException {
1064 for (RequestHandler2 handler2 : requestHandler2s) {
1065 handler2.beforeAttempt(context);
1066 checkInterrupted();
1067 }
1068 }
1069
1070 private <T> void afterAttempt(HandlerAfterAttemptContext context) throws InterruptedException {
1071 for (RequestHandler2 handler2 : requestHandler2s) {
1072 handler2.afterAttempt(context);
1073 checkInterrupted(context.getResponse());
1074 }
1075 }
1076
1077
1080 private Response<Output> executeHelper() throws InterruptedException {
1081
1084 awsRequestMetrics
1085 .addPropertyWith(Field.RequestType, requestConfig.getRequestType())
1086 .addPropertyWith(Field.ServiceName, request.getServiceName())
1087 .addPropertyWith(Field.ServiceEndpoint, request.getEndpoint());
1088
1089
1090 final Map<String, List<String>> originalParameters = new LinkedHashMap<String, List<String>>(request.getParameters());
1091 final Map<String, String> originalHeaders = new HashMap<String, String>(request.getHeaders());
1092
1093 final ExecOneRequestParams execOneParams = new ExecOneRequestParams();
1094 final InputStream originalContent = request.getContent();
1095 if (originalContent != null && originalContent.markSupported()
1096 && !(originalContent instanceof BufferedInputStream)) {
1097
1098 final int readLimit = requestConfig.getRequestClientOptions().getReadLimit();
1099 originalContent.mark(readLimit);
1100 }
1101 awsRequestMetrics.startEvent(AwsClientSideMonitoringMetrics.ApiCallLatency);
1102 while (true) {
1103 checkInterrupted();
1104 if (originalContent instanceof BufferedInputStream && originalContent.markSupported()) {
1105
1106 final int readLimit = requestConfig.getRequestClientOptions().getReadLimit();
1107 originalContent.mark(readLimit);
1108 }
1109 execOneParams.initPerRetry();
1110 URI redirectedURI = execOneParams.redirectedURI;
1111 if (redirectedURI != null) {
1112
1115 String scheme = redirectedURI.getScheme();
1116 String beforeAuthority = scheme == null ? "" : scheme + ":;
1117 String authority = redirectedURI.getAuthority();
1118 String path = redirectedURI.getPath();
1119
1120 request.setEndpoint(URI.create(beforeAuthority + authority));
1121 request.setResourcePath(SdkHttpUtils.urlEncode(path, true));
1122 awsRequestMetrics.addPropertyWith(Field.RedirectLocation,
1123 redirectedURI.toString());
1124
1125 }
1126 if (execOneParams.authRetryParam != null) {
1127 request.setEndpoint(execOneParams.authRetryParam.getEndpointForRetry());
1128 }
1129 awsRequestMetrics.setCounter(Field.RequestCount, execOneParams.requestCount);
1130 if (execOneParams.isRetry()) {
1131 request.setParameters(originalParameters);
1132 request.setHeaders(originalHeaders);
1133 request.setContent(originalContent);
1134 }
1135
1136 Response<Output> response = null;
1137 Exception savedException = null;
1138 boolean thrown = false;
1139 try {
1140 HandlerBeforeAttemptContext beforeAttemptContext = HandlerBeforeAttemptContext.builder()
1141 .withRequest(request)
1142 .build();
1143
1144 beforeAttempt(beforeAttemptContext);
1145 response = executeOneRequest(execOneParams);
1146 savedException = execOneParams.retriedException;
1147
1148 if (response != null) {
1149 return response;
1150 }
1151 } catch (IOException ioe) {
1152 savedException = ioe;
1153 handleRetryableException(execOneParams, ioe);
1154 } catch (InterruptedException ie) {
1155 savedException = ie;
1156 thrown = true;
1157 throw ie;
1158 } catch (RuntimeException e) {
1159 savedException = e;
1160 thrown = true;
1161 throw lastReset(captureExceptionMetrics(e));
1162 } catch (Error e) {
1163 thrown = true;
1164 throw lastReset(captureExceptionMetrics(e));
1165 } finally {
1166
1173 if (!execOneParams.leaveHttpConnectionOpen || thrown) {
1174 if (execOneParams.apacheResponse != null) {
1175 HttpEntity entity = execOneParams.apacheResponse.getEntity();
1176 if (entity != null) {
1177 try {
1178 closeQuietly(entity.getContent(), log);
1179 } catch (IOException e) {
1180 log.warn("Cannot close the response content.", e);
1181 }
1182 }
1183 }
1184 }
1185
1186 HandlerAfterAttemptContext afterAttemptContext = HandlerAfterAttemptContext.builder()
1187 .withRequest(request)
1188 .withResponse(response)
1189 .withException(savedException)
1190 .build();
1191
1192
1196 afterAttempt(afterAttemptContext);
1197 }
1198 }
1199 }
1200
1201 private void handleRetryableException(ExecOneRequestParams execOneParams, Exception e) {
1202 captureExceptionMetrics(e);
1203 awsRequestMetrics.addProperty(Field.AWSRequestID, null);
1204 SdkClientException sdkClientException;
1205 if (!(e instanceof SdkClientException)) {
1206 sdkClientException = new SdkClientException(
1207 "Unable to execute HTTP request: " + e.getMessage(), e);
1208 } else {
1209 sdkClientException = (SdkClientException) e;
1210 }
1211 boolean willRetry = shouldRetry(execOneParams, sdkClientException);
1212 if (log.isTraceEnabled()) {
1213 log.trace(sdkClientException.getMessage() + (willRetry ? " Request will be retried." : ""), e);
1214 } else if (log.isDebugEnabled()) {
1215 log.debug(sdkClientException.getMessage() + (willRetry ? " Request will be retried." : ""));
1216 }
1217 if (!willRetry) {
1218 throw lastReset(sdkClientException);
1219 }
1220
1221 execOneParams.retriedException = sdkClientException;
1222 }
1223
1224
1233 private <T extends Throwable> T lastReset(final T t) {
1234 try {
1235 InputStream content = request.getContent();
1236 if (content != null) {
1237 if (content.markSupported()) {
1238 content.reset();
1239 }
1240 }
1241 } catch (Exception ex) {
1242 log.debug("FYI: failed to reset content inputstream before throwing up", ex);
1243 }
1244 return t;
1245 }
1246
1247
1250 private AWSCredentials getCredentialsFromContext() {
1251 final AWSCredentialsProvider credentialsProvider = executionContext.getCredentialsProvider();
1252
1253 AWSCredentials credentials = null;
1254 if (credentialsProvider != null) {
1255 awsRequestMetrics.startEvent(Field.CredentialsRequestTime);
1256 try {
1257 credentials = credentialsProvider.getCredentials();
1258 } finally {
1259 awsRequestMetrics.endEvent(Field.CredentialsRequestTime);
1260 }
1261 }
1262 return credentials;
1263 }
1264
1265
1268 private Response<Output> executeOneRequest(ExecOneRequestParams execOneParams)
1269 throws IOException, InterruptedException {
1270
1271 if (execOneParams.isRetry()) {
1272 resetRequestInputStream(request, execOneParams.retriedException);
1273 }
1274 checkInterrupted();
1275 if (requestLog.isDebugEnabled()) {
1276 requestLog.debug((execOneParams.isRetry() ? "Retrying " : "Sending ") + "Request: " + request);
1277 }
1278 final AWSCredentials credentials = getCredentialsFromContext();
1279 final ProgressListener listener = requestConfig.getProgressListener();
1280
1281 if (execOneParams.isRetry()) {
1282 pauseBeforeRetry(execOneParams, listener);
1283 }
1284 updateRetryHeaderInfo(request, execOneParams);
1285 sdkRequestHeaderProvider.addSdkRequestRetryHeader(request, execOneParams.requestCount);
1286
1287
1288 execOneParams.newSigner(request, executionContext);
1289 if (execOneParams.signer != null &&
1290 (credentials != null || execOneParams.signer instanceof CanHandleNullCredentials)) {
1291 awsRequestMetrics.startEvent(Field.RequestSigningTime);
1292 try {
1293 if (timeOffset != 0) {
1294
1295
1296
1297
1298
1299
1300
1301
1302 request.setTimeOffset(timeOffset);
1303 }
1304 execOneParams.signer.sign(request, credentials);
1305 } finally {
1306 awsRequestMetrics.endEvent(Field.RequestSigningTime);
1307 }
1308 }
1309
1310 checkInterrupted();
1311 execOneParams.newApacheRequest(httpRequestFactory, request, httpClientSettings);
1312
1313 captureConnectionPoolMetrics();
1314
1315 final HttpClientContext localRequestContext =
1316 ApacheUtils.newClientContext(httpClientSettings, ImmutableMapParameter.of
1317 (AWSRequestMetrics.SIMPLE_NAME, awsRequestMetrics));
1318
1319 execOneParams.resetBeforeHttpRequest();
1320 publishProgress(listener, ProgressEventType.HTTP_REQUEST_STARTED_EVENT);
1321 awsRequestMetrics.startEvent(Field.HttpRequestTime);
1322 awsRequestMetrics.setCounter(Field.RetryCapacityConsumed, retryCapacity.consumedCapacity());
1323
1324
1325 executionContext.getClientExecutionTrackerTask().setCurrentHttpRequest(execOneParams.apacheRequest);
1326 final HttpRequestAbortTaskTracker requestAbortTaskTracker = httpRequestTimer
1327 .startTimer(execOneParams.apacheRequest, getRequestTimeout(requestConfig));
1328
1329 try {
1330 execOneParams.apacheResponse = httpClient.execute(execOneParams.apacheRequest, localRequestContext);
1331 if (shouldBufferHttpEntity(responseHandler.needsConnectionLeftOpen(),
1332 executionContext,
1333 execOneParams,
1334 requestAbortTaskTracker)) {
1335 execOneParams.apacheResponse
1336 .setEntity(new BufferedHttpEntity(
1337 execOneParams.apacheResponse.getEntity()));
1338 }
1339 } catch (IOException ioe) {
1340
1341 if (executionContext.getClientExecutionTrackerTask().hasTimeoutExpired()) {
1342 throw new InterruptedException();
1343 } else if (requestAbortTaskTracker.httpRequestAborted()) {
1344
1345
1346 if (ioe instanceof RequestAbortedException) {
1347 Thread.interrupted();
1348 }
1349 throw new HttpRequestTimeoutException(ioe);
1350 } else {
1351 throw ioe;
1352 }
1353 } finally {
1354 requestAbortTaskTracker.cancelTask();
1355 awsRequestMetrics.endEvent(Field.HttpRequestTime);
1356 }
1357
1358 publishProgress(listener, ProgressEventType.HTTP_REQUEST_COMPLETED_EVENT);
1359 final StatusLine statusLine = execOneParams.apacheResponse.getStatusLine();
1360 final int statusCode = statusLine == null ? -1 : statusLine.getStatusCode();
1361
1362
1363 clockSkewAdjuster.updateEstimatedSkew(new AdjustmentRequest()
1364 .clientRequest(request)
1365 .serviceResponse(execOneParams.apacheResponse));
1366
1367 if (ApacheUtils.isRequestSuccessful(execOneParams.apacheResponse)) {
1368 return handleSuccessResponse(execOneParams, localRequestContext, statusCode);
1369 }
1370
1371 return handleServiceErrorResponse(execOneParams, localRequestContext, statusCode);
1372 }
1373
1374
1377 private Response<Output> handleServiceErrorResponse(ExecOneRequestParams execOneParams, HttpClientContext localRequestContext, int statusCode) throws IOException, InterruptedException {
1378 if (isTemporaryRedirect(execOneParams.apacheResponse)) {
1379
1384 Header[] locationHeaders = execOneParams.apacheResponse.getHeaders("location");
1385 String redirectedLocation = locationHeaders[0].getValue();
1386 if (log.isDebugEnabled()) {
1387 log.debug("Redirecting to: " + redirectedLocation);
1388 }
1389 execOneParams.redirectedURI = URI.create(redirectedLocation);
1390 awsRequestMetrics.addPropertyWith(Field.StatusCode, statusCode)
1391 .addPropertyWith(Field.AWSRequestID, null);
1392 return null;
1393 }
1394 execOneParams.leaveHttpConnectionOpen = errorResponseHandler.needsConnectionLeftOpen();
1395 final SdkBaseException exception = handleErrorResponse(execOneParams.apacheRequest,
1396 execOneParams.apacheResponse,
1397 localRequestContext);
1398
1399 ClockSkewAdjustment clockSkewAdjustment =
1400 clockSkewAdjuster.getAdjustment(new AdjustmentRequest().exception(exception)
1401 .clientRequest(request)
1402 .serviceResponse(execOneParams.apacheResponse));
1403
1404 if (clockSkewAdjustment.shouldAdjustForSkew()) {
1405 timeOffset = clockSkewAdjustment.inSeconds();
1406 request.setTimeOffset(timeOffset);
1407 SDKGlobalTime.setGlobalTimeOffset(timeOffset);
1408 }
1409
1410
1411
1412 execOneParams.authRetryParam = null;
1413 AuthErrorRetryStrategy authRetry = executionContext.getAuthErrorRetryStrategy();
1414 if (authRetry != null && exception instanceof AmazonServiceException) {
1415 HttpResponse httpResponse = ApacheUtils.createResponse(request, execOneParams.apacheRequest, execOneParams.apacheResponse, localRequestContext);
1416 execOneParams.authRetryParam = authRetry
1417 .shouldRetryWithAuthParam(request, httpResponse, (AmazonServiceException) exception);
1418 }
1419 if (execOneParams.authRetryParam == null && !shouldRetry(execOneParams, exception)) {
1420 throw exception;
1421 }
1422
1423
1424
1425 if (RetryUtils.isThrottlingException(exception)) {
1426 awsRequestMetrics.incrementCounterWith(Field.ThrottleException)
1427 .addProperty(Field.ThrottleException, exception);
1428 }
1429
1430 execOneParams.retriedException = exception;
1431
1432 return null;
1433 }
1434
1435
1438 private Response<Output> handleSuccessResponse(ExecOneRequestParams execOneParams, HttpClientContext localRequestContext, int statusCode) throws IOException, InterruptedException {
1439 awsRequestMetrics.addProperty(Field.StatusCode, statusCode);
1440
1444 execOneParams.leaveHttpConnectionOpen = responseHandler.needsConnectionLeftOpen();
1445 HttpResponse httpResponse = ApacheUtils.createResponse(request, execOneParams.apacheRequest, execOneParams.apacheResponse, localRequestContext);
1446 Output response = handleResponse(httpResponse);
1447
1448
1453 if (execOneParams.isRetry() && executionContext.retryCapacityConsumed()) {
1454 retryCapacity.release(execOneParams.lastConsumedRetryCapacity);
1455 } else {
1456 retryCapacity.release();
1457 }
1458 return new Response<Output>(response, httpResponse);
1459 }
1460
1461
1469 private void resetRequestInputStream(final Request<?> request, SdkBaseException retriedException)
1470 throws ResetException {
1471 InputStream requestInputStream = request.getContent();
1472 if (requestInputStream != null) {
1473 if (requestInputStream.markSupported()) {
1474 try {
1475 requestInputStream.reset();
1476 } catch (IOException ex) {
1477 ResetException resetException = new ResetException(
1478 "The request to the service failed with a retryable reason, but resetting the request input " +
1479 "stream has failed. See exception.getExtraInfo or debug-level logging for the original failure " +
1480 "that caused this retry.",
1481 ex);
1482 resetException.setExtraInfo(retriedException.getMessage());
1483 throw resetException;
1484 }
1485 }
1486 }
1487 }
1488
1489
1492 private boolean shouldBufferHttpEntity(final boolean needsConnectionLeftOpen,
1493 final ExecutionContext execContext,
1494 ExecOneRequestParams execParams,
1495 final HttpRequestAbortTaskTracker requestAbortTaskTracker) {
1496 return (execContext.getClientExecutionTrackerTask().isEnabled() ||
1497 requestAbortTaskTracker.isEnabled())
1498 && !needsConnectionLeftOpen && execParams.apacheResponse.getEntity() != null;
1499 }
1500
1501
1502
1505 private void captureConnectionPoolMetrics() {
1506 if (awsRequestMetrics.isEnabled() &&
1507 httpClient.getHttpClientConnectionManager() instanceof
1508 ConnPoolControl<?>) {
1509 final PoolStats stats = ((ConnPoolControl<?>) httpClient
1510 .getHttpClientConnectionManager()).getTotalStats();
1511
1512 awsRequestMetrics
1513 .withCounter(HttpClientPoolAvailableCount, stats.getAvailable())
1514 .withCounter(HttpClientPoolLeasedCount, stats.getLeased())
1515 .withCounter(HttpClientPoolPendingCount, stats.getPending());
1516 }
1517
1518 }
1519
1520
1523 private <T extends Throwable> T captureExceptionMetrics(T t) {
1524 awsRequestMetrics.incrementCounterWith(Field.Exception)
1525 .addProperty(Field.Exception, t);
1526 if (t instanceof AmazonServiceException) {
1527 AmazonServiceException ase = (AmazonServiceException) t;
1528 if (RetryUtils.isThrottlingException(ase)) {
1529 awsRequestMetrics.incrementCounterWith(Field.ThrottleException)
1530 .addProperty(Field.ThrottleException, ase);
1531 }
1532 }
1533 return t;
1534 }
1535
1536
1540 private void setSdkTransactionId(Request<?> request) {
1541 request.addHeader(HEADER_SDK_TRANSACTION_ID,
1542 new UUID(random.nextLong(), random.nextLong()).toString());
1543 }
1544
1545
1548 private void setUserAgent(Request<?> request) {
1549 RequestClientOptions opts = requestConfig.getRequestClientOptions();
1550 if (opts != null) {
1551 request.addHeader(HEADER_USER_AGENT, RuntimeHttpUtils
1552 .getUserAgent(config, opts.getClientMarker(Marker.USER_AGENT)));
1553 } else {
1554 request.addHeader(HEADER_USER_AGENT, RuntimeHttpUtils.getUserAgent(config, null));
1555 }
1556 }
1557
1558
1565 private void updateRetryHeaderInfo(Request<?> request,
1566 ExecOneRequestParams execOneRequestParams) {
1567 int availableRetryCapacity = retryCapacity.availableCapacity();
1568
1569 String headerValue = String.format("%s/%s/%s",
1570 execOneRequestParams.requestCount - 1,
1571 execOneRequestParams.lastBackoffDelay,
1572 availableRetryCapacity >= 0 ?
1573 availableRetryCapacity : "");
1574
1575 request.addHeader(HEADER_SDK_RETRY_INFO, headerValue);
1576 }
1577
1578
1585 private boolean shouldRetry(ExecOneRequestParams params, SdkBaseException exception) {
1586 final int retriesAttempted = params.requestCount - 1;
1587 final HttpRequestBase method = params.apacheRequest;
1588
1589
1590 if (method instanceof HttpEntityEnclosingRequest) {
1591 HttpEntity entity = ((HttpEntityEnclosingRequest) method).getEntity();
1592 if (entity != null && !entity.isRepeatable()) {
1593 if (log.isDebugEnabled()) {
1594 log.debug("Entity not repeatable");
1595 }
1596 return false;
1597 }
1598 }
1599
1600 RetryPolicyContext context = RetryPolicyContext.builder()
1601 .request(request)
1602 .originalRequest(requestConfig.getOriginalRequest())
1603 .exception(exception)
1604 .retriesAttempted(retriesAttempted)
1605 .httpStatusCode(params.getStatusCode())
1606 .build();
1607
1608 if (!acquireRetryCapacity(context, params)) {
1609 return false;
1610 }
1611
1612
1613
1614 if (!retryPolicy.shouldRetry(context)) {
1615
1616 if (executionContext.retryCapacityConsumed()) {
1617 retryCapacity.release(THROTTLED_RETRY_COST);
1618 }
1619 reportMaxRetriesExceededIfRetryable(context);
1620 return false;
1621 }
1622
1623 return true;
1624 }
1625
1626
1631 private boolean acquireRetryCapacity(RetryPolicyContext context, ExecOneRequestParams params) {
1632 switch (retryMode) {
1633 case LEGACY:
1634 return legacyAcquireRetryCapacity(context, params);
1635 case STANDARD:
1636 return standardAcquireRetryCapacity(context, params);
1637 default:
1638 throw new IllegalStateException("Unsupported retry mode: " + retryMode);
1639 }
1640 }
1641
1642 private boolean standardAcquireRetryCapacity(RetryPolicyContext context, ExecOneRequestParams params) {
1643 SdkBaseException exception = context.exception();
1644 if (isTimeoutError(exception)) {
1645 return doAcquireCapacity(context, TIMEOUT_RETRY_COST, params);
1646 }
1647
1648 return doAcquireCapacity(context, THROTTLED_RETRY_COST, params);
1649 }
1650
1651 private boolean isTimeoutError(SdkBaseException exception) {
1652 Throwable cause = exception.getCause();
1653 return cause instanceof ConnectTimeoutException || cause instanceof SocketTimeoutException;
1654 }
1655
1656 private boolean legacyAcquireRetryCapacity(RetryPolicyContext context, ExecOneRequestParams params) {
1657
1658 if (!RetryUtils.isThrottlingException(context.exception())) {
1659
1660 return doAcquireCapacity(context, THROTTLED_RETRY_COST, params);
1661 } else {
1662 return true;
1663 }
1664 }
1665
1666 private boolean doAcquireCapacity(RetryPolicyContext context, int retryCost, ExecOneRequestParams params) {
1667
1668
1669 if (!retryCapacity.acquire(retryCost)) {
1670 awsRequestMetrics.incrementCounter(ThrottledRetryCount);
1671 reportMaxRetriesExceededIfRetryable(context);
1672 return false;
1673 }
1674 params.lastConsumedRetryCapacity = retryCost;
1675 executionContext.markRetryCapacityConsumed();
1676 return true;
1677 }
1678
1679 private void reportMaxRetriesExceededIfRetryable(RetryPolicyContext context) {
1680 if (retryPolicy instanceof RetryPolicyAdapter && ((RetryPolicyAdapter) retryPolicy).isRetryable(context)) {
1681 awsRequestMetrics.addPropertyWith(MaxRetriesExceeded, true);
1682 }
1683 }
1684
1685
1693 @SuppressWarnings("deprecation")
1694 private Output handleResponse(HttpResponse httpResponse) throws IOException,
1695 InterruptedException {
1696 ProgressListener listener = requestConfig.getProgressListener();
1697 try {
1698
1701 CountingInputStream countingInputStream = null;
1702 InputStream is = httpResponse.getContent();
1703 if (is != null) {
1704 if (System.getProperty(PROFILING_SYSTEM_PROPERTY) != null) {
1705 is = countingInputStream = new CountingInputStream(is);
1706 httpResponse.setContent(is);
1707 }
1708 httpResponse.setContent(ProgressInputStream.inputStreamForResponse(is, listener));
1709 }
1710 Map<String, String> headers = httpResponse.getHeaders();
1711 String s = headers.get("Content-Length");
1712 if (s != null) {
1713 try {
1714 long contentLength = Long.parseLong(s);
1715 publishResponseContentLength(listener, contentLength);
1716 } catch (NumberFormatException e) {
1717 log.warn("Cannot parse the Content-Length header of the response.");
1718 }
1719 }
1720
1721 Output awsResponse;
1722 awsRequestMetrics.startEvent(Field.ResponseProcessingTime);
1723 publishProgress(listener, ProgressEventType.HTTP_RESPONSE_STARTED_EVENT);
1724 try {
1725 awsResponse = responseHandler
1726 .handle(beforeUnmarshalling(httpResponse));
1727 } finally {
1728 awsRequestMetrics.endEvent(Field.ResponseProcessingTime);
1729 }
1730 publishProgress(listener, ProgressEventType.HTTP_RESPONSE_COMPLETED_EVENT);
1731
1732 if (countingInputStream != null) {
1733 awsRequestMetrics
1734 .setCounter(Field.BytesProcessed, countingInputStream.getByteCount());
1735 }
1736 return awsResponse;
1737 } catch (CRC32MismatchException e) {
1738 throw e;
1739 } catch (IOException e) {
1740 throw e;
1741 } catch (AmazonClientException e) {
1742 throw e;
1743 } catch (InterruptedException e) {
1744 throw e;
1745 } catch (Exception e) {
1746 String errorMessage =
1747 "Unable to unmarshall response (" + e.getMessage() + "). Response Code: "
1748 + httpResponse.getStatusCode() + ", Response Text: " +
1749 httpResponse.getStatusText();
1750 throw new SdkClientException(errorMessage, e);
1751 }
1752 }
1753
1754
1761 private HttpResponse beforeUnmarshalling(HttpResponse origHttpResponse) {
1762 HttpResponse toReturn = origHttpResponse;
1763 for (RequestHandler2 requestHandler : requestHandler2s) {
1764 toReturn = requestHandler.beforeUnmarshalling(request, toReturn);
1765 }
1766 return toReturn;
1767 }
1768
1769
1776 private SdkBaseException handleErrorResponse(HttpRequestBase method,
1777 final org.apache.http.HttpResponse apacheHttpResponse,
1778 final HttpContext context)
1779 throws IOException, InterruptedException {
1780 final StatusLine statusLine = apacheHttpResponse.getStatusLine();
1781 final int statusCode;
1782 final String reasonPhrase;
1783 if (statusLine == null) {
1784 statusCode = -1;
1785 reasonPhrase = null;
1786 } else {
1787 statusCode = statusLine.getStatusCode();
1788 reasonPhrase = statusLine.getReasonPhrase();
1789 }
1790 HttpResponse response = ApacheUtils.createResponse(request, method, apacheHttpResponse, context);
1791 SdkBaseException exception;
1792 try {
1793 exception = errorResponseHandler.handle(response);
1794 if (requestLog.isDebugEnabled()) {
1795 requestLog.debug("Received error response: " + exception);
1796 }
1797 } catch (InterruptedException e) {
1798 throw e;
1799 } catch (Exception e) {
1800 if (e instanceof IOException) {
1801 throw (IOException) e;
1802 } else {
1803 String errorMessage = "Unable to unmarshall error response (" + e.getMessage() +
1804 "). Response Code: "
1805 + (statusLine == null ? "None" : statusCode) +
1806 ", Response Text: " + reasonPhrase;
1807 throw new SdkClientException(errorMessage, e);
1808 }
1809 }
1810
1811 exception.fillInStackTrace();
1812 return exception;
1813 }
1814
1815
1818 private void pauseBeforeRetry(ExecOneRequestParams execOneParams,
1819 final ProgressListener listener) throws InterruptedException {
1820 publishProgress(listener, ProgressEventType.CLIENT_REQUEST_RETRY_EVENT);
1821
1822 awsRequestMetrics.startEvent(Field.RetryPauseTime);
1823 try {
1824 doPauseBeforeRetry(execOneParams);
1825 } finally {
1826 awsRequestMetrics.endEvent(Field.RetryPauseTime);
1827 }
1828 }
1829
1830
1833 private void doPauseBeforeRetry(ExecOneRequestParams execOneParams) throws InterruptedException {
1834 final int retriesAttempted = execOneParams.requestCount - 2;
1835 RetryPolicyContext context = RetryPolicyContext.builder()
1836 .request(request)
1837 .originalRequest(requestConfig.getOriginalRequest())
1838 .retriesAttempted(retriesAttempted)
1839 .exception(execOneParams.retriedException)
1840 .build();
1841
1842 if (context.exception() != null) {
1843 long delay = retryPolicy.computeDelayBeforeNextRetry(context);
1844 execOneParams.lastBackoffDelay = delay;
1845
1846 if (log.isDebugEnabled()) {
1847 log.debug("Retriable error detected, " + "will retry in " + delay +
1848 "ms, attempt number: " + retriesAttempted);
1849 }
1850 Thread.sleep(delay);
1851 }
1852 }
1853
1854
1861 private int getRequestTimeout(RequestConfig requestConfig) {
1862 if (requestConfig.getRequestTimeout() != null) {
1863 return requestConfig.getRequestTimeout();
1864 } else {
1865 return config.getRequestTimeout();
1866 }
1867 }
1868
1869
1876 private int getClientExecutionTimeout(RequestConfig requestConfig) {
1877 if (requestConfig.getClientExecutionTimeout() != null) {
1878 return requestConfig.getClientExecutionTimeout();
1879 } else {
1880 return config.getClientExecutionTimeout();
1881 }
1882 }
1883
1884
1887 private class ExecOneRequestParams {
1888 int requestCount;
1889
1892 long lastBackoffDelay = 0;
1893 SdkBaseException retriedException;
1894 HttpRequestBase apacheRequest;
1895 org.apache.http.HttpResponse apacheResponse;
1896 URI redirectedURI;
1897 AuthRetryParameters authRetryParam;
1898 int lastConsumedRetryCapacity;
1899
1905 boolean leaveHttpConnectionOpen;
1906 private Signer signer;
1907 private URI signerURI;
1908
1909 boolean isRetry() {
1910 return requestCount > 1 || redirectedURI != null || authRetryParam != null;
1911 }
1912
1913 void initPerRetry() {
1914 requestCount++;
1915 apacheRequest = null;
1916 apacheResponse = null;
1917 leaveHttpConnectionOpen = false;
1918 }
1919
1920 void newSigner(final Request<?> request, final ExecutionContext execContext) {
1921 final SignerProviderContext.Builder signerProviderContext = SignerProviderContext
1922 .builder()
1923 .withRequest(request)
1924 .withRequestConfig(requestConfig);
1925 if (authRetryParam != null) {
1926 signerURI = authRetryParam.getEndpointForRetry();
1927 signer = authRetryParam.getSignerForRetry();
1928
1929 execContext.setSigner(signer);
1930 } else if (redirectedURI != null && !redirectedURI.equals(signerURI)) {
1931 signerURI = redirectedURI;
1932 signer = execContext.getSigner(signerProviderContext
1933 .withUri(signerURI)
1934 .withIsRedirect(true)
1935 .build());
1936
1937 if (signer instanceof AWS4Signer) {
1938 String regionName = ((AWS4Signer) signer).getRegionName();
1939 if (regionName != null) {
1940 request.addHandlerContext(HandlerContextKey.SIGNING_REGION, regionName);
1941 }
1942 }
1943 } else if (signer == null) {
1944 signerURI = request.getEndpoint();
1945 signer = execContext
1946 .getSigner(signerProviderContext.withUri(signerURI).build());
1947 }
1948 }
1949
1950
1953 HttpRequestBase newApacheRequest(
1954 final HttpRequestFactory<HttpRequestBase> httpRequestFactory,
1955 final Request<?> request,
1956 final HttpClientSettings options) throws IOException {
1957
1958 apacheRequest = httpRequestFactory.create(request, options);
1959 if (redirectedURI != null) {
1960 apacheRequest.setURI(redirectedURI);
1961 }
1962 return apacheRequest;
1963 }
1964
1965 void resetBeforeHttpRequest() {
1966 retriedException = null;
1967 authRetryParam = null;
1968 redirectedURI = null;
1969 }
1970
1971 private Integer getStatusCode() {
1972 if (apacheResponse == null || apacheResponse.getStatusLine() == null) {
1973 return null;
1974 }
1975 return apacheResponse.getStatusLine().getStatusCode();
1976 }
1977 }
1978 }
1979 }
1980