1 /* 2 * Copyright (c) 2015, 2019, 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 jdk.internal.net.http.common; 27 28 import sun.net.NetProperties; 29 import sun.net.util.IPAddressUtil; 30 import sun.net.www.HeaderParser; 31 32 import javax.net.ssl.ExtendedSSLSession; 33 import javax.net.ssl.SSLException; 34 import javax.net.ssl.SSLHandshakeException; 35 import javax.net.ssl.SSLParameters; 36 import javax.net.ssl.SSLSession; 37 import java.io.ByteArrayOutputStream; 38 import java.io.Closeable; 39 import java.io.IOException; 40 import java.io.PrintStream; 41 import java.io.UncheckedIOException; 42 import java.io.UnsupportedEncodingException; 43 import java.lang.System.Logger.Level; 44 import java.net.ConnectException; 45 import java.net.InetSocketAddress; 46 import java.net.URI; 47 import java.net.URLPermission; 48 import java.net.http.HttpClient; 49 import java.net.http.HttpHeaders; 50 import java.net.http.HttpTimeoutException; 51 import java.nio.ByteBuffer; 52 import java.nio.CharBuffer; 53 import java.nio.charset.CharacterCodingException; 54 import java.nio.charset.Charset; 55 import java.nio.charset.CodingErrorAction; 56 import java.nio.charset.StandardCharsets; 57 import java.security.AccessController; 58 import java.security.PrivilegedAction; 59 import java.text.Normalizer; 60 import java.util.Arrays; 61 import java.util.Collection; 62 import java.util.Collections; 63 import java.util.List; 64 import java.util.Set; 65 import java.util.TreeSet; 66 import java.util.concurrent.CompletableFuture; 67 import java.util.concurrent.CompletionException; 68 import java.util.concurrent.ExecutionException; 69 import java.util.function.BiPredicate; 70 import java.util.function.Function; 71 import java.util.function.Predicate; 72 import java.util.function.Supplier; 73 import java.util.stream.Collectors; 74 import java.util.stream.Stream; 75 76 import static java.lang.String.format; 77 import static java.util.stream.Collectors.joining; 78 import jdk.internal.net.http.HttpRequestImpl; 79 80 /** 81 * Miscellaneous utilities 82 */ 83 public final class Utils { 84 85 public static final boolean ASSERTIONSENABLED; 86 87 static { 88 boolean enabled = false; 89 assert enabled = true; 90 ASSERTIONSENABLED = enabled; 91 } 92 93 // public static final boolean TESTING; 94 // static { 95 // if (ASSERTIONSENABLED) { 96 // PrivilegedAction<String> action = () -> System.getProperty("test.src"); 97 // TESTING = AccessController.doPrivileged(action) != null; 98 // } else TESTING = false; 99 // } 100 public static final boolean DEBUG = // Revisit: temporary dev flag. 101 getBooleanProperty(DebugLogger.HTTP_NAME, false); 102 public static final boolean DEBUG_WS = // Revisit: temporary dev flag. 103 getBooleanProperty(DebugLogger.WS_NAME, false); 104 public static final boolean DEBUG_HPACK = // Revisit: temporary dev flag. 105 getBooleanProperty(DebugLogger.HPACK_NAME, false); 106 public static final boolean TESTING = DEBUG; 107 108 public static final boolean isHostnameVerificationDisabled = // enabled by default 109 hostnameVerificationDisabledValue(); 110 111 private static boolean hostnameVerificationDisabledValue() { 112 String prop = getProperty("jdk.internal.httpclient.disableHostnameVerification"); 113 if (prop == null) 114 return false; 115 return prop.isEmpty() ? true : Boolean.parseBoolean(prop); 116 } 117 118 /** 119 * Allocated buffer size. Must never be higher than 16K. But can be lower 120 * if smaller allocation units preferred. HTTP/2 mandates that all 121 * implementations support frame payloads of at least 16K. 122 */ 123 private static final int DEFAULT_BUFSIZE = 16 * 1024; 124 125 public static final int BUFSIZE = getIntegerNetProperty( 126 "jdk.httpclient.bufsize", DEFAULT_BUFSIZE 127 ); 128 129 public static final BiPredicate<String,String> ACCEPT_ALL = (x,y) -> true; 130 131 private static final Set<String> DISALLOWED_HEADERS_SET = getDisallowedHeaders(); 132 133 private static Set<String> getDisallowedHeaders() { 134 Set<String> headers = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); 135 headers.addAll(Set.of("connection", "content-length", "expect", "host", "upgrade")); 136 137 String v = getNetProperty("jdk.httpclient.allowRestrictedHeaders"); 138 if (v != null) { 139 // any headers found are removed from set. 140 String[] tokens = v.trim().split(","); 141 for (String token : tokens) { 142 headers.remove(token); 143 } 144 return Collections.unmodifiableSet(headers); 145 } else { 146 return Collections.unmodifiableSet(headers); 147 } 148 } 149 150 public static final BiPredicate<String, String> 151 ALLOWED_HEADERS = (header, unused) -> !DISALLOWED_HEADERS_SET.contains(header); 152 153 public static final BiPredicate<String, String> VALIDATE_USER_HEADER = 154 (name, value) -> { 155 assert name != null : "null header name"; 156 assert value != null : "null header value"; 157 if (!isValidName(name)) { 158 throw newIAE("invalid header name: \"%s\"", name); 159 } 160 if (!Utils.ALLOWED_HEADERS.test(name, null)) { 161 throw newIAE("restricted header name: \"%s\"", name); 162 } 163 if (!isValidValue(value)) { 164 throw newIAE("invalid header value for %s: \"%s\"", name, value); 165 } 166 return true; 167 }; 168 169 // Headers that are not generally restricted, and can therefore be set by users, 170 // but can in some contexts be overridden by the implementation. 171 // Currently, only contains "Authorization" which will 172 // be overridden, when an Authenticator is set on the HttpClient. 173 // Needs to be BiPred<String,String> to fit with general form of predicates 174 // used by caller. 175 176 public static final BiPredicate<String, String> CONTEXT_RESTRICTED(HttpClient client) { 177 return (k, v) -> client.authenticator() == null || 178 ! (k.equalsIgnoreCase("Authorization") 179 && k.equalsIgnoreCase("Proxy-Authorization")); 180 } 181 182 private static final Predicate<String> IS_PROXY_HEADER = (k) -> 183 k != null && k.length() > 6 && "proxy-".equalsIgnoreCase(k.substring(0,6)); 184 private static final Predicate<String> NO_PROXY_HEADER = 185 IS_PROXY_HEADER.negate(); 186 private static final Predicate<String> ALL_HEADERS = (s) -> true; 187 188 private static final Set<String> PROXY_AUTH_DISABLED_SCHEMES; 189 private static final Set<String> PROXY_AUTH_TUNNEL_DISABLED_SCHEMES; 190 static { 191 String proxyAuthDisabled = 192 getNetProperty("jdk.http.auth.proxying.disabledSchemes"); 193 String proxyAuthTunnelDisabled = 194 getNetProperty("jdk.http.auth.tunneling.disabledSchemes"); 195 PROXY_AUTH_DISABLED_SCHEMES = 196 proxyAuthDisabled == null ? Set.of() : 197 Stream.of(proxyAuthDisabled.split(",")) 198 .map(String::trim) 199 .filter((s) -> !s.isEmpty()) 200 .collect(Collectors.toUnmodifiableSet()); 201 PROXY_AUTH_TUNNEL_DISABLED_SCHEMES = 202 proxyAuthTunnelDisabled == null ? Set.of() : 203 Stream.of(proxyAuthTunnelDisabled.split(",")) 204 .map(String::trim) 205 .filter((s) -> !s.isEmpty()) 206 .collect(Collectors.toUnmodifiableSet()); 207 } 208 209 public static <T> CompletableFuture<T> wrapForDebug(Logger logger, String name, CompletableFuture<T> cf) { 210 if (logger.on()) { 211 return cf.handle((r,t) -> { 212 logger.log("%s completed %s", name, t == null ? "successfully" : t ); 213 return cf; 214 }).thenCompose(Function.identity()); 215 } else { 216 return cf; 217 } 218 } 219 220 private static final String WSPACES = " \t\r\n"; 221 private static final boolean isAllowedForProxy(String name, 222 String value, 223 Set<String> disabledSchemes, 224 Predicate<String> allowedKeys) { 225 if (!allowedKeys.test(name)) return false; 226 if (disabledSchemes.isEmpty()) return true; 227 if (name.equalsIgnoreCase("proxy-authorization")) { 228 if (value.isEmpty()) return false; 229 for (String scheme : disabledSchemes) { 230 int slen = scheme.length(); 231 int vlen = value.length(); 232 if (vlen == slen) { 233 if (value.equalsIgnoreCase(scheme)) { 234 return false; 235 } 236 } else if (vlen > slen) { 237 if (value.substring(0,slen).equalsIgnoreCase(scheme)) { 238 int c = value.codePointAt(slen); 239 if (WSPACES.indexOf(c) > -1 240 || Character.isSpaceChar(c) 241 || Character.isWhitespace(c)) { 242 return false; 243 } 244 } 245 } 246 } 247 } 248 return true; 249 } 250 251 public static final BiPredicate<String, String> PROXY_TUNNEL_FILTER = 252 (s,v) -> isAllowedForProxy(s, v, PROXY_AUTH_TUNNEL_DISABLED_SCHEMES, 253 IS_PROXY_HEADER); 254 public static final BiPredicate<String, String> PROXY_FILTER = 255 (s,v) -> isAllowedForProxy(s, v, PROXY_AUTH_DISABLED_SCHEMES, 256 ALL_HEADERS); 257 public static final BiPredicate<String, String> NO_PROXY_HEADERS_FILTER = 258 (n,v) -> Utils.NO_PROXY_HEADER.test(n); 259 260 261 public static boolean proxyHasDisabledSchemes(boolean tunnel) { 262 return tunnel ? ! PROXY_AUTH_TUNNEL_DISABLED_SCHEMES.isEmpty() 263 : ! PROXY_AUTH_DISABLED_SCHEMES.isEmpty(); 264 } 265 266 // WebSocket connection Upgrade headers 267 private static final String HEADER_CONNECTION = "Connection"; 268 private static final String HEADER_UPGRADE = "Upgrade"; 269 270 public static final void setWebSocketUpgradeHeaders(HttpRequestImpl request) { 271 request.setSystemHeader(HEADER_UPGRADE, "websocket"); 272 request.setSystemHeader(HEADER_CONNECTION, "Upgrade"); 273 } 274 275 public static IllegalArgumentException newIAE(String message, Object... args) { 276 return new IllegalArgumentException(format(message, args)); 277 } 278 public static ByteBuffer getBuffer() { 279 return ByteBuffer.allocate(BUFSIZE); 280 } 281 282 public static Throwable getCompletionCause(Throwable x) { 283 if (!(x instanceof CompletionException) 284 && !(x instanceof ExecutionException)) return x; 285 final Throwable cause = x.getCause(); 286 if (cause == null) { 287 throw new InternalError("Unexpected null cause", x); 288 } 289 return cause; 290 } 291 292 public static IOException getIOException(Throwable t) { 293 if (t instanceof IOException) { 294 return (IOException) t; 295 } 296 Throwable cause = t.getCause(); 297 if (cause != null) { 298 return getIOException(cause); 299 } 300 return new IOException(t); 301 } 302 303 /** 304 * Adds a more specific exception detail message, based on the given 305 * exception type and the message supplier. This is primarily to present 306 * more descriptive messages in IOExceptions that may be visible to calling 307 * code. 308 * 309 * @return a possibly new exception that has as its detail message, the 310 * message from the messageSupplier, and the given throwable as its 311 * cause. Otherwise returns the given throwable 312 */ 313 public static Throwable wrapWithExtraDetail(Throwable t, 314 Supplier<String> messageSupplier) { 315 if (!(t instanceof IOException)) 316 return t; 317 318 if (t instanceof SSLHandshakeException) 319 return t; // no need to decorate 320 321 String msg = messageSupplier.get(); 322 if (msg == null) 323 return t; 324 325 if (t instanceof ConnectionExpiredException) { 326 if (t.getCause() instanceof SSLHandshakeException) 327 return t; // no need to decorate 328 IOException ioe = new IOException(msg, t.getCause()); 329 t = new ConnectionExpiredException(ioe); 330 } else { 331 IOException ioe = new IOException(msg, t); 332 t = ioe; 333 } 334 return t; 335 } 336 337 private Utils() { } 338 339 /** 340 * Returns the security permissions required to connect to the proxy, or 341 * {@code null} if none is required or applicable. 342 */ 343 public static URLPermission permissionForProxy(InetSocketAddress proxyAddress) { 344 if (proxyAddress == null) 345 return null; 346 347 StringBuilder sb = new StringBuilder(); 348 sb.append("socket://") 349 .append(proxyAddress.getHostString()).append(":") 350 .append(proxyAddress.getPort()); 351 String urlString = sb.toString(); 352 return new URLPermission(urlString, "CONNECT"); 353 } 354 355 /** 356 * Returns the security permission required for the given details. 357 */ 358 public static URLPermission permissionForServer(URI uri, 359 String method, 360 Stream<String> headers) { 361 String urlString = new StringBuilder() 362 .append(uri.getScheme()).append("://") 363 .append(uri.getRawAuthority()) 364 .append(uri.getRawPath()).toString(); 365 366 StringBuilder actionStringBuilder = new StringBuilder(method); 367 String collected = headers.collect(joining(",")); 368 if (!collected.isEmpty()) { 369 actionStringBuilder.append(":").append(collected); 370 } 371 return new URLPermission(urlString, actionStringBuilder.toString()); 372 } 373 374 375 // ABNF primitives defined in RFC 7230 376 private static final boolean[] tchar = new boolean[256]; 377 private static final boolean[] fieldvchar = new boolean[256]; 378 379 static { 380 char[] allowedTokenChars = 381 ("!#$%&'*+-.^_`|~0123456789" + 382 "abcdefghijklmnopqrstuvwxyz" + 383 "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); 384 for (char c : allowedTokenChars) { 385 tchar[c] = true; 386 } 387 for (char c = 0x21; c < 0xFF; c++) { 388 fieldvchar[c] = true; 389 } 390 fieldvchar[0x7F] = false; // a little hole (DEL) in the range 391 } 392 393 /* 394 * Validates a RFC 7230 field-name. 395 */ 396 public static boolean isValidName(String token) { 397 for (int i = 0; i < token.length(); i++) { 398 char c = token.charAt(i); 399 if (c > 255 || !tchar[c]) { 400 return false; 401 } 402 } 403 return !token.isEmpty(); 404 } 405 406 public static class ServerName { 407 ServerName(String name, boolean isLiteral) { 408 this.name = name; 409 this.isLiteral = isLiteral; 410 } 411 412 final String name; 413 final boolean isLiteral; 414 415 public String getName() { 416 return name; 417 } 418 419 public boolean isLiteral() { 420 return isLiteral; 421 } 422 } 423 424 /** 425 * Analyse the given address and determine if it is literal or not, 426 * returning the address in String form. 427 */ 428 public static ServerName getServerName(InetSocketAddress addr) { 429 String host = addr.getHostString(); 430 byte[] literal = IPAddressUtil.textToNumericFormatV4(host); 431 if (literal == null) { 432 // not IPv4 literal. Check IPv6 433 literal = IPAddressUtil.textToNumericFormatV6(host); 434 return new ServerName(host, literal != null); 435 } else { 436 return new ServerName(host, true); 437 } 438 } 439 440 private static boolean isLoopbackLiteral(byte[] bytes) { 441 if (bytes.length == 4) { 442 return bytes[0] == 127; 443 } else if (bytes.length == 16) { 444 for (int i=0; i<14; i++) 445 if (bytes[i] != 0) 446 return false; 447 if (bytes[15] != 1) 448 return false; 449 return true; 450 } else 451 throw new InternalError(); 452 } 453 454 /* 455 * Validates a RFC 7230 field-value. 456 * 457 * "Obsolete line folding" rule 458 * 459 * obs-fold = CRLF 1*( SP / HTAB ) 460 * 461 * is not permitted! 462 */ 463 public static boolean isValidValue(String token) { 464 for (int i = 0; i < token.length(); i++) { 465 char c = token.charAt(i); 466 if (c > 255) { 467 return false; 468 } 469 if (c == ' ' || c == '\t') { 470 continue; 471 } else if (!fieldvchar[c]) { 472 return false; // forbidden byte 473 } 474 } 475 return true; 476 } 477 478 479 public static int getIntegerNetProperty(String name, int defaultValue) { 480 return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> 481 NetProperties.getInteger(name, defaultValue)); 482 } 483 484 public static String getNetProperty(String name) { 485 return AccessController.doPrivileged((PrivilegedAction<String>) () -> 486 NetProperties.get(name)); 487 } 488 489 public static boolean getBooleanProperty(String name, boolean def) { 490 return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> 491 Boolean.parseBoolean(System.getProperty(name, String.valueOf(def)))); 492 } 493 494 public static String getProperty(String name) { 495 return AccessController.doPrivileged((PrivilegedAction<String>) () -> 496 System.getProperty(name)); 497 } 498 499 public static int getIntegerProperty(String name, int defaultValue) { 500 return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> 501 Integer.parseInt(System.getProperty(name, String.valueOf(defaultValue)))); 502 } 503 504 public static SSLParameters copySSLParameters(SSLParameters p) { 505 SSLParameters p1 = new SSLParameters(); 506 p1.setAlgorithmConstraints(p.getAlgorithmConstraints()); 507 p1.setCipherSuites(p.getCipherSuites()); 508 // JDK 8 EXCL START 509 p1.setEnableRetransmissions(p.getEnableRetransmissions()); 510 p1.setMaximumPacketSize(p.getMaximumPacketSize()); 511 // JDK 8 EXCL END 512 p1.setEndpointIdentificationAlgorithm(p.getEndpointIdentificationAlgorithm()); 513 p1.setNeedClientAuth(p.getNeedClientAuth()); 514 String[] protocols = p.getProtocols(); 515 if (protocols != null) { 516 p1.setProtocols(protocols.clone()); 517 } 518 p1.setSNIMatchers(p.getSNIMatchers()); 519 p1.setServerNames(p.getServerNames()); 520 p1.setUseCipherSuitesOrder(p.getUseCipherSuitesOrder()); 521 p1.setWantClientAuth(p.getWantClientAuth()); 522 return p1; 523 } 524 525 /** 526 * Set limit to position, and position to mark. 527 */ 528 public static void flipToMark(ByteBuffer buffer, int mark) { 529 buffer.limit(buffer.position()); 530 buffer.position(mark); 531 } 532 533 public static String stackTrace(Throwable t) { 534 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 535 String s = null; 536 try { 537 PrintStream p = new PrintStream(bos, true, "US-ASCII"); 538 t.printStackTrace(p); 539 s = bos.toString("US-ASCII"); 540 } catch (UnsupportedEncodingException ex) { 541 throw new InternalError(ex); // Can't happen 542 } 543 return s; 544 } 545 546 /** 547 * Copies as much of src to dst as possible. 548 * Return number of bytes copied 549 */ 550 public static int copy(ByteBuffer src, ByteBuffer dst) { 551 int srcLen = src.remaining(); 552 int dstLen = dst.remaining(); 553 if (srcLen > dstLen) { 554 int diff = srcLen - dstLen; 555 int limit = src.limit(); 556 src.limit(limit - diff); 557 dst.put(src); 558 src.limit(limit); 559 } else { 560 dst.put(src); 561 } 562 return srcLen - src.remaining(); 563 } 564 565 /** Threshold beyond which data is no longer copied into the current 566 * buffer, if that buffer has enough unused space. */ 567 private static final int COPY_THRESHOLD = 8192; 568 569 /** 570 * Adds the data from buffersToAdd to currentList. Either 1) appends the 571 * data from a particular buffer to the last buffer in the list ( if 572 * there is enough unused space ), or 2) adds it to the list. 573 * 574 * @return the number of bytes added 575 */ 576 public static long accumulateBuffers(List<ByteBuffer> currentList, 577 List<ByteBuffer> buffersToAdd) { 578 long accumulatedBytes = 0; 579 for (ByteBuffer bufferToAdd : buffersToAdd) { 580 int remaining = bufferToAdd.remaining(); 581 if (remaining <= 0) 582 continue; 583 int listSize = currentList.size(); 584 if (listSize == 0) { 585 currentList.add(bufferToAdd); 586 accumulatedBytes = remaining; 587 continue; 588 } 589 590 ByteBuffer lastBuffer = currentList.get(listSize - 1); 591 int freeSpace = lastBuffer.capacity() - lastBuffer.limit(); 592 if (remaining <= COPY_THRESHOLD && freeSpace >= remaining) { 593 // append the new data to the unused space in the last buffer 594 int position = lastBuffer.position(); 595 int limit = lastBuffer.limit(); 596 lastBuffer.position(limit); 597 lastBuffer.limit(limit + remaining); 598 lastBuffer.put(bufferToAdd); 599 lastBuffer.position(position); 600 } else { 601 currentList.add(bufferToAdd); 602 } 603 accumulatedBytes += remaining; 604 } 605 return accumulatedBytes; 606 } 607 608 public static ByteBuffer copy(ByteBuffer src) { 609 ByteBuffer dst = ByteBuffer.allocate(src.remaining()); 610 dst.put(src); 611 dst.flip(); 612 return dst; 613 } 614 615 public static ByteBuffer copyAligned(ByteBuffer src) { 616 int len = src.remaining(); 617 int size = ((len + 7) >> 3) << 3; 618 assert size >= len; 619 ByteBuffer dst = ByteBuffer.allocate(size); 620 dst.put(src); 621 dst.flip(); 622 return dst; 623 } 624 625 public static String dump(Object... objects) { 626 return Arrays.toString(objects); 627 } 628 629 public static String stringOf(Collection<?> source) { 630 // We don't know anything about toString implementation of this 631 // collection, so let's create an array 632 return Arrays.toString(source.toArray()); 633 } 634 635 public static long remaining(ByteBuffer[] bufs) { 636 long remain = 0; 637 for (ByteBuffer buf : bufs) { 638 remain += buf.remaining(); 639 } 640 return remain; 641 } 642 643 public static boolean hasRemaining(List<ByteBuffer> bufs) { 644 synchronized (bufs) { 645 for (ByteBuffer buf : bufs) { 646 if (buf.hasRemaining()) 647 return true; 648 } 649 } 650 return false; 651 } 652 653 public static long remaining(List<ByteBuffer> bufs) { 654 long remain = 0; 655 synchronized (bufs) { 656 for (ByteBuffer buf : bufs) { 657 remain += buf.remaining(); 658 } 659 } 660 return remain; 661 } 662 663 public static int remaining(List<ByteBuffer> bufs, int max) { 664 long remain = 0; 665 synchronized (bufs) { 666 for (ByteBuffer buf : bufs) { 667 remain += buf.remaining(); 668 if (remain > max) { 669 throw new IllegalArgumentException("too many bytes"); 670 } 671 } 672 } 673 return (int) remain; 674 } 675 676 public static int remaining(ByteBuffer[] refs, int max) { 677 long remain = 0; 678 for (ByteBuffer b : refs) { 679 remain += b.remaining(); 680 if (remain > max) { 681 throw new IllegalArgumentException("too many bytes"); 682 } 683 } 684 return (int) remain; 685 } 686 687 public static void close(Closeable... closeables) { 688 for (Closeable c : closeables) { 689 try { 690 c.close(); 691 } catch (IOException ignored) { } 692 } 693 } 694 695 // Put all these static 'empty' singletons here 696 public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0); 697 public static final ByteBuffer[] EMPTY_BB_ARRAY = new ByteBuffer[0]; 698 public static final List<ByteBuffer> EMPTY_BB_LIST = List.of(); 699 700 /** 701 * Returns a slice of size {@code amount} from the given buffer. If the 702 * buffer contains more data than {@code amount}, then the slice's capacity 703 * ( and, but not just, its limit ) is set to {@code amount}. If the buffer 704 * does not contain more data than {@code amount}, then the slice's capacity 705 * will be the same as the given buffer's capacity. 706 */ 707 public static ByteBuffer sliceWithLimitedCapacity(ByteBuffer buffer, int amount) { 708 final int index = buffer.position() + amount; 709 final int limit = buffer.limit(); 710 if (index != limit) { 711 // additional data in the buffer 712 buffer.limit(index); // ensures that the slice does not go beyond 713 } else { 714 // no additional data in the buffer 715 buffer.limit(buffer.capacity()); // allows the slice full capacity 716 } 717 718 ByteBuffer newb = buffer.slice(); 719 buffer.position(index); 720 buffer.limit(limit); // restore the original buffer's limit 721 newb.limit(amount); // slices limit to amount (capacity may be greater) 722 return newb; 723 } 724 725 /** 726 * Get the Charset from the Content-encoding header. Defaults to 727 * UTF_8 728 */ 729 public static Charset charsetFrom(HttpHeaders headers) { 730 String type = headers.firstValue("Content-type") 731 .orElse("text/html; charset=utf-8"); 732 int i = type.indexOf(";"); 733 if (i >= 0) type = type.substring(i+1); 734 try { 735 HeaderParser parser = new HeaderParser(type); 736 String value = parser.findValue("charset"); 737 if (value == null) return StandardCharsets.UTF_8; 738 return Charset.forName(value); 739 } catch (Throwable x) { 740 Log.logTrace("Can't find charset in \"{0}\" ({1})", type, x); 741 return StandardCharsets.UTF_8; 742 } 743 } 744 745 public static UncheckedIOException unchecked(IOException e) { 746 return new UncheckedIOException(e); 747 } 748 749 /** 750 * Get a logger for debug HTTP traces. 751 * 752 * The logger should only be used with levels whose severity is 753 * {@code <= DEBUG}. By default, this logger will forward all messages 754 * logged to an internal logger named "jdk.internal.httpclient.debug". 755 * In addition, if the property -Djdk.internal.httpclient.debug=true is set, 756 * it will print the messages on stderr. 757 * The logger will add some decoration to the printed message, in the form of 758 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 759 * 760 * @param dbgTag A lambda that returns a string that identifies the caller 761 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") 762 * 763 * @return A logger for HTTP internal debug traces 764 */ 765 public static Logger getDebugLogger(Supplier<String> dbgTag) { 766 return getDebugLogger(dbgTag, DEBUG); 767 } 768 769 /** 770 * Get a logger for debug HTTP traces.The logger should only be used 771 * with levels whose severity is {@code <= DEBUG}. 772 * 773 * By default, this logger will forward all messages logged to an internal 774 * logger named "jdk.internal.httpclient.debug". 775 * In addition, if the message severity level is >= to 776 * the provided {@code errLevel} it will print the messages on stderr. 777 * The logger will add some decoration to the printed message, in the form of 778 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 779 * 780 * @apiNote To obtain a logger that will always print things on stderr in 781 * addition to forwarding to the internal logger, use 782 * {@code getDebugLogger(this::dbgTag, Level.ALL);}. 783 * This is also equivalent to calling 784 * {@code getDebugLogger(this::dbgTag, true);}. 785 * To obtain a logger that will only forward to the internal logger, 786 * use {@code getDebugLogger(this::dbgTag, Level.OFF);}. 787 * This is also equivalent to calling 788 * {@code getDebugLogger(this::dbgTag, false);}. 789 * 790 * @param dbgTag A lambda that returns a string that identifies the caller 791 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") 792 * @param errLevel The level above which messages will be also printed on 793 * stderr (in addition to be forwarded to the internal logger). 794 * 795 * @return A logger for HTTP internal debug traces 796 */ 797 static Logger getDebugLogger(Supplier<String> dbgTag, Level errLevel) { 798 return DebugLogger.createHttpLogger(dbgTag, Level.OFF, errLevel); 799 } 800 801 /** 802 * Get a logger for debug HTTP traces.The logger should only be used 803 * with levels whose severity is {@code <= DEBUG}. 804 * 805 * By default, this logger will forward all messages logged to an internal 806 * logger named "jdk.internal.httpclient.debug". 807 * In addition, the provided boolean {@code on==true}, it will print the 808 * messages on stderr. 809 * The logger will add some decoration to the printed message, in the form of 810 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 811 * 812 * @apiNote To obtain a logger that will always print things on stderr in 813 * addition to forwarding to the internal logger, use 814 * {@code getDebugLogger(this::dbgTag, true);}. 815 * This is also equivalent to calling 816 * {@code getDebugLogger(this::dbgTag, Level.ALL);}. 817 * To obtain a logger that will only forward to the internal logger, 818 * use {@code getDebugLogger(this::dbgTag, false);}. 819 * This is also equivalent to calling 820 * {@code getDebugLogger(this::dbgTag, Level.OFF);}. 821 * 822 * @param dbgTag A lambda that returns a string that identifies the caller 823 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") 824 * @param on Whether messages should also be printed on 825 * stderr (in addition to be forwarded to the internal logger). 826 * 827 * @return A logger for HTTP internal debug traces 828 */ 829 public static Logger getDebugLogger(Supplier<String> dbgTag, boolean on) { 830 Level errLevel = on ? Level.ALL : Level.OFF; 831 return getDebugLogger(dbgTag, errLevel); 832 } 833 834 /** 835 * Return the host string from a HttpRequestImpl 836 * 837 * @param request 838 * @return 839 */ 840 public static String hostString(HttpRequestImpl request) { 841 URI uri = request.uri(); 842 int port = uri.getPort(); 843 String host = uri.getHost(); 844 845 boolean defaultPort; 846 if (port == -1) { 847 defaultPort = true; 848 } else if (uri.getScheme().equalsIgnoreCase("https")) { 849 defaultPort = port == 443; 850 } else { 851 defaultPort = port == 80; 852 } 853 854 if (defaultPort) { 855 return host; 856 } else { 857 return host + ":" + Integer.toString(port); 858 } 859 } 860 861 /** 862 * Get a logger for debug HPACK traces.The logger should only be used 863 * with levels whose severity is {@code <= DEBUG}. 864 * 865 * By default, this logger will forward all messages logged to an internal 866 * logger named "jdk.internal.httpclient.hpack.debug". 867 * In addition, if the message severity level is >= to 868 * the provided {@code errLevel} it will print the messages on stderr. 869 * The logger will add some decoration to the printed message, in the form of 870 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 871 * 872 * @apiNote To obtain a logger that will always print things on stderr in 873 * addition to forwarding to the internal logger, use 874 * {@code getHpackLogger(this::dbgTag, Level.ALL);}. 875 * This is also equivalent to calling 876 * {@code getHpackLogger(this::dbgTag, true);}. 877 * To obtain a logger that will only forward to the internal logger, 878 * use {@code getHpackLogger(this::dbgTag, Level.OFF);}. 879 * This is also equivalent to calling 880 * {@code getHpackLogger(this::dbgTag, false);}. 881 * 882 * @param dbgTag A lambda that returns a string that identifies the caller 883 * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") 884 * @param errLevel The level above which messages will be also printed on 885 * stderr (in addition to be forwarded to the internal logger). 886 * 887 * @return A logger for HPACK internal debug traces 888 */ 889 public static Logger getHpackLogger(Supplier<String> dbgTag, Level errLevel) { 890 Level outLevel = Level.OFF; 891 return DebugLogger.createHpackLogger(dbgTag, outLevel, errLevel); 892 } 893 894 /** 895 * Get a logger for debug HPACK traces.The logger should only be used 896 * with levels whose severity is {@code <= DEBUG}. 897 * 898 * By default, this logger will forward all messages logged to an internal 899 * logger named "jdk.internal.httpclient.hpack.debug". 900 * In addition, the provided boolean {@code on==true}, it will print the 901 * messages on stderr. 902 * The logger will add some decoration to the printed message, in the form of 903 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 904 * 905 * @apiNote To obtain a logger that will always print things on stderr in 906 * addition to forwarding to the internal logger, use 907 * {@code getHpackLogger(this::dbgTag, true);}. 908 * This is also equivalent to calling 909 * {@code getHpackLogger(this::dbgTag, Level.ALL);}. 910 * To obtain a logger that will only forward to the internal logger, 911 * use {@code getHpackLogger(this::dbgTag, false);}. 912 * This is also equivalent to calling 913 * {@code getHpackLogger(this::dbgTag, Level.OFF);}. 914 * 915 * @param dbgTag A lambda that returns a string that identifies the caller 916 * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") 917 * @param on Whether messages should also be printed on 918 * stderr (in addition to be forwarded to the internal logger). 919 * 920 * @return A logger for HPACK internal debug traces 921 */ 922 public static Logger getHpackLogger(Supplier<String> dbgTag, boolean on) { 923 Level errLevel = on ? Level.ALL : Level.OFF; 924 return getHpackLogger(dbgTag, errLevel); 925 } 926 927 /** 928 * Get a logger for debug WebSocket traces.The logger should only be used 929 * with levels whose severity is {@code <= DEBUG}. 930 * 931 * By default, this logger will forward all messages logged to an internal 932 * logger named "jdk.internal.httpclient.websocket.debug". 933 * In addition, if the message severity level is >= to 934 * the provided {@code errLevel} it will print the messages on stderr. 935 * The logger will add some decoration to the printed message, in the form of 936 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 937 * 938 * @apiNote To obtain a logger that will always print things on stderr in 939 * addition to forwarding to the internal logger, use 940 * {@code getWebSocketLogger(this::dbgTag, Level.ALL);}. 941 * This is also equivalent to calling 942 * {@code getWSLogger(this::dbgTag, true);}. 943 * To obtain a logger that will only forward to the internal logger, 944 * use {@code getWebSocketLogger(this::dbgTag, Level.OFF);}. 945 * This is also equivalent to calling 946 * {@code getWSLogger(this::dbgTag, false);}. 947 * 948 * @param dbgTag A lambda that returns a string that identifies the caller 949 * (e.g: "WebSocket(3)") 950 * @param errLevel The level above which messages will be also printed on 951 * stderr (in addition to be forwarded to the internal logger). 952 * 953 * @return A logger for HPACK internal debug traces 954 */ 955 public static Logger getWebSocketLogger(Supplier<String> dbgTag, Level errLevel) { 956 Level outLevel = Level.OFF; 957 return DebugLogger.createWebSocketLogger(dbgTag, outLevel, errLevel); 958 } 959 960 /** 961 * Get a logger for debug WebSocket traces.The logger should only be used 962 * with levels whose severity is {@code <= DEBUG}. 963 * 964 * By default, this logger will forward all messages logged to an internal 965 * logger named "jdk.internal.httpclient.websocket.debug". 966 * In addition, the provided boolean {@code on==true}, it will print the 967 * messages on stderr. 968 * The logger will add some decoration to the printed message, in the form of 969 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 970 * 971 * @apiNote To obtain a logger that will always print things on stderr in 972 * addition to forwarding to the internal logger, use 973 * {@code getWebSocketLogger(this::dbgTag, true);}. 974 * This is also equivalent to calling 975 * {@code getWebSocketLogger(this::dbgTag, Level.ALL);}. 976 * To obtain a logger that will only forward to the internal logger, 977 * use {@code getWebSocketLogger(this::dbgTag, false);}. 978 * This is also equivalent to calling 979 * {@code getHpackLogger(this::dbgTag, Level.OFF);}. 980 * 981 * @param dbgTag A lambda that returns a string that identifies the caller 982 * (e.g: "WebSocket(3)") 983 * @param on Whether messages should also be printed on 984 * stderr (in addition to be forwarded to the internal logger). 985 * 986 * @return A logger for WebSocket internal debug traces 987 */ 988 public static Logger getWebSocketLogger(Supplier<String> dbgTag, boolean on) { 989 Level errLevel = on ? Level.ALL : Level.OFF; 990 return getWebSocketLogger(dbgTag, errLevel); 991 } 992 993 /** 994 * SSLSessions returned to user are wrapped in an immutable object 995 */ 996 public static SSLSession immutableSession(SSLSession session) { 997 if (session instanceof ExtendedSSLSession) 998 return new ImmutableExtendedSSLSession((ExtendedSSLSession)session); 999 else 1000 return new ImmutableSSLSession(session); 1001 } 1002 1003 /** 1004 * Enabled by default. May be disabled for testing. Use with care 1005 */ 1006 public static boolean isHostnameVerificationDisabled() { 1007 return isHostnameVerificationDisabled; 1008 } 1009 1010 public static InetSocketAddress resolveAddress(InetSocketAddress address) { 1011 if (address != null && address.isUnresolved()) { 1012 // The default proxy selector may select a proxy whose address is 1013 // unresolved. We must resolve the address before connecting to it. 1014 address = new InetSocketAddress(address.getHostString(), address.getPort()); 1015 } 1016 return address; 1017 } 1018 1019 public static Throwable toConnectException(Throwable e) { 1020 if (e == null) return null; 1021 e = getCompletionCause(e); 1022 if (e instanceof ConnectException) return e; 1023 if (e instanceof SecurityException) return e; 1024 if (e instanceof SSLException) return e; 1025 if (e instanceof Error) return e; 1026 if (e instanceof HttpTimeoutException) return e; 1027 Throwable cause = e; 1028 e = new ConnectException(e.getMessage()); 1029 e.initCause(cause); 1030 return e; 1031 } 1032 1033 /** 1034 * Returns the smallest (closest to zero) positive number {@code m} (which 1035 * is also a power of 2) such that {@code n <= m}. 1036 * <pre>{@code 1037 * n pow2Size(n) 1038 * ----------------------- 1039 * 0 1 1040 * 1 1 1041 * 2 2 1042 * 3 4 1043 * 4 4 1044 * 5 8 1045 * 6 8 1046 * 7 8 1047 * 8 8 1048 * 9 16 1049 * 10 16 1050 * ... ... 1051 * 2147483647 1073741824 1052 * } </pre> 1053 * 1054 * The result is capped at {@code 1 << 30} as beyond that int wraps. 1055 * 1056 * @param n 1057 * capacity 1058 * 1059 * @return the size of the array 1060 * @apiNote Used to size arrays in circular buffers (rings), usually in 1061 * order to squeeze extra performance substituting {@code %} operation for 1062 * {@code &}, which is up to 2 times faster. 1063 */ 1064 public static int pow2Size(int n) { 1065 if (n < 0) { 1066 throw new IllegalArgumentException(); 1067 } else if (n == 0) { 1068 return 1; 1069 } else if (n >= (1 << 30)) { // 2^31 is a negative int 1070 return 1 << 30; 1071 } else { 1072 return 1 << (32 - Integer.numberOfLeadingZeros(n - 1)); 1073 } 1074 } 1075 1076 // -- toAsciiString-like support to encode path and query URI segments 1077 1078 private static final char[] hexDigits = { 1079 '0', '1', '2', '3', '4', '5', '6', '7', 1080 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 1081 }; 1082 1083 private static void appendEscape(StringBuilder sb, byte b) { 1084 sb.append('%'); 1085 sb.append(hexDigits[(b >> 4) & 0x0f]); 1086 sb.append(hexDigits[(b >> 0) & 0x0f]); 1087 } 1088 1089 // Encodes all characters >= \u0080 into escaped, normalized UTF-8 octets, 1090 // assuming that s is otherwise legal 1091 // 1092 public static String encode(String s) { 1093 int n = s.length(); 1094 if (n == 0) 1095 return s; 1096 1097 // First check whether we actually need to encode 1098 for (int i = 0;;) { 1099 if (s.charAt(i) >= '\u0080') 1100 break; 1101 if (++i >= n) 1102 return s; 1103 } 1104 1105 String ns = Normalizer.normalize(s, Normalizer.Form.NFC); 1106 ByteBuffer bb = null; 1107 try { 1108 bb = StandardCharsets.UTF_8.newEncoder() 1109 .onMalformedInput(CodingErrorAction.REPORT) 1110 .onUnmappableCharacter(CodingErrorAction.REPORT) 1111 .encode(CharBuffer.wrap(ns)); 1112 } catch (CharacterCodingException x) { 1113 assert false : x; 1114 } 1115 1116 StringBuilder sb = new StringBuilder(); 1117 while (bb.hasRemaining()) { 1118 int b = bb.get() & 0xff; 1119 if (b >= 0x80) 1120 appendEscape(sb, (byte)b); 1121 else 1122 sb.append((char)b); 1123 } 1124 return sb.toString(); 1125 } 1126 }