/* * Copyright (C) 2012 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.okhttp; import com.squareup.okhttp.internal.Util; import com.squareup.okhttp.internal.http.HttpAuthenticator; import com.squareup.okhttp.internal.http.HttpURLConnectionImpl; import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl; import com.squareup.okhttp.internal.http.OkResponseCacheAdapter; import com.squareup.okhttp.internal.tls.OkHostnameVerifier; import java.net.CookieHandler; import java.net.HttpURLConnection; import java.net.Proxy; import java.net.ProxySelector; import java.net.ResponseCache; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; /** Configures and creates HTTP connections. */ public final class OkHttpClient implements URLStreamHandlerFactory { private static final List<String> DEFAULT_TRANSPORTS = Util.immutableList(Arrays.asList("spdy/3", "http/1.1")); private final RouteDatabase routeDatabase; private final Dispatcher dispatcher; private Proxy proxy; private List<String> transports; private ProxySelector proxySelector; private CookieHandler cookieHandler; private ResponseCache responseCache; private SSLSocketFactory sslSocketFactory; private HostnameVerifier hostnameVerifier; private OkAuthenticator authenticator; private ConnectionPool connectionPool; private boolean followProtocolRedirects = true; private int connectTimeout; private int readTimeout; public OkHttpClient() { routeDatabase = new RouteDatabase(); dispatcher = new Dispatcher(); } private OkHttpClient(OkHttpClient copyFrom) { routeDatabase = copyFrom.routeDatabase; dispatcher = copyFrom.dispatcher; } /** * Sets the default connect timeout for new connections. A value of 0 means no timeout. * * @see URLConnection#setConnectTimeout(int) */ public void setConnectTimeout(long timeout, TimeUnit unit) { if (timeout < 0) { throw new IllegalArgumentException("timeout < 0"); } if (unit == null) { throw new IllegalArgumentException("unit == null"); } long millis = unit.toMillis(timeout); if (millis > Integer.MAX_VALUE) { throw new IllegalArgumentException("Timeout too large."); } connectTimeout = (int) millis; } /** Default connect timeout (in milliseconds). */ public int getConnectTimeout() { return connectTimeout; } /** * Sets the default read timeout for new connections. A value of 0 means no timeout. * * @see URLConnection#setReadTimeout(int) */ public void setReadTimeout(long timeout, TimeUnit unit) { if (timeout < 0) { throw new IllegalArgumentException("timeout < 0"); } if (unit == null) { throw new IllegalArgumentException("unit == null"); } long millis = unit.toMillis(timeout); if (millis > Integer.MAX_VALUE) { throw new IllegalArgumentException("Timeout too large."); } readTimeout = (int) millis; } /** Default read timeout (in milliseconds). */ public int getReadTimeout() { return readTimeout; } /** * Sets the HTTP proxy that will be used by connections created by this * client. This takes precedence over {@link #setProxySelector}, which is * only honored when this proxy is null (which it is by default). To disable * proxy use completely, call {@code setProxy(Proxy.NO_PROXY)}. */ public OkHttpClient setProxy(Proxy proxy) { this.proxy = proxy; return this; } public Proxy getProxy() { return proxy; } /** * Sets the proxy selection policy to be used if no {@link #setProxy proxy} * is specified explicitly. The proxy selector may return multiple proxies; * in that case they will be tried in sequence until a successful connection * is established. * * <p>If unset, the {@link ProxySelector#getDefault() system-wide default} * proxy selector will be used. */ public OkHttpClient setProxySelector(ProxySelector proxySelector) { this.proxySelector = proxySelector; return this; } public ProxySelector getProxySelector() { return proxySelector; } /** * Sets the cookie handler to be used to read outgoing cookies and write * incoming cookies. * * <p>If unset, the {@link CookieHandler#getDefault() system-wide default} * cookie handler will be used. */ public OkHttpClient setCookieHandler(CookieHandler cookieHandler) { this.cookieHandler = cookieHandler; return this; } public CookieHandler getCookieHandler() { return cookieHandler; } /** * Sets the response cache to be used to read and write cached responses. * * <p>If unset, the {@link ResponseCache#getDefault() system-wide default} * response cache will be used. */ public OkHttpClient setResponseCache(ResponseCache responseCache) { this.responseCache = responseCache; return this; } public ResponseCache getResponseCache() { return responseCache; } public OkResponseCache getOkResponseCache() { if (responseCache instanceof HttpResponseCache) { return ((HttpResponseCache) responseCache).okResponseCache; } else if (responseCache != null) { return new OkResponseCacheAdapter(responseCache); } else { return null; } } /** * Sets the socket factory used to secure HTTPS connections. * * <p>If unset, the {@link HttpsURLConnection#getDefaultSSLSocketFactory() * system-wide default} SSL socket factory will be used. */ public OkHttpClient setSslSocketFactory(SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; return this; } public SSLSocketFactory getSslSocketFactory() { return sslSocketFactory; } /** * Sets the verifier used to confirm that response certificates apply to * requested hostnames for HTTPS connections. * * <p>If unset, the {@link HttpsURLConnection#getDefaultHostnameVerifier() * system-wide default} hostname verifier will be used. */ public OkHttpClient setHostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; return this; } public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } /** * Sets the authenticator used to respond to challenges from the remote web * server or proxy server. * * <p>If unset, the {@link java.net.Authenticator#setDefault system-wide default} * authenticator will be used. */ public OkHttpClient setAuthenticator(OkAuthenticator authenticator) { this.authenticator = authenticator; return this; } public OkAuthenticator getAuthenticator() { return authenticator; } /** * Sets the connection pool used to recycle HTTP and HTTPS connections. * * <p>If unset, the {@link ConnectionPool#getDefault() system-wide * default} connection pool will be used. */ public OkHttpClient setConnectionPool(ConnectionPool connectionPool) { this.connectionPool = connectionPool; return this; } public ConnectionPool getConnectionPool() { return connectionPool; } /** * Configure this client to follow redirects from HTTPS to HTTP and from HTTP * to HTTPS. * * <p>If unset, protocol redirects will be followed. This is different than * the built-in {@code HttpURLConnection}'s default. */ public OkHttpClient setFollowProtocolRedirects(boolean followProtocolRedirects) { this.followProtocolRedirects = followProtocolRedirects; return this; } public boolean getFollowProtocolRedirects() { return followProtocolRedirects; } public RouteDatabase getRoutesDatabase() { return routeDatabase; } /** * Configure the transports used by this client to communicate with remote * servers. By default this client will prefer the most efficient transport * available, falling back to more ubiquitous transports. Applications should * only call this method to avoid specific compatibility problems, such as web * servers that behave incorrectly when SPDY is enabled. * * <p>The following transports are currently supported: * <ul> * <li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">http/1.1</a> * <li><a href="http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3">spdy/3</a> * </ul> * * <p><strong>This is an evolving set.</strong> Future releases may drop * support for transitional transports (like spdy/3), in favor of their * successors (spdy/4 or http/2.0). The http/1.1 transport will never be * dropped. * * <p>If multiple protocols are specified, <a * href="https://technotes.googlecode.com/git/nextprotoneg.html">NPN</a> will * be used to negotiate a transport. Future releases may use another mechanism * (such as <a href="http://tools.ietf.org/html/draft-friedl-tls-applayerprotoneg-02">ALPN</a>) * to negotiate a transport. * * @param transports the transports to use, in order of preference. The list * must contain "http/1.1". It must not contain null. */ public OkHttpClient setTransports(List<String> transports) { transports = Util.immutableList(transports); if (!transports.contains("http/1.1")) { throw new IllegalArgumentException("transports doesn't contain http/1.1: " + transports); } if (transports.contains(null)) { throw new IllegalArgumentException("transports must not contain null"); } if (transports.contains("")) { throw new IllegalArgumentException("transports contains an empty string"); } this.transports = transports; return this; } public List<String> getTransports() { return transports; } /** * Schedules {@code request} to be executed. */ /* OkHttp 2.0: public */ void enqueue(Request request, Response.Receiver responseReceiver) { // Create the HttpURLConnection immediately so the enqueued job gets the current settings of // this client. Otherwise changes to this client (socket factory, redirect policy, etc.) may // incorrectly be reflected in the request when it is dispatched later. dispatcher.enqueue(copyWithDefaults(), request, responseReceiver); } /** * Cancels all scheduled tasks tagged with {@code tag}. Requests that are already * in flight might not be canceled. */ /* OkHttp 2.0: public */ void cancel(Object tag) { dispatcher.cancel(tag); } public HttpURLConnection open(URL url) { return open(url, proxy); } HttpURLConnection open(URL url, Proxy proxy) { String protocol = url.getProtocol(); OkHttpClient copy = copyWithDefaults(); copy.proxy = proxy; if (protocol.equals("http")) return new HttpURLConnectionImpl(url, copy); if (protocol.equals("https")) return new HttpsURLConnectionImpl(url, copy); throw new IllegalArgumentException("Unexpected protocol: " + protocol); } /** * Returns a shallow copy of this OkHttpClient that uses the system-wide default for * each field that hasn't been explicitly configured. */ private OkHttpClient copyWithDefaults() { OkHttpClient result = new OkHttpClient(this); result.proxy = proxy; result.proxySelector = proxySelector != null ? proxySelector : ProxySelector.getDefault(); result.cookieHandler = cookieHandler != null ? cookieHandler : CookieHandler.getDefault(); result.responseCache = responseCache != null ? responseCache : ResponseCache.getDefault(); result.sslSocketFactory = sslSocketFactory != null ? sslSocketFactory : HttpsURLConnection.getDefaultSSLSocketFactory(); result.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : OkHostnameVerifier.INSTANCE; result.authenticator = authenticator != null ? authenticator : HttpAuthenticator.SYSTEM_DEFAULT; result.connectionPool = connectionPool != null ? connectionPool : ConnectionPool.getDefault(); result.followProtocolRedirects = followProtocolRedirects; result.transports = transports != null ? transports : DEFAULT_TRANSPORTS; result.connectTimeout = connectTimeout; result.readTimeout = readTimeout; return result; } /** * Creates a URLStreamHandler as a {@link URL#setURLStreamHandlerFactory}. * * <p>This code configures OkHttp to handle all HTTP and HTTPS connections * created with {@link URL#openConnection()}: <pre> {@code * * OkHttpClient okHttpClient = new OkHttpClient(); * URL.setURLStreamHandlerFactory(okHttpClient); * }</pre> */ public URLStreamHandler createURLStreamHandler(final String protocol) { if (!protocol.equals("http") && !protocol.equals("https")) return null; return new URLStreamHandler() { @Override protected URLConnection openConnection(URL url) { return open(url); } @Override protected URLConnection openConnection(URL url, Proxy proxy) { return open(url, proxy); } @Override protected int getDefaultPort() { if (protocol.equals("http")) return 80; if (protocol.equals("https")) return 443; throw new AssertionError(); } }; } }