author | aghaisas |
Wed, 26 Jun 2019 12:12:25 +0530 | |
branch | metal-prototype-branch |
changeset 57429 | 6c1c6874a144 |
parent 50681 | 4254bed3c09d |
child 56795 | 03ece2518428 |
permissions | -rw-r--r-- |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
1 |
/* |
49765 | 2 |
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
4 |
* |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
5 |
* This code is free software; you can redistribute it and/or modify it |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
7 |
* published by the Free Software Foundation. Oracle designates this |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
8 |
* particular file as subject to the "Classpath" exception as provided |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
9 |
* by Oracle in the LICENSE file that accompanied this code. |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
10 |
* |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
15 |
* accompanied this code). |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
16 |
* |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
17 |
* You should have received a copy of the GNU General Public License version |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
20 |
* |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
22 |
* or visit www.oracle.com if you need additional information or have any |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
23 |
* questions. |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
24 |
*/ |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
25 |
|
49765 | 26 |
package java.net.http; |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
27 |
|
50681 | 28 |
import java.util.ArrayList; |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
29 |
import java.util.List; |
50681 | 30 |
import java.util.Locale; |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
31 |
import java.util.Map; |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
32 |
import java.util.Optional; |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
33 |
import java.util.OptionalLong; |
50681 | 34 |
import java.util.TreeMap; |
35 |
import java.util.TreeSet; |
|
36 |
import java.util.function.BiPredicate; |
|
37 |
import static java.lang.String.CASE_INSENSITIVE_ORDER; |
|
38 |
import static java.util.Collections.unmodifiableMap; |
|
48083 | 39 |
import static java.util.Objects.requireNonNull; |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
40 |
|
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
41 |
/** |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
42 |
* A read-only view of a set of HTTP headers. |
49765 | 43 |
* |
50681 | 44 |
* <p> An {@code HttpHeaders} is not typically created directly, but rather |
45 |
* returned from an {@link HttpRequest#headers() HttpRequest} or an |
|
46 |
* {@link HttpResponse#headers() HttpResponse}. Specific HTTP headers can be |
|
47 |
* set for a {@linkplain HttpRequest request} through one of the request |
|
48 |
* builder's {@link HttpRequest.Builder#header(String, String) headers} methods. |
|
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
49 |
* |
48083 | 50 |
* <p> The methods of this class ( that accept a String header name ), and the |
50681 | 51 |
* {@code Map} returned by the {@link #map() map} method, operate without regard |
52 |
* to case when retrieving the header value(s). |
|
53 |
* |
|
54 |
* <p> An HTTP header name may appear more than once in the HTTP protocol. As |
|
55 |
* such, headers are represented as a name and a list of values. Each occurrence |
|
56 |
* of a header value is added verbatim, to the appropriate header name list, |
|
57 |
* without interpreting its value. In particular, {@code HttpHeaders} does not |
|
58 |
* perform any splitting or joining of comma separated header value strings. The |
|
59 |
* order of elements in a header value list is preserved when {@link |
|
60 |
* HttpRequest.Builder#header(String, String) building} a request. For |
|
61 |
* responses, the order of elements in a header value list is the order in which |
|
62 |
* they were received. The {@code Map} returned by the {@code map} method, |
|
63 |
* however, does not provide any guarantee with regard to the ordering of its |
|
64 |
* entries. |
|
48083 | 65 |
* |
49765 | 66 |
* <p> {@code HttpHeaders} instances are immutable. |
48083 | 67 |
* |
49765 | 68 |
* @since 11 |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
69 |
*/ |
50681 | 70 |
public final class HttpHeaders { |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
71 |
|
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
72 |
/** |
50681 | 73 |
* Returns an {@link Optional} containing the first header string value of |
74 |
* the given named (and possibly multi-valued) header. If the header is not |
|
75 |
* present, then the returned {@code Optional} is empty. |
|
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
76 |
* |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
77 |
* @param name the header name |
50681 | 78 |
* @return an {@code Optional<String>} containing the first named header |
79 |
* string value, if present |
|
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
80 |
*/ |
48083 | 81 |
public Optional<String> firstValue(String name) { |
82 |
return allValues(name).stream().findFirst(); |
|
83 |
} |
|
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
84 |
|
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
85 |
/** |
50681 | 86 |
* Returns an {@link OptionalLong} containing the first header string value |
87 |
* of the named header field. If the header is not present, then the |
|
88 |
* Optional is empty. If the header is present but contains a value that |
|
89 |
* does not parse as a {@code Long} value, then an exception is thrown. |
|
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
90 |
* |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
91 |
* @param name the header name |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
92 |
* @return an {@code OptionalLong} |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
93 |
* @throws NumberFormatException if a value is found, but does not parse as |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
94 |
* a Long |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
95 |
*/ |
48083 | 96 |
public OptionalLong firstValueAsLong(String name) { |
97 |
return allValues(name).stream().mapToLong(Long::valueOf).findFirst(); |
|
98 |
} |
|
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
99 |
|
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
100 |
/** |
50681 | 101 |
* Returns an unmodifiable List of all of the header string values of the |
102 |
* given named header. Always returns a List, which may be empty if the |
|
103 |
* header is not present. |
|
48083 | 104 |
* |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
105 |
* @param name the header name |
50681 | 106 |
* @return a List of headers string values |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
107 |
*/ |
48083 | 108 |
public List<String> allValues(String name) { |
109 |
requireNonNull(name); |
|
110 |
List<String> values = map().get(name); |
|
111 |
// Making unmodifiable list out of empty in order to make a list which |
|
112 |
// throws UOE unconditionally |
|
50681 | 113 |
return values != null ? values : List.of(); |
48083 | 114 |
} |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
115 |
|
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
116 |
/** |
48083 | 117 |
* Returns an unmodifiable multi Map view of this HttpHeaders. |
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
118 |
* |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
119 |
* @return the Map |
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
120 |
*/ |
50681 | 121 |
public Map<String,List<String>> map() { |
122 |
return headers; |
|
123 |
} |
|
48083 | 124 |
|
125 |
/** |
|
126 |
* Tests this HTTP headers instance for equality with the given object. |
|
127 |
* |
|
128 |
* <p> If the given object is not an {@code HttpHeaders} then this |
|
129 |
* method returns {@code false}. Two HTTP headers are equal if each |
|
130 |
* of their corresponding {@linkplain #map() maps} are equal. |
|
131 |
* |
|
132 |
* <p> This method satisfies the general contract of the {@link |
|
133 |
* Object#equals(Object) Object.equals} method. |
|
134 |
* |
|
135 |
* @param obj the object to which this object is to be compared |
|
136 |
* @return {@code true} if, and only if, the given object is an {@code |
|
137 |
* HttpHeaders} that is equal to this HTTP headers |
|
138 |
*/ |
|
139 |
public final boolean equals(Object obj) { |
|
140 |
if (!(obj instanceof HttpHeaders)) |
|
141 |
return false; |
|
142 |
HttpHeaders that = (HttpHeaders)obj; |
|
143 |
return this.map().equals(that.map()); |
|
144 |
} |
|
145 |
||
146 |
/** |
|
147 |
* Computes a hash code for this HTTP headers instance. |
|
148 |
* |
|
149 |
* <p> The hash code is based upon the components of the HTTP headers |
|
49765 | 150 |
* {@link #map() map}, and satisfies the general contract of the |
48083 | 151 |
* {@link Object#hashCode Object.hashCode} method. |
152 |
* |
|
153 |
* @return the hash-code value for this HTTP headers |
|
154 |
*/ |
|
155 |
public final int hashCode() { |
|
50681 | 156 |
int h = 0; |
157 |
for (Map.Entry<String, List<String>> e : map().entrySet()) { |
|
158 |
h += entryHash(e); |
|
159 |
} |
|
160 |
return h; |
|
48083 | 161 |
} |
162 |
||
163 |
/** |
|
164 |
* Returns this HTTP headers as a string. |
|
165 |
* |
|
166 |
* @return a string describing the HTTP headers |
|
167 |
*/ |
|
168 |
@Override |
|
169 |
public String toString() { |
|
170 |
StringBuilder sb = new StringBuilder(); |
|
49765 | 171 |
sb.append(super.toString()).append(" { "); |
48083 | 172 |
sb.append(map()); |
173 |
sb.append(" }"); |
|
174 |
return sb.toString(); |
|
175 |
} |
|
50681 | 176 |
|
177 |
/** |
|
178 |
* Returns an HTTP headers from the given map. The given map's key |
|
179 |
* represents the header name, and its value the list of string header |
|
180 |
* values for that header name. |
|
181 |
* |
|
182 |
* <p> An HTTP header name may appear more than once in the HTTP protocol. |
|
183 |
* Such, <i>multi-valued</i>, headers must be represented by a single entry |
|
184 |
* in the given map, whose entry value is a list that represents the |
|
185 |
* multiple header string values. Leading and trailing whitespaces are |
|
186 |
* removed from all string values retrieved from the given map and its lists |
|
187 |
* before processing. Only headers that, after filtering, contain at least |
|
188 |
* one, possibly empty string, value will be added to the HTTP headers. |
|
189 |
* |
|
190 |
* @apiNote The primary purpose of this method is for testing frameworks. |
|
191 |
* Per-request headers can be set through one of the {@code HttpRequest} |
|
192 |
* {@link HttpRequest.Builder#header(String, String) headers} methods. |
|
193 |
* |
|
194 |
* @param headerMap the map containing the header names and values |
|
195 |
* @param filter a filter that can be used to inspect each |
|
196 |
* header-name-and-value pair in the given map to determine if |
|
197 |
* it should, or should not, be added to the to the HTTP |
|
198 |
* headers |
|
199 |
* @return an HTTP headers instance containing the given headers |
|
200 |
* @throws NullPointerException if any of: {@code headerMap}, a key or value |
|
201 |
* in the given map, or an entry in the map's value list, or |
|
202 |
* {@code filter}, is {@code null} |
|
203 |
* @throws IllegalArgumentException if the given {@code headerMap} contains |
|
204 |
* any two keys that are equal ( without regard to case ); or if the |
|
205 |
* given map contains any key whose length, after trimming |
|
206 |
* whitespaces, is {@code 0} |
|
207 |
*/ |
|
208 |
public static HttpHeaders of(Map<String,List<String>> headerMap, |
|
209 |
BiPredicate<String,String> filter) { |
|
210 |
requireNonNull(headerMap); |
|
211 |
requireNonNull(filter); |
|
212 |
return headersOf(headerMap, filter); |
|
213 |
} |
|
214 |
||
215 |
// -- |
|
216 |
||
217 |
private static final HttpHeaders NO_HEADERS = new HttpHeaders(Map.of()); |
|
218 |
||
219 |
private final Map<String,List<String>> headers; |
|
220 |
||
221 |
private HttpHeaders(Map<String,List<String>> headers) { |
|
222 |
this.headers = headers; |
|
223 |
} |
|
224 |
||
225 |
private static final int entryHash(Map.Entry<String, List<String>> e) { |
|
226 |
String key = e.getKey(); |
|
227 |
List<String> value = e.getValue(); |
|
228 |
// we know that by construction key and values can't be null |
|
229 |
int keyHash = key.toLowerCase(Locale.ROOT).hashCode(); |
|
230 |
int valueHash = value.hashCode(); |
|
231 |
return keyHash ^ valueHash; |
|
232 |
} |
|
233 |
||
234 |
// Returns a new HTTP headers after performing a structural copy and filtering. |
|
235 |
private static HttpHeaders headersOf(Map<String,List<String>> map, |
|
236 |
BiPredicate<String,String> filter) { |
|
237 |
TreeMap<String,List<String>> other = new TreeMap<>(CASE_INSENSITIVE_ORDER); |
|
238 |
TreeSet<String> notAdded = new TreeSet<>(CASE_INSENSITIVE_ORDER); |
|
239 |
ArrayList<String> tempList = new ArrayList<>(); |
|
240 |
map.forEach((key, value) -> { |
|
241 |
String headerName = requireNonNull(key).trim(); |
|
242 |
if (headerName.isEmpty()) { |
|
243 |
throw new IllegalArgumentException("empty key"); |
|
244 |
} |
|
245 |
List<String> headerValues = requireNonNull(value); |
|
246 |
headerValues.forEach(headerValue -> { |
|
247 |
headerValue = requireNonNull(headerValue).trim(); |
|
248 |
if (filter.test(headerName, headerValue)) { |
|
249 |
tempList.add(headerValue); |
|
250 |
} |
|
251 |
}); |
|
252 |
||
253 |
if (tempList.isEmpty()) { |
|
254 |
if (other.containsKey(headerName) |
|
255 |
|| notAdded.contains(headerName.toLowerCase(Locale.ROOT))) |
|
256 |
throw new IllegalArgumentException("duplicate key: " + headerName); |
|
257 |
notAdded.add(headerName.toLowerCase(Locale.ROOT)); |
|
258 |
} else if (other.put(headerName, List.copyOf(tempList)) != null) { |
|
259 |
throw new IllegalArgumentException("duplicate key: " + headerName); |
|
260 |
} |
|
261 |
tempList.clear(); |
|
262 |
}); |
|
263 |
return other.isEmpty() ? NO_HEADERS : new HttpHeaders(unmodifiableMap(other)); |
|
264 |
} |
|
42460
7133f144981a
8170648: Move java.net.http package out of Java SE to incubator namespace
michaelm
parents:
diff
changeset
|
265 |
} |