Home | History | Annotate | Download | only in internal
      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 
     17 package com.squareup.okhttp.internal;
     18 
     19 import com.squareup.okhttp.internal.spdy.Header;
     20 import java.io.ByteArrayInputStream;
     21 import java.io.Closeable;
     22 import java.io.EOFException;
     23 import java.io.File;
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.io.OutputStream;
     27 import java.io.UnsupportedEncodingException;
     28 import java.net.ServerSocket;
     29 import java.net.Socket;
     30 import java.net.URI;
     31 import java.net.URL;
     32 import java.nio.charset.Charset;
     33 import java.security.MessageDigest;
     34 import java.security.NoSuchAlgorithmException;
     35 import java.util.ArrayList;
     36 import java.util.Arrays;
     37 import java.util.Collections;
     38 import java.util.List;
     39 import java.util.concurrent.ThreadFactory;
     40 import okio.ByteString;
     41 import okio.OkBuffer;
     42 import okio.Source;
     43 
     44 import static java.util.concurrent.TimeUnit.NANOSECONDS;
     45 
     46 /** Junk drawer of utility methods. */
     47 public final class Util {
     48   public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
     49   public static final String[] EMPTY_STRING_ARRAY = new String[0];
     50   public static final InputStream EMPTY_INPUT_STREAM = new ByteArrayInputStream(EMPTY_BYTE_ARRAY);
     51 
     52   /** A cheap and type-safe constant for the US-ASCII Charset. */
     53   public static final Charset US_ASCII = Charset.forName("US-ASCII");
     54 
     55   /** A cheap and type-safe constant for the UTF-8 Charset. */
     56   public static final Charset UTF_8 = Charset.forName("UTF-8");
     57 
     58   private Util() {
     59   }
     60 
     61   public static int getEffectivePort(URI uri) {
     62     return getEffectivePort(uri.getScheme(), uri.getPort());
     63   }
     64 
     65   public static int getEffectivePort(URL url) {
     66     return getEffectivePort(url.getProtocol(), url.getPort());
     67   }
     68 
     69   private static int getEffectivePort(String scheme, int specifiedPort) {
     70     return specifiedPort != -1 ? specifiedPort : getDefaultPort(scheme);
     71   }
     72 
     73   public static int getDefaultPort(String protocol) {
     74     if ("http".equals(protocol)) return 80;
     75     if ("https".equals(protocol)) return 443;
     76     return -1;
     77   }
     78 
     79   public static void checkOffsetAndCount(long arrayLength, long offset, long count) {
     80     if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
     81       throw new ArrayIndexOutOfBoundsException();
     82     }
     83   }
     84 
     85   /** Returns true if two possibly-null objects are equal. */
     86   public static boolean equal(Object a, Object b) {
     87     return a == b || (a != null && a.equals(b));
     88   }
     89 
     90   /**
     91    * Closes {@code closeable}, ignoring any checked exceptions. Does nothing
     92    * if {@code closeable} is null.
     93    */
     94   public static void closeQuietly(Closeable closeable) {
     95     if (closeable != null) {
     96       try {
     97         closeable.close();
     98       } catch (RuntimeException rethrown) {
     99         throw rethrown;
    100       } catch (Exception ignored) {
    101       }
    102     }
    103   }
    104 
    105   /**
    106    * Closes {@code socket}, ignoring any checked exceptions. Does nothing if
    107    * {@code socket} is null.
    108    */
    109   public static void closeQuietly(Socket socket) {
    110     if (socket != null) {
    111       try {
    112         socket.close();
    113       } catch (RuntimeException rethrown) {
    114         throw rethrown;
    115       } catch (Exception ignored) {
    116       }
    117     }
    118   }
    119 
    120   /**
    121    * Closes {@code serverSocket}, ignoring any checked exceptions. Does nothing if
    122    * {@code serverSocket} is null.
    123    */
    124   public static void closeQuietly(ServerSocket serverSocket) {
    125     if (serverSocket != null) {
    126       try {
    127         serverSocket.close();
    128       } catch (RuntimeException rethrown) {
    129         throw rethrown;
    130       } catch (Exception ignored) {
    131       }
    132     }
    133   }
    134 
    135   /**
    136    * Closes {@code a} and {@code b}. If either close fails, this completes
    137    * the other close and rethrows the first encountered exception.
    138    */
    139   public static void closeAll(Closeable a, Closeable b) throws IOException {
    140     Throwable thrown = null;
    141     try {
    142       a.close();
    143     } catch (Throwable e) {
    144       thrown = e;
    145     }
    146     try {
    147       b.close();
    148     } catch (Throwable e) {
    149       if (thrown == null) thrown = e;
    150     }
    151     if (thrown == null) return;
    152     if (thrown instanceof IOException) throw (IOException) thrown;
    153     if (thrown instanceof RuntimeException) throw (RuntimeException) thrown;
    154     if (thrown instanceof Error) throw (Error) thrown;
    155     throw new AssertionError(thrown);
    156   }
    157 
    158   /**
    159    * Deletes the contents of {@code dir}. Throws an IOException if any file
    160    * could not be deleted, or if {@code dir} is not a readable directory.
    161    */
    162   public static void deleteContents(File dir) throws IOException {
    163     File[] files = dir.listFiles();
    164     if (files == null) {
    165       throw new IOException("not a readable directory: " + dir);
    166     }
    167     for (File file : files) {
    168       if (file.isDirectory()) {
    169         deleteContents(file);
    170       }
    171       if (!file.delete()) {
    172         throw new IOException("failed to delete file: " + file);
    173       }
    174     }
    175   }
    176 
    177   /**
    178    * Fills 'dst' with bytes from 'in', throwing EOFException if insufficient bytes are available.
    179    */
    180   public static void readFully(InputStream in, byte[] dst) throws IOException {
    181     readFully(in, dst, 0, dst.length);
    182   }
    183 
    184   /**
    185    * Reads exactly 'byteCount' bytes from 'in' (into 'dst' at offset 'offset'), and throws
    186    * EOFException if insufficient bytes are available.
    187    *
    188    * Used to implement {@link java.io.DataInputStream#readFully(byte[], int, int)}.
    189    */
    190   public static void readFully(InputStream in, byte[] dst, int offset, int byteCount)
    191       throws IOException {
    192     if (byteCount == 0) {
    193       return;
    194     }
    195     if (in == null) {
    196       throw new NullPointerException("in == null");
    197     }
    198     if (dst == null) {
    199       throw new NullPointerException("dst == null");
    200     }
    201     checkOffsetAndCount(dst.length, offset, byteCount);
    202     while (byteCount > 0) {
    203       int bytesRead = in.read(dst, offset, byteCount);
    204       if (bytesRead < 0) {
    205         throw new EOFException();
    206       }
    207       offset += bytesRead;
    208       byteCount -= bytesRead;
    209     }
    210   }
    211 
    212   /** Returns the remainder of 'source' as a buffer, closing it when done. */
    213   public static OkBuffer readFully(Source source) throws IOException {
    214     OkBuffer result = new OkBuffer();
    215     while (source.read(result, 2048) != -1) {
    216     }
    217     source.close();
    218     return result;
    219   }
    220 
    221   /** Reads until {@code in} is exhausted or the timeout has elapsed. */
    222   public static boolean skipAll(Source in, int timeoutMillis) throws IOException {
    223     // TODO: Implement deadlines everywhere so they can do this work.
    224     long startNanos = System.nanoTime();
    225     OkBuffer skipBuffer = new OkBuffer();
    226     while (NANOSECONDS.toMillis(System.nanoTime() - startNanos) < timeoutMillis) {
    227       long read = in.read(skipBuffer, 2048);
    228       if (read == -1) return true; // Successfully exhausted the stream.
    229       skipBuffer.clear();
    230     }
    231     return false; // Ran out of time.
    232   }
    233 
    234   /**
    235    * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
    236    * Returns the total number of bytes transferred.
    237    */
    238   public static int copy(InputStream in, OutputStream out) throws IOException {
    239     int total = 0;
    240     byte[] buffer = new byte[8192];
    241     int c;
    242     while ((c = in.read(buffer)) != -1) {
    243       total += c;
    244       out.write(buffer, 0, c);
    245     }
    246     return total;
    247   }
    248 
    249   /** Returns a 32 character string containing a hash of {@code s}. */
    250   public static String hash(String s) {
    251     try {
    252       MessageDigest messageDigest = MessageDigest.getInstance("MD5");
    253       byte[] md5bytes = messageDigest.digest(s.getBytes("UTF-8"));
    254       return ByteString.of(md5bytes).hex();
    255     } catch (NoSuchAlgorithmException e) {
    256       throw new AssertionError(e);
    257     } catch (UnsupportedEncodingException e) {
    258       throw new AssertionError(e);
    259     }
    260   }
    261 
    262   /** Returns an immutable copy of {@code list}. */
    263   public static <T> List<T> immutableList(List<T> list) {
    264     return Collections.unmodifiableList(new ArrayList<T>(list));
    265   }
    266 
    267   /** Returns an immutable list containing {@code elements}. */
    268   public static <T> List<T> immutableList(T... elements) {
    269     return Collections.unmodifiableList(Arrays.asList(elements.clone()));
    270   }
    271 
    272   public static ThreadFactory threadFactory(final String name, final boolean daemon) {
    273     return new ThreadFactory() {
    274       @Override public Thread newThread(Runnable runnable) {
    275         Thread result = new Thread(runnable, name);
    276         result.setDaemon(daemon);
    277         return result;
    278       }
    279     };
    280   }
    281 
    282   public static List<Header> headerEntries(String... elements) {
    283     List<Header> result = new ArrayList<Header>(elements.length / 2);
    284     for (int i = 0; i < elements.length; i += 2) {
    285       result.add(new Header(elements[i], elements[i + 1]));
    286     }
    287     return result;
    288   }
    289 }
    290