1 /* 2 * Copyright (C) 2012 Square, Inc. 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 package com.squareup.okhttp; 17 18 import com.squareup.okhttp.internal.Util; 19 import com.squareup.okhttp.internal.http.Dispatcher; 20 import com.squareup.okhttp.internal.http.HttpAuthenticator; 21 import com.squareup.okhttp.internal.http.HttpURLConnectionImpl; 22 import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl; 23 import com.squareup.okhttp.internal.http.OkResponseCacheAdapter; 24 import com.squareup.okhttp.internal.tls.OkHostnameVerifier; 25 import java.net.CookieHandler; 26 import java.net.HttpURLConnection; 27 import java.net.Proxy; 28 import java.net.ProxySelector; 29 import java.net.ResponseCache; 30 import java.net.URL; 31 import java.net.URLConnection; 32 import java.net.URLStreamHandler; 33 import java.net.URLStreamHandlerFactory; 34 import java.util.Arrays; 35 import java.util.List; 36 import java.util.concurrent.TimeUnit; 37 import javax.net.ssl.HostnameVerifier; 38 import javax.net.ssl.HttpsURLConnection; 39 import javax.net.ssl.SSLSocketFactory; 40 41 /** Configures and creates HTTP connections. */ 42 public final class OkHttpClient implements URLStreamHandlerFactory { 43 private static final List<String> DEFAULT_TRANSPORTS 44 = Util.immutableList(Arrays.asList("spdy/3", "http/1.1")); 45 46 private final RouteDatabase routeDatabase; 47 private final Dispatcher dispatcher; 48 private Proxy proxy; 49 private List<String> transports; 50 private ProxySelector proxySelector; 51 private CookieHandler cookieHandler; 52 private ResponseCache responseCache; 53 private SSLSocketFactory sslSocketFactory; 54 private HostnameVerifier hostnameVerifier; 55 private OkAuthenticator authenticator; 56 private ConnectionPool connectionPool; 57 private boolean followProtocolRedirects = true; 58 private int connectTimeout; 59 private int readTimeout; 60 61 public OkHttpClient() { 62 routeDatabase = new RouteDatabase(); 63 dispatcher = new Dispatcher(); 64 } 65 66 private OkHttpClient(OkHttpClient copyFrom) { 67 routeDatabase = copyFrom.routeDatabase; 68 dispatcher = copyFrom.dispatcher; 69 } 70 71 /** 72 * Sets the default connect timeout for new connections. A value of 0 means no timeout. 73 * 74 * @see URLConnection#setConnectTimeout(int) 75 */ 76 public void setConnectTimeout(long timeout, TimeUnit unit) { 77 if (timeout < 0) { 78 throw new IllegalArgumentException("timeout < 0"); 79 } 80 if (unit == null) { 81 throw new IllegalArgumentException("unit == null"); 82 } 83 long millis = unit.toMillis(timeout); 84 if (millis > Integer.MAX_VALUE) { 85 throw new IllegalArgumentException("Timeout too large."); 86 } 87 connectTimeout = (int) millis; 88 } 89 90 /** Default connect timeout (in milliseconds). */ 91 public int getConnectTimeout() { 92 return connectTimeout; 93 } 94 95 /** 96 * Sets the default read timeout for new connections. A value of 0 means no timeout. 97 * 98 * @see URLConnection#setReadTimeout(int) 99 */ 100 public void setReadTimeout(long timeout, TimeUnit unit) { 101 if (timeout < 0) { 102 throw new IllegalArgumentException("timeout < 0"); 103 } 104 if (unit == null) { 105 throw new IllegalArgumentException("unit == null"); 106 } 107 long millis = unit.toMillis(timeout); 108 if (millis > Integer.MAX_VALUE) { 109 throw new IllegalArgumentException("Timeout too large."); 110 } 111 readTimeout = (int) millis; 112 } 113 114 /** Default read timeout (in milliseconds). */ 115 public int getReadTimeout() { 116 return readTimeout; 117 } 118 119 /** 120 * Sets the HTTP proxy that will be used by connections created by this 121 * client. This takes precedence over {@link #setProxySelector}, which is 122 * only honored when this proxy is null (which it is by default). To disable 123 * proxy use completely, call {@code setProxy(Proxy.NO_PROXY)}. 124 */ 125 public OkHttpClient setProxy(Proxy proxy) { 126 this.proxy = proxy; 127 return this; 128 } 129 130 public Proxy getProxy() { 131 return proxy; 132 } 133 134 /** 135 * Sets the proxy selection policy to be used if no {@link #setProxy proxy} 136 * is specified explicitly. The proxy selector may return multiple proxies; 137 * in that case they will be tried in sequence until a successful connection 138 * is established. 139 * 140 * <p>If unset, the {@link ProxySelector#getDefault() system-wide default} 141 * proxy selector will be used. 142 */ 143 public OkHttpClient setProxySelector(ProxySelector proxySelector) { 144 this.proxySelector = proxySelector; 145 return this; 146 } 147 148 public ProxySelector getProxySelector() { 149 return proxySelector; 150 } 151 152 /** 153 * Sets the cookie handler to be used to read outgoing cookies and write 154 * incoming cookies. 155 * 156 * <p>If unset, the {@link CookieHandler#getDefault() system-wide default} 157 * cookie handler will be used. 158 */ 159 public OkHttpClient setCookieHandler(CookieHandler cookieHandler) { 160 this.cookieHandler = cookieHandler; 161 return this; 162 } 163 164 public CookieHandler getCookieHandler() { 165 return cookieHandler; 166 } 167 168 /** 169 * Sets the response cache to be used to read and write cached responses. 170 * 171 * <p>If unset, the {@link ResponseCache#getDefault() system-wide default} 172 * response cache will be used. 173 */ 174 public OkHttpClient setResponseCache(ResponseCache responseCache) { 175 this.responseCache = responseCache; 176 return this; 177 } 178 179 public ResponseCache getResponseCache() { 180 return responseCache; 181 } 182 183 public OkResponseCache getOkResponseCache() { 184 if (responseCache instanceof HttpResponseCache) { 185 return ((HttpResponseCache) responseCache).okResponseCache; 186 } else if (responseCache != null) { 187 return new OkResponseCacheAdapter(responseCache); 188 } else { 189 return null; 190 } 191 } 192 193 /** 194 * Sets the socket factory used to secure HTTPS connections. 195 * 196 * <p>If unset, the {@link HttpsURLConnection#getDefaultSSLSocketFactory() 197 * system-wide default} SSL socket factory will be used. 198 */ 199 public OkHttpClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) { 200 this.sslSocketFactory = sslSocketFactory; 201 return this; 202 } 203 204 public SSLSocketFactory getSslSocketFactory() { 205 return sslSocketFactory; 206 } 207 208 /** 209 * Sets the verifier used to confirm that response certificates apply to 210 * requested hostnames for HTTPS connections. 211 * 212 * <p>If unset, the {@link HttpsURLConnection#getDefaultHostnameVerifier() 213 * system-wide default} hostname verifier will be used. 214 */ 215 public OkHttpClient setHostnameVerifier(HostnameVerifier hostnameVerifier) { 216 this.hostnameVerifier = hostnameVerifier; 217 return this; 218 } 219 220 public HostnameVerifier getHostnameVerifier() { 221 return hostnameVerifier; 222 } 223 224 /** 225 * Sets the authenticator used to respond to challenges from the remote web 226 * server or proxy server. 227 * 228 * <p>If unset, the {@link java.net.Authenticator#setDefault system-wide default} 229 * authenticator will be used. 230 */ 231 public OkHttpClient setAuthenticator(OkAuthenticator authenticator) { 232 this.authenticator = authenticator; 233 return this; 234 } 235 236 public OkAuthenticator getAuthenticator() { 237 return authenticator; 238 } 239 240 /** 241 * Sets the connection pool used to recycle HTTP and HTTPS connections. 242 * 243 * <p>If unset, the {@link ConnectionPool#getDefault() system-wide 244 * default} connection pool will be used. 245 */ 246 public OkHttpClient setConnectionPool(ConnectionPool connectionPool) { 247 this.connectionPool = connectionPool; 248 return this; 249 } 250 251 public ConnectionPool getConnectionPool() { 252 return connectionPool; 253 } 254 255 /** 256 * Configure this client to follow redirects from HTTPS to HTTP and from HTTP 257 * to HTTPS. 258 * 259 * <p>If unset, protocol redirects will be followed. This is different than 260 * the built-in {@code HttpURLConnection}'s default. 261 */ 262 public OkHttpClient setFollowProtocolRedirects(boolean followProtocolRedirects) { 263 this.followProtocolRedirects = followProtocolRedirects; 264 return this; 265 } 266 267 public boolean getFollowProtocolRedirects() { 268 return followProtocolRedirects; 269 } 270 271 public RouteDatabase getRoutesDatabase() { 272 return routeDatabase; 273 } 274 275 /** 276 * Configure the transports used by this client to communicate with remote 277 * servers. By default this client will prefer the most efficient transport 278 * available, falling back to more ubiquitous transports. Applications should 279 * only call this method to avoid specific compatibility problems, such as web 280 * servers that behave incorrectly when SPDY is enabled. 281 * 282 * <p>The following transports are currently supported: 283 * <ul> 284 * <li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">http/1.1</a> 285 * <li><a href="http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3">spdy/3</a> 286 * </ul> 287 * 288 * <p><strong>This is an evolving set.</strong> Future releases may drop 289 * support for transitional transports (like spdy/3), in favor of their 290 * successors (spdy/4 or http/2.0). The http/1.1 transport will never be 291 * dropped. 292 * 293 * <p>If multiple protocols are specified, <a 294 * href="https://technotes.googlecode.com/git/nextprotoneg.html">NPN</a> will 295 * be used to negotiate a transport. Future releases may use another mechanism 296 * (such as <a href="http://tools.ietf.org/html/draft-friedl-tls-applayerprotoneg-02">ALPN</a>) 297 * to negotiate a transport. 298 * 299 * @param transports the transports to use, in order of preference. The list 300 * must contain "http/1.1". It must not contain null. 301 */ 302 public OkHttpClient setTransports(List<String> transports) { 303 transports = Util.immutableList(transports); 304 if (!transports.contains("http/1.1")) { 305 throw new IllegalArgumentException("transports doesn't contain http/1.1: " + transports); 306 } 307 if (transports.contains(null)) { 308 throw new IllegalArgumentException("transports must not contain null"); 309 } 310 if (transports.contains("")) { 311 throw new IllegalArgumentException("transports contains an empty string"); 312 } 313 this.transports = transports; 314 return this; 315 } 316 317 public List<String> getTransports() { 318 return transports; 319 } 320 321 /** 322 * Schedules {@code request} to be executed. 323 */ 324 public void enqueue(Request request, Response.Receiver responseReceiver) { 325 // Create the HttpURLConnection immediately so the enqueued job gets the current settings of 326 // this client. Otherwise changes to this client (socket factory, redirect policy, etc.) may 327 // incorrectly be reflected in the request when it is dispatched later. 328 dispatcher.enqueue(open(request.url()), request, responseReceiver); 329 } 330 331 /** 332 * Cancels all scheduled tasks tagged with {@code tag}. Requests that are already 333 * in flight might not be canceled. 334 */ 335 public void cancel(Object tag) { 336 dispatcher.cancel(tag); 337 } 338 339 public HttpURLConnection open(URL url) { 340 return open(url, proxy); 341 } 342 343 HttpURLConnection open(URL url, Proxy proxy) { 344 String protocol = url.getProtocol(); 345 OkHttpClient copy = copyWithDefaults(); 346 copy.proxy = proxy; 347 348 if (protocol.equals("http")) return new HttpURLConnectionImpl(url, copy); 349 if (protocol.equals("https")) return new HttpsURLConnectionImpl(url, copy); 350 throw new IllegalArgumentException("Unexpected protocol: " + protocol); 351 } 352 353 /** 354 * Returns a shallow copy of this OkHttpClient that uses the system-wide default for 355 * each field that hasn't been explicitly configured. 356 */ 357 private OkHttpClient copyWithDefaults() { 358 OkHttpClient result = new OkHttpClient(this); 359 result.proxy = proxy; 360 result.proxySelector = proxySelector != null ? proxySelector : ProxySelector.getDefault(); 361 result.cookieHandler = cookieHandler != null ? cookieHandler : CookieHandler.getDefault(); 362 result.responseCache = responseCache != null ? responseCache : ResponseCache.getDefault(); 363 result.sslSocketFactory = sslSocketFactory != null 364 ? sslSocketFactory 365 : HttpsURLConnection.getDefaultSSLSocketFactory(); 366 result.hostnameVerifier = hostnameVerifier != null 367 ? hostnameVerifier 368 : OkHostnameVerifier.INSTANCE; 369 result.authenticator = authenticator != null 370 ? authenticator 371 : HttpAuthenticator.SYSTEM_DEFAULT; 372 result.connectionPool = connectionPool != null ? connectionPool : ConnectionPool.getDefault(); 373 result.followProtocolRedirects = followProtocolRedirects; 374 result.transports = transports != null ? transports : DEFAULT_TRANSPORTS; 375 result.connectTimeout = connectTimeout; 376 result.readTimeout = readTimeout; 377 return result; 378 } 379 380 /** 381 * Creates a URLStreamHandler as a {@link URL#setURLStreamHandlerFactory}. 382 * 383 * <p>This code configures OkHttp to handle all HTTP and HTTPS connections 384 * created with {@link URL#openConnection()}: <pre> {@code 385 * 386 * OkHttpClient okHttpClient = new OkHttpClient(); 387 * URL.setURLStreamHandlerFactory(okHttpClient); 388 * }</pre> 389 */ 390 public URLStreamHandler createURLStreamHandler(final String protocol) { 391 if (!protocol.equals("http") && !protocol.equals("https")) return null; 392 393 return new URLStreamHandler() { 394 @Override protected URLConnection openConnection(URL url) { 395 return open(url); 396 } 397 398 @Override protected URLConnection openConnection(URL url, Proxy proxy) { 399 return open(url, proxy); 400 } 401 402 @Override protected int getDefaultPort() { 403 if (protocol.equals("http")) return 80; 404 if (protocol.equals("https")) return 443; 405 throw new AssertionError(); 406 } 407 }; 408 } 409 } 410