1 /* 2 * Copyright (c) 2015, 2017, 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.incubator.http.internal.common; 27 28 import jdk.incubator.http.HttpHeaders; 29 import sun.net.NetProperties; 30 import sun.net.util.IPAddressUtil; 31 32 import javax.net.ssl.SSLParameters; 33 import java.io.ByteArrayOutputStream; 34 import java.io.Closeable; 35 import java.io.IOException; 36 import java.io.PrintStream; 37 import java.io.UncheckedIOException; 38 import java.io.UnsupportedEncodingException; 39 import java.lang.System.Logger; 40 import java.lang.System.Logger.Level; 41 import java.net.InetSocketAddress; 42 import java.net.URI; 43 import java.net.URLPermission; 44 import java.nio.ByteBuffer; 45 import java.nio.charset.Charset; 46 import java.nio.charset.StandardCharsets; 47 import java.security.AccessController; 48 import java.security.PrivilegedAction; 49 import java.util.Arrays; 50 import java.util.Collection; 51 import java.util.List; 52 import java.util.Set; 53 import java.util.concurrent.CompletionException; 54 import java.util.concurrent.ExecutionException; 55 import java.util.function.Predicate; 56 import java.util.function.Supplier; 57 import java.util.stream.Stream; 58 59 import static java.util.stream.Collectors.joining; 60 61 /** 62 * Miscellaneous utilities 63 */ 64 public final class Utils { 65 66 public static final boolean ASSERTIONSENABLED; 67 static { 68 boolean enabled = false; 69 assert enabled = true; 70 ASSERTIONSENABLED = enabled; 71 } 72 // public static final boolean TESTING; 73 // static { 74 // if (ASSERTIONSENABLED) { 75 // PrivilegedAction<String> action = () -> System.getProperty("test.src"); 76 // TESTING = AccessController.doPrivileged(action) != null; 77 // } else TESTING = false; 78 // } 79 public static final boolean DEBUG = // Revisit: temporary dev flag. 80 getBooleanProperty(DebugLogger.HTTP_NAME, false); 81 public static final boolean DEBUG_HPACK = // Revisit: temporary dev flag. 82 getBooleanProperty(DebugLogger.HPACK_NAME, false); 83 public static final boolean TESTING = DEBUG; 84 85 /** 86 * Allocated buffer size. Must never be higher than 16K. But can be lower 87 * if smaller allocation units preferred. HTTP/2 mandates that all 88 * implementations support frame payloads of at least 16K. 89 */ 90 private static final int DEFAULT_BUFSIZE = 16 * 1024; 91 92 public static final int BUFSIZE = getIntegerNetProperty( 93 "jdk.httpclient.bufsize", DEFAULT_BUFSIZE 94 ); 95 96 private static final Set<String> DISALLOWED_HEADERS_SET = Set.of( 97 "authorization", "connection", "cookie", "content-length", 98 "date", "expect", "from", "host", "origin", "proxy-authorization", 99 "referer", "user-agent", "upgrade", "via", "warning"); 100 101 public static final Predicate<String> 102 ALLOWED_HEADERS = header -> !Utils.DISALLOWED_HEADERS_SET.contains(header); 103 104 public static ByteBuffer getBuffer() { 105 return ByteBuffer.allocate(BUFSIZE); 106 } 107 108 public static Throwable getCompletionCause(Throwable x) { 109 if (!(x instanceof CompletionException) 110 && !(x instanceof ExecutionException)) return x; 111 final Throwable cause = x.getCause(); 112 return cause == null ? x : cause; 113 } 114 115 public static IOException getIOException(Throwable t) { 116 if (t instanceof IOException) { 117 return (IOException) t; 118 } 119 Throwable cause = t.getCause(); 120 if (cause != null) { 121 return getIOException(cause); 122 } 123 return new IOException(t); 124 } 125 126 private Utils() { } 127 128 /** 129 * Returns the security permissions required to connect to the proxy, or 130 * {@code null} if none is required or applicable. 131 */ 132 public static URLPermission permissionForProxy(InetSocketAddress proxyAddress) { 133 if (proxyAddress == null) 134 return null; 135 136 StringBuilder sb = new StringBuilder(); 137 sb.append("socket://") 138 .append(proxyAddress.getHostString()).append(":") 139 .append(proxyAddress.getPort()); 140 String urlString = sb.toString(); 141 return new URLPermission(urlString, "CONNECT"); 142 } 143 144 /** 145 * Returns the security permission required for the given details. 146 */ 147 public static URLPermission permissionForServer(URI uri, 148 String method, 149 Stream<String> headers) { 150 String urlString = new StringBuilder() 151 .append(uri.getScheme()).append("://") 152 .append(uri.getAuthority()) 153 .append(uri.getPath()).toString(); 154 155 StringBuilder actionStringBuilder = new StringBuilder(method); 156 String collected = headers.collect(joining(",")); 157 if (!collected.isEmpty()) { 158 actionStringBuilder.append(":").append(collected); 159 } 160 return new URLPermission(urlString, actionStringBuilder.toString()); 161 } 162 163 164 // ABNF primitives defined in RFC 7230 165 private static final boolean[] tchar = new boolean[256]; 166 private static final boolean[] fieldvchar = new boolean[256]; 167 168 static { 169 char[] allowedTokenChars = 170 ("!#$%&'*+-.^_`|~0123456789" + 171 "abcdefghijklmnopqrstuvwxyz" + 172 "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); 173 for (char c : allowedTokenChars) { 174 tchar[c] = true; 175 } 176 for (char c = 0x21; c < 0xFF; c++) { 177 fieldvchar[c] = true; 178 } 179 fieldvchar[0x7F] = false; // a little hole (DEL) in the range 180 } 181 182 /* 183 * Validates a RFC 7230 field-name. 184 */ 185 public static boolean isValidName(String token) { 186 for (int i = 0; i < token.length(); i++) { 187 char c = token.charAt(i); 188 if (c > 255 || !tchar[c]) { 189 return false; 190 } 191 } 192 return !token.isEmpty(); 193 } 194 195 /** 196 * If the address was created with a domain name, then return 197 * the domain name string. If created with a literal IP address 198 * then return null. We do this to avoid doing a reverse lookup 199 * Used to populate the TLS SNI parameter. So, SNI is only set 200 * when a domain name was supplied. 201 */ 202 public static String getServerName(InetSocketAddress addr) { 203 String host = addr.getHostString(); 204 if (IPAddressUtil.textToNumericFormatV4(host) != null) 205 return null; 206 if (IPAddressUtil.textToNumericFormatV6(host) != null) 207 return null; 208 return host; 209 } 210 211 /* 212 * Validates a RFC 7230 field-value. 213 * 214 * "Obsolete line folding" rule 215 * 216 * obs-fold = CRLF 1*( SP / HTAB ) 217 * 218 * is not permitted! 219 */ 220 public static boolean isValidValue(String token) { 221 boolean accepted = true; 222 for (int i = 0; i < token.length(); i++) { 223 char c = token.charAt(i); 224 if (c > 255) { 225 return false; 226 } 227 if (accepted) { 228 if (c == ' ' || c == '\t') { 229 accepted = false; 230 } else if (!fieldvchar[c]) { 231 return false; // forbidden byte 232 } 233 } else { 234 if (c != ' ' && c != '\t') { 235 if (fieldvchar[c]) { 236 accepted = true; 237 } else { 238 return false; // forbidden byte 239 } 240 } 241 } 242 } 243 return accepted; 244 } 245 246 public static int getIntegerNetProperty(String name, int defaultValue) { 247 return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> 248 NetProperties.getInteger(name, defaultValue)); 249 } 250 251 static String getNetProperty(String name) { 252 return AccessController.doPrivileged((PrivilegedAction<String>) () -> 253 NetProperties.get(name)); 254 } 255 256 static boolean getBooleanProperty(String name, boolean def) { 257 return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> 258 Boolean.parseBoolean(System.getProperty(name, String.valueOf(def)))); 259 } 260 261 public static SSLParameters copySSLParameters(SSLParameters p) { 262 SSLParameters p1 = new SSLParameters(); 263 p1.setAlgorithmConstraints(p.getAlgorithmConstraints()); 264 p1.setCipherSuites(p.getCipherSuites()); 265 // JDK 8 EXCL START 266 p1.setEnableRetransmissions(p.getEnableRetransmissions()); 267 p1.setMaximumPacketSize(p.getMaximumPacketSize()); 268 // JDK 8 EXCL END 269 p1.setEndpointIdentificationAlgorithm(p.getEndpointIdentificationAlgorithm()); 270 p1.setNeedClientAuth(p.getNeedClientAuth()); 271 String[] protocols = p.getProtocols(); 272 if (protocols != null) { 273 p1.setProtocols(protocols.clone()); 274 } 275 p1.setSNIMatchers(p.getSNIMatchers()); 276 p1.setServerNames(p.getServerNames()); 277 p1.setUseCipherSuitesOrder(p.getUseCipherSuitesOrder()); 278 p1.setWantClientAuth(p.getWantClientAuth()); 279 return p1; 280 } 281 282 /** 283 * Set limit to position, and position to mark. 284 */ 285 public static void flipToMark(ByteBuffer buffer, int mark) { 286 buffer.limit(buffer.position()); 287 buffer.position(mark); 288 } 289 290 public static String stackTrace(Throwable t) { 291 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 292 String s = null; 293 try { 294 PrintStream p = new PrintStream(bos, true, "US-ASCII"); 295 t.printStackTrace(p); 296 s = bos.toString("US-ASCII"); 297 } catch (UnsupportedEncodingException ex) { 298 throw new InternalError(ex); // Can't happen 299 } 300 return s; 301 } 302 303 /** 304 * Copies as much of src to dst as possible. 305 * Return number of bytes copied 306 */ 307 public static int copy(ByteBuffer src, ByteBuffer dst) { 308 int srcLen = src.remaining(); 309 int dstLen = dst.remaining(); 310 if (srcLen > dstLen) { 311 int diff = srcLen - dstLen; 312 int limit = src.limit(); 313 src.limit(limit - diff); 314 dst.put(src); 315 src.limit(limit); 316 } else { 317 dst.put(src); 318 } 319 return srcLen - src.remaining(); 320 } 321 322 /** Threshold beyond which data is no longer copied into the current 323 * buffer, if that buffer has enough unused space. */ 324 private static final int COPY_THRESHOLD = 8192; 325 326 /** 327 * Adds the data from buffersToAdd to currentList. Either 1) appends the 328 * data from a particular buffer to the last buffer in the list ( if 329 * there is enough unused space ), or 2) adds it to the list. 330 * 331 * @return the number of bytes added 332 */ 333 public static long accumulateBuffers(List<ByteBuffer> currentList, 334 List<ByteBuffer> buffersToAdd) { 335 long accumulatedBytes = 0; 336 for (ByteBuffer bufferToAdd : buffersToAdd) { 337 int remaining = bufferToAdd.remaining(); 338 if (remaining <= 0) 339 continue; 340 int listSize = currentList.size(); 341 if (listSize == 0) { 342 currentList.add(bufferToAdd); 343 accumulatedBytes = remaining; 344 continue; 345 } 346 347 ByteBuffer lastBuffer = currentList.get(listSize - 1); 348 int freeSpace = lastBuffer.capacity() - lastBuffer.limit(); 349 if (remaining <= COPY_THRESHOLD && freeSpace >= remaining) { 350 // append the new data to the unused space in the last buffer 351 int position = lastBuffer.position(); 352 int limit = lastBuffer.limit(); 353 lastBuffer.position(limit); 354 lastBuffer.limit(limit + remaining); 355 lastBuffer.put(bufferToAdd); 356 lastBuffer.position(position); 357 } else { 358 currentList.add(bufferToAdd); 359 } 360 accumulatedBytes += remaining; 361 } 362 return accumulatedBytes; 363 } 364 365 public static ByteBuffer copy(ByteBuffer src) { 366 ByteBuffer dst = ByteBuffer.allocate(src.remaining()); 367 dst.put(src); 368 dst.flip(); 369 return dst; 370 } 371 372 public static String dump(Object... objects) { 373 return Arrays.toString(objects); 374 } 375 376 public static String stringOf(Collection<?> source) { 377 // We don't know anything about toString implementation of this 378 // collection, so let's create an array 379 return Arrays.toString(source.toArray()); 380 } 381 382 public static long remaining(ByteBuffer[] bufs) { 383 long remain = 0; 384 for (ByteBuffer buf : bufs) { 385 remain += buf.remaining(); 386 } 387 return remain; 388 } 389 390 public static boolean hasRemaining(List<ByteBuffer> bufs) { 391 synchronized (bufs) { 392 for (ByteBuffer buf : bufs) { 393 if (buf.hasRemaining()) 394 return true; 395 } 396 } 397 return false; 398 } 399 400 public static long remaining(List<ByteBuffer> bufs) { 401 long remain = 0; 402 synchronized (bufs) { 403 for (ByteBuffer buf : bufs) { 404 remain += buf.remaining(); 405 } 406 } 407 return remain; 408 } 409 410 public static int remaining(List<ByteBuffer> bufs, int max) { 411 long remain = 0; 412 synchronized (bufs) { 413 for (ByteBuffer buf : bufs) { 414 remain += buf.remaining(); 415 if (remain > max) { 416 throw new IllegalArgumentException("too many bytes"); 417 } 418 } 419 } 420 return (int) remain; 421 } 422 423 public static long remaining(ByteBufferReference[] refs) { 424 long remain = 0; 425 for (ByteBufferReference ref : refs) { 426 remain += ref.get().remaining(); 427 } 428 return remain; 429 } 430 431 public static int remaining(ByteBufferReference[] refs, int max) { 432 long remain = 0; 433 for (ByteBufferReference ref : refs) { 434 remain += ref.get().remaining(); 435 if (remain > max) { 436 throw new IllegalArgumentException("too many bytes"); 437 } 438 } 439 return (int) remain; 440 } 441 442 public static int remaining(ByteBuffer[] refs, int max) { 443 long remain = 0; 444 for (ByteBuffer b : refs) { 445 remain += b.remaining(); 446 if (remain > max) { 447 throw new IllegalArgumentException("too many bytes"); 448 } 449 } 450 return (int) remain; 451 } 452 453 public static void close(Closeable... closeables) { 454 for (Closeable c : closeables) { 455 try { 456 c.close(); 457 } catch (IOException ignored) { } 458 } 459 } 460 461 // Put all these static 'empty' singletons here 462 public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0); 463 public static final ByteBuffer[] EMPTY_BB_ARRAY = new ByteBuffer[0]; 464 public static final List<ByteBuffer> EMPTY_BB_LIST = List.of(); 465 public static final ByteBufferReference[] EMPTY_BBR_ARRAY = new ByteBufferReference[0]; 466 467 public static ByteBuffer slice(ByteBuffer buffer, int amount) { 468 ByteBuffer newb = buffer.slice(); 469 newb.limit(amount); 470 buffer.position(buffer.position() + amount); 471 return newb; 472 } 473 474 /** 475 * Get the Charset from the Content-encoding header. Defaults to 476 * UTF_8 477 */ 478 public static Charset charsetFrom(HttpHeaders headers) { 479 String encoding = headers.firstValue("Content-encoding") 480 .orElse("UTF_8"); 481 try { 482 return Charset.forName(encoding); 483 } catch (IllegalArgumentException e) { 484 return StandardCharsets.UTF_8; 485 } 486 } 487 488 public static UncheckedIOException unchecked(IOException e) { 489 return new UncheckedIOException(e); 490 } 491 492 /** 493 * Get a logger for debug HTTP traces. 494 * 495 * The logger should only be used with levels whose severity is 496 * {@code <= DEBUG}. By default, this logger will forward all messages 497 * logged to an internal logger named "jdk.internal.httpclient.debug". 498 * In addition, if the property -Djdk.internal.httpclient.debug=true is set, 499 * it will print the messages on stderr. 500 * The logger will add some decoration to the printed message, in the form of 501 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 502 * 503 * @param dbgTag A lambda that returns a string that identifies the caller 504 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") 505 * 506 * @return A logger for HTTP internal debug traces 507 */ 508 public static Logger getDebugLogger(Supplier<String> dbgTag) { 509 return getDebugLogger(dbgTag, DEBUG); 510 } 511 512 /** 513 * Get a logger for debug HTTP traces.The logger should only be used 514 * with levels whose severity is {@code <= DEBUG}. 515 * 516 * By default, this logger will forward all messages logged to an internal 517 * logger named "jdk.internal.httpclient.debug". 518 * In addition, if the message severity level is >= to 519 * the provided {@code errLevel} it will print the messages on stderr. 520 * The logger will add some decoration to the printed message, in the form of 521 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 522 * 523 * @apiNote To obtain a logger that will always print things on stderr in 524 * addition to forwarding to the internal logger, use 525 * {@code getDebugLogger(this::dbgTag, Level.ALL);}. 526 * This is also equivalent to calling 527 * {@code getDebugLogger(this::dbgTag, true);}. 528 * To obtain a logger that will only forward to the internal logger, 529 * use {@code getDebugLogger(this::dbgTag, Level.OFF);}. 530 * This is also equivalent to calling 531 * {@code getDebugLogger(this::dbgTag, false);}. 532 * 533 * @param dbgTag A lambda that returns a string that identifies the caller 534 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") 535 * @param errLevel The level above which messages will be also printed on 536 * stderr (in addition to be forwarded to the internal logger). 537 * 538 * @return A logger for HTTP internal debug traces 539 */ 540 static Logger getDebugLogger(Supplier<String> dbgTag, Level errLevel) { 541 return DebugLogger.createHttpLogger(dbgTag, Level.OFF, errLevel); 542 } 543 544 /** 545 * Get a logger for debug HTTP traces.The logger should only be used 546 * with levels whose severity is {@code <= DEBUG}. 547 * 548 * By default, this logger will forward all messages logged to an internal 549 * logger named "jdk.internal.httpclient.debug". 550 * In addition, the provided boolean {@code on==true}, it will print the 551 * messages on stderr. 552 * The logger will add some decoration to the printed message, in the form of 553 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 554 * 555 * @apiNote To obtain a logger that will always print things on stderr in 556 * addition to forwarding to the internal logger, use 557 * {@code getDebugLogger(this::dbgTag, true);}. 558 * This is also equivalent to calling 559 * {@code getDebugLogger(this::dbgTag, Level.ALL);}. 560 * To obtain a logger that will only forward to the internal logger, 561 * use {@code getDebugLogger(this::dbgTag, false);}. 562 * This is also equivalent to calling 563 * {@code getDebugLogger(this::dbgTag, Level.OFF);}. 564 * 565 * @param dbgTag A lambda that returns a string that identifies the caller 566 * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") 567 * @param on Whether messages should also be printed on 568 * stderr (in addition to be forwarded to the internal logger). 569 * 570 * @return A logger for HTTP internal debug traces 571 */ 572 public static Logger getDebugLogger(Supplier<String> dbgTag, boolean on) { 573 Level errLevel = on ? Level.ALL : Level.OFF; 574 return getDebugLogger(dbgTag, errLevel); 575 } 576 577 /** 578 * Get a logger for debug HPACK traces.The logger should only be used 579 * with levels whose severity is {@code <= DEBUG}. 580 * 581 * By default, this logger will forward all messages logged to an internal 582 * logger named "jdk.internal.httpclient.hpack.debug". 583 * In addition, if the message severity level is >= to 584 * the provided {@code outLevel} it will print the messages on stdout. 585 * The logger will add some decoration to the printed message, in the form of 586 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 587 * 588 * @apiNote To obtain a logger that will always print things on stdout in 589 * addition to forwarding to the internal logger, use 590 * {@code getHpackLogger(this::dbgTag, Level.ALL);}. 591 * This is also equivalent to calling 592 * {@code getHpackLogger(this::dbgTag, true);}. 593 * To obtain a logger that will only forward to the internal logger, 594 * use {@code getHpackLogger(this::dbgTag, Level.OFF);}. 595 * This is also equivalent to calling 596 * {@code getHpackLogger(this::dbgTag, false);}. 597 * 598 * @param dbgTag A lambda that returns a string that identifies the caller 599 * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") 600 * @param outLevel The level above which messages will be also printed on 601 * stdout (in addition to be forwarded to the internal logger). 602 * 603 * @return A logger for HPACK internal debug traces 604 */ 605 public static Logger getHpackLogger(Supplier<String> dbgTag, Level outLevel) { 606 Level errLevel = Level.OFF; 607 return DebugLogger.createHpackLogger(dbgTag, outLevel, errLevel); 608 } 609 610 /** 611 * Get a logger for debug HPACK traces.The logger should only be used 612 * with levels whose severity is {@code <= DEBUG}. 613 * 614 * By default, this logger will forward all messages logged to an internal 615 * logger named "jdk.internal.httpclient.hpack.debug". 616 * In addition, the provided boolean {@code on==true}, it will print the 617 * messages on stdout. 618 * The logger will add some decoration to the printed message, in the form of 619 * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} 620 * 621 * @apiNote To obtain a logger that will always print things on stdout in 622 * addition to forwarding to the internal logger, use 623 * {@code getHpackLogger(this::dbgTag, true);}. 624 * This is also equivalent to calling 625 * {@code getHpackLogger(this::dbgTag, Level.ALL);}. 626 * To obtain a logger that will only forward to the internal logger, 627 * use {@code getHpackLogger(this::dbgTag, false);}. 628 * This is also equivalent to calling 629 * {@code getHpackLogger(this::dbgTag, Level.OFF);}. 630 * 631 * @param dbgTag A lambda that returns a string that identifies the caller 632 * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") 633 * @param on Whether messages should also be printed on 634 * stdout (in addition to be forwarded to the internal logger). 635 * 636 * @return A logger for HPACK internal debug traces 637 */ 638 public static Logger getHpackLogger(Supplier<String> dbgTag, boolean on) { 639 Level outLevel = on ? Level.ALL : Level.OFF; 640 return getHpackLogger(dbgTag, outLevel); 641 } 642 }