| /* |
| * 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.squareup.okhttp.internal; |
| |
| import java.io.Closeable; |
| import java.io.EOFException; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.net.Socket; |
| import java.net.URI; |
| import java.net.URL; |
| import java.nio.ByteOrder; |
| import java.nio.charset.Charset; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| /** Junk drawer of utility methods. */ |
| public final class Util { |
| 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 ISO-8859-1 Charset. */ |
| public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); |
| |
| /** A cheap and type-safe constant for the US-ASCII Charset. */ |
| public static final Charset US_ASCII = Charset.forName("US-ASCII"); |
| |
| /** A cheap and type-safe constant for the UTF-8 Charset. */ |
| public static final Charset UTF_8 = Charset.forName("UTF-8"); |
| private static AtomicReference<byte[]> skipBuffer = new AtomicReference<byte[]>(); |
| |
| private Util() { |
| } |
| |
| public static int getEffectivePort(URI uri) { |
| return getEffectivePort(uri.getScheme(), uri.getPort()); |
| } |
| |
| public static int getEffectivePort(URL url) { |
| return getEffectivePort(url.getProtocol(), url.getPort()); |
| } |
| |
| private static int getEffectivePort(String scheme, int specifiedPort) { |
| return specifiedPort != -1 ? specifiedPort : getDefaultPort(scheme); |
| } |
| |
| public static int getDefaultPort(String scheme) { |
| if ("http".equalsIgnoreCase(scheme)) { |
| return 80; |
| } else if ("https".equalsIgnoreCase(scheme)) { |
| return 443; |
| } else { |
| return -1; |
| } |
| } |
| |
| public static void checkOffsetAndCount(int arrayLength, int offset, int count) { |
| if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| } |
| |
| public static void pokeInt(byte[] dst, int offset, int value, ByteOrder order) { |
| if (order == ByteOrder.BIG_ENDIAN) { |
| dst[offset++] = (byte) ((value >> 24) & 0xff); |
| dst[offset++] = (byte) ((value >> 16) & 0xff); |
| dst[offset++] = (byte) ((value >> 8) & 0xff); |
| dst[offset] = (byte) ((value >> 0) & 0xff); |
| } else { |
| dst[offset++] = (byte) ((value >> 0) & 0xff); |
| dst[offset++] = (byte) ((value >> 8) & 0xff); |
| dst[offset++] = (byte) ((value >> 16) & 0xff); |
| dst[offset] = (byte) ((value >> 24) & 0xff); |
| } |
| } |
| |
| /** 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. |
| */ |
| 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 (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. |
| */ |
| 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); |
| } |
| |
| /** |
| * Deletes the contents of {@code dir}. Throws an IOException if any file |
| * could not be deleted, or if {@code dir} is not a readable directory. |
| */ |
| public static void deleteContents(File dir) throws IOException { |
| File[] files = dir.listFiles(); |
| if (files == null) { |
| throw new IOException("not a readable directory: " + dir); |
| } |
| for (File file : files) { |
| if (file.isDirectory()) { |
| deleteContents(file); |
| } |
| if (!file.delete()) { |
| throw new IOException("failed to delete file: " + file); |
| } |
| } |
| } |
| |
| /** |
| * Implements InputStream.read(int) in terms of InputStream.read(byte[], int, int). |
| * InputStream assumes that you implement InputStream.read(int) and provides default |
| * implementations of the others, but often the opposite is more efficient. |
| */ |
| public static int readSingleByte(InputStream in) throws IOException { |
| byte[] buffer = new byte[1]; |
| int result = in.read(buffer, 0, 1); |
| return (result != -1) ? buffer[0] & 0xff : -1; |
| } |
| |
| /** |
| * Implements OutputStream.write(int) in terms of OutputStream.write(byte[], int, int). |
| * OutputStream assumes that you implement OutputStream.write(int) and provides default |
| * implementations of the others, but often the opposite is more efficient. |
| */ |
| public static void writeSingleByte(OutputStream out, int b) throws IOException { |
| byte[] buffer = new byte[1]; |
| buffer[0] = (byte) (b & 0xff); |
| out.write(buffer); |
| } |
| |
| /** |
| * Fills 'dst' with bytes from 'in', throwing EOFException if insufficient bytes are available. |
| */ |
| public static void readFully(InputStream in, byte[] dst) throws IOException { |
| readFully(in, dst, 0, dst.length); |
| } |
| |
| /** |
| * Reads exactly 'byteCount' bytes from 'in' (into 'dst' at offset 'offset'), and throws |
| * EOFException if insufficient bytes are available. |
| * |
| * Used to implement {@link java.io.DataInputStream#readFully(byte[], int, int)}. |
| */ |
| public static void readFully(InputStream in, byte[] dst, int offset, int byteCount) |
| throws IOException { |
| if (byteCount == 0) { |
| return; |
| } |
| if (in == null) { |
| throw new NullPointerException("in == null"); |
| } |
| if (dst == null) { |
| throw new NullPointerException("dst == null"); |
| } |
| checkOffsetAndCount(dst.length, offset, byteCount); |
| while (byteCount > 0) { |
| int bytesRead = in.read(dst, offset, byteCount); |
| if (bytesRead < 0) { |
| throw new EOFException(); |
| } |
| offset += bytesRead; |
| byteCount -= bytesRead; |
| } |
| } |
| |
| /** Returns the remainder of 'reader' as a string, closing it when done. */ |
| public static String readFully(Reader reader) throws IOException { |
| try { |
| StringWriter writer = new StringWriter(); |
| char[] buffer = new char[1024]; |
| int count; |
| while ((count = reader.read(buffer)) != -1) { |
| writer.write(buffer, 0, count); |
| } |
| return writer.toString(); |
| } finally { |
| reader.close(); |
| } |
| } |
| |
| public static void skipAll(InputStream in) throws IOException { |
| do { |
| in.skip(Long.MAX_VALUE); |
| } while (in.read() != -1); |
| } |
| |
| /** |
| * Call {@code in.read()} repeatedly until either the stream is exhausted or |
| * {@code byteCount} bytes have been read. |
| * |
| * <p>This method reuses the skip buffer but is careful to never use it at |
| * the same time that another stream is using it. Otherwise streams that use |
| * the caller's buffer for consistency checks like CRC could be clobbered by |
| * other threads. A thread-local buffer is also insufficient because some |
| * streams may call other streams in their skip() method, also clobbering the |
| * buffer. |
| */ |
| public static long skipByReading(InputStream in, long byteCount) throws IOException { |
| // acquire the shared skip buffer. |
| byte[] buffer = skipBuffer.getAndSet(null); |
| if (buffer == null) { |
| buffer = new byte[4096]; |
| } |
| |
| long skipped = 0; |
| while (skipped < byteCount) { |
| int toRead = (int) Math.min(byteCount - skipped, buffer.length); |
| int read = in.read(buffer, 0, toRead); |
| if (read == -1) { |
| break; |
| } |
| skipped += read; |
| if (read < toRead) { |
| break; |
| } |
| } |
| |
| // release the shared skip buffer. |
| skipBuffer.set(buffer); |
| |
| return skipped; |
| } |
| |
| /** |
| * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed. |
| * Returns the total number of bytes transferred. |
| */ |
| public static int copy(InputStream in, OutputStream out) throws IOException { |
| int total = 0; |
| byte[] buffer = new byte[8192]; |
| int c; |
| while ((c = in.read(buffer)) != -1) { |
| total += c; |
| out.write(buffer, 0, c); |
| } |
| return total; |
| } |
| |
| /** |
| * Returns the ASCII characters up to but not including the next "\r\n", or |
| * "\n". |
| * |
| * @throws java.io.EOFException if the stream is exhausted before the next newline |
| * character. |
| */ |
| public static String readAsciiLine(InputStream in) throws IOException { |
| // TODO: support UTF-8 here instead |
| StringBuilder result = new StringBuilder(80); |
| while (true) { |
| int c = in.read(); |
| if (c == -1) { |
| throw new EOFException(); |
| } else if (c == '\n') { |
| break; |
| } |
| |
| result.append((char) c); |
| } |
| int length = result.length(); |
| if (length > 0 && result.charAt(length - 1) == '\r') { |
| result.setLength(length - 1); |
| } |
| return result.toString(); |
| } |
| } |