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