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 java.io.Closeable;
     20 import java.io.IOException;
     21 import java.io.InterruptedIOException;
     22 import java.io.UnsupportedEncodingException;
     23 import java.lang.reflect.Array;
     24 import java.net.ServerSocket;
     25 import java.net.Socket;
     26 import java.net.URI;
     27 import java.net.URL;
     28 import java.nio.charset.Charset;
     29 import java.security.MessageDigest;
     30 import java.security.NoSuchAlgorithmException;
     31 import java.util.ArrayList;
     32 import java.util.Arrays;
     33 import java.util.Collections;
     34 import java.util.LinkedHashMap;
     35 import java.util.List;
     36 import java.util.Map;
     37 import java.util.concurrent.ThreadFactory;
     38 import java.util.concurrent.TimeUnit;
     39 import okio.Buffer;
     40 import okio.ByteString;
     41 import okio.Source;
     42 
     43 /** Junk drawer of utility methods. */
     44 public final class Util {
     45   public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
     46   public static final String[] EMPTY_STRING_ARRAY = new String[0];
     47 
     48   /** A cheap and type-safe constant for the UTF-8 Charset. */
     49   public static final Charset UTF_8 = Charset.forName("UTF-8");
     50 
     51   private Util() {
     52   }
     53 
     54   public static int getEffectivePort(URI uri) {
     55     return getEffectivePort(uri.getScheme(), uri.getPort());
     56   }
     57 
     58   public static int getEffectivePort(URL url) {
     59     return getEffectivePort(url.getProtocol(), url.getPort());
     60   }
     61 
     62   private static int getEffectivePort(String scheme, int specifiedPort) {
     63     return specifiedPort != -1 ? specifiedPort : getDefaultPort(scheme);
     64   }
     65 
     66   public static int getDefaultPort(String protocol) {
     67     if ("http".equals(protocol)) return 80;
     68     if ("https".equals(protocol)) return 443;
     69     return -1;
     70   }
     71 
     72   public static void checkOffsetAndCount(long arrayLength, long offset, long count) {
     73     if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
     74       throw new ArrayIndexOutOfBoundsException();
     75     }
     76   }
     77 
     78   /** Returns true if two possibly-null objects are equal. */
     79   public static boolean equal(Object a, Object b) {
     80     return a == b || (a != null && a.equals(b));
     81   }
     82 
     83   /**
     84    * Closes {@code closeable}, ignoring any checked exceptions. Does nothing
     85    * if {@code closeable} is null.
     86    */
     87   public static void closeQuietly(Closeable closeable) {
     88     if (closeable != null) {
     89       try {
     90         closeable.close();
     91       } catch (RuntimeException rethrown) {
     92         throw rethrown;
     93       } catch (Exception ignored) {
     94       }
     95     }
     96   }
     97 
     98   /**
     99    * Closes {@code socket}, ignoring any checked exceptions. Does nothing if
    100    * {@code socket} is null.
    101    */
    102   public static void closeQuietly(Socket socket) {
    103     if (socket != null) {
    104       try {
    105         socket.close();
    106       } catch (RuntimeException rethrown) {
    107         throw rethrown;
    108       } catch (Exception ignored) {
    109       }
    110     }
    111   }
    112 
    113   /**
    114    * Closes {@code serverSocket}, ignoring any checked exceptions. Does nothing if
    115    * {@code serverSocket} is null.
    116    */
    117   public static void closeQuietly(ServerSocket serverSocket) {
    118     if (serverSocket != null) {
    119       try {
    120         serverSocket.close();
    121       } catch (RuntimeException rethrown) {
    122         throw rethrown;
    123       } catch (Exception ignored) {
    124       }
    125     }
    126   }
    127 
    128   /**
    129    * Closes {@code a} and {@code b}. If either close fails, this completes
    130    * the other close and rethrows the first encountered exception.
    131    */
    132   public static void closeAll(Closeable a, Closeable b) throws IOException {
    133     Throwable thrown = null;
    134     try {
    135       a.close();
    136     } catch (Throwable e) {
    137       thrown = e;
    138     }
    139     try {
    140       b.close();
    141     } catch (Throwable e) {
    142       if (thrown == null) thrown = e;
    143     }
    144     if (thrown == null) return;
    145     if (thrown instanceof IOException) throw (IOException) thrown;
    146     if (thrown instanceof RuntimeException) throw (RuntimeException) thrown;
    147     if (thrown instanceof Error) throw (Error) thrown;
    148     throw new AssertionError(thrown);
    149   }
    150 
    151   /**
    152    * Attempts to exhaust {@code source}, returning true if successful. This is useful when reading
    153    * a complete source is helpful, such as when doing so completes a cache body or frees a socket
    154    * connection for reuse.
    155    */
    156   public static boolean discard(Source source, int timeout, TimeUnit timeUnit) {
    157     try {
    158       return skipAll(source, timeout, timeUnit);
    159     } catch (IOException e) {
    160       return false;
    161     }
    162   }
    163 
    164   /**
    165    * Reads until {@code in} is exhausted or the deadline has been reached. This is careful to not
    166    * extend the deadline if one exists already.
    167    */
    168   public static boolean skipAll(Source source, int duration, TimeUnit timeUnit) throws IOException {
    169     long now = System.nanoTime();
    170     long originalDuration = source.timeout().hasDeadline()
    171         ? source.timeout().deadlineNanoTime() - now
    172         : Long.MAX_VALUE;
    173     source.timeout().deadlineNanoTime(now + Math.min(originalDuration, timeUnit.toNanos(duration)));
    174     try {
    175       Buffer skipBuffer = new Buffer();
    176       while (source.read(skipBuffer, 2048) != -1) {
    177         skipBuffer.clear();
    178       }
    179       return true; // Success! The source has been exhausted.
    180     } catch (InterruptedIOException e) {
    181       return false; // We ran out of time before exhausting the source.
    182     } finally {
    183       if (originalDuration == Long.MAX_VALUE) {
    184         source.timeout().clearDeadline();
    185       } else {
    186         source.timeout().deadlineNanoTime(now + originalDuration);
    187       }
    188     }
    189   }
    190 
    191   /** Returns a 32 character string containing an MD5 hash of {@code s}. */
    192   public static String md5Hex(String s) {
    193     try {
    194       MessageDigest messageDigest = MessageDigest.getInstance("MD5");
    195       byte[] md5bytes = messageDigest.digest(s.getBytes("UTF-8"));
    196       return ByteString.of(md5bytes).hex();
    197     } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
    198       throw new AssertionError(e);
    199     }
    200   }
    201 
    202   /** Returns a Base 64-encoded string containing a SHA-1 hash of {@code s}. */
    203   public static String shaBase64(String s) {
    204     try {
    205       MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
    206       byte[] sha1Bytes = messageDigest.digest(s.getBytes("UTF-8"));
    207       return ByteString.of(sha1Bytes).base64();
    208     } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
    209       throw new AssertionError(e);
    210     }
    211   }
    212 
    213   /** Returns a SHA-1 hash of {@code s}. */
    214   public static ByteString sha1(ByteString s) {
    215     try {
    216       MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
    217       byte[] sha1Bytes = messageDigest.digest(s.toByteArray());
    218       return ByteString.of(sha1Bytes);
    219     } catch (NoSuchAlgorithmException e) {
    220       throw new AssertionError(e);
    221     }
    222   }
    223 
    224   /** Returns an immutable copy of {@code list}. */
    225   public static <T> List<T> immutableList(List<T> list) {
    226     return Collections.unmodifiableList(new ArrayList<>(list));
    227   }
    228 
    229   /** Returns an immutable list containing {@code elements}. */
    230   public static <T> List<T> immutableList(T... elements) {
    231     return Collections.unmodifiableList(Arrays.asList(elements.clone()));
    232   }
    233 
    234   /** Returns an immutable copy of {@code map}. */
    235   public static <K, V> Map<K, V> immutableMap(Map<K, V> map) {
    236     return Collections.unmodifiableMap(new LinkedHashMap<>(map));
    237   }
    238 
    239   public static ThreadFactory threadFactory(final String name, final boolean daemon) {
    240     return new ThreadFactory() {
    241       @Override public Thread newThread(Runnable runnable) {
    242         Thread result = new Thread(runnable, name);
    243         result.setDaemon(daemon);
    244         return result;
    245       }
    246     };
    247   }
    248 
    249   /**
    250    * Returns an array containing containing only elements found in {@code first}  and also in
    251    * {@code second}. The returned elements are in the same order as in {@code first}.
    252    */
    253   @SuppressWarnings("unchecked")
    254   public static <T> T[] intersect(Class<T> arrayType, T[] first, T[] second) {
    255     List<T> result = intersect(first, second);
    256     return result.toArray((T[]) Array.newInstance(arrayType, result.size()));
    257   }
    258 
    259   /**
    260    * Returns a list containing containing only elements found in {@code first}  and also in
    261    * {@code second}. The returned elements are in the same order as in {@code first}.
    262    */
    263   private static <T> List<T> intersect(T[] first, T[] second) {
    264     List<T> result = new ArrayList<>();
    265     for (T a : first) {
    266       for (T b : second) {
    267         if (a.equals(b)) {
    268           result.add(b);
    269           break;
    270         }
    271       }
    272     }
    273     return result;
    274   }
    275 }
    276