1 /* |
|
2 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package java.net.http.internal.common; |
|
27 |
|
28 import java.net.http.HttpHeaders; |
|
29 import sun.net.NetProperties; |
|
30 import sun.net.util.IPAddressUtil; |
|
31 import sun.net.www.HeaderParser; |
|
32 |
|
33 import javax.net.ssl.SSLParameters; |
|
34 import java.io.ByteArrayOutputStream; |
|
35 import java.io.Closeable; |
|
36 import java.io.IOException; |
|
37 import java.io.PrintStream; |
|
38 import java.io.UncheckedIOException; |
|
39 import java.io.UnsupportedEncodingException; |
|
40 import java.lang.System.Logger; |
|
41 import java.lang.System.Logger.Level; |
|
42 import java.net.InetSocketAddress; |
|
43 import java.net.URI; |
|
44 import java.net.URLPermission; |
|
45 import java.nio.ByteBuffer; |
|
46 import java.nio.charset.Charset; |
|
47 import java.nio.charset.StandardCharsets; |
|
48 import java.security.AccessController; |
|
49 import java.security.PrivilegedAction; |
|
50 import java.util.Arrays; |
|
51 import java.util.Collection; |
|
52 import java.util.Collections; |
|
53 import java.util.List; |
|
54 import java.util.Set; |
|
55 import java.util.TreeSet; |
|
56 import java.util.concurrent.CompletionException; |
|
57 import java.util.concurrent.ExecutionException; |
|
58 import java.util.function.BiPredicate; |
|
59 import java.util.function.Predicate; |
|
60 import java.util.function.Supplier; |
|
61 import java.util.stream.Collectors; |
|
62 import java.util.stream.Stream; |
|
63 |
|
64 import static java.util.stream.Collectors.joining; |
|
65 |
|
66 /** |
|
67 * Miscellaneous utilities |
|
68 */ |
|
69 public final class Utils { |
|
70 |
|
71 public static final boolean ASSERTIONSENABLED; |
|
72 static { |
|
73 boolean enabled = false; |
|
74 assert enabled = true; |
|
75 ASSERTIONSENABLED = enabled; |
|
76 } |
|
77 // public static final boolean TESTING; |
|
78 // static { |
|
79 // if (ASSERTIONSENABLED) { |
|
80 // PrivilegedAction<String> action = () -> System.getProperty("test.src"); |
|
81 // TESTING = AccessController.doPrivileged(action) != null; |
|
82 // } else TESTING = false; |
|
83 // } |
|
84 public static final boolean DEBUG = // Revisit: temporary dev flag. |
|
85 getBooleanProperty(DebugLogger.HTTP_NAME, false); |
|
86 public static final boolean DEBUG_HPACK = // Revisit: temporary dev flag. |
|
87 getBooleanProperty(DebugLogger.HPACK_NAME, false); |
|
88 public static final boolean TESTING = DEBUG; |
|
89 |
|
90 /** |
|
91 * Allocated buffer size. Must never be higher than 16K. But can be lower |
|
92 * if smaller allocation units preferred. HTTP/2 mandates that all |
|
93 * implementations support frame payloads of at least 16K. |
|
94 */ |
|
95 private static final int DEFAULT_BUFSIZE = 16 * 1024; |
|
96 |
|
97 public static final int BUFSIZE = getIntegerNetProperty( |
|
98 "jdk.httpclient.bufsize", DEFAULT_BUFSIZE |
|
99 ); |
|
100 |
|
101 private static final Set<String> DISALLOWED_HEADERS_SET; |
|
102 static { |
|
103 // A case insensitive TreeSet of strings. |
|
104 TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); |
|
105 treeSet.addAll(Set.of("connection", "content-length", |
|
106 "date", "expect", "from", "host", "origin", |
|
107 "referer", "upgrade", |
|
108 "via", "warning")); |
|
109 DISALLOWED_HEADERS_SET = Collections.unmodifiableSet(treeSet); |
|
110 } |
|
111 |
|
112 public static final Predicate<String> |
|
113 ALLOWED_HEADERS = header -> !DISALLOWED_HEADERS_SET.contains(header); |
|
114 |
|
115 private static final Predicate<String> IS_PROXY_HEADER = (k) -> |
|
116 k != null && k.length() > 6 && "proxy-".equalsIgnoreCase(k.substring(0,6)); |
|
117 private static final Predicate<String> NO_PROXY_HEADER = |
|
118 IS_PROXY_HEADER.negate(); |
|
119 private static final Predicate<String> ALL_HEADERS = (s) -> true; |
|
120 |
|
121 private static final Set<String> PROXY_AUTH_DISABLED_SCHEMES; |
|
122 private static final Set<String> PROXY_AUTH_TUNNEL_DISABLED_SCHEMES; |
|
123 static { |
|
124 String proxyAuthDisabled = |
|
125 getNetProperty("jdk.http.auth.proxying.disabledSchemes"); |
|
126 String proxyAuthTunnelDisabled = |
|
127 getNetProperty("jdk.http.auth.tunneling.disabledSchemes"); |
|
128 PROXY_AUTH_DISABLED_SCHEMES = |
|
129 proxyAuthDisabled == null ? Set.of() : |
|
130 Stream.of(proxyAuthDisabled.split(",")) |
|
131 .map(String::trim) |
|
132 .filter((s) -> !s.isEmpty()) |
|
133 .collect(Collectors.toUnmodifiableSet()); |
|
134 PROXY_AUTH_TUNNEL_DISABLED_SCHEMES = |
|
135 proxyAuthTunnelDisabled == null ? Set.of() : |
|
136 Stream.of(proxyAuthTunnelDisabled.split(",")) |
|
137 .map(String::trim) |
|
138 .filter((s) -> !s.isEmpty()) |
|
139 .collect(Collectors.toUnmodifiableSet()); |
|
140 } |
|
141 |
|
142 private static final String WSPACES = " \t\r\n"; |
|
143 private static final boolean isAllowedForProxy(String name, |
|
144 List<String> value, |
|
145 Set<String> disabledSchemes, |
|
146 Predicate<String> allowedKeys) { |
|
147 if (!allowedKeys.test(name)) return false; |
|
148 if (disabledSchemes.isEmpty()) return true; |
|
149 if (name.equalsIgnoreCase("proxy-authorization")) { |
|
150 if (value.isEmpty()) return false; |
|
151 for (String scheme : disabledSchemes) { |
|
152 int slen = scheme.length(); |
|
153 for (String v : value) { |
|
154 int vlen = v.length(); |
|
155 if (vlen == slen) { |
|
156 if (v.equalsIgnoreCase(scheme)) { |
|
157 return false; |
|
158 } |
|
159 } else if (vlen > slen) { |
|
160 if (v.substring(0,slen).equalsIgnoreCase(scheme)) { |
|
161 int c = v.codePointAt(slen); |
|
162 if (WSPACES.indexOf(c) > -1 |
|
163 || Character.isSpaceChar(c) |
|
164 || Character.isWhitespace(c)) { |
|
165 return false; |
|
166 } |
|
167 } |
|
168 } |
|
169 } |
|
170 } |
|
171 } |
|
172 return true; |
|
173 } |
|
174 |
|
175 public static final BiPredicate<String, List<String>> PROXY_TUNNEL_FILTER = |
|
176 (s,v) -> isAllowedForProxy(s, v, PROXY_AUTH_TUNNEL_DISABLED_SCHEMES, |
|
177 IS_PROXY_HEADER); |
|
178 public static final BiPredicate<String, List<String>> PROXY_FILTER = |
|
179 (s,v) -> isAllowedForProxy(s, v, PROXY_AUTH_DISABLED_SCHEMES, |
|
180 ALL_HEADERS); |
|
181 public static final BiPredicate<String, List<String>> NO_PROXY_HEADERS_FILTER = |
|
182 (n,v) -> Utils.NO_PROXY_HEADER.test(n); |
|
183 |
|
184 |
|
185 public static boolean proxyHasDisabledSchemes(boolean tunnel) { |
|
186 return tunnel ? ! PROXY_AUTH_TUNNEL_DISABLED_SCHEMES.isEmpty() |
|
187 : ! PROXY_AUTH_DISABLED_SCHEMES.isEmpty(); |
|
188 } |
|
189 |
|
190 public static ByteBuffer getBuffer() { |
|
191 return ByteBuffer.allocate(BUFSIZE); |
|
192 } |
|
193 |
|
194 public static Throwable getCompletionCause(Throwable x) { |
|
195 if (!(x instanceof CompletionException) |
|
196 && !(x instanceof ExecutionException)) return x; |
|
197 final Throwable cause = x.getCause(); |
|
198 if (cause == null) { |
|
199 throw new InternalError("Unexpected null cause", x); |
|
200 } |
|
201 return cause; |
|
202 } |
|
203 |
|
204 public static IOException getIOException(Throwable t) { |
|
205 if (t instanceof IOException) { |
|
206 return (IOException) t; |
|
207 } |
|
208 Throwable cause = t.getCause(); |
|
209 if (cause != null) { |
|
210 return getIOException(cause); |
|
211 } |
|
212 return new IOException(t); |
|
213 } |
|
214 |
|
215 private Utils() { } |
|
216 |
|
217 /** |
|
218 * Returns the security permissions required to connect to the proxy, or |
|
219 * {@code null} if none is required or applicable. |
|
220 */ |
|
221 public static URLPermission permissionForProxy(InetSocketAddress proxyAddress) { |
|
222 if (proxyAddress == null) |
|
223 return null; |
|
224 |
|
225 StringBuilder sb = new StringBuilder(); |
|
226 sb.append("socket://") |
|
227 .append(proxyAddress.getHostString()).append(":") |
|
228 .append(proxyAddress.getPort()); |
|
229 String urlString = sb.toString(); |
|
230 return new URLPermission(urlString, "CONNECT"); |
|
231 } |
|
232 |
|
233 /** |
|
234 * Returns the security permission required for the given details. |
|
235 */ |
|
236 public static URLPermission permissionForServer(URI uri, |
|
237 String method, |
|
238 Stream<String> headers) { |
|
239 String urlString = new StringBuilder() |
|
240 .append(uri.getScheme()).append("://") |
|
241 .append(uri.getAuthority()) |
|
242 .append(uri.getPath()).toString(); |
|
243 |
|
244 StringBuilder actionStringBuilder = new StringBuilder(method); |
|
245 String collected = headers.collect(joining(",")); |
|
246 if (!collected.isEmpty()) { |
|
247 actionStringBuilder.append(":").append(collected); |
|
248 } |
|
249 return new URLPermission(urlString, actionStringBuilder.toString()); |
|
250 } |
|
251 |
|
252 |
|
253 // ABNF primitives defined in RFC 7230 |
|
254 private static final boolean[] tchar = new boolean[256]; |
|
255 private static final boolean[] fieldvchar = new boolean[256]; |
|
256 |
|
257 static { |
|
258 char[] allowedTokenChars = |
|
259 ("!#$%&'*+-.^_`|~0123456789" + |
|
260 "abcdefghijklmnopqrstuvwxyz" + |
|
261 "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); |
|
262 for (char c : allowedTokenChars) { |
|
263 tchar[c] = true; |
|
264 } |
|
265 for (char c = 0x21; c < 0xFF; c++) { |
|
266 fieldvchar[c] = true; |
|
267 } |
|
268 fieldvchar[0x7F] = false; // a little hole (DEL) in the range |
|
269 } |
|
270 |
|
271 /* |
|
272 * Validates a RFC 7230 field-name. |
|
273 */ |
|
274 public static boolean isValidName(String token) { |
|
275 for (int i = 0; i < token.length(); i++) { |
|
276 char c = token.charAt(i); |
|
277 if (c > 255 || !tchar[c]) { |
|
278 return false; |
|
279 } |
|
280 } |
|
281 return !token.isEmpty(); |
|
282 } |
|
283 |
|
284 /** |
|
285 * If the address was created with a domain name, then return |
|
286 * the domain name string. If created with a literal IP address |
|
287 * then return null. We do this to avoid doing a reverse lookup |
|
288 * Used to populate the TLS SNI parameter. So, SNI is only set |
|
289 * when a domain name was supplied. |
|
290 */ |
|
291 public static String getServerName(InetSocketAddress addr) { |
|
292 String host = addr.getHostString(); |
|
293 if (IPAddressUtil.textToNumericFormatV4(host) != null) |
|
294 return null; |
|
295 if (IPAddressUtil.textToNumericFormatV6(host) != null) |
|
296 return null; |
|
297 return host; |
|
298 } |
|
299 |
|
300 /* |
|
301 * Validates a RFC 7230 field-value. |
|
302 * |
|
303 * "Obsolete line folding" rule |
|
304 * |
|
305 * obs-fold = CRLF 1*( SP / HTAB ) |
|
306 * |
|
307 * is not permitted! |
|
308 */ |
|
309 public static boolean isValidValue(String token) { |
|
310 boolean accepted = true; |
|
311 for (int i = 0; i < token.length(); i++) { |
|
312 char c = token.charAt(i); |
|
313 if (c > 255) { |
|
314 return false; |
|
315 } |
|
316 if (accepted) { |
|
317 if (c == ' ' || c == '\t') { |
|
318 accepted = false; |
|
319 } else if (!fieldvchar[c]) { |
|
320 return false; // forbidden byte |
|
321 } |
|
322 } else { |
|
323 if (c != ' ' && c != '\t') { |
|
324 if (fieldvchar[c]) { |
|
325 accepted = true; |
|
326 } else { |
|
327 return false; // forbidden byte |
|
328 } |
|
329 } |
|
330 } |
|
331 } |
|
332 return accepted; |
|
333 } |
|
334 |
|
335 public static int getIntegerNetProperty(String name, int defaultValue) { |
|
336 return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> |
|
337 NetProperties.getInteger(name, defaultValue)); |
|
338 } |
|
339 |
|
340 static String getNetProperty(String name) { |
|
341 return AccessController.doPrivileged((PrivilegedAction<String>) () -> |
|
342 NetProperties.get(name)); |
|
343 } |
|
344 |
|
345 static boolean getBooleanProperty(String name, boolean def) { |
|
346 return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> |
|
347 Boolean.parseBoolean(System.getProperty(name, String.valueOf(def)))); |
|
348 } |
|
349 |
|
350 public static SSLParameters copySSLParameters(SSLParameters p) { |
|
351 SSLParameters p1 = new SSLParameters(); |
|
352 p1.setAlgorithmConstraints(p.getAlgorithmConstraints()); |
|
353 p1.setCipherSuites(p.getCipherSuites()); |
|
354 // JDK 8 EXCL START |
|
355 p1.setEnableRetransmissions(p.getEnableRetransmissions()); |
|
356 p1.setMaximumPacketSize(p.getMaximumPacketSize()); |
|
357 // JDK 8 EXCL END |
|
358 p1.setEndpointIdentificationAlgorithm(p.getEndpointIdentificationAlgorithm()); |
|
359 p1.setNeedClientAuth(p.getNeedClientAuth()); |
|
360 String[] protocols = p.getProtocols(); |
|
361 if (protocols != null) { |
|
362 p1.setProtocols(protocols.clone()); |
|
363 } |
|
364 p1.setSNIMatchers(p.getSNIMatchers()); |
|
365 p1.setServerNames(p.getServerNames()); |
|
366 p1.setUseCipherSuitesOrder(p.getUseCipherSuitesOrder()); |
|
367 p1.setWantClientAuth(p.getWantClientAuth()); |
|
368 return p1; |
|
369 } |
|
370 |
|
371 /** |
|
372 * Set limit to position, and position to mark. |
|
373 */ |
|
374 public static void flipToMark(ByteBuffer buffer, int mark) { |
|
375 buffer.limit(buffer.position()); |
|
376 buffer.position(mark); |
|
377 } |
|
378 |
|
379 public static String stackTrace(Throwable t) { |
|
380 ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
|
381 String s = null; |
|
382 try { |
|
383 PrintStream p = new PrintStream(bos, true, "US-ASCII"); |
|
384 t.printStackTrace(p); |
|
385 s = bos.toString("US-ASCII"); |
|
386 } catch (UnsupportedEncodingException ex) { |
|
387 throw new InternalError(ex); // Can't happen |
|
388 } |
|
389 return s; |
|
390 } |
|
391 |
|
392 /** |
|
393 * Copies as much of src to dst as possible. |
|
394 * Return number of bytes copied |
|
395 */ |
|
396 public static int copy(ByteBuffer src, ByteBuffer dst) { |
|
397 int srcLen = src.remaining(); |
|
398 int dstLen = dst.remaining(); |
|
399 if (srcLen > dstLen) { |
|
400 int diff = srcLen - dstLen; |
|
401 int limit = src.limit(); |
|
402 src.limit(limit - diff); |
|
403 dst.put(src); |
|
404 src.limit(limit); |
|
405 } else { |
|
406 dst.put(src); |
|
407 } |
|
408 return srcLen - src.remaining(); |
|
409 } |
|
410 |
|
411 /** Threshold beyond which data is no longer copied into the current |
|
412 * buffer, if that buffer has enough unused space. */ |
|
413 private static final int COPY_THRESHOLD = 8192; |
|
414 |
|
415 /** |
|
416 * Adds the data from buffersToAdd to currentList. Either 1) appends the |
|
417 * data from a particular buffer to the last buffer in the list ( if |
|
418 * there is enough unused space ), or 2) adds it to the list. |
|
419 * |
|
420 * @return the number of bytes added |
|
421 */ |
|
422 public static long accumulateBuffers(List<ByteBuffer> currentList, |
|
423 List<ByteBuffer> buffersToAdd) { |
|
424 long accumulatedBytes = 0; |
|
425 for (ByteBuffer bufferToAdd : buffersToAdd) { |
|
426 int remaining = bufferToAdd.remaining(); |
|
427 if (remaining <= 0) |
|
428 continue; |
|
429 int listSize = currentList.size(); |
|
430 if (listSize == 0) { |
|
431 currentList.add(bufferToAdd); |
|
432 accumulatedBytes = remaining; |
|
433 continue; |
|
434 } |
|
435 |
|
436 ByteBuffer lastBuffer = currentList.get(listSize - 1); |
|
437 int freeSpace = lastBuffer.capacity() - lastBuffer.limit(); |
|
438 if (remaining <= COPY_THRESHOLD && freeSpace >= remaining) { |
|
439 // append the new data to the unused space in the last buffer |
|
440 int position = lastBuffer.position(); |
|
441 int limit = lastBuffer.limit(); |
|
442 lastBuffer.position(limit); |
|
443 lastBuffer.limit(limit + remaining); |
|
444 lastBuffer.put(bufferToAdd); |
|
445 lastBuffer.position(position); |
|
446 } else { |
|
447 currentList.add(bufferToAdd); |
|
448 } |
|
449 accumulatedBytes += remaining; |
|
450 } |
|
451 return accumulatedBytes; |
|
452 } |
|
453 |
|
454 public static ByteBuffer copy(ByteBuffer src) { |
|
455 ByteBuffer dst = ByteBuffer.allocate(src.remaining()); |
|
456 dst.put(src); |
|
457 dst.flip(); |
|
458 return dst; |
|
459 } |
|
460 |
|
461 public static String dump(Object... objects) { |
|
462 return Arrays.toString(objects); |
|
463 } |
|
464 |
|
465 public static String stringOf(Collection<?> source) { |
|
466 // We don't know anything about toString implementation of this |
|
467 // collection, so let's create an array |
|
468 return Arrays.toString(source.toArray()); |
|
469 } |
|
470 |
|
471 public static long remaining(ByteBuffer[] bufs) { |
|
472 long remain = 0; |
|
473 for (ByteBuffer buf : bufs) { |
|
474 remain += buf.remaining(); |
|
475 } |
|
476 return remain; |
|
477 } |
|
478 |
|
479 public static boolean hasRemaining(List<ByteBuffer> bufs) { |
|
480 synchronized (bufs) { |
|
481 for (ByteBuffer buf : bufs) { |
|
482 if (buf.hasRemaining()) |
|
483 return true; |
|
484 } |
|
485 } |
|
486 return false; |
|
487 } |
|
488 |
|
489 public static long remaining(List<ByteBuffer> bufs) { |
|
490 long remain = 0; |
|
491 synchronized (bufs) { |
|
492 for (ByteBuffer buf : bufs) { |
|
493 remain += buf.remaining(); |
|
494 } |
|
495 } |
|
496 return remain; |
|
497 } |
|
498 |
|
499 public static int remaining(List<ByteBuffer> bufs, int max) { |
|
500 long remain = 0; |
|
501 synchronized (bufs) { |
|
502 for (ByteBuffer buf : bufs) { |
|
503 remain += buf.remaining(); |
|
504 if (remain > max) { |
|
505 throw new IllegalArgumentException("too many bytes"); |
|
506 } |
|
507 } |
|
508 } |
|
509 return (int) remain; |
|
510 } |
|
511 |
|
512 public static long remaining(ByteBufferReference[] refs) { |
|
513 long remain = 0; |
|
514 for (ByteBufferReference ref : refs) { |
|
515 remain += ref.get().remaining(); |
|
516 } |
|
517 return remain; |
|
518 } |
|
519 |
|
520 public static int remaining(ByteBufferReference[] refs, int max) { |
|
521 long remain = 0; |
|
522 for (ByteBufferReference ref : refs) { |
|
523 remain += ref.get().remaining(); |
|
524 if (remain > max) { |
|
525 throw new IllegalArgumentException("too many bytes"); |
|
526 } |
|
527 } |
|
528 return (int) remain; |
|
529 } |
|
530 |
|
531 public static int remaining(ByteBuffer[] refs, int max) { |
|
532 long remain = 0; |
|
533 for (ByteBuffer b : refs) { |
|
534 remain += b.remaining(); |
|
535 if (remain > max) { |
|
536 throw new IllegalArgumentException("too many bytes"); |
|
537 } |
|
538 } |
|
539 return (int) remain; |
|
540 } |
|
541 |
|
542 public static void close(Closeable... closeables) { |
|
543 for (Closeable c : closeables) { |
|
544 try { |
|
545 c.close(); |
|
546 } catch (IOException ignored) { } |
|
547 } |
|
548 } |
|
549 |
|
550 // Put all these static 'empty' singletons here |
|
551 public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0); |
|
552 public static final ByteBuffer[] EMPTY_BB_ARRAY = new ByteBuffer[0]; |
|
553 public static final List<ByteBuffer> EMPTY_BB_LIST = List.of(); |
|
554 public static final ByteBufferReference[] EMPTY_BBR_ARRAY = new ByteBufferReference[0]; |
|
555 |
|
556 /** |
|
557 * Returns a slice of size {@code amount} from the given buffer. If the |
|
558 * buffer contains more data than {@code amount}, then the slice's capacity |
|
559 * ( and, but not just, its limit ) is set to {@code amount}. If the buffer |
|
560 * does not contain more data than {@code amount}, then the slice's capacity |
|
561 * will be the same as the given buffer's capacity. |
|
562 */ |
|
563 public static ByteBuffer sliceWithLimitedCapacity(ByteBuffer buffer, int amount) { |
|
564 final int index = buffer.position() + amount; |
|
565 final int limit = buffer.limit(); |
|
566 if (index != limit) { |
|
567 // additional data in the buffer |
|
568 buffer.limit(index); // ensures that the slice does not go beyond |
|
569 } else { |
|
570 // no additional data in the buffer |
|
571 buffer.limit(buffer.capacity()); // allows the slice full capacity |
|
572 } |
|
573 |
|
574 ByteBuffer newb = buffer.slice(); |
|
575 buffer.position(index); |
|
576 buffer.limit(limit); // restore the original buffer's limit |
|
577 newb.limit(amount); // slices limit to amount (capacity may be greater) |
|
578 return newb; |
|
579 } |
|
580 |
|
581 /** |
|
582 * Get the Charset from the Content-encoding header. Defaults to |
|
583 * UTF_8 |
|
584 */ |
|
585 public static Charset charsetFrom(HttpHeaders headers) { |
|
586 String type = headers.firstValue("Content-type") |
|
587 .orElse("text/html; charset=utf-8"); |
|
588 int i = type.indexOf(";"); |
|
589 if (i >= 0) type = type.substring(i+1); |
|
590 try { |
|
591 HeaderParser parser = new HeaderParser(type); |
|
592 String value = parser.findValue("charset"); |
|
593 if (value == null) return StandardCharsets.UTF_8; |
|
594 return Charset.forName(value); |
|
595 } catch (Throwable x) { |
|
596 Log.logTrace("Can't find charset in \"{0}\" ({1})", type, x); |
|
597 return StandardCharsets.UTF_8; |
|
598 } |
|
599 } |
|
600 |
|
601 public static UncheckedIOException unchecked(IOException e) { |
|
602 return new UncheckedIOException(e); |
|
603 } |
|
604 |
|
605 /** |
|
606 * Get a logger for debug HTTP traces. |
|
607 * |
|
608 * The logger should only be used with levels whose severity is |
|
609 * {@code <= DEBUG}. By default, this logger will forward all messages |
|
610 * logged to an internal logger named "jdk.internal.httpclient.debug". |
|
611 * In addition, if the property -Djdk.internal.httpclient.debug=true is set, |
|
612 * it will print the messages on stderr. |
|
613 * The logger will add some decoration to the printed message, in the form of |
|
614 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} |
|
615 * |
|
616 * @param dbgTag A lambda that returns a string that identifies the caller |
|
617 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") |
|
618 * |
|
619 * @return A logger for HTTP internal debug traces |
|
620 */ |
|
621 public static Logger getDebugLogger(Supplier<String> dbgTag) { |
|
622 return getDebugLogger(dbgTag, DEBUG); |
|
623 } |
|
624 |
|
625 /** |
|
626 * Get a logger for debug HTTP traces.The logger should only be used |
|
627 * with levels whose severity is {@code <= DEBUG}. |
|
628 * |
|
629 * By default, this logger will forward all messages logged to an internal |
|
630 * logger named "jdk.internal.httpclient.debug". |
|
631 * In addition, if the message severity level is >= to |
|
632 * the provided {@code errLevel} it will print the messages on stderr. |
|
633 * The logger will add some decoration to the printed message, in the form of |
|
634 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} |
|
635 * |
|
636 * @apiNote To obtain a logger that will always print things on stderr in |
|
637 * addition to forwarding to the internal logger, use |
|
638 * {@code getDebugLogger(this::dbgTag, Level.ALL);}. |
|
639 * This is also equivalent to calling |
|
640 * {@code getDebugLogger(this::dbgTag, true);}. |
|
641 * To obtain a logger that will only forward to the internal logger, |
|
642 * use {@code getDebugLogger(this::dbgTag, Level.OFF);}. |
|
643 * This is also equivalent to calling |
|
644 * {@code getDebugLogger(this::dbgTag, false);}. |
|
645 * |
|
646 * @param dbgTag A lambda that returns a string that identifies the caller |
|
647 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") |
|
648 * @param errLevel The level above which messages will be also printed on |
|
649 * stderr (in addition to be forwarded to the internal logger). |
|
650 * |
|
651 * @return A logger for HTTP internal debug traces |
|
652 */ |
|
653 static Logger getDebugLogger(Supplier<String> dbgTag, Level errLevel) { |
|
654 return DebugLogger.createHttpLogger(dbgTag, Level.OFF, errLevel); |
|
655 } |
|
656 |
|
657 /** |
|
658 * Get a logger for debug HTTP traces.The logger should only be used |
|
659 * with levels whose severity is {@code <= DEBUG}. |
|
660 * |
|
661 * By default, this logger will forward all messages logged to an internal |
|
662 * logger named "jdk.internal.httpclient.debug". |
|
663 * In addition, the provided boolean {@code on==true}, it will print the |
|
664 * messages on stderr. |
|
665 * The logger will add some decoration to the printed message, in the form of |
|
666 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} |
|
667 * |
|
668 * @apiNote To obtain a logger that will always print things on stderr in |
|
669 * addition to forwarding to the internal logger, use |
|
670 * {@code getDebugLogger(this::dbgTag, true);}. |
|
671 * This is also equivalent to calling |
|
672 * {@code getDebugLogger(this::dbgTag, Level.ALL);}. |
|
673 * To obtain a logger that will only forward to the internal logger, |
|
674 * use {@code getDebugLogger(this::dbgTag, false);}. |
|
675 * This is also equivalent to calling |
|
676 * {@code getDebugLogger(this::dbgTag, Level.OFF);}. |
|
677 * |
|
678 * @param dbgTag A lambda that returns a string that identifies the caller |
|
679 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") |
|
680 * @param on Whether messages should also be printed on |
|
681 * stderr (in addition to be forwarded to the internal logger). |
|
682 * |
|
683 * @return A logger for HTTP internal debug traces |
|
684 */ |
|
685 public static Logger getDebugLogger(Supplier<String> dbgTag, boolean on) { |
|
686 Level errLevel = on ? Level.ALL : Level.OFF; |
|
687 return getDebugLogger(dbgTag, errLevel); |
|
688 } |
|
689 |
|
690 /** |
|
691 * Get a logger for debug HPACK traces.The logger should only be used |
|
692 * with levels whose severity is {@code <= DEBUG}. |
|
693 * |
|
694 * By default, this logger will forward all messages logged to an internal |
|
695 * logger named "jdk.internal.httpclient.hpack.debug". |
|
696 * In addition, if the message severity level is >= to |
|
697 * the provided {@code outLevel} it will print the messages on stdout. |
|
698 * The logger will add some decoration to the printed message, in the form of |
|
699 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} |
|
700 * |
|
701 * @apiNote To obtain a logger that will always print things on stdout in |
|
702 * addition to forwarding to the internal logger, use |
|
703 * {@code getHpackLogger(this::dbgTag, Level.ALL);}. |
|
704 * This is also equivalent to calling |
|
705 * {@code getHpackLogger(this::dbgTag, true);}. |
|
706 * To obtain a logger that will only forward to the internal logger, |
|
707 * use {@code getHpackLogger(this::dbgTag, Level.OFF);}. |
|
708 * This is also equivalent to calling |
|
709 * {@code getHpackLogger(this::dbgTag, false);}. |
|
710 * |
|
711 * @param dbgTag A lambda that returns a string that identifies the caller |
|
712 * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") |
|
713 * @param outLevel The level above which messages will be also printed on |
|
714 * stdout (in addition to be forwarded to the internal logger). |
|
715 * |
|
716 * @return A logger for HPACK internal debug traces |
|
717 */ |
|
718 public static Logger getHpackLogger(Supplier<String> dbgTag, Level outLevel) { |
|
719 Level errLevel = Level.OFF; |
|
720 return DebugLogger.createHpackLogger(dbgTag, outLevel, errLevel); |
|
721 } |
|
722 |
|
723 /** |
|
724 * Get a logger for debug HPACK traces.The logger should only be used |
|
725 * with levels whose severity is {@code <= DEBUG}. |
|
726 * |
|
727 * By default, this logger will forward all messages logged to an internal |
|
728 * logger named "jdk.internal.httpclient.hpack.debug". |
|
729 * In addition, the provided boolean {@code on==true}, it will print the |
|
730 * messages on stdout. |
|
731 * The logger will add some decoration to the printed message, in the form of |
|
732 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} |
|
733 * |
|
734 * @apiNote To obtain a logger that will always print things on stdout in |
|
735 * addition to forwarding to the internal logger, use |
|
736 * {@code getHpackLogger(this::dbgTag, true);}. |
|
737 * This is also equivalent to calling |
|
738 * {@code getHpackLogger(this::dbgTag, Level.ALL);}. |
|
739 * To obtain a logger that will only forward to the internal logger, |
|
740 * use {@code getHpackLogger(this::dbgTag, false);}. |
|
741 * This is also equivalent to calling |
|
742 * {@code getHpackLogger(this::dbgTag, Level.OFF);}. |
|
743 * |
|
744 * @param dbgTag A lambda that returns a string that identifies the caller |
|
745 * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") |
|
746 * @param on Whether messages should also be printed on |
|
747 * stdout (in addition to be forwarded to the internal logger). |
|
748 * |
|
749 * @return A logger for HPACK internal debug traces |
|
750 */ |
|
751 public static Logger getHpackLogger(Supplier<String> dbgTag, boolean on) { |
|
752 Level outLevel = on ? Level.ALL : Level.OFF; |
|
753 return getHpackLogger(dbgTag, outLevel); |
|
754 } |
|
755 } |
|