311 lines
10 KiB
Java
311 lines
10 KiB
Java
/* GENERATED SOURCE. DO NOT MODIFY. */
|
|
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.okhttp.internal;
|
|
|
|
import com.android.okhttp.HttpUrl;
|
|
import java.io.Closeable;
|
|
import java.io.IOException;
|
|
import java.io.InterruptedIOException;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.lang.reflect.Array;
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
import java.nio.charset.Charset;
|
|
import java.security.MessageDigest;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ThreadFactory;
|
|
import java.util.concurrent.TimeUnit;
|
|
import com.android.okhttp.okio.Buffer;
|
|
import com.android.okhttp.okio.ByteString;
|
|
import com.android.okhttp.okio.Source;
|
|
|
|
/** Junk drawer of utility methods.
|
|
* @hide This class is not part of the Android public SDK API*/
|
|
public final class Util {
|
|
@android.compat.annotation.UnsupportedAppUsage
|
|
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
|
public static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|
|
|
/** A cheap and type-safe constant for the UTF-8 Charset. */
|
|
@android.compat.annotation.UnsupportedAppUsage
|
|
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
|
|
|
private Util() {
|
|
}
|
|
|
|
public static void checkOffsetAndCount(long arrayLength, long offset, long count) {
|
|
if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
|
|
throw new ArrayIndexOutOfBoundsException();
|
|
}
|
|
}
|
|
|
|
/** Returns true if two possibly-null objects are equal. */
|
|
public static boolean equal(Object a, Object b) {
|
|
return a == b || (a != null && a.equals(b));
|
|
}
|
|
|
|
/**
|
|
* Closes {@code closeable}, ignoring any checked exceptions. Does nothing
|
|
* if {@code closeable} is null.
|
|
*/
|
|
@android.compat.annotation.UnsupportedAppUsage
|
|
public static void closeQuietly(Closeable closeable) {
|
|
if (closeable != null) {
|
|
try {
|
|
closeable.close();
|
|
} catch (RuntimeException rethrown) {
|
|
throw rethrown;
|
|
} catch (Exception ignored) {
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes {@code socket}, ignoring any checked exceptions. Does nothing if
|
|
* {@code socket} is null.
|
|
*/
|
|
public static void closeQuietly(Socket socket) {
|
|
if (socket != null) {
|
|
try {
|
|
socket.close();
|
|
} catch (AssertionError e) {
|
|
if (!isAndroidGetsocknameError(e)) throw e;
|
|
} catch (RuntimeException rethrown) {
|
|
throw rethrown;
|
|
} catch (Exception ignored) {
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes {@code serverSocket}, ignoring any checked exceptions. Does nothing if
|
|
* {@code serverSocket} is null.
|
|
*/
|
|
public static void closeQuietly(ServerSocket serverSocket) {
|
|
if (serverSocket != null) {
|
|
try {
|
|
serverSocket.close();
|
|
} catch (RuntimeException rethrown) {
|
|
throw rethrown;
|
|
} catch (Exception ignored) {
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes {@code a} and {@code b}. If either close fails, this completes
|
|
* the other close and rethrows the first encountered exception.
|
|
*/
|
|
@android.compat.annotation.UnsupportedAppUsage
|
|
public static void closeAll(Closeable a, Closeable b) throws IOException {
|
|
Throwable thrown = null;
|
|
try {
|
|
a.close();
|
|
} catch (Throwable e) {
|
|
thrown = e;
|
|
}
|
|
try {
|
|
b.close();
|
|
} catch (Throwable e) {
|
|
if (thrown == null) thrown = e;
|
|
}
|
|
if (thrown == null) return;
|
|
if (thrown instanceof IOException) throw (IOException) thrown;
|
|
if (thrown instanceof RuntimeException) throw (RuntimeException) thrown;
|
|
if (thrown instanceof Error) throw (Error) thrown;
|
|
throw new AssertionError(thrown);
|
|
}
|
|
|
|
/**
|
|
* Attempts to exhaust {@code source}, returning true if successful. This is useful when reading
|
|
* a complete source is helpful, such as when doing so completes a cache body or frees a socket
|
|
* connection for reuse.
|
|
*/
|
|
public static boolean discard(Source source, int timeout, TimeUnit timeUnit) {
|
|
try {
|
|
return skipAll(source, timeout, timeUnit);
|
|
} catch (IOException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads until {@code in} is exhausted or the deadline has been reached. This is careful to not
|
|
* extend the deadline if one exists already.
|
|
*/
|
|
public static boolean skipAll(Source source, int duration, TimeUnit timeUnit) throws IOException {
|
|
long now = System.nanoTime();
|
|
long originalDuration = source.timeout().hasDeadline()
|
|
? source.timeout().deadlineNanoTime() - now
|
|
: Long.MAX_VALUE;
|
|
source.timeout().deadlineNanoTime(now + Math.min(originalDuration, timeUnit.toNanos(duration)));
|
|
try {
|
|
Buffer skipBuffer = new Buffer();
|
|
while (source.read(skipBuffer, 2048) != -1) {
|
|
skipBuffer.clear();
|
|
}
|
|
return true; // Success! The source has been exhausted.
|
|
} catch (InterruptedIOException e) {
|
|
return false; // We ran out of time before exhausting the source.
|
|
} finally {
|
|
if (originalDuration == Long.MAX_VALUE) {
|
|
source.timeout().clearDeadline();
|
|
} else {
|
|
source.timeout().deadlineNanoTime(now + originalDuration);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Returns a 32 character string containing an MD5 hash of {@code s}. */
|
|
public static String md5Hex(String s) {
|
|
try {
|
|
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
|
byte[] md5bytes = messageDigest.digest(s.getBytes("UTF-8"));
|
|
return ByteString.of(md5bytes).hex();
|
|
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
/** Returns a Base 64-encoded string containing a SHA-1 hash of {@code s}. */
|
|
public static String shaBase64(String s) {
|
|
try {
|
|
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
|
|
byte[] sha1Bytes = messageDigest.digest(s.getBytes("UTF-8"));
|
|
return ByteString.of(sha1Bytes).base64();
|
|
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
/** Returns a SHA-1 hash of {@code s}. */
|
|
public static ByteString sha1(ByteString s) {
|
|
try {
|
|
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
|
|
byte[] sha1Bytes = messageDigest.digest(s.toByteArray());
|
|
return ByteString.of(sha1Bytes);
|
|
} catch (NoSuchAlgorithmException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
/** Returns an immutable copy of {@code list}. */
|
|
public static <T> List<T> immutableList(List<T> list) {
|
|
return Collections.unmodifiableList(new ArrayList<>(list));
|
|
}
|
|
|
|
/** Returns an immutable list containing {@code elements}. */
|
|
public static <T> List<T> immutableList(T... elements) {
|
|
return Collections.unmodifiableList(Arrays.asList(elements.clone()));
|
|
}
|
|
|
|
/** Returns an immutable copy of {@code map}. */
|
|
public static <K, V> Map<K, V> immutableMap(Map<K, V> map) {
|
|
return Collections.unmodifiableMap(new LinkedHashMap<>(map));
|
|
}
|
|
|
|
public static ThreadFactory threadFactory(final String name, final boolean daemon) {
|
|
return new ThreadFactory() {
|
|
@Override public Thread newThread(Runnable runnable) {
|
|
Thread result = new Thread(runnable, name);
|
|
result.setDaemon(daemon);
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns an array containing containing only elements found in {@code first} and also in
|
|
* {@code second}. The returned elements are in the same order as in {@code first}.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public static <T> T[] intersect(Class<T> arrayType, T[] first, T[] second) {
|
|
List<T> result = intersect(first, second);
|
|
return result.toArray((T[]) Array.newInstance(arrayType, result.size()));
|
|
}
|
|
|
|
/**
|
|
* Returns a list containing containing only elements found in {@code first} and also in
|
|
* {@code second}. The returned elements are in the same order as in {@code first}.
|
|
*/
|
|
private static <T> List<T> intersect(T[] first, T[] second) {
|
|
List<T> result = new ArrayList<>();
|
|
for (T a : first) {
|
|
for (T b : second) {
|
|
if (a.equals(b)) {
|
|
result.add(b);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static String hostHeader(HttpUrl url, boolean includeDefaultPort) {
|
|
String host = url.host().contains(":")
|
|
? "[" + url.host() + "]"
|
|
: url.host();
|
|
return includeDefaultPort || url.port() != HttpUrl.defaultPort(url.scheme())
|
|
? host + ":" + url.port()
|
|
: host;
|
|
}
|
|
|
|
/** Returns {@code s} with control characters and non-ASCII characters replaced with '?'. */
|
|
public static String toHumanReadableAscii(String s) {
|
|
for (int i = 0, length = s.length(), c; i < length; i += Character.charCount(c)) {
|
|
c = s.codePointAt(i);
|
|
if (c > '\u001f' && c < '\u007f') continue;
|
|
|
|
Buffer buffer = new Buffer();
|
|
buffer.writeUtf8(s, 0, i);
|
|
for (int j = i; j < length; j += Character.charCount(c)) {
|
|
c = s.codePointAt(j);
|
|
buffer.writeUtf8CodePoint(c > '\u001f' && c < '\u007f' ? c : '?');
|
|
}
|
|
return buffer.readUtf8();
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Returns true if {@code e} is due to a firmware bug fixed after Android 4.2.2.
|
|
* https://code.google.com/p/android/issues/detail?id=54072
|
|
*/
|
|
public static boolean isAndroidGetsocknameError(AssertionError e) {
|
|
return e.getCause() != null && e.getMessage() != null
|
|
&& e.getMessage().contains("getsockname failed");
|
|
}
|
|
|
|
public static boolean contains(String[] array, String value) {
|
|
return Arrays.asList(array).contains(value);
|
|
}
|
|
|
|
public static String[] concat(String[] array, String value) {
|
|
String[] result = new String[array.length + 1];
|
|
System.arraycopy(array, 0, result, 0, array.length);
|
|
result[result.length - 1] = value;
|
|
return result;
|
|
}
|
|
}
|