• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.core.internal.http.pipeline.stages;
17 
18 import static java.util.Collections.singletonList;
19 import static software.amazon.awssdk.utils.StringUtils.lowerCase;
20 
21 import java.io.ByteArrayInputStream;
22 import java.nio.charset.StandardCharsets;
23 import software.amazon.awssdk.annotations.SdkInternalApi;
24 import software.amazon.awssdk.core.SdkProtocolMetadata;
25 import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
26 import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
27 import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline;
28 import software.amazon.awssdk.http.SdkHttpFullRequest;
29 import software.amazon.awssdk.http.SdkHttpMethod;
30 
31 /**
32  * Modifies an HTTP request by moving query parameters to the body under the following conditions:
33  * - It is a POST request
34  * - There is no content stream provider
35  * - There are query parameters to transfer
36  */
37 @SdkInternalApi
38 public class QueryParametersToBodyStage implements MutableRequestToRequestPipeline {
39 
40     private static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=" +
41                                                        lowerCase(StandardCharsets.UTF_8.toString());
42 
43     @Override
execute(SdkHttpFullRequest.Builder request, RequestExecutionContext context)44     public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, RequestExecutionContext context)
45             throws Exception {
46 
47         if (shouldPutParamsInBody(request.build(), context)) {
48             return changeQueryParametersToFormData(request.build()).toBuilder();
49         }
50         return request;
51     }
52 
shouldPutParamsInBody(SdkHttpFullRequest request, RequestExecutionContext context)53     private boolean shouldPutParamsInBody(SdkHttpFullRequest request, RequestExecutionContext context) {
54         SdkProtocolMetadata protocolMetadata =
55             context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.PROTOCOL_METADATA);
56         if (protocolMetadata == null) {
57             return false;
58         }
59         String protocol = protocolMetadata.serviceProtocol();
60         boolean isQueryProtocol = "query".equalsIgnoreCase(protocol) || "ec2".equalsIgnoreCase(protocol);
61 
62         return isQueryProtocol &&
63                request.method() == SdkHttpMethod.POST &&
64                !request.contentStreamProvider().isPresent() &&
65                request.numRawQueryParameters() > 0;
66     }
67 
changeQueryParametersToFormData(SdkHttpFullRequest request)68     private SdkHttpFullRequest changeQueryParametersToFormData(SdkHttpFullRequest request) {
69         byte[] params = request.encodedQueryParametersAsFormData().orElse("")
70                              .getBytes(StandardCharsets.UTF_8);
71 
72         return request.toBuilder().clearQueryParameters()
73                     .contentStreamProvider(() -> new ByteArrayInputStream(params))
74                     .putHeader("Content-Length", singletonList(String.valueOf(params.length)))
75                     .putHeader("Content-Type", singletonList(DEFAULT_CONTENT_TYPE))
76                     .build();
77     }
78 }
79