1
15
16 package software.amazon.awssdk.http;
17
18 import static software.amazon.awssdk.utils.CollectionUtils.deepCopyMap;
19 import static software.amazon.awssdk.utils.CollectionUtils.deepUnmodifiableMap;
20
21 import java.io.Serializable;
22 import java.util.ArrayList;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.TreeMap;
28 import software.amazon.awssdk.annotations.Immutable;
29 import software.amazon.awssdk.annotations.SdkInternalApi;
30 import software.amazon.awssdk.utils.CollectionUtils;
31 import software.amazon.awssdk.utils.Validate;
32
33
37 @SdkInternalApi
38 @Immutable
39 class DefaultSdkHttpFullResponse implements SdkHttpFullResponse, Serializable {
40
41 private static final long serialVersionUID = 1L;
42
43 private final String statusText;
44 private final int statusCode;
45 private final Map<String, List<String>> headers;
46 private final transient AbortableInputStream content;
47
48 private DefaultSdkHttpFullResponse(Builder builder) {
49 this.statusCode = Validate.isNotNegative(builder.statusCode, "Status code must not be negative.");
50 this.statusText = builder.statusText;
51 this.headers = builder.headersAreFromToBuilder
52 ? builder.headers
53 : deepUnmodifiableMap(builder.headers, () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
54 this.content = builder.content;
55 }
56
57 @Override
58 public Map<String, List<String>> headers() {
59 return headers;
60 }
61
62 @Override
63 public Optional<AbortableInputStream> content() {
64 return Optional.ofNullable(content);
65 }
66
67 @Override
68 public Optional<String> statusText() {
69 return Optional.ofNullable(statusText);
70 }
71
72 @Override
73 public int statusCode() {
74 return statusCode;
75 }
76
77 @Override
78 public SdkHttpFullResponse.Builder toBuilder() {
79 return new Builder(this);
80 }
81
82
85 static final class Builder implements SdkHttpFullResponse.Builder {
86 private String statusText;
87 private int statusCode;
88 private AbortableInputStream content;
89
90 private boolean headersAreFromToBuilder;
91 private Map<String, List<String>> headers;
92
93 Builder() {
94 headersAreFromToBuilder = false;
95 headers = new LinkedHashMap<>();
96 }
97
98 private Builder(DefaultSdkHttpFullResponse defaultSdkHttpFullResponse) {
99 statusText = defaultSdkHttpFullResponse.statusText;
100 statusCode = defaultSdkHttpFullResponse.statusCode;
101 content = defaultSdkHttpFullResponse.content;
102 headersAreFromToBuilder = true;
103 headers = defaultSdkHttpFullResponse.headers;
104 }
105
106 @Override
107 public String statusText() {
108 return statusText;
109 }
110
111 @Override
112 public Builder statusText(String statusText) {
113 this.statusText = statusText;
114 return this;
115 }
116
117 @Override
118 public int statusCode() {
119 return statusCode;
120 }
121
122 @Override
123 public Builder statusCode(int statusCode) {
124 this.statusCode = statusCode;
125 return this;
126 }
127
128 @Override
129 public AbortableInputStream content() {
130 return content;
131 }
132
133 @Override
134 public Builder content(AbortableInputStream content) {
135 this.content = content;
136 return this;
137 }
138
139 @Override
140 public Builder putHeader(String headerName, List<String> headerValues) {
141 Validate.paramNotNull(headerName, "headerName");
142 Validate.paramNotNull(headerValues, "headerValues");
143 copyHeadersIfNeeded();
144 this.headers.put(headerName, new ArrayList<>(headerValues));
145 return this;
146 }
147
148 @Override
149 public SdkHttpFullResponse.Builder appendHeader(String headerName, String headerValue) {
150 Validate.paramNotNull(headerName, "headerName");
151 Validate.paramNotNull(headerValue, "headerValue");
152 copyHeadersIfNeeded();
153 this.headers.computeIfAbsent(headerName, k -> new ArrayList<>()).add(headerValue);
154 return this;
155 }
156
157 @Override
158 public Builder headers(Map<String, List<String>> headers) {
159 Validate.paramNotNull(headers, "headers");
160 this.headers = CollectionUtils.deepCopyMap(headers);
161 headersAreFromToBuilder = false;
162 return this;
163 }
164
165 @Override
166 public Builder removeHeader(String headerName) {
167 copyHeadersIfNeeded();
168 this.headers.remove(headerName);
169 return this;
170 }
171
172 @Override
173 public Builder clearHeaders() {
174 this.headers = new LinkedHashMap<>();
175 headersAreFromToBuilder = false;
176 return this;
177 }
178
179 @Override
180 public Map<String, List<String>> headers() {
181 return CollectionUtils.unmodifiableMapOfLists(this.headers);
182 }
183
184 private void copyHeadersIfNeeded() {
185 if (headersAreFromToBuilder) {
186 headersAreFromToBuilder = false;
187 this.headers = deepCopyMap(headers);
188 }
189 }
190
191
194 public SdkHttpFullResponse build() {
195 return new DefaultSdkHttpFullResponse(this);
196 }
197 }
198 }
199