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