1
16 package okhttp3.internal;
17
18 import java.io.Closeable;
19 import java.io.IOException;
20 import java.io.InterruptedIOException;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.net.IDN;
24 import java.net.InetAddress;
25 import java.net.ServerSocket;
26 import java.net.Socket;
27 import java.net.UnknownHostException;
28 import java.nio.charset.Charset;
29 import java.security.AccessControlException;
30 import java.security.GeneralSecurityException;
31 import java.security.KeyStore;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.Comparator;
36 import java.util.LinkedHashMap;
37 import java.util.List;
38 import java.util.Locale;
39 import java.util.Map;
40 import java.util.TimeZone;
41 import java.util.concurrent.ThreadFactory;
42 import java.util.concurrent.TimeUnit;
43 import java.util.regex.Pattern;
44 import javax.annotation.Nullable;
45 import javax.net.ssl.TrustManager;
46 import javax.net.ssl.TrustManagerFactory;
47 import javax.net.ssl.X509TrustManager;
48 import okhttp3.Headers;
49 import okhttp3.HttpUrl;
50 import okhttp3.RequestBody;
51 import okhttp3.ResponseBody;
52 import okhttp3.internal.http2.Header;
53 import okio.Buffer;
54 import okio.BufferedSource;
55 import okio.ByteString;
56 import okio.Options;
57 import okio.Source;
58
59 import static java.nio.charset.StandardCharsets.UTF_16BE;
60 import static java.nio.charset.StandardCharsets.UTF_16LE;
61 import static java.nio.charset.StandardCharsets.UTF_8;
62
63
64 public final class Util {
65 public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
66 public static final String[] EMPTY_STRING_ARRAY = new String[0];
67 public static final Headers EMPTY_HEADERS = Headers.of();
68
69 public static final ResponseBody EMPTY_RESPONSE = ResponseBody.create(null, EMPTY_BYTE_ARRAY);
70 public static final RequestBody EMPTY_REQUEST = RequestBody.create(null, EMPTY_BYTE_ARRAY);
71
72
73 private static final Options UNICODE_BOMS = Options.of(
74 ByteString.decodeHex("efbbbf"),
75 ByteString.decodeHex("feff"),
76 ByteString.decodeHex("fffe"),
77 ByteString.decodeHex("0000ffff"),
78 ByteString.decodeHex("ffff0000")
79 );
80
81 private static final Charset UTF_32BE = Charset.forName("UTF-32BE");
82 private static final Charset UTF_32LE = Charset.forName("UTF-32LE");
83
84
85 public static final TimeZone UTC = TimeZone.getTimeZone("GMT");
86
87 public static final Comparator<String> NATURAL_ORDER = String::compareTo;
88
89 private static final Method addSuppressedExceptionMethod;
90
91 static {
92 Method m;
93 try {
94 m = Throwable.class.getDeclaredMethod("addSuppressed", Throwable.class);
95 } catch (Exception e) {
96 m = null;
97 }
98 addSuppressedExceptionMethod = m;
99 }
100
101 public static void addSuppressedIfPossible(Throwable e, Throwable suppressed) {
102 if (addSuppressedExceptionMethod != null) {
103 try {
104 addSuppressedExceptionMethod.invoke(e, suppressed);
105 } catch (InvocationTargetException | IllegalAccessException ignored) {
106 }
107 }
108 }
109
110
120 private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile(
121 "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)");
122
123 private Util() {
124 }
125
126 public static void checkOffsetAndCount(long arrayLength, long offset, long count) {
127 if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
128 throw new ArrayIndexOutOfBoundsException();
129 }
130 }
131
132
136 public static void closeQuietly(Closeable closeable) {
137 if (closeable != null) {
138 try {
139 closeable.close();
140 } catch (RuntimeException rethrown) {
141 throw rethrown;
142 } catch (Exception ignored) {
143 }
144 }
145 }
146
147
151 public static void closeQuietly(Socket socket) {
152 if (socket != null) {
153 try {
154 socket.close();
155 } catch (AssertionError e) {
156 if (!isAndroidGetsocknameError(e)) throw e;
157 } catch (RuntimeException rethrown) {
158 throw rethrown;
159 } catch (Exception ignored) {
160 }
161 }
162 }
163
164
168 public static void closeQuietly(ServerSocket serverSocket) {
169 if (serverSocket != null) {
170 try {
171 serverSocket.close();
172 } catch (RuntimeException rethrown) {
173 throw rethrown;
174 } catch (Exception ignored) {
175 }
176 }
177 }
178
179
184 public static boolean discard(Source source, int timeout, TimeUnit timeUnit) {
185 try {
186 return skipAll(source, timeout, timeUnit);
187 } catch (IOException e) {
188 return false;
189 }
190 }
191
192
196 public static boolean skipAll(Source source, int duration, TimeUnit timeUnit) throws IOException {
197 long now = System.nanoTime();
198 long originalDuration = source.timeout().hasDeadline()
199 ? source.timeout().deadlineNanoTime() - now
200 : Long.MAX_VALUE;
201 source.timeout().deadlineNanoTime(now + Math.min(originalDuration, timeUnit.toNanos(duration)));
202 try {
203 Buffer skipBuffer = new Buffer();
204 while (source.read(skipBuffer, 8192) != -1) {
205 skipBuffer.clear();
206 }
207 return true;
208 } catch (InterruptedIOException e) {
209 return false;
210 } finally {
211 if (originalDuration == Long.MAX_VALUE) {
212 source.timeout().clearDeadline();
213 } else {
214 source.timeout().deadlineNanoTime(now + originalDuration);
215 }
216 }
217 }
218
219
220 public static <T> List<T> immutableList(List<T> list) {
221 return Collections.unmodifiableList(new ArrayList<>(list));
222 }
223
224
225 public static <K, V> Map<K, V> immutableMap(Map<K, V> map) {
226 return map.isEmpty()
227 ? Collections.emptyMap()
228 : Collections.unmodifiableMap(new LinkedHashMap<>(map));
229 }
230
231
232 @SafeVarargs
233 public static <T> List<T> immutableList(T... elements) {
234 return Collections.unmodifiableList(Arrays.asList(elements.clone()));
235 }
236
237 public static ThreadFactory threadFactory(String name, boolean daemon) {
238 return runnable -> {
239 Thread result = new Thread(runnable, name);
240 result.setDaemon(daemon);
241 return result;
242 };
243 }
244
245
249 public static String[] intersect(
250 Comparator<? super String> comparator, String[] first, String[] second) {
251 List<String> result = new ArrayList<>();
252 for (String a : first) {
253 for (String b : second) {
254 if (comparator.compare(a, b) == 0) {
255 result.add(a);
256 break;
257 }
258 }
259 }
260 return result.toArray(new String[result.size()]);
261 }
262
263
269 public static boolean nonEmptyIntersection(
270 Comparator<String> comparator, String[] first, String[] second) {
271 if (first == null || second == null || first.length == 0 || second.length == 0) {
272 return false;
273 }
274 for (String a : first) {
275 for (String b : second) {
276 if (comparator.compare(a, b) == 0) {
277 return true;
278 }
279 }
280 }
281 return false;
282 }
283
284 public static String hostHeader(HttpUrl url, boolean includeDefaultPort) {
285 String host = url.host().contains(":")
286 ? "[" + url.host() + "]"
287 : url.host();
288 return includeDefaultPort || url.port() != HttpUrl.defaultPort(url.scheme())
289 ? host + ":" + url.port()
290 : host;
291 }
292
293
297 public static boolean isAndroidGetsocknameError(AssertionError e) {
298 return e.getCause() != null && e.getMessage() != null
299 && e.getMessage().contains("getsockname failed");
300 }
301
302 public static int indexOf(Comparator<String> comparator, String[] array, String value) {
303 for (int i = 0, size = array.length; i < size; i++) {
304 if (comparator.compare(array[i], value) == 0) return i;
305 }
306 return -1;
307 }
308
309 public static String[] concat(String[] array, String value) {
310 String[] result = new String[array.length + 1];
311 System.arraycopy(array, 0, result, 0, array.length);
312 result[result.length - 1] = value;
313 return result;
314 }
315
316
320 public static int skipLeadingAsciiWhitespace(String input, int pos, int limit) {
321 for (int i = pos; i < limit; i++) {
322 switch (input.charAt(i)) {
323 case '\t':
324 case '\n':
325 case '\f':
326 case '\r':
327 case ' ':
328 continue;
329 default:
330 return i;
331 }
332 }
333 return limit;
334 }
335
336
340 public static int skipTrailingAsciiWhitespace(String input, int pos, int limit) {
341 for (int i = limit - 1; i >= pos; i--) {
342 switch (input.charAt(i)) {
343 case '\t':
344 case '\n':
345 case '\f':
346 case '\r':
347 case ' ':
348 continue;
349 default:
350 return i + 1;
351 }
352 }
353 return pos;
354 }
355
356
357 public static String trimSubstring(String string, int pos, int limit) {
358 int start = skipLeadingAsciiWhitespace(string, pos, limit);
359 int end = skipTrailingAsciiWhitespace(string, start, limit);
360 return string.substring(start, end);
361 }
362
363
367 public static int delimiterOffset(String input, int pos, int limit, String delimiters) {
368 for (int i = pos; i < limit; i++) {
369 if (delimiters.indexOf(input.charAt(i)) != -1) return i;
370 }
371 return limit;
372 }
373
374
378 public static int delimiterOffset(String input, int pos, int limit, char delimiter) {
379 for (int i = pos; i < limit; i++) {
380 if (input.charAt(i) == delimiter) return i;
381 }
382 return limit;
383 }
384
385
393 public static String canonicalizeHost(String host) {
394
395 if (host.contains(":")) {
396
397 InetAddress inetAddress = host.startsWith("[") && host.endsWith("]")
398 ? decodeIpv6(host, 1, host.length() - 1)
399 : decodeIpv6(host, 0, host.length());
400 if (inetAddress == null) return null;
401 byte[] address = inetAddress.getAddress();
402 if (address.length == 16) return inet6AddressToAscii(address);
403 if (address.length == 4) return inetAddress.getHostAddress();
404 throw new AssertionError("Invalid IPv6 address: '" + host + "'");
405 }
406
407 try {
408 String result = IDN.toASCII(host).toLowerCase(Locale.US);
409 if (result.isEmpty()) return null;
410
411
412 if (containsInvalidHostnameAsciiCodes(result)) {
413 return null;
414 }
415
416 return result;
417 } catch (IllegalArgumentException e) {
418 return null;
419 }
420 }
421
422 private static boolean containsInvalidHostnameAsciiCodes(String hostnameAscii) {
423 for (int i = 0; i < hostnameAscii.length(); i++) {
424 char c = hostnameAscii.charAt(i);
425
426
427
428 if (c <= '\u001f' || c >= '\u007f') {
429 return true;
430 }
431
432
433
434 if (" #%/:?@[\\]".indexOf(c) != -1) {
435 return true;
436 }
437 }
438 return false;
439 }
440
441
446 public static int indexOfControlOrNonAscii(String input) {
447 for (int i = 0, length = input.length(); i < length; i++) {
448 char c = input.charAt(i);
449 if (c <= '\u001f' || c >= '\u007f') {
450 return i;
451 }
452 }
453 return -1;
454 }
455
456
457 public static boolean verifyAsIpAddress(String host) {
458 return VERIFY_AS_IP_ADDRESS.matcher(host).matches();
459 }
460
461
462 public static String format(String format, Object... args) {
463 return String.format(Locale.US, format, args);
464 }
465
466 public static Charset bomAwareCharset(BufferedSource source, Charset charset) throws IOException {
467 switch (source.select(UNICODE_BOMS)) {
468 case 0: return UTF_8;
469 case 1: return UTF_16BE;
470 case 2: return UTF_16LE;
471 case 3: return UTF_32BE;
472 case 4: return UTF_32LE;
473 case -1: return charset;
474 default: throw new AssertionError();
475 }
476 }
477
478 public static int checkDuration(String name, long duration, TimeUnit unit) {
479 if (duration < 0) throw new IllegalArgumentException(name + " < 0");
480 if (unit == null) throw new NullPointerException("unit == null");
481 long millis = unit.toMillis(duration);
482 if (millis > Integer.MAX_VALUE) throw new IllegalArgumentException(name + " too large.");
483 if (millis == 0 && duration > 0) throw new IllegalArgumentException(name + " too small.");
484 return (int) millis;
485 }
486
487 public static int decodeHexDigit(char c) {
488 if (c >= '0' && c <= '9') return c - '0';
489 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
490 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
491 return -1;
492 }
493
494
495 private static @Nullable InetAddress decodeIpv6(String input, int pos, int limit) {
496 byte[] address = new byte[16];
497 int b = 0;
498 int compress = -1;
499 int groupOffset = -1;
500
501 for (int i = pos; i < limit; ) {
502 if (b == address.length) return null;
503
504
505 if (i + 2 <= limit && input.regionMatches(i, "::", 0, 2)) {
506
507 if (compress != -1) return null;
508 i += 2;
509 b += 2;
510 compress = b;
511 if (i == limit) break;
512 } else if (b != 0) {
513
514 if (input.regionMatches(i, ":", 0, 1)) {
515 i++;
516 } else if (input.regionMatches(i, ".", 0, 1)) {
517
518 if (!decodeIpv4Suffix(input, groupOffset, limit, address, b - 2)) return null;
519 b += 2;
520 break;
521 } else {
522 return null;
523 }
524 }
525
526
527 int value = 0;
528 groupOffset = i;
529 for (; i < limit; i++) {
530 char c = input.charAt(i);
531 int hexDigit = decodeHexDigit(c);
532 if (hexDigit == -1) break;
533 value = (value << 4) + hexDigit;
534 }
535 int groupLength = i - groupOffset;
536 if (groupLength == 0 || groupLength > 4) return null;
537
538
539 address[b++] = (byte) ((value >>> 8) & 0xff);
540 address[b++] = (byte) (value & 0xff);
541 }
542
543
544
545
546
547
548
549
550
551
552 if (b != address.length) {
553 if (compress == -1) return null;
554 System.arraycopy(address, compress, address, address.length - (b - compress), b - compress);
555 Arrays.fill(address, compress, compress + (address.length - b), (byte) 0);
556 }
557
558 try {
559 return InetAddress.getByAddress(address);
560 } catch (UnknownHostException e) {
561 throw new AssertionError();
562 }
563 }
564
565
566 private static boolean decodeIpv4Suffix(
567 String input, int pos, int limit, byte[] address, int addressOffset) {
568 int b = addressOffset;
569
570 for (int i = pos; i < limit; ) {
571 if (b == address.length) return false;
572
573
574 if (b != addressOffset) {
575 if (input.charAt(i) != '.') return false;
576 i++;
577 }
578
579
580 int value = 0;
581 int groupOffset = i;
582 for (; i < limit; i++) {
583 char c = input.charAt(i);
584 if (c < '0' || c > '9') break;
585 if (value == 0 && groupOffset != i) return false;
586 value = (value * 10) + c - '0';
587 if (value > 255) return false;
588 }
589 int groupLength = i - groupOffset;
590 if (groupLength == 0) return false;
591
592
593 address[b++] = (byte) value;
594 }
595
596 if (b != addressOffset + 4) return false;
597 return true;
598 }
599
600
601 private static String inet6AddressToAscii(byte[] address) {
602
603
604
605 int longestRunOffset = -1;
606 int longestRunLength = 0;
607 for (int i = 0; i < address.length; i += 2) {
608 int currentRunOffset = i;
609 while (i < 16 && address[i] == 0 && address[i + 1] == 0) {
610 i += 2;
611 }
612 int currentRunLength = i - currentRunOffset;
613 if (currentRunLength > longestRunLength && currentRunLength >= 4) {
614 longestRunOffset = currentRunOffset;
615 longestRunLength = currentRunLength;
616 }
617 }
618
619
620 Buffer result = new Buffer();
621 for (int i = 0; i < address.length; ) {
622 if (i == longestRunOffset) {
623 result.writeByte(':');
624 i += longestRunLength;
625 if (i == 16) result.writeByte(':');
626 } else {
627 if (i > 0) result.writeByte(':');
628 int group = (address[i] & 0xff) << 8 | address[i + 1] & 0xff;
629 result.writeHexadecimalUnsignedLong(group);
630 i += 2;
631 }
632 }
633 return result.readUtf8();
634 }
635
636 public static X509TrustManager platformTrustManager() {
637 try {
638 TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
639 TrustManagerFactory.getDefaultAlgorithm());
640 trustManagerFactory.init((KeyStore) null);
641 TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
642 if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
643 throw new IllegalStateException("Unexpected default trust managers:"
644 + Arrays.toString(trustManagers));
645 }
646 return (X509TrustManager) trustManagers[0];
647 } catch (GeneralSecurityException e) {
648 throw new AssertionError("No System TLS", e);
649 }
650 }
651
652 public static Headers toHeaders(List<Header> headerBlock) {
653 Headers.Builder builder = new Headers.Builder();
654 for (Header header : headerBlock) {
655 Internal.instance.addLenient(builder, header.name.utf8(), header.value.utf8());
656 }
657 return builder.build();
658 }
659
660 public static List<Header> toHeaderBlock(Headers headers) {
661 List<Header> result = new ArrayList<>();
662 for (int i = 0; i < headers.size(); i++) {
663 result.add(new Header(headers.name(i), headers.value(i)));
664 }
665 return result;
666 }
667
668
672 public static String getSystemProperty(String key, @Nullable String defaultValue) {
673 String value;
674 try {
675 value = System.getProperty(key);
676 } catch (AccessControlException ex) {
677 return defaultValue;
678 }
679 return value != null ? value : defaultValue;
680 }
681
682
683 public static boolean sameConnection(HttpUrl a, HttpUrl b) {
684 return a.host().equals(b.host())
685 && a.port() == b.port()
686 && a.scheme().equals(b.scheme());
687 }
688 }
689