• 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 import static software.amazon.awssdk.utils.CollectionUtils.unmodifiableMapOfLists;
21 
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Optional;
30 import java.util.function.BiConsumer;
31 import software.amazon.awssdk.annotations.Immutable;
32 import software.amazon.awssdk.annotations.SdkInternalApi;
33 import software.amazon.awssdk.internal.http.LowCopyListMap;
34 import software.amazon.awssdk.utils.StringUtils;
35 import software.amazon.awssdk.utils.Validate;
36 
37 /**
38  * Internal implementation of {@link SdkHttpFullResponse}, buildable via {@link SdkHttpFullResponse#builder()}. Returned by HTTP
39  * implementation to represent a service response.
40  */
41 @SdkInternalApi
42 @Immutable
43 class DefaultSdkHttpFullResponse implements SdkHttpFullResponse {
44     private static final long serialVersionUID = 1;
45     private final String statusText;
46     private final int statusCode;
47     private final transient AbortableInputStream content;
48     private transient LowCopyListMap.ForBuildable headers;
49 
DefaultSdkHttpFullResponse(Builder builder)50     private DefaultSdkHttpFullResponse(Builder builder) {
51         this.statusCode = Validate.isNotNegative(builder.statusCode, "Status code must not be negative.");
52         this.statusText = builder.statusText;
53         this.content = builder.content;
54         this.headers = builder.headers.forBuildable();
55     }
56 
57     @Override
headers()58     public Map<String, List<String>> headers() {
59         return headers.forExternalRead();
60     }
61 
62     @Override
matchingHeaders(String header)63     public List<String> matchingHeaders(String header) {
64         return unmodifiableList(headers.forInternalRead().getOrDefault(header, emptyList()));
65     }
66 
67     @Override
firstMatchingHeader(String headerName)68     public Optional<String> firstMatchingHeader(String headerName) {
69         List<String> headers = this.headers.forInternalRead().get(headerName);
70         if (headers == null || headers.isEmpty()) {
71             return Optional.empty();
72         }
73 
74         String header = headers.get(0);
75         if (StringUtils.isEmpty(header)) {
76             return Optional.empty();
77         }
78 
79         return Optional.of(header);
80     }
81 
82     @Override
firstMatchingHeader(Collection<String> headersToFind)83     public Optional<String> firstMatchingHeader(Collection<String> headersToFind) {
84         for (String headerName : headersToFind) {
85             Optional<String> header = firstMatchingHeader(headerName);
86             if (header.isPresent()) {
87                 return header;
88             }
89         }
90 
91         return Optional.empty();
92     }
93 
94     @Override
forEachHeader(BiConsumer<? super String, ? super List<String>> consumer)95     public void forEachHeader(BiConsumer<? super String, ? super List<String>> consumer) {
96         this.headers.forInternalRead().forEach((k, v) -> consumer.accept(k, unmodifiableList(v)));
97     }
98 
99     @Override
numHeaders()100     public int numHeaders() {
101         return this.headers.forInternalRead().size();
102     }
103 
104     @Override
content()105     public Optional<AbortableInputStream> content() {
106         return Optional.ofNullable(content);
107     }
108 
109     @Override
statusText()110     public Optional<String> statusText() {
111         return Optional.ofNullable(statusText);
112     }
113 
114     @Override
statusCode()115     public int statusCode() {
116         return statusCode;
117     }
118 
119     @Override
toBuilder()120     public SdkHttpFullResponse.Builder toBuilder() {
121         return new Builder(this);
122     }
123 
readObject(ObjectInputStream ois)124     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
125         ois.defaultReadObject();
126         headers = LowCopyListMap.emptyHeaders().forBuildable();
127     }
128 
129     @Override
equals(Object o)130     public boolean equals(Object o) {
131         if (this == o) {
132             return true;
133         }
134         if (o == null || getClass() != o.getClass()) {
135             return false;
136         }
137         DefaultSdkHttpFullResponse that = (DefaultSdkHttpFullResponse) o;
138         return (statusCode == that.statusCode) &&
139                Objects.equals(statusText, that.statusText) &&
140                Objects.equals(headers, that.headers);
141     }
142 
143     @Override
hashCode()144     public int hashCode() {
145         int result = statusText != null ? statusText.hashCode() : 0;
146         result = 31 * result + statusCode;
147         result = 31 * result + Objects.hashCode(headers);
148         return result;
149     }
150 
151     /**
152      * Builder for a {@link DefaultSdkHttpFullResponse}.
153      */
154     static final class Builder implements SdkHttpFullResponse.Builder {
155         private String statusText;
156         private int statusCode;
157         private AbortableInputStream content;
158         private LowCopyListMap.ForBuilder headers;
159 
Builder()160         Builder() {
161             headers = LowCopyListMap.emptyHeaders();
162         }
163 
Builder(DefaultSdkHttpFullResponse defaultSdkHttpFullResponse)164         private Builder(DefaultSdkHttpFullResponse defaultSdkHttpFullResponse) {
165             statusText = defaultSdkHttpFullResponse.statusText;
166             statusCode = defaultSdkHttpFullResponse.statusCode;
167             content = defaultSdkHttpFullResponse.content;
168             headers = defaultSdkHttpFullResponse.headers.forBuilder();
169         }
170 
171         @Override
statusText()172         public String statusText() {
173             return statusText;
174         }
175 
176         @Override
statusText(String statusText)177         public Builder statusText(String statusText) {
178             this.statusText = statusText;
179             return this;
180         }
181 
182         @Override
statusCode()183         public int statusCode() {
184             return statusCode;
185         }
186 
187         @Override
statusCode(int statusCode)188         public Builder statusCode(int statusCode) {
189             this.statusCode = statusCode;
190             return this;
191         }
192 
193         @Override
content()194         public AbortableInputStream content() {
195             return content;
196         }
197 
198         @Override
content(AbortableInputStream content)199         public Builder content(AbortableInputStream content) {
200             this.content = content;
201             return this;
202         }
203 
204         @Override
putHeader(String headerName, List<String> headerValues)205         public Builder putHeader(String headerName, List<String> headerValues) {
206             Validate.paramNotNull(headerName, "headerName");
207             Validate.paramNotNull(headerValues, "headerValues");
208             this.headers.forInternalWrite().put(headerName, new ArrayList<>(headerValues));
209             return this;
210         }
211 
212         @Override
appendHeader(String headerName, String headerValue)213         public SdkHttpFullResponse.Builder appendHeader(String headerName, String headerValue) {
214             Validate.paramNotNull(headerName, "headerName");
215             Validate.paramNotNull(headerValue, "headerValue");
216             this.headers.forInternalWrite().computeIfAbsent(headerName, k -> new ArrayList<>()).add(headerValue);
217             return this;
218         }
219 
220         @Override
headers(Map<String, List<String>> headers)221         public Builder headers(Map<String, List<String>> headers) {
222             Validate.paramNotNull(headers, "headers");
223             this.headers.setFromExternal(headers);
224             return this;
225         }
226 
227         @Override
removeHeader(String headerName)228         public Builder removeHeader(String headerName) {
229             this.headers.forInternalWrite().remove(headerName);
230             return this;
231         }
232 
233         @Override
clearHeaders()234         public Builder clearHeaders() {
235             this.headers.clear();
236             return this;
237         }
238 
239         @Override
headers()240         public Map<String, List<String>> headers() {
241             return unmodifiableMapOfLists(this.headers.forInternalRead());
242         }
243 
244         @Override
matchingHeaders(String header)245         public List<String> matchingHeaders(String header) {
246             return unmodifiableList(headers.forInternalRead().getOrDefault(header, emptyList()));
247         }
248 
249         @Override
firstMatchingHeader(String headerName)250         public Optional<String> firstMatchingHeader(String headerName) {
251             List<String> headers = this.headers.forInternalRead().get(headerName);
252             if (headers == null || headers.isEmpty()) {
253                 return Optional.empty();
254             }
255 
256             String header = headers.get(0);
257             if (StringUtils.isEmpty(header)) {
258                 return Optional.empty();
259             }
260 
261             return Optional.of(header);
262         }
263 
264         @Override
firstMatchingHeader(Collection<String> headersToFind)265         public Optional<String> firstMatchingHeader(Collection<String> headersToFind) {
266             for (String headerName : headersToFind) {
267                 Optional<String> header = firstMatchingHeader(headerName);
268                 if (header.isPresent()) {
269                     return header;
270                 }
271             }
272 
273             return Optional.empty();
274         }
275 
276         @Override
forEachHeader(BiConsumer<? super String, ? super List<String>> consumer)277         public void forEachHeader(BiConsumer<? super String, ? super List<String>> consumer) {
278             headers.forInternalRead().forEach((k, v) -> consumer.accept(k, unmodifiableList(v)));
279         }
280 
281         @Override
numHeaders()282         public int numHeaders() {
283             return headers.forInternalRead().size();
284         }
285 
286         /**
287          * @return An immutable {@link DefaultSdkHttpFullResponse} object.
288          */
289         @Override
build()290         public SdkHttpFullResponse build() {
291             return new DefaultSdkHttpFullResponse(this);
292         }
293     }
294 }
295