okhttp3.OkHttpClient.kt Maven / Gradle / Ivy
/*
* 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 okhttp3
import okhttp3.Protocol.HTTP_1_1
import okhttp3.Protocol.HTTP_2
import okhttp3.internal.asFactory
import okhttp3.internal.checkDuration
import okhttp3.internal.concurrent.TaskRunner
import okhttp3.internal.immutableListOf
import okhttp3.internal.platform.Platform
import okhttp3.internal.proxy.NullProxySelector
import okhttp3.internal.tls.CertificateChainCleaner
import okhttp3.internal.tls.OkHostnameVerifier
import okhttp3.internal.toImmutableList
import okhttp3.internal.ws.RealWebSocket
import okio.Sink
import okio.Source
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
import java.net.Proxy
import java.net.ProxySelector
import java.net.Socket
import java.security.GeneralSecurityException
import java.time.Duration
import java.util.Collections
import java.util.Random
import java.util.concurrent.ExecutorService
import java.util.concurrent.TimeUnit
import javax.net.SocketFactory
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
/**
* Factory for [calls][Call], which can be used to send HTTP requests and read their responses.
*
* ## OkHttpClients Should Be Shared
*
* OkHttp performs best when you create a single `OkHttpClient` instance and reuse it for all of
* your HTTP calls. This is because each client holds its own connection pool and thread pools.
* Reusing connections and threads reduces latency and saves memory. Conversely, creating a client
* for each request wastes resources on idle pools.
*
* Use `new OkHttpClient()` to create a shared instance with the default settings:
*
* ```
* // The singleton HTTP client.
* public final OkHttpClient client = new OkHttpClient();
* ```
*
* Or use `new OkHttpClient.Builder()` to create a shared instance with custom settings:
*
* ```
* // The singleton HTTP client.
* public final OkHttpClient client = new OkHttpClient.Builder()
* .addInterceptor(new HttpLoggingInterceptor())
* .cache(new Cache(cacheDir, cacheSize))
* .build();
* ```
*
* ## Customize Your Client With newBuilder()
*
* You can customize a shared OkHttpClient instance with [newBuilder]. This builds a client that
* shares the same connection pool, thread pools, and configuration. Use the builder methods to
* configure the derived client for a specific purpose.
*
* This example shows a call with a short 500 millisecond timeout:
*
* ```
* OkHttpClient eagerClient = client.newBuilder()
* .readTimeout(500, TimeUnit.MILLISECONDS)
* .build();
* Response response = eagerClient.newCall(request).execute();
* ```
*
* ## Shutdown Isn't Necessary
*
* The threads and connections that are held will be released automatically if they remain idle. But
* if you are writing a application that needs to aggressively release unused resources you may do
* so.
*
* Shutdown the dispatcher's executor service with [shutdown()][ExecutorService.shutdown]. This will
* also cause future calls to the client to be rejected.
*
* ```
* client.dispatcher().executorService().shutdown();
* ```
*
* Clear the connection pool with [evictAll()][ConnectionPool.evictAll]. Note that the connection
* pool's daemon thread may not exit immediately.
*
* ```
* client.connectionPool().evictAll();
* ```
*
* If your client has a cache, call [close()][Cache.close]. Note that it is an error to create calls
* against a cache that is closed, and doing so will cause the call to crash.
*
* ```
* client.cache().close();
* ```
*
* OkHttp also uses daemon threads for HTTP/2 connections. These will exit automatically if they
* remain idle.
*/
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher
@get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool
/**
* Returns an immutable list of interceptors that observe the full span of each call: from before
* the connection is established (if any) until after the response source is selected (either the
* origin server, cache, or both).
*/
@get:JvmName("interceptors") val interceptors: List =
builder.interceptors.toImmutableList()
/**
* Returns an immutable list of interceptors that observe a single network request and response.
* These interceptors must call [Interceptor.Chain.proceed] exactly once: it is an error for
* a network interceptor to short-circuit or repeat a network request.
*/
@get:JvmName("networkInterceptors") val networkInterceptors: List =
builder.networkInterceptors.toImmutableList()
@get:JvmName("eventListenerFactory") val eventListenerFactory: EventListener.Factory =
builder.eventListenerFactory
@get:JvmName("retryOnConnectionFailure") val retryOnConnectionFailure: Boolean =
builder.retryOnConnectionFailure
@get:JvmName("authenticator") val authenticator: Authenticator = builder.authenticator
@get:JvmName("followRedirects") val followRedirects: Boolean = builder.followRedirects
@get:JvmName("followSslRedirects") val followSslRedirects: Boolean = builder.followSslRedirects
@get:JvmName("cookieJar") val cookieJar: CookieJar = builder.cookieJar
@get:JvmName("cache") val cache: Cache? = builder.cache
@get:JvmName("dns") val dns: Dns = builder.dns
@get:JvmName("proxy") val proxy: Proxy? = builder.proxy
@get:JvmName("proxySelector") val proxySelector: ProxySelector =
when {
// Defer calls to ProxySelector.getDefault() because it can throw a SecurityException.
builder.proxy != null -> NullProxySelector
else -> builder.proxySelector ?: ProxySelector.getDefault() ?: NullProxySelector
}
@get:JvmName("proxyAuthenticator") val proxyAuthenticator: Authenticator =
builder.proxyAuthenticator
@get:JvmName("socketFactory") val socketFactory: SocketFactory = builder.socketFactory
private val sslSocketFactoryOrNull: SSLSocketFactory?
@get:JvmName("sslSocketFactory") val sslSocketFactory: SSLSocketFactory
get() = sslSocketFactoryOrNull ?: throw IllegalStateException("CLEARTEXT-only client")
@get:JvmName("x509TrustManager") val x509TrustManager: X509TrustManager?
@get:JvmName("connectionSpecs") val connectionSpecs: List =
builder.connectionSpecs
@get:JvmName("protocols") val protocols: List = builder.protocols
@get:JvmName("hostnameVerifier") val hostnameVerifier: HostnameVerifier = builder.hostnameVerifier
@get:JvmName("certificatePinner") val certificatePinner: CertificatePinner
@get:JvmName("certificateChainCleaner") val certificateChainCleaner: CertificateChainCleaner?
/**
* Default call timeout (in milliseconds). By default there is no timeout for complete calls, but
* there is for the connect, write, and read actions within a call.
*/
@get:JvmName("callTimeoutMillis") val callTimeoutMillis: Int = builder.callTimeout
/** Default connect timeout (in milliseconds). The default is 10 seconds. */
@get:JvmName("connectTimeoutMillis") val connectTimeoutMillis: Int = builder.connectTimeout
/** Default read timeout (in milliseconds). The default is 10 seconds. */
@get:JvmName("readTimeoutMillis") val readTimeoutMillis: Int = builder.readTimeout
/** Default write timeout (in milliseconds). The default is 10 seconds. */
@get:JvmName("writeTimeoutMillis") val writeTimeoutMillis: Int = builder.writeTimeout
/** Web socket and HTTP/2 ping interval (in milliseconds). By default pings are not sent. */
@get:JvmName("pingIntervalMillis") val pingIntervalMillis: Int = builder.pingInterval
constructor() : this(Builder())
init {
if (builder.sslSocketFactoryOrNull != null || connectionSpecs.none { it.isTls }) {
this.sslSocketFactoryOrNull = builder.sslSocketFactoryOrNull
this.certificateChainCleaner = builder.certificateChainCleaner
this.x509TrustManager = builder.x509TrustManagerOrNull
} else {
this.x509TrustManager = Platform.get().platformTrustManager()
Platform.get().configureTrustManager(x509TrustManager)
this.sslSocketFactoryOrNull = newSslSocketFactory(x509TrustManager!!)
this.certificateChainCleaner = CertificateChainCleaner.get(x509TrustManager!!)
}
if (sslSocketFactoryOrNull != null) {
Platform.get().configureSslSocketFactory(sslSocketFactoryOrNull)
}
this.certificatePinner = builder.certificatePinner
.withCertificateChainCleaner(certificateChainCleaner)
check(null !in (interceptors as List)) {
"Null interceptor: $interceptors"
}
check(null !in (networkInterceptors as List)) {
"Null network interceptor: $networkInterceptors"
}
}
/** Prepares the [request] to be executed at some point in the future. */
override fun newCall(request: Request): Call {
return RealCall.newRealCall(this, request, forWebSocket = false)
}
/** Uses [request] to connect a new web socket. */
override fun newWebSocket(request: Request, listener: WebSocketListener): WebSocket {
val webSocket = RealWebSocket(
TaskRunner.INSTANCE,
request,
listener,
Random(),
pingIntervalMillis.toLong()
)
webSocket.connect(this)
return webSocket
}
open fun newBuilder(): Builder = Builder(this)
@JvmName("-deprecated_dispatcher")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "dispatcher"),
level = DeprecationLevel.ERROR)
fun dispatcher(): Dispatcher = dispatcher
@JvmName("-deprecated_connectionPool")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "connectionPool"),
level = DeprecationLevel.ERROR)
fun connectionPool(): ConnectionPool = connectionPool
@JvmName("-deprecated_interceptors")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "interceptors"),
level = DeprecationLevel.ERROR)
fun interceptors(): List = interceptors
@JvmName("-deprecated_networkInterceptors")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "networkInterceptors"),
level = DeprecationLevel.ERROR)
fun networkInterceptors(): List = networkInterceptors
@JvmName("-deprecated_eventListenerFactory")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "eventListenerFactory"),
level = DeprecationLevel.ERROR)
fun eventListenerFactory(): EventListener.Factory = eventListenerFactory
@JvmName("-deprecated_retryOnConnectionFailure")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "retryOnConnectionFailure"),
level = DeprecationLevel.ERROR)
fun retryOnConnectionFailure(): Boolean = retryOnConnectionFailure
@JvmName("-deprecated_authenticator")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "authenticator"),
level = DeprecationLevel.ERROR)
fun authenticator(): Authenticator = authenticator
@JvmName("-deprecated_followRedirects")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "followRedirects"),
level = DeprecationLevel.ERROR)
fun followRedirects(): Boolean = followRedirects
@JvmName("-deprecated_followSslRedirects")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "followSslRedirects"),
level = DeprecationLevel.ERROR)
fun followSslRedirects(): Boolean = followSslRedirects
@JvmName("-deprecated_cookieJar")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "cookieJar"),
level = DeprecationLevel.ERROR)
fun cookieJar(): CookieJar = cookieJar
@JvmName("-deprecated_cache")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "cache"),
level = DeprecationLevel.ERROR)
fun cache(): Cache? = cache
@JvmName("-deprecated_dns")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "dns"),
level = DeprecationLevel.ERROR)
fun dns(): Dns = dns
@JvmName("-deprecated_proxy")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "proxy"),
level = DeprecationLevel.ERROR)
fun proxy(): Proxy? = proxy
@JvmName("-deprecated_proxySelector")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "proxySelector"),
level = DeprecationLevel.ERROR)
fun proxySelector(): ProxySelector = proxySelector
@JvmName("-deprecated_proxyAuthenticator")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "proxyAuthenticator"),
level = DeprecationLevel.ERROR)
fun proxyAuthenticator(): Authenticator = proxyAuthenticator
@JvmName("-deprecated_socketFactory")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "socketFactory"),
level = DeprecationLevel.ERROR)
fun socketFactory(): SocketFactory = socketFactory
@JvmName("-deprecated_sslSocketFactory")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "sslSocketFactory"),
level = DeprecationLevel.ERROR)
fun sslSocketFactory(): SSLSocketFactory = sslSocketFactory
@JvmName("-deprecated_connectionSpecs")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "connectionSpecs"),
level = DeprecationLevel.ERROR)
fun connectionSpecs(): List = connectionSpecs
@JvmName("-deprecated_protocols")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "protocols"),
level = DeprecationLevel.ERROR)
fun protocols(): List = protocols
@JvmName("-deprecated_hostnameVerifier")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "hostnameVerifier"),
level = DeprecationLevel.ERROR)
fun hostnameVerifier(): HostnameVerifier = hostnameVerifier
@JvmName("-deprecated_certificatePinner")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "certificatePinner"),
level = DeprecationLevel.ERROR)
fun certificatePinner(): CertificatePinner = certificatePinner
@JvmName("-deprecated_callTimeoutMillis")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "callTimeoutMillis"),
level = DeprecationLevel.ERROR)
fun callTimeoutMillis(): Int = callTimeoutMillis
@JvmName("-deprecated_connectTimeoutMillis")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "connectTimeoutMillis"),
level = DeprecationLevel.ERROR)
fun connectTimeoutMillis(): Int = connectTimeoutMillis
@JvmName("-deprecated_readTimeoutMillis")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "readTimeoutMillis"),
level = DeprecationLevel.ERROR)
fun readTimeoutMillis(): Int = readTimeoutMillis
@JvmName("-deprecated_writeTimeoutMillis")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "writeTimeoutMillis"),
level = DeprecationLevel.ERROR)
fun writeTimeoutMillis(): Int = writeTimeoutMillis
@JvmName("-deprecated_pingIntervalMillis")
@Deprecated(
message = "moved to val",
replaceWith = ReplaceWith(expression = "pingIntervalMillis"),
level = DeprecationLevel.ERROR)
fun pingIntervalMillis(): Int = pingIntervalMillis
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher()
internal var connectionPool: ConnectionPool = ConnectionPool()
internal val interceptors: MutableList = mutableListOf()
internal val networkInterceptors: MutableList = mutableListOf()
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
internal var retryOnConnectionFailure = true
internal var authenticator: Authenticator = Authenticator.NONE
internal var followRedirects = true
internal var followSslRedirects = true
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
internal var cache: Cache? = null
internal var dns: Dns = Dns.SYSTEM
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
internal var x509TrustManagerOrNull: X509TrustManager? = null
internal var connectionSpecs: List = DEFAULT_CONNECTION_SPECS
internal var protocols: List = DEFAULT_PROTOCOLS
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
internal var certificateChainCleaner: CertificateChainCleaner? = null
internal var callTimeout = 0
internal var connectTimeout = 10_000
internal var readTimeout = 10_000
internal var writeTimeout = 10_000
internal var pingInterval = 0
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
this.connectionPool = okHttpClient.connectionPool
this.interceptors += okHttpClient.interceptors
this.networkInterceptors += okHttpClient.networkInterceptors
this.eventListenerFactory = okHttpClient.eventListenerFactory
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure
this.authenticator = okHttpClient.authenticator
this.followRedirects = okHttpClient.followRedirects
this.followSslRedirects = okHttpClient.followSslRedirects
this.cookieJar = okHttpClient.cookieJar
this.cache = okHttpClient.cache
this.dns = okHttpClient.dns
this.proxy = okHttpClient.proxy
this.proxySelector = okHttpClient.proxySelector
this.proxyAuthenticator = okHttpClient.proxyAuthenticator
this.socketFactory = okHttpClient.socketFactory
this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull
this.x509TrustManagerOrNull = okHttpClient.x509TrustManager
this.connectionSpecs = okHttpClient.connectionSpecs
this.protocols = okHttpClient.protocols
this.hostnameVerifier = okHttpClient.hostnameVerifier
this.certificatePinner = okHttpClient.certificatePinner
this.certificateChainCleaner = okHttpClient.certificateChainCleaner
this.callTimeout = okHttpClient.callTimeoutMillis
this.connectTimeout = okHttpClient.connectTimeoutMillis
this.readTimeout = okHttpClient.readTimeoutMillis
this.writeTimeout = okHttpClient.writeTimeoutMillis
this.pingInterval = okHttpClient.pingIntervalMillis
}
/**
* Sets the dispatcher used to set policy and execute asynchronous requests. Must not be null.
*/
fun dispatcher(dispatcher: Dispatcher) = apply {
this.dispatcher = dispatcher
}
/**
* Sets the connection pool used to recycle HTTP and HTTPS connections.
*
* If unset, a new connection pool will be used.
*/
fun connectionPool(connectionPool: ConnectionPool) = apply {
this.connectionPool = connectionPool
}
/**
* Returns a modifiable list of interceptors that observe the full span of each call: from
* before the connection is established (if any) until after the response source is selected
* (either the origin server, cache, or both).
*/
fun interceptors(): MutableList = interceptors
fun addInterceptor(interceptor: Interceptor) = apply {
interceptors += interceptor
}
@JvmName("-addInterceptor") // Prefix with '-' to prevent ambiguous overloads from Java.
inline fun addInterceptor(crossinline block: (chain: Interceptor.Chain) -> Response) =
addInterceptor(Interceptor { chain -> block(chain) })
/**
* Returns a modifiable list of interceptors that observe a single network request and response.
* These interceptors must call [Interceptor.Chain.proceed] exactly once: it is an error for a
* network interceptor to short-circuit or repeat a network request.
*/
fun networkInterceptors(): MutableList = networkInterceptors
fun addNetworkInterceptor(interceptor: Interceptor) = apply {
networkInterceptors += interceptor
}
@JvmName("-addNetworkInterceptor") // Prefix with '-' to prevent ambiguous overloads from Java.
inline fun addNetworkInterceptor(crossinline block: (chain: Interceptor.Chain) -> Response) =
addNetworkInterceptor(Interceptor { chain -> block(chain) })
/**
* Configure a single client scoped listener that will receive all analytic events for this
* client.
*
* @see EventListener for semantics and restrictions on listener implementations.
*/
fun eventListener(eventListener: EventListener) = apply {
this.eventListenerFactory = eventListener.asFactory()
}
/**
* Configure a factory to provide per-call scoped listeners that will receive analytic events
* for this client.
*
* @see EventListener for semantics and restrictions on listener implementations.
*/
fun eventListenerFactory(eventListenerFactory: EventListener.Factory) = apply {
this.eventListenerFactory = eventListenerFactory
}
/**
* Configure this client to retry or not when a connectivity problem is encountered. By default,
* this client silently recovers from the following problems:
*
* * **Unreachable IP addresses.** If the URL's host has multiple IP addresses,
* failure to reach any individual IP address doesn't fail the overall request. This can
* increase availability of multi-homed services.
*
* * **Stale pooled connections.** The [ConnectionPool] reuses sockets
* to decrease request latency, but these connections will occasionally time out.
*
* * **Unreachable proxy servers.** A [ProxySelector] can be used to
* attempt multiple proxy servers in sequence, eventually falling back to a direct
* connection.
*
* Set this to false to avoid retrying requests when doing so is destructive. In this case the
* calling application should do its own recovery of connectivity failures.
*/
fun retryOnConnectionFailure(retryOnConnectionFailure: Boolean) = apply {
this.retryOnConnectionFailure = retryOnConnectionFailure
}
/**
* Sets the authenticator used to respond to challenges from origin servers. Use
* [proxyAuthenticator] to set the authenticator for proxy servers.
*
* If unset, the [no authentication will be attempted][Authenticator.NONE].
*/
fun authenticator(authenticator: Authenticator) = apply {
this.authenticator = authenticator
}
/** Configure this client to follow redirects. If unset, redirects will be followed. */
fun followRedirects(followRedirects: Boolean) = apply {
this.followRedirects = followRedirects
}
/**
* Configure this client to follow redirects from HTTPS to HTTP and from HTTP to HTTPS.
*
* If unset, protocol redirects will be followed. This is different than the built-in
* `HttpURLConnection`'s default.
*/
fun followSslRedirects(followProtocolRedirects: Boolean) = apply {
this.followSslRedirects = followProtocolRedirects
}
/**
* Sets the handler that can accept cookies from incoming HTTP responses and provides cookies to
* outgoing HTTP requests.
*
* If unset, [no cookies][CookieJar.NO_COOKIES] will be accepted nor provided.
*/
fun cookieJar(cookieJar: CookieJar) = apply {
this.cookieJar = cookieJar
}
/** Sets the response cache to be used to read and write cached responses. */
fun cache(cache: Cache?) = apply {
this.cache = cache
}
/**
* Sets the DNS service used to lookup IP addresses for hostnames.
*
* If unset, the [system-wide default][Dns.SYSTEM] DNS will be used.
*/
fun dns(dns: Dns) = apply {
this.dns = dns
}
/**
* Sets the HTTP proxy that will be used by connections created by this client. This takes
* precedence over [proxySelector], which is only honored when this proxy is null (which it is
* by default). To disable proxy use completely, call `proxy(Proxy.NO_PROXY)`.
*/
fun proxy(proxy: Proxy?) = apply {
this.proxy = proxy
}
/**
* Sets the proxy selection policy to be used if no [proxy][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.
*
* If unset, the [system-wide default][ProxySelector.getDefault] proxy selector will be used.
*/
fun proxySelector(proxySelector: ProxySelector) = apply {
this.proxySelector = proxySelector
}
/**
* Sets the authenticator used to respond to challenges from proxy servers. Use [authenticator]
* to set the authenticator for origin servers.
*
* If unset, the [no authentication will be attempted][Authenticator.NONE].
*/
fun proxyAuthenticator(proxyAuthenticator: Authenticator) = apply {
this.proxyAuthenticator = proxyAuthenticator
}
/**
* Sets the socket factory used to create connections. OkHttp only uses the parameterless
* [SocketFactory.createSocket] method to create unconnected sockets. Overriding this method,
* e. g., allows the socket to be bound to a specific local address.
*
* If unset, the [system-wide default][SocketFactory.getDefault] socket factory will be used.
*/
fun socketFactory(socketFactory: SocketFactory) = apply {
require(socketFactory !is SSLSocketFactory) { "socketFactory instanceof SSLSocketFactory" }
this.socketFactory = socketFactory
}
/**
* Sets the socket factory used to secure HTTPS connections. If unset, the system default will
* be used.
*
* @deprecated [SSLSocketFactory] does not expose its [X509TrustManager], which is a field that
* OkHttp needs to build a clean certificate chain. This method instead must use reflection
* to extract the trust manager. Applications should prefer to call
* `sslSocketFactory(SSLSocketFactory, X509TrustManager)`, which avoids such reflection.
*/
@Deprecated(
message = "Use the sslSocketFactory overload that accepts a X509TrustManager.",
level = DeprecationLevel.ERROR
)
fun sslSocketFactory(sslSocketFactory: SSLSocketFactory) = apply {
this.sslSocketFactoryOrNull = sslSocketFactory
this.certificateChainCleaner = Platform.get().buildCertificateChainCleaner(sslSocketFactory)
}
/**
* Sets the socket factory and trust manager used to secure HTTPS connections. If unset, the
* system defaults will be used.
*
* Most applications should not call this method, and instead use the system defaults. Those
* classes include special optimizations that can be lost if the implementations are decorated.
*
* If necessary, you can create and configure the defaults yourself with the following code:
*
* ```
* TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
* TrustManagerFactory.getDefaultAlgorithm());
* trustManagerFactory.init((KeyStore) null);
* TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
* if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
* throw new IllegalStateException("Unexpected default trust managers:"
* + Arrays.toString(trustManagers));
* }
* X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
*
* SSLContext sslContext = SSLContext.getInstance("TLS");
* sslContext.init(null, new TrustManager[] { trustManager }, null);
* SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
*
* OkHttpClient client = new OkHttpClient.Builder()
* .sslSocketFactory(sslSocketFactory, trustManager)
* .build();
* ```
*/
fun sslSocketFactory(
sslSocketFactory: SSLSocketFactory,
trustManager: X509TrustManager
) = apply {
this.sslSocketFactoryOrNull = sslSocketFactory
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager)
this.x509TrustManagerOrNull = trustManager
}
fun connectionSpecs(connectionSpecs: List) = apply {
this.connectionSpecs = connectionSpecs.toImmutableList()
}
/**
* Configure the protocols 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 protocols. Applications should only call this method to avoid specific
* compatibility problems, such as web servers that behave incorrectly when HTTP/2 is enabled.
*
* The following protocols are currently supported:
*
* * [http/1.1][rfc_2616]
* * [h2][rfc_7540]
* * [h2 with prior knowledge(cleartext only)][rfc_7540_34]
*
* **This is an evolving set.** Future releases include support for transitional
* protocols. The http/1.1 transport will never be dropped.
*
* If multiple protocols are specified, [ALPN][alpn] will be used to negotiate a transport.
* Protocol negotiation is only attempted for HTTPS URLs.
*
* [Protocol.HTTP_1_0] is not supported in this set. Requests are initiated with `HTTP/1.1`. If
* the server responds with `HTTP/1.0`, that will be exposed by [Response.protocol].
*
* [alpn]: http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg
* [rfc_2616]: http://www.w3.org/Protocols/rfc2616/rfc2616.html
* [rfc_7540]: https://tools.ietf.org/html/rfc7540
* [rfc_7540_34]: https://tools.ietf.org/html/rfc7540#section-3.4
*
* @param protocols the protocols to use, in order of preference. If the list contains
* [Protocol.H2_PRIOR_KNOWLEDGE] then that must be the only protocol and HTTPS URLs will not
* be supported. Otherwise the list must contain [Protocol.HTTP_1_1]. The list must
* not contain null or [Protocol.HTTP_1_0].
*/
fun protocols(protocols: List) = apply {
// Create a private copy of the list.
val protocolsCopy = protocols.toMutableList()
// Validate that the list has everything we require and nothing we forbid.
require(Protocol.H2_PRIOR_KNOWLEDGE in protocolsCopy || HTTP_1_1 in protocolsCopy) {
"protocols must contain h2_prior_knowledge or http/1.1: $protocolsCopy"
}
require(Protocol.H2_PRIOR_KNOWLEDGE !in protocolsCopy || protocolsCopy.size <= 1) {
"protocols containing h2_prior_knowledge cannot use other protocols: $protocolsCopy"
}
require(Protocol.HTTP_1_0 !in protocolsCopy) {
"protocols must not contain http/1.0: $protocolsCopy"
}
require(null !in (protocolsCopy as List)) {
"protocols must not contain null"
}
// Remove protocols that we no longer support.
@Suppress("DEPRECATION")
protocolsCopy.remove(Protocol.SPDY_3)
// Assign as an unmodifiable list. This is effectively immutable.
this.protocols = Collections.unmodifiableList(protocolsCopy)
}
/**
* Sets the verifier used to confirm that response certificates apply to requested hostnames for
* HTTPS connections.
*
* If unset, a default hostname verifier will be used.
*/
fun hostnameVerifier(hostnameVerifier: HostnameVerifier) = apply {
this.hostnameVerifier = hostnameVerifier
}
/**
* Sets the certificate pinner that constrains which certificates are trusted. By default HTTPS
* connections rely on only the [SSL socket factory][sslSocketFactory] to establish trust.
* Pinning certificates avoids the need to trust certificate authorities.
*/
fun certificatePinner(certificatePinner: CertificatePinner) = apply {
this.certificatePinner = certificatePinner
}
/**
* Sets the default timeout for complete calls. A value of 0 means no timeout, otherwise values
* must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The call timeout spans the entire call: resolving DNS, connecting, writing the request body,
* server processing, and reading the response body. If the call requires redirects or retries
* all must complete within one timeout period.
*
* The default value is 0 which imposes no timeout.
*/
fun callTimeout(timeout: Long, unit: TimeUnit) = apply {
callTimeout = checkDuration("timeout", timeout, unit)
}
/**
* Sets the default timeout for complete calls. A value of 0 means no timeout, otherwise values
* must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The call timeout spans the entire call: resolving DNS, connecting, writing the request body,
* server processing, and reading the response body. If the call requires redirects or retries
* all must complete within one timeout period.
*
* The default value is 0 which imposes no timeout.
*/
@IgnoreJRERequirement
fun callTimeout(duration: Duration) = apply {
callTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS)
}
/**
* Sets the default connect timeout for new connections. A value of 0 means no timeout,
* otherwise values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The connect timeout is applied when connecting a TCP socket to the target host. The default
* value is 10 seconds.
*/
fun connectTimeout(timeout: Long, unit: TimeUnit) = apply {
connectTimeout = checkDuration("timeout", timeout, unit)
}
/**
* Sets the default connect timeout for new connections. A value of 0 means no timeout,
* otherwise values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The connect timeout is applied when connecting a TCP socket to the target host. The default
* value is 10 seconds.
*/
@IgnoreJRERequirement
fun connectTimeout(duration: Duration) = apply {
connectTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS)
}
/**
* Sets the default read timeout for new connections. A value of 0 means no timeout, otherwise
* values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The read timeout is applied to both the TCP socket and for individual read IO operations
* including on [Source] of the [Response]. The default value is 10 seconds.
*
* @see Socket.setSoTimeout
* @see Source.timeout
*/
fun readTimeout(timeout: Long, unit: TimeUnit) = apply {
readTimeout = checkDuration("timeout", timeout, unit)
}
/**
* Sets the default read timeout for new connections. A value of 0 means no timeout, otherwise
* values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The read timeout is applied to both the TCP socket and for individual read IO operations
* including on [Source] of the [Response]. The default value is 10 seconds.
*
* @see Socket.setSoTimeout
* @see Source.timeout
*/
@IgnoreJRERequirement
fun readTimeout(duration: Duration) = apply {
readTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS)
}
/**
* Sets the default write timeout for new connections. A value of 0 means no timeout, otherwise
* values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The write timeout is applied for individual write IO operations. The default value is 10
* seconds.
*
* @see Sink.timeout
*/
fun writeTimeout(timeout: Long, unit: TimeUnit) = apply {
writeTimeout = checkDuration("timeout", timeout, unit)
}
/**
* Sets the default write timeout for new connections. A value of 0 means no timeout, otherwise
* values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The write timeout is applied for individual write IO operations. The default value is 10
* seconds.
*
* @see Sink.timeout
*/
@IgnoreJRERequirement
fun writeTimeout(duration: Duration) = apply {
writeTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS)
}
/**
* Sets the interval between HTTP/2 and web socket pings initiated by this client. Use this to
* automatically send ping frames until either the connection fails or it is closed. This keeps
* the connection alive and may detect connectivity failures.
*
* If the server does not respond to each ping with a pong within `interval`, this client will
* assume that connectivity has been lost. When this happens on a web socket the connection is
* canceled and its listener is [notified][WebSocketListener.onFailure]. When it happens on an
* HTTP/2 connection the connection is closed and any calls it is carrying
* [will fail with an IOException][java.io.IOException].
*
* The default value of 0 disables client-initiated pings.
*/
fun pingInterval(interval: Long, unit: TimeUnit) = apply {
pingInterval = checkDuration("interval", interval, unit)
}
/**
* Sets the interval between HTTP/2 and web socket pings initiated by this client. Use this to
* automatically send ping frames until either the connection fails or it is closed. This keeps
* the connection alive and may detect connectivity failures.
*
* If the server does not respond to each ping with a pong within `interval`, this client will
* assume that connectivity has been lost. When this happens on a web socket the connection is
* canceled and its listener is [notified][WebSocketListener.onFailure]. When it happens on an
* HTTP/2 connection the connection is closed and any calls it is carrying
* [will fail with an IOException][java.io.IOException].
*
* The default value of 0 disables client-initiated pings.
*/
@IgnoreJRERequirement
fun pingInterval(duration: Duration) = apply {
pingInterval = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS)
}
fun build(): OkHttpClient = OkHttpClient(this)
}
companion object {
internal val DEFAULT_PROTOCOLS = immutableListOf(HTTP_2, HTTP_1_1)
internal val DEFAULT_CONNECTION_SPECS = immutableListOf(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT)
private fun newSslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory {
try {
val sslContext = Platform.get().newSSLContext()
sslContext.init(null, arrayOf(trustManager), null)
return sslContext.socketFactory
} catch (e: GeneralSecurityException) {
throw AssertionError("No System TLS", e) // The system has no TLS. Just give up.
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy