• 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.http;
17 
18 import static java.util.Collections.emptyList;
19 import static java.util.Collections.unmodifiableList;
20 
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.function.BiConsumer;
28 import java.util.function.Consumer;
29 import software.amazon.awssdk.annotations.Immutable;
30 import software.amazon.awssdk.annotations.SdkInternalApi;
31 import software.amazon.awssdk.internal.http.LowCopyListMap;
32 import software.amazon.awssdk.utils.CollectionUtils;
33 import software.amazon.awssdk.utils.StringUtils;
34 import software.amazon.awssdk.utils.ToString;
35 import software.amazon.awssdk.utils.Validate;
36 import software.amazon.awssdk.utils.http.SdkHttpUtils;
37 
38 /**
39  * Internal implementation of {@link SdkHttpFullRequest}, buildable via {@link SdkHttpFullRequest#builder()}. Provided to HTTP
40  * implementation to execute a request.
41  */
42 @SdkInternalApi
43 @Immutable
44 final class DefaultSdkHttpFullRequest implements SdkHttpFullRequest {
45     private final String protocol;
46     private final String host;
47     private final Integer port;
48     private final String path;
49     private final LowCopyListMap.ForBuildable queryParameters;
50     private final LowCopyListMap.ForBuildable headers;
51     private final SdkHttpMethod httpMethod;
52     private final ContentStreamProvider contentStreamProvider;
53 
DefaultSdkHttpFullRequest(Builder builder)54     private DefaultSdkHttpFullRequest(Builder builder) {
55         this.protocol = standardizeProtocol(builder.protocol);
56         this.host = Validate.paramNotNull(builder.host, "host");
57         this.port = standardizePort(builder.port);
58         this.path = standardizePath(builder.path);
59         this.httpMethod = Validate.paramNotNull(builder.httpMethod, "method");
60         this.contentStreamProvider = builder.contentStreamProvider;
61         this.queryParameters = builder.queryParameters.forBuildable();
62         this.headers = builder.headers.forBuildable();
63     }
64 
standardizeProtocol(String protocol)65     private String standardizeProtocol(String protocol) {
66         Validate.paramNotNull(protocol, "protocol");
67 
68         String standardizedProtocol = StringUtils.lowerCase(protocol);
69         Validate.isTrue(standardizedProtocol.equals("http") || standardizedProtocol.equals("https"),
70                         "Protocol must be 'http' or 'https', but was %s", protocol);
71 
72         return standardizedProtocol;
73     }
74 
standardizePath(String path)75     private String standardizePath(String path) {
76         if (StringUtils.isEmpty(path)) {
77             return "";
78         }
79 
80         StringBuilder standardizedPath = new StringBuilder();
81 
82         // Path must always start with '/'
83         if (!path.startsWith("/")) {
84             standardizedPath.append('/');
85         }
86 
87         standardizedPath.append(path);
88 
89         return standardizedPath.toString();
90     }
91 
standardizePort(Integer port)92     private Integer standardizePort(Integer port) {
93         Validate.isTrue(port == null || port >= -1,
94                         "Port must be positive (or null/-1 to indicate no port), but was '%s'", port);
95 
96         if (port != null && port == -1) {
97             return null;
98         }
99 
100         return port;
101     }
102 
103     @Override
protocol()104     public String protocol() {
105         return protocol;
106     }
107 
108     @Override
host()109     public String host() {
110         return host;
111     }
112 
113     @Override
port()114     public int port() {
115         return Optional.ofNullable(port).orElseGet(() -> SdkHttpUtils.standardPort(protocol()));
116     }
117 
118     @Override
headers()119     public Map<String, List<String>> headers() {
120         return headers.forExternalRead();
121     }
122 
123     @Override
matchingHeaders(String header)124     public List<String> matchingHeaders(String header) {
125         return unmodifiableList(headers.forInternalRead().getOrDefault(header, emptyList()));
126     }
127 
128     @Override
firstMatchingHeader(String headerName)129     public Optional<String> firstMatchingHeader(String headerName) {
130         List<String> headers = this.headers.forInternalRead().get(headerName);
131         if (headers == null || headers.isEmpty()) {
132             return Optional.empty();
133         }
134 
135         String header = headers.get(0);
136         if (StringUtils.isEmpty(header)) {
137             return Optional.empty();
138         }
139 
140         return Optional.of(header);
141     }
142 
143     @Override
firstMatchingHeader(Collection<String> headersToFind)144     public Optional<String> firstMatchingHeader(Collection<String> headersToFind) {
145         for (String headerName : headersToFind) {
146             Optional<String> header = firstMatchingHeader(headerName);
147             if (header.isPresent()) {
148                 return header;
149             }
150         }
151 
152         return Optional.empty();
153     }
154 
155     @Override
forEachHeader(BiConsumer<? super String, ? super List<String>> consumer)156     public void forEachHeader(BiConsumer<? super String, ? super List<String>> consumer) {
157         headers.forInternalRead().forEach((k, v) -> consumer.accept(k, Collections.unmodifiableList(v)));
158     }
159 
160     @Override
forEachRawQueryParameter(BiConsumer<? super String, ? super List<String>> consumer)161     public void forEachRawQueryParameter(BiConsumer<? super String, ? super List<String>> consumer) {
162         queryParameters.forInternalRead().forEach((k, v) -> consumer.accept(k, Collections.unmodifiableList(v)));
163     }
164 
165     @Override
numHeaders()166     public int numHeaders() {
167         return headers.forInternalRead().size();
168     }
169 
170     @Override
numRawQueryParameters()171     public int numRawQueryParameters() {
172         return queryParameters.forInternalRead().size();
173     }
174 
175     @Override
encodedQueryParameters()176     public Optional<String> encodedQueryParameters() {
177         return SdkHttpUtils.encodeAndFlattenQueryParameters(queryParameters.forInternalRead());
178     }
179 
180     @Override
encodedQueryParametersAsFormData()181     public Optional<String> encodedQueryParametersAsFormData() {
182         return SdkHttpUtils.encodeAndFlattenFormData(queryParameters.forInternalRead());
183     }
184 
185     @Override
encodedPath()186     public String encodedPath() {
187         return path;
188     }
189 
190     @Override
rawQueryParameters()191     public Map<String, List<String>> rawQueryParameters() {
192         return queryParameters.forExternalRead();
193     }
194 
195     @Override
firstMatchingRawQueryParameter(String key)196     public Optional<String> firstMatchingRawQueryParameter(String key) {
197         List<String> values = queryParameters.forInternalRead().get(key);
198         return values == null ? Optional.empty() : values.stream().findFirst();
199     }
200 
201     @Override
firstMatchingRawQueryParameter(Collection<String> keys)202     public Optional<String> firstMatchingRawQueryParameter(Collection<String> keys) {
203         for (String key : keys) {
204             Optional<String> result = firstMatchingRawQueryParameter(key);
205             if (result.isPresent()) {
206                 return result;
207             }
208         }
209 
210         return Optional.empty();
211     }
212 
213     @Override
firstMatchingRawQueryParameters(String key)214     public List<String> firstMatchingRawQueryParameters(String key) {
215         List<String> values = queryParameters.forInternalRead().get(key);
216         return values == null ? emptyList() : unmodifiableList(values);
217     }
218 
219     @Override
method()220     public SdkHttpMethod method() {
221         return httpMethod;
222     }
223 
224     @Override
contentStreamProvider()225     public Optional<ContentStreamProvider> contentStreamProvider() {
226         return Optional.ofNullable(contentStreamProvider);
227     }
228 
229     @Override
toBuilder()230     public SdkHttpFullRequest.Builder toBuilder() {
231         return new Builder(this);
232     }
233 
234     @Override
toString()235     public String toString() {
236         return ToString.builder("DefaultSdkHttpFullRequest")
237                        .add("httpMethod", httpMethod)
238                        .add("protocol", protocol)
239                        .add("host", host)
240                        .add("port", port)
241                        .add("encodedPath", path)
242                        .add("headers", headers.forInternalRead().keySet())
243                        .add("queryParameters", queryParameters.forInternalRead().keySet())
244                        .build();
245     }
246 
247     /**
248      * Builder for a {@link DefaultSdkHttpFullRequest}.
249      */
250     static final class Builder implements SdkHttpFullRequest.Builder {
251         private String protocol;
252         private String host;
253         private Integer port;
254         private String path;
255         private LowCopyListMap.ForBuilder queryParameters;
256         private LowCopyListMap.ForBuilder headers;
257         private SdkHttpMethod httpMethod;
258         private ContentStreamProvider contentStreamProvider;
259 
Builder()260         Builder() {
261             queryParameters = LowCopyListMap.emptyQueryParameters();
262             headers = LowCopyListMap.emptyHeaders();
263         }
264 
Builder(DefaultSdkHttpFullRequest request)265         Builder(DefaultSdkHttpFullRequest request) {
266             queryParameters = request.queryParameters.forBuilder();
267             headers = request.headers.forBuilder();
268             protocol = request.protocol;
269             host = request.host;
270             port = request.port;
271             path = request.path;
272             httpMethod = request.httpMethod;
273             contentStreamProvider = request.contentStreamProvider;
274         }
275 
276         @Override
protocol()277         public String protocol() {
278             return protocol;
279         }
280 
281         @Override
protocol(String protocol)282         public SdkHttpFullRequest.Builder protocol(String protocol) {
283             this.protocol = protocol;
284             return this;
285         }
286 
287         @Override
host()288         public String host() {
289             return host;
290         }
291 
292         @Override
host(String host)293         public SdkHttpFullRequest.Builder host(String host) {
294             this.host = host;
295             return this;
296         }
297 
298         @Override
port()299         public Integer port() {
300             return port;
301         }
302 
303         @Override
port(Integer port)304         public SdkHttpFullRequest.Builder port(Integer port) {
305             this.port = port;
306             return this;
307         }
308 
309         @Override
encodedPath(String path)310         public DefaultSdkHttpFullRequest.Builder encodedPath(String path) {
311             this.path = path;
312             return this;
313         }
314 
315         @Override
encodedPath()316         public String encodedPath() {
317             return path;
318         }
319 
320         @Override
putRawQueryParameter(String paramName, List<String> paramValues)321         public DefaultSdkHttpFullRequest.Builder putRawQueryParameter(String paramName, List<String> paramValues) {
322             this.queryParameters.forInternalWrite().put(paramName, new ArrayList<>(paramValues));
323             return this;
324         }
325 
326         @Override
appendRawQueryParameter(String paramName, String paramValue)327         public SdkHttpFullRequest.Builder appendRawQueryParameter(String paramName, String paramValue) {
328             this.queryParameters.forInternalWrite().computeIfAbsent(paramName, k -> new ArrayList<>()).add(paramValue);
329             return this;
330         }
331 
332         @Override
rawQueryParameters(Map<String, List<String>> queryParameters)333         public DefaultSdkHttpFullRequest.Builder rawQueryParameters(Map<String, List<String>> queryParameters) {
334             this.queryParameters.setFromExternal(queryParameters);
335             return this;
336         }
337 
338         @Override
removeQueryParameter(String paramName)339         public Builder removeQueryParameter(String paramName) {
340             this.queryParameters.forInternalWrite().remove(paramName);
341             return this;
342         }
343 
344         @Override
clearQueryParameters()345         public Builder clearQueryParameters() {
346             this.queryParameters.forInternalWrite().clear();
347             return this;
348         }
349 
350         @Override
rawQueryParameters()351         public Map<String, List<String>> rawQueryParameters() {
352             return CollectionUtils.unmodifiableMapOfLists(queryParameters.forInternalRead());
353         }
354 
355         @Override
method(SdkHttpMethod httpMethod)356         public DefaultSdkHttpFullRequest.Builder method(SdkHttpMethod httpMethod) {
357             this.httpMethod = httpMethod;
358             return this;
359         }
360 
361         @Override
method()362         public SdkHttpMethod method() {
363             return httpMethod;
364         }
365 
366         @Override
putHeader(String headerName, List<String> headerValues)367         public DefaultSdkHttpFullRequest.Builder putHeader(String headerName, List<String> headerValues) {
368             this.headers.forInternalWrite().put(headerName, new ArrayList<>(headerValues));
369             return this;
370         }
371 
372         @Override
appendHeader(String headerName, String headerValue)373         public SdkHttpFullRequest.Builder appendHeader(String headerName, String headerValue) {
374             this.headers.forInternalWrite().computeIfAbsent(headerName, k -> new ArrayList<>()).add(headerValue);
375             return this;
376         }
377 
378         @Override
headers(Map<String, List<String>> headers)379         public DefaultSdkHttpFullRequest.Builder headers(Map<String, List<String>> headers) {
380             this.headers.setFromExternal(headers);
381             return this;
382         }
383 
384         @Override
removeHeader(String headerName)385         public SdkHttpFullRequest.Builder removeHeader(String headerName) {
386             this.headers.forInternalWrite().remove(headerName);
387             return this;
388         }
389 
390         @Override
clearHeaders()391         public SdkHttpFullRequest.Builder clearHeaders() {
392             this.headers.clear();
393             return this;
394         }
395 
396         @Override
headers()397         public Map<String, List<String>> headers() {
398             return CollectionUtils.unmodifiableMapOfLists(this.headers.forInternalRead());
399         }
400 
401         @Override
matchingHeaders(String header)402         public List<String> matchingHeaders(String header) {
403             return unmodifiableList(headers.forInternalRead().getOrDefault(header, emptyList()));
404         }
405 
406         @Override
firstMatchingHeader(String headerName)407         public Optional<String> firstMatchingHeader(String headerName) {
408             List<String> headers = this.headers.forInternalRead().get(headerName);
409             if (headers == null || headers.isEmpty()) {
410                 return Optional.empty();
411             }
412 
413             String header = headers.get(0);
414             if (StringUtils.isEmpty(header)) {
415                 return Optional.empty();
416             }
417 
418             return Optional.of(header);
419         }
420 
421         @Override
firstMatchingHeader(Collection<String> headersToFind)422         public Optional<String> firstMatchingHeader(Collection<String> headersToFind) {
423             for (String headerName : headersToFind) {
424                 Optional<String> header = firstMatchingHeader(headerName);
425                 if (header.isPresent()) {
426                     return header;
427                 }
428             }
429 
430             return Optional.empty();
431         }
432 
433         @Override
forEachHeader(BiConsumer<? super String, ? super List<String>> consumer)434         public void forEachHeader(BiConsumer<? super String, ? super List<String>> consumer) {
435             headers.forInternalRead().forEach((k, v) -> consumer.accept(k, unmodifiableList(v)));
436         }
437 
438         @Override
forEachRawQueryParameter(BiConsumer<? super String, ? super List<String>> consumer)439         public void forEachRawQueryParameter(BiConsumer<? super String, ? super List<String>> consumer) {
440             queryParameters.forInternalRead().forEach((k, v) -> consumer.accept(k, unmodifiableList(v)));
441         }
442 
443         @Override
numHeaders()444         public int numHeaders() {
445             return headers.forInternalRead().size();
446         }
447 
448         @Override
numRawQueryParameters()449         public int numRawQueryParameters() {
450             return queryParameters.forInternalRead().size();
451         }
452 
453         @Override
encodedQueryParameters()454         public Optional<String> encodedQueryParameters() {
455             return SdkHttpUtils.encodeAndFlattenQueryParameters(queryParameters.forInternalRead());
456         }
457 
458         @Override
contentStreamProvider(ContentStreamProvider contentStreamProvider)459         public DefaultSdkHttpFullRequest.Builder contentStreamProvider(ContentStreamProvider contentStreamProvider) {
460             this.contentStreamProvider = contentStreamProvider;
461             return this;
462         }
463 
464         @Override
contentStreamProvider()465         public ContentStreamProvider contentStreamProvider() {
466             return contentStreamProvider;
467         }
468 
469         @Override
copy()470         public SdkHttpFullRequest.Builder copy() {
471             return build().toBuilder();
472         }
473 
474         @Override
applyMutation(Consumer<SdkHttpRequest.Builder> mutator)475         public SdkHttpFullRequest.Builder applyMutation(Consumer<SdkHttpRequest.Builder> mutator) {
476             mutator.accept(this);
477             return this;
478         }
479 
480         @Override
build()481         public DefaultSdkHttpFullRequest build() {
482             return new DefaultSdkHttpFullRequest(this);
483         }
484     }
485 
486 }
487