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 org.assertj.core.api.Assertions.assertThat; 20 import static org.assertj.core.api.Assertions.fail; 21 22 import java.io.ByteArrayInputStream; 23 import java.net.URI; 24 import java.nio.charset.StandardCharsets; 25 import java.util.stream.Stream; 26 import org.junit.jupiter.api.BeforeEach; 27 import org.junit.jupiter.api.Test; 28 import software.amazon.awssdk.core.Protocol; 29 import software.amazon.awssdk.core.SdkProtocolMetadata; 30 import software.amazon.awssdk.core.http.ExecutionContext; 31 import software.amazon.awssdk.core.http.NoopTestRequest; 32 import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 33 import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; 34 import software.amazon.awssdk.core.internal.http.RequestExecutionContext; 35 import software.amazon.awssdk.http.ContentStreamProvider; 36 import software.amazon.awssdk.http.SdkHttpFullRequest; 37 import software.amazon.awssdk.http.SdkHttpMethod; 38 import software.amazon.awssdk.utils.IoUtils; 39 40 public class QueryParametersToBodyStageTest { 41 public static final URI HTTP_LOCALHOST = URI.create("http://localhost:8080"); 42 private QueryParametersToBodyStage stage; 43 private SdkHttpFullRequest.Builder requestBuilder; 44 45 @BeforeEach setup()46 public void setup() { 47 stage = new QueryParametersToBodyStage(); 48 requestBuilder = SdkHttpFullRequest.builder() 49 .protocol(Protocol.HTTPS.toString()) 50 .method(SdkHttpMethod.POST) 51 .putRawQueryParameter("key", singletonList("value")) 52 .uri(HTTP_LOCALHOST); 53 } 54 verifyParametersMovedToBody(SdkHttpFullRequest output)55 private void verifyParametersMovedToBody(SdkHttpFullRequest output) throws Exception { 56 assertThat(output.rawQueryParameters()).hasSize(0); 57 assertThat(output.headers()) 58 .containsKey("Content-Length") 59 .containsEntry("Content-Type", singletonList("application/x-www-form-urlencoded; charset=utf-8")); 60 assertThat(output.contentStreamProvider()).isNotEmpty(); 61 String bodyContent = new String(IoUtils.toByteArray(output.contentStreamProvider().get().newStream())); 62 assertThat(bodyContent).isEqualTo("key=value"); 63 64 } 65 verifyParametersUnaltered(SdkHttpFullRequest output, int numOfParams)66 private void verifyParametersUnaltered(SdkHttpFullRequest output, int numOfParams) { 67 assertThat(output.rawQueryParameters()).hasSize(numOfParams); 68 assertThat(output.headers()).isEmpty(); 69 } 70 71 @Test postRequestWithNoBody_queryProtocol_parametersAreMovedToTheBody()72 public void postRequestWithNoBody_queryProtocol_parametersAreMovedToTheBody() throws Exception { 73 RequestExecutionContext requestExecutionContext = createRequestExecutionContext("query"); 74 SdkHttpFullRequest output = stage.execute(requestBuilder, requestExecutionContext).build(); 75 76 verifyParametersMovedToBody(output); 77 } 78 79 @Test postRequestWithNoBody_ec2Protocol_parametersAreMovedToTheBody()80 public void postRequestWithNoBody_ec2Protocol_parametersAreMovedToTheBody() throws Exception { 81 RequestExecutionContext requestExecutionContext = createRequestExecutionContext("ec2"); 82 SdkHttpFullRequest output = stage.execute(requestBuilder, requestExecutionContext).build(); 83 84 verifyParametersMovedToBody(output); 85 } 86 87 @Test postRequestWithNoBody_nonQueryProtocol_isUnaltered()88 public void postRequestWithNoBody_nonQueryProtocol_isUnaltered() throws Exception { 89 RequestExecutionContext requestExecutionContext = createRequestExecutionContext("json"); 90 SdkHttpFullRequest output = stage.execute(requestBuilder, requestExecutionContext).build(); 91 92 int numOfParams = 1; 93 verifyParametersUnaltered(output, numOfParams); 94 assertThat(output.contentStreamProvider()).isEmpty(); 95 } 96 97 @Test nonPostRequestsWithNoBodyAreUnaltered()98 public void nonPostRequestsWithNoBodyAreUnaltered() { 99 Stream.of(SdkHttpMethod.values()) 100 .filter(m -> !m.equals(SdkHttpMethod.POST)) 101 .forEach(method -> { 102 try { 103 nonPostRequestsUnaltered(method); 104 } catch (Exception e) { 105 fail("Exception thrown during stage execution"); 106 } 107 }); 108 } 109 110 @Test postWithContentIsUnaltered()111 public void postWithContentIsUnaltered() throws Exception { 112 byte[] contentBytes = "hello".getBytes(StandardCharsets.UTF_8); 113 ContentStreamProvider contentProvider = () -> new ByteArrayInputStream(contentBytes); 114 115 requestBuilder = requestBuilder.contentStreamProvider(contentProvider); 116 RequestExecutionContext requestExecutionContext = createRequestExecutionContext("query"); 117 SdkHttpFullRequest output = stage.execute(requestBuilder, requestExecutionContext).build(); 118 119 int numOfParams = 1; 120 verifyParametersUnaltered(output, numOfParams); 121 assertThat(IoUtils.toByteArray(output.contentStreamProvider().get().newStream())).isEqualTo(contentBytes); 122 } 123 124 @Test onlyAlterRequestsIfParamsArePresent()125 public void onlyAlterRequestsIfParamsArePresent() throws Exception { 126 requestBuilder = requestBuilder.clearQueryParameters(); 127 RequestExecutionContext requestExecutionContext = createRequestExecutionContext("query"); 128 SdkHttpFullRequest output = stage.execute(requestBuilder, requestExecutionContext).build(); 129 130 int numOfParams = 0; 131 verifyParametersUnaltered(output, numOfParams); 132 assertThat(output.contentStreamProvider()).isEmpty(); 133 } 134 nonPostRequestsUnaltered(SdkHttpMethod method)135 private void nonPostRequestsUnaltered(SdkHttpMethod method) throws Exception { 136 requestBuilder = requestBuilder.method(method); 137 RequestExecutionContext requestExecutionContext = createRequestExecutionContext("query"); 138 SdkHttpFullRequest output = stage.execute(requestBuilder, requestExecutionContext).build(); 139 140 int numOfParams = 1; 141 verifyParametersUnaltered(output, numOfParams); 142 assertThat(output.contentStreamProvider()).isEmpty(); 143 } 144 createRequestExecutionContext(String serviceProtocol)145 private RequestExecutionContext createRequestExecutionContext(String serviceProtocol) { 146 ExecutionAttributes executionAttributes = ExecutionAttributes.builder() 147 .put(SdkInternalExecutionAttribute.PROTOCOL_METADATA, 148 new TestProtocolMetadata(serviceProtocol)) 149 .build(); 150 ExecutionContext executionContext = ExecutionContext.builder() 151 .executionAttributes(executionAttributes) 152 .build(); 153 return RequestExecutionContext.builder() 154 .originalRequest(NoopTestRequest.builder().build()) 155 .executionContext(executionContext) 156 .build(); 157 } 158 159 private final class TestProtocolMetadata implements SdkProtocolMetadata { 160 161 private String serviceProtocol; 162 TestProtocolMetadata(String serviceProtocol)163 TestProtocolMetadata(String serviceProtocol) { 164 this.serviceProtocol = serviceProtocol; 165 } 166 167 @Override serviceProtocol()168 public String serviceProtocol() { 169 return serviceProtocol; 170 } 171 } 172 } 173