1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.squareup.okhttp.internal;
18
19import java.io.Closeable;
20import java.io.EOFException;
21import java.io.File;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
25import java.io.Reader;
26import java.io.StringWriter;
27import java.net.Socket;
28import java.net.URI;
29import java.net.URL;
30import java.nio.ByteOrder;
31import java.nio.charset.Charset;
32import java.util.ArrayList;
33import java.util.Collections;
34import java.util.List;
35import java.util.concurrent.ThreadFactory;
36import java.util.concurrent.atomic.AtomicReference;
37
38/** Junk drawer of utility methods. */
39public final class Util {
40  public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
41  public static final String[] EMPTY_STRING_ARRAY = new String[0];
42
43  /** A cheap and type-safe constant for the ISO-8859-1 Charset. */
44  public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
45
46  /** A cheap and type-safe constant for the US-ASCII Charset. */
47  public static final Charset US_ASCII = Charset.forName("US-ASCII");
48
49  /** A cheap and type-safe constant for the UTF-8 Charset. */
50  public static final Charset UTF_8 = Charset.forName("UTF-8");
51  private static AtomicReference<byte[]> skipBuffer = new AtomicReference<byte[]>();
52
53  private Util() {
54  }
55
56  public static int getEffectivePort(URI uri) {
57    return getEffectivePort(uri.getScheme(), uri.getPort());
58  }
59
60  public static int getEffectivePort(URL url) {
61    return getEffectivePort(url.getProtocol(), url.getPort());
62  }
63
64  private static int getEffectivePort(String scheme, int specifiedPort) {
65    return specifiedPort != -1 ? specifiedPort : getDefaultPort(scheme);
66  }
67
68  public static int getDefaultPort(String scheme) {
69    if ("http".equalsIgnoreCase(scheme)) {
70      return 80;
71    } else if ("https".equalsIgnoreCase(scheme)) {
72      return 443;
73    } else {
74      return -1;
75    }
76  }
77
78  public static void checkOffsetAndCount(int arrayLength, int offset, int count) {
79    if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
80      throw new ArrayIndexOutOfBoundsException();
81    }
82  }
83
84  public static void pokeInt(byte[] dst, int offset, int value, ByteOrder order) {
85    if (order == ByteOrder.BIG_ENDIAN) {
86      dst[offset++] = (byte) ((value >> 24) & 0xff);
87      dst[offset++] = (byte) ((value >> 16) & 0xff);
88      dst[offset++] = (byte) ((value >> 8) & 0xff);
89      dst[offset] = (byte) ((value >> 0) & 0xff);
90    } else {
91      dst[offset++] = (byte) ((value >> 0) & 0xff);
92      dst[offset++] = (byte) ((value >> 8) & 0xff);
93      dst[offset++] = (byte) ((value >> 16) & 0xff);
94      dst[offset] = (byte) ((value >> 24) & 0xff);
95    }
96  }
97
98  /** Returns true if two possibly-null objects are equal. */
99  public static boolean equal(Object a, Object b) {
100    return a == b || (a != null && a.equals(b));
101  }
102
103  /**
104   * Closes {@code closeable}, ignoring any checked exceptions. Does nothing
105   * if {@code closeable} is null.
106   */
107  public static void closeQuietly(Closeable closeable) {
108    if (closeable != null) {
109      try {
110        closeable.close();
111      } catch (RuntimeException rethrown) {
112        throw rethrown;
113      } catch (Exception ignored) {
114      }
115    }
116  }
117
118  /**
119   * Closes {@code socket}, ignoring any checked exceptions. Does nothing if
120   * {@code socket} is null.
121   */
122  public static void closeQuietly(Socket socket) {
123    if (socket != null) {
124      try {
125        socket.close();
126      } catch (RuntimeException rethrown) {
127        throw rethrown;
128      } catch (Exception ignored) {
129      }
130    }
131  }
132
133  /**
134   * Closes {@code a} and {@code b}. If either close fails, this completes
135   * the other close and rethrows the first encountered exception.
136   */
137  public static void closeAll(Closeable a, Closeable b) throws IOException {
138    Throwable thrown = null;
139    try {
140      a.close();
141    } catch (Throwable e) {
142      thrown = e;
143    }
144    try {
145      b.close();
146    } catch (Throwable e) {
147      if (thrown == null) thrown = e;
148    }
149    if (thrown == null) return;
150    if (thrown instanceof IOException) throw (IOException) thrown;
151    if (thrown instanceof RuntimeException) throw (RuntimeException) thrown;
152    if (thrown instanceof Error) throw (Error) thrown;
153    throw new AssertionError(thrown);
154  }
155
156  /**
157   * Deletes the contents of {@code dir}. Throws an IOException if any file
158   * could not be deleted, or if {@code dir} is not a readable directory.
159   */
160  public static void deleteContents(File dir) throws IOException {
161    File[] files = dir.listFiles();
162    if (files == null) {
163      throw new IOException("not a readable directory: " + dir);
164    }
165    for (File file : files) {
166      if (file.isDirectory()) {
167        deleteContents(file);
168      }
169      if (!file.delete()) {
170        throw new IOException("failed to delete file: " + file);
171      }
172    }
173  }
174
175  /**
176   * Implements InputStream.read(int) in terms of InputStream.read(byte[], int, int).
177   * InputStream assumes that you implement InputStream.read(int) and provides default
178   * implementations of the others, but often the opposite is more efficient.
179   */
180  public static int readSingleByte(InputStream in) throws IOException {
181    byte[] buffer = new byte[1];
182    int result = in.read(buffer, 0, 1);
183    return (result != -1) ? buffer[0] & 0xff : -1;
184  }
185
186  /**
187   * Implements OutputStream.write(int) in terms of OutputStream.write(byte[], int, int).
188   * OutputStream assumes that you implement OutputStream.write(int) and provides default
189   * implementations of the others, but often the opposite is more efficient.
190   */
191  public static void writeSingleByte(OutputStream out, int b) throws IOException {
192    byte[] buffer = new byte[1];
193    buffer[0] = (byte) (b & 0xff);
194    out.write(buffer);
195  }
196
197  /**
198   * Fills 'dst' with bytes from 'in', throwing EOFException if insufficient bytes are available.
199   */
200  public static void readFully(InputStream in, byte[] dst) throws IOException {
201    readFully(in, dst, 0, dst.length);
202  }
203
204  /**
205   * Reads exactly 'byteCount' bytes from 'in' (into 'dst' at offset 'offset'), and throws
206   * EOFException if insufficient bytes are available.
207   *
208   * Used to implement {@link java.io.DataInputStream#readFully(byte[], int, int)}.
209   */
210  public static void readFully(InputStream in, byte[] dst, int offset, int byteCount)
211      throws IOException {
212    if (byteCount == 0) {
213      return;
214    }
215    if (in == null) {
216      throw new NullPointerException("in == null");
217    }
218    if (dst == null) {
219      throw new NullPointerException("dst == null");
220    }
221    checkOffsetAndCount(dst.length, offset, byteCount);
222    while (byteCount > 0) {
223      int bytesRead = in.read(dst, offset, byteCount);
224      if (bytesRead < 0) {
225        throw new EOFException();
226      }
227      offset += bytesRead;
228      byteCount -= bytesRead;
229    }
230  }
231
232  /** Returns the remainder of 'reader' as a string, closing it when done. */
233  public static String readFully(Reader reader) throws IOException {
234    try {
235      StringWriter writer = new StringWriter();
236      char[] buffer = new char[1024];
237      int count;
238      while ((count = reader.read(buffer)) != -1) {
239        writer.write(buffer, 0, count);
240      }
241      return writer.toString();
242    } finally {
243      reader.close();
244    }
245  }
246
247  public static void skipAll(InputStream in) throws IOException {
248    do {
249      in.skip(Long.MAX_VALUE);
250    } while (in.read() != -1);
251  }
252
253  /**
254   * Call {@code in.read()} repeatedly until either the stream is exhausted or
255   * {@code byteCount} bytes have been read.
256   *
257   * <p>This method reuses the skip buffer but is careful to never use it at
258   * the same time that another stream is using it. Otherwise streams that use
259   * the caller's buffer for consistency checks like CRC could be clobbered by
260   * other threads. A thread-local buffer is also insufficient because some
261   * streams may call other streams in their skip() method, also clobbering the
262   * buffer.
263   */
264  public static long skipByReading(InputStream in, long byteCount) throws IOException {
265    // acquire the shared skip buffer.
266    byte[] buffer = skipBuffer.getAndSet(null);
267    if (buffer == null) {
268      buffer = new byte[4096];
269    }
270
271    long skipped = 0;
272    while (skipped < byteCount) {
273      int toRead = (int) Math.min(byteCount - skipped, buffer.length);
274      int read = in.read(buffer, 0, toRead);
275      if (read == -1) {
276        break;
277      }
278      skipped += read;
279      if (read < toRead) {
280        break;
281      }
282    }
283
284    // release the shared skip buffer.
285    skipBuffer.set(buffer);
286
287    return skipped;
288  }
289
290  /**
291   * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
292   * Returns the total number of bytes transferred.
293   */
294  public static int copy(InputStream in, OutputStream out) throws IOException {
295    int total = 0;
296    byte[] buffer = new byte[8192];
297    int c;
298    while ((c = in.read(buffer)) != -1) {
299      total += c;
300      out.write(buffer, 0, c);
301    }
302    return total;
303  }
304
305  /**
306   * Returns the ASCII characters up to but not including the next "\r\n", or
307   * "\n".
308   *
309   * @throws java.io.EOFException if the stream is exhausted before the next newline
310   * character.
311   */
312  public static String readAsciiLine(InputStream in) throws IOException {
313    // TODO: support UTF-8 here instead
314    StringBuilder result = new StringBuilder(80);
315    while (true) {
316      int c = in.read();
317      if (c == -1) {
318        throw new EOFException();
319      } else if (c == '\n') {
320        break;
321      }
322
323      result.append((char) c);
324    }
325    int length = result.length();
326    if (length > 0 && result.charAt(length - 1) == '\r') {
327      result.setLength(length - 1);
328    }
329    return result.toString();
330  }
331
332  /** Returns an immutable copy of {@code list}. */
333  public static <T> List<T> immutableList(List<T> list) {
334    return Collections.unmodifiableList(new ArrayList<T>(list));
335  }
336
337  public static ThreadFactory daemonThreadFactory(final String name) {
338    return new ThreadFactory() {
339      @Override public Thread newThread(Runnable runnable) {
340        Thread result = new Thread(runnable, name);
341        result.setDaemon(true);
342        return result;
343      }
344    };
345  }
346}
347