This guide is the deep reference for engineers, architects, and library maintainers integrating, operating, or extending OkHttp — the HTTP client for Java, Kotlin, and Android maintained by Square/Block. It documents OkHttp's architecture, its complete public configuration surface, the full module ecosystem, security posture, and internal design, all derived directly from the OkHttp 5.x source (commit 7a35098).
Where the User Guide shows how to make requests, this guide explains how OkHttp is built and how to operate it well in production.
1. Executive Summary
OkHttp is a mature, widely-deployed HTTP client distributed as a set of small, focused JVM/Android libraries under the Maven group com.squareup.okhttp3. It is the de-facto networking layer for a large share of the Android ecosystem and the transport beneath higher-level clients such as Retrofit.
Key characteristics:
- Immutable, builder-based API.
OkHttpClient,Request,Response,Headers,HttpUrl, andCacheControlare immutable value types created throughBuilderclasses. Clients are cheaply derived withnewBuilder(). - Connection efficiency by default. A shared
ConnectionPoolreuses HTTP/1.x sockets and multiplexes HTTP/2. Transparent gzip, response caching, and Happy Eyeballs fast-fallback are built in. - Resilience. Automatic recovery from unreachable IPs, stale pooled connections, and unreachable proxies; automatic redirect, authentication-challenge, and retry handling.
- Layered request processing. Every call flows through an ordered chain of interceptors (application then OkHttp's own then network), making behaviour observable and extensible.
- Pluggable trust & DNS. TLS configuration, certificate pinning, custom trust managers, and DNS (including DNS-over-HTTPS) are all replaceable.
- Standards-driven. Implements RFC 9110/9111/9112/9113 (HTTP semantics, caching, HTTP/1.1, HTTP/2), RFC 6455 (WebSockets), and SSE. Deliberately minimal in configurability where configuration would only serve to violate specs.
Runtime footprint: Android 5.0+ (API 21+) and Java 8+. Depends only on Okio and the Kotlin standard library. License: Apache 2.0.
2. Architecture
2.1 The request lifecycle
OkHttp models the work of satisfying one logical HTTP request — possibly involving redirects, authentication follow-ups, and retries across routes — as a single Call. A high-level request (Request) is rewritten, dispatched, connected, transmitted, and the response is read back, rewritten, and returned.
┌──────────────────────────────────────────────────────────────┐
│ OkHttpClient │
│ (shared config: dispatcher, connection pool, interceptors, │
│ cache, dns, TLS, timeouts, protocols) │
└───────────────┬───────────────────────────────────────────────┘
│ newCall(request)
▼
┌────────────┐ execute() / enqueue(Callback)
│ Call │──────────────────────────────────┐
└────────────┘ │
│ ▼
│ ┌────────────┐
│ async path │ Dispatcher │
│ ◀───────────────────────────────│ (limits + │
│ │ executor) │
▼ └────────────┘
┌──────────────────────────────────────────┐
│ Interceptor Chain │
│ 1. Application interceptors (your code) │
│ 2. RetryAndFollowUpInterceptor │
│ 3. BridgeInterceptor (cookies, gzip, │
│ Content-Length, User-Agent, Host…) │
│ 4. CacheInterceptor (Cache read/write) │
│ 5. ConnectInterceptor (pool → route → │
│ socket/TLS) │
│ 6. Network interceptors (your code) │
│ 7. CallServerInterceptor (write request, │
│ read response) │
└──────────────────┬─────────────────────────┘
▼
┌──────────────────────────────────────────┐
│ Connection ← Route ← Address ← URL │
│ (ConnectionPool reuses & multiplexes) │
└──────────────────────────────────────────┘
▼
Origin server
Note
The interceptor chain is the heart of OkHttp's design. Built-in behaviours (retries, header bridging, caching, connecting, transmitting) are themselves interceptors. Your application and network interceptors are inserted at the ends of this chain.
2.2 URL → Address → Route → Connection
OkHttp resolves what to connect to through four layered types (source: docs/features/connections.md):
| Type | Nature | What it captures |
|---|---|---|
HttpUrl |
Abstract | scheme (http/https), host, port, path, query, fragment. Says what to fetch, not how. |
Address |
Static | The server plus all static connection config: port, HTTPS settings (TLS factory, hostname verifier, pinner), proxy authenticator, DNS, protocols, connection specs. URLs sharing an address can share a socket. |
Route |
Dynamic | A concrete IP (from DNS), a specific proxy (from ProxySelector), and the TLS version to negotiate. One address may yield many routes. |
Connection |
Live | An established socket (direct, TLS, or tunneled through an HTTP proxy) carrying requests. Pooled and reused. |
When you request a URL, OkHttp: (1) builds an Address from the URL + client config; (2) tries to reuse a pooled Connection for that address; (3) if none, selects a Route (DNS lookup, proxy, TLS version); (4) connects (direct socket / TLS tunnel / direct TLS, with handshakes); (5) sends the request and reads the response; then returns the connection to the pool for reuse, evicting it after inactivity.
2.3 Request rewriting
OkHttp rewrites your high-level request before transmission for correctness and efficiency. It may add Content-Length, Transfer-Encoding, User-Agent, Host, Connection, Content-Type, Accept-Encoding: gzip (unless present), and a Cookie header (if a CookieJar has cookies). For stale cached responses it performs conditional GETs by adding If-Modified-Since / If-None-Match. On the response side, if transparent compression was used it drops Content-Encoding and Content-Length (they no longer describe the decompressed body), and merges network + cache responses for successful conditional GETs.
2.4 Dispatch model
- Synchronous (
Call.execute()): the caller's thread blocks; the caller manages concurrency. - Asynchronous (
Call.enqueue(Callback)): theDispatcherruns the call on itsExecutorServiceand invokes the callback on a dispatcher thread. The dispatcher enforcesmaxRequests(default 64) andmaxRequestsPerHost(default 5).
2.5 Module topology
OkHttp is a multi-module Gradle/Kotlin-Multiplatform project. The core okhttp module targets commonJvmAndroid with jvmMain and androidMain source sets; feature modules layer onto it.
okhttp (core: okhttp3)
├── mockwebserver3 test server (+ junit4 / junit5 / deprecated mockwebserver)
├── logging-interceptor okhttp3.logging
├── okhttp-tls okhttp3.tls (HeldCertificate, HandshakeCertificates)
├── okhttp-sse okhttp3.sse (EventSource)
├── okhttp-dnsoverhttps okhttp3.dnsoverhttps (DnsOverHttps)
├── okhttp-brotli okhttp3.brotli (BrotliInterceptor)
├── okhttp-zstd okhttp3.zstd (Zstd)
├── okhttp-coroutines okhttp3.coroutines (executeAsync)
├── okhttp-urlconnection JavaNetAuthenticator, JavaNetCookieJar
├── okhttp-java-net-cookiejar okhttp3.java.net.cookiejar.JavaNetCookieJar
├── okhttp-idna-mapping-table IDNA host-name mapping
├── okhttp-bom Bill of Materials (version alignment)
└── okcurl CLI (okhttp3.curl)
Note
Each publishable module ships a checked-in
api/*.apisignature file (the Kotlin binary-compatibility validator output). These files are the authoritative, exhaustive list of every public class, field, and function — useful when auditing the API surface or planning an upgrade.
2.6 Repository structure
| Path | Contents |
|---|---|
okhttp/ |
Core client. Public API in src/commonJvmAndroid/kotlin/okhttp3/; internals in okhttp3/internal/. Android specifics in src/androidMain/. |
mockwebserver/, mockwebserver-junit4/, -junit5/, -deprecated/ |
Test server and integrations. |
okhttp-* modules |
Optional features (see topology above). |
okcurl/ |
Clikt-based CLI entry point. |
samples/ |
Runnable examples: guide/recipes (30+ recipes), crawler, slack, static-server, unixdomainsockets, tlssurvey, compare. |
docs/ |
MkDocs site: features/*.md (conceptual docs), changelogs/, security/, contribute/. |
android-test/, regression-test/, container-tests/, okhttp-osgi-tests/, maven-tests/, native-image-tests/ |
Cross-environment test suites. |
build-logic/ |
Gradle convention plugins (okhttp.jvm-conventions, etc.). |
3. System Requirements
| Concern | Requirement |
|---|---|
| Java | 8 or newer |
| Android | 5.0+ (API level 21+) |
| Runtime dependencies | Okio, Kotlin standard library (both small, backward-compatible) |
| Build (for contributors) | Gradle with the configuration cache and isolated-projects enabled; multi-module KMP build |
| Optional TLS provider | Conscrypt (BoringSSL-backed) — used automatically if installed as the first JCE security provider |
| License | Apache License 2.0 |
Important
The legacy
3.12.xbranch supports Android 2.3+ (API 9+) and Java 7+, but those platforms lack TLS 1.2 and must not be used for production. There is no security backport guarantee for that branch.
Optional Conscrypt activation:
Security.insertProviderAt(Conscrypt.newProvider(), 1);
4. Configuration Reference — OkHttpClient.Builder
OkHttpClient is configured exclusively through OkHttpClient.Builder. The table below lists every public builder setting with its default value, taken from the source.
4.1 Timeouts
| Method | Default | Meaning |
|---|---|---|
callTimeout(Long, TimeUnit) / (Duration) |
0 (no timeout) |
Whole-call budget: DNS + connect + write + server + read, across all redirects/retries. For WebSockets/duplex, only the initial setup. |
connectTimeout(Long, TimeUnit) / (Duration) |
10_000 ms |
TCP connect to host. |
readTimeout(Long, TimeUnit) / (Duration) |
10_000 ms |
Socket reads and each response-body read. |
writeTimeout(Long, TimeUnit) / (Duration) |
10_000 ms |
Each request-body write. |
pingInterval(Long, TimeUnit) / (Duration) |
0 (disabled) |
HTTP/2 & WebSocket ping cadence; a missed pong fails the connection. |
webSocketCloseTimeout(Long, TimeUnit) / (Duration) |
60_000 ms |
Wait for graceful WebSocket shutdown after close(). |
All timeout values must be 0 (off) or between 1 and Integer.MAX_VALUE when converted to milliseconds. Each method has long+TimeUnit, java.time.Duration, and Kotlin Duration overloads.
4.2 Connections & protocols
| Method | Default | Meaning |
|---|---|---|
connectionPool(ConnectionPool) |
new pool | Socket reuse pool. A default is created and shared if unset. |
dispatcher(Dispatcher) |
new Dispatcher() |
Async concurrency policy + executor. |
protocols(List<Protocol>) |
[HTTP_2, HTTP_1_1] |
Preferred protocols; ALPN-negotiated over HTTPS. Must contain HTTP_1_1 or be [H2_PRIOR_KNOWLEDGE]; never HTTP_1_0 or null. SPDY_3 is silently removed. |
connectionSpecs(List<ConnectionSpec>) |
[MODERN_TLS, CLEARTEXT] |
TLS/cleartext negotiation policies, tried in order. |
fastFallback(Boolean) |
true |
Happy Eyeballs (RFC 6555) concurrent connect racing. false reverts to 4.x sequential connects. |
retryOnConnectionFailure(Boolean) |
true |
Silently recover from unreachable IPs, stale pooled connections, and unreachable proxies. Set false when retries would be destructive. |
followRedirects(Boolean) |
true |
Follow HTTP redirects. |
followSslRedirects(Boolean) |
true |
Follow cross-scheme redirects (HTTP↔HTTPS); still gated by followRedirects. |
dns(Dns) |
Dns.SYSTEM |
Hostname → IP resolver. |
socketFactory(SocketFactory) |
system default | Creates unconnected sockets (must not be an SSLSocketFactory). |
proxy(Proxy) |
none | Explicit proxy; takes precedence over proxySelector. Use Proxy.NO_PROXY to disable proxying entirely. |
proxySelector(ProxySelector) |
system default | Proxy selection when no explicit proxy is set. |
4.3 TLS & trust
| Method | Default | Meaning |
|---|---|---|
sslSocketFactory(SSLSocketFactory, X509TrustManager) |
platform default | Secure HTTPS connections. Preferred overload. |
sslSocketFactory(SSLSocketFactory) |
— | Deprecated (DeprecationLevel.ERROR): forces reflection to recover the trust manager. |
hostnameVerifier(HostnameVerifier) |
OkHostnameVerifier |
Confirms a server certificate matches the requested hostname. |
certificatePinner(CertificatePinner) |
CertificatePinner.DEFAULT (no pins) |
Restricts trusted certificates per host. |
Caution
If no connection spec is TLS-capable (CLEARTEXT-only client), the client has no SSL socket factory and accessing
sslSocketFactorythrowsIllegalStateException("CLEARTEXT-only client"). The builder validates this invariant atbuild()time.
4.4 Behaviour & integrations
| Method | Default | Meaning |
|---|---|---|
addInterceptor(Interceptor) |
— | Append an application interceptor (sees the original request, runs once). |
addNetworkInterceptor(Interceptor) |
— | Append a network interceptor (sees redirects/retries and the live Connection). |
authenticator(Authenticator) |
Authenticator.NONE |
Responds to origin (401) challenges. |
proxyAuthenticator(Authenticator) |
Authenticator.NONE |
Responds to proxy (407) challenges. |
cookieJar(CookieJar) |
CookieJar.NO_COOKIES |
Stores/sends cookies. |
cache(Cache) |
none | Response cache (see §6). |
eventListener(EventListener) |
EventListener.NONE |
Single shared metrics listener. |
eventListenerFactory(EventListener.Factory) |
— | Per-call metrics listener factory. |
minWebSocketMessageToCompress(Long) |
1024 |
Minimum outbound WS message size (bytes) eligible for compression; 0 compresses all. |
Tip
Mutating a route-affecting setting (
dns,proxy,proxySelector,proxyAuthenticator,socketFactory,sslSocketFactory,connectionSpecs,protocols,hostnameVerifier,certificatePinner) on a derived builder resets the internalRouteDatabaseso stale route blacklists don't leak between configurations.
5. Public API Reference
The core okhttp3 package (artifact okhttp, signatures from okhttp/api/jvm/okhttp.api) exposes the following public types. Builder/value types are immutable; interfaces are extension points.
5.1 Core request/response types
| Type | Kind | Purpose |
|---|---|---|
OkHttpClient |
class (Call.Factory, WebSocket.Factory) |
The configured client. newCall(), newWebSocket(), newBuilder(), plus read-only accessors for every setting. |
Request |
immutable | One HTTP request: url(), method(), headers(), body(), cacheControl(), tag(), toCurl(), newBuilder(). |
Request.Builder |
builder | url(...), get/head/post/put/patch/delete/method(...), header/addHeader/removeHeader, headers, cacheControl, tag, gzip(), cacheUrlOverride. |
Response |
immutable (Closeable) |
code(), message(), isSuccessful(), isRedirect(), headers(), body(), peekBody(n), request(), protocol(), handshake(), cacheResponse(), networkResponse(), priorResponse(), challenges(), trailers(), sentRequestAtMillis(), receivedResponseAtMillis(). |
Response.Builder |
builder | Construct synthetic responses (used by interceptors and tests). |
RequestBody |
abstract | contentType(), contentLength(), writeTo(sink), isOneShot(), isDuplex(), sha256(), plus many create(...) factories (String, byte[], File, FileDescriptor, ByteString, Okio Path). |
ResponseBody |
abstract (Closeable) |
contentType(), contentLength(), string(), bytes(), byteString(), source(), byteStream(), charStream(). |
Call |
interface (Cloneable) |
execute(), enqueue(callback), cancel(), isExecuted(), isCanceled(), request(), timeout(), clone(), addEventListener(), tag(...). |
Call.Factory |
interface | newCall(request). |
Callback |
interface | onResponse(call, response), onFailure(call, e). |
5.2 Headers, URLs, media types, bodies
| Type | Purpose |
|---|---|
Headers / Headers.Builder |
Multi-map of header name/value pairs; get, values, name(i), value(i), getDate, getInstant, toMultimap, of(...). Builder adds add, addUnsafeNonAscii, set, removeAll. |
HttpUrl / HttpUrl.Builder |
Typed URL with full encode/decode accessors, resolve(), redact(), topPrivateDomain(), queryParameter*, defaultPort(), conversions to/from java.net.URI/URL. |
MediaType |
Parsed Content-Type: type(), subtype(), charset(), parameter(); MediaType.get(str) / parse(str). |
FormBody / FormBody.Builder |
application/x-www-form-urlencoded body. |
MultipartBody / .Builder / .Part |
multipart/* body with predefined types MIXED, ALTERNATIVE, DIGEST, PARALLEL, FORM. |
MultipartReader / .Part |
Parse multipart responses. |
CacheControl / .Builder |
Cache-Control directive model; constants FORCE_CACHE, FORCE_NETWORK. |
5.3 Connection, TLS & negotiation types
| Type | Purpose |
|---|---|
Connection |
Live connection: route(), socket(), handshake(), protocol(). |
ConnectionPool |
Socket reuse pool: connectionCount(), idleConnectionCount(), evictAll(). |
ConnectionSpec / .Builder |
TLS policy; constants RESTRICTED_TLS, MODERN_TLS, COMPATIBLE_TLS, CLEARTEXT. |
CipherSuite |
~150 named TLS cipher-suite constants; forJavaName(), javaName(). |
TlsVersion |
enum SSL_3_0, TLS_1_0, TLS_1_1, TLS_1_2, TLS_1_3. |
Protocol |
enum HTTP_1_0, HTTP_1_1, HTTP_2, H2_PRIOR_KNOWLEDGE, HTTP_3, QUIC, SPDY_3 (deprecated). |
Handshake |
TLS handshake details: tlsVersion(), cipherSuite(), peerCertificates(), localCertificates(), principals. |
CertificatePinner / .Builder / .Pin |
Certificate pinning; static pin(), sha256Hash(), sha1Hash(). |
Address |
Static connection configuration for a server. |
Route |
A concrete IP + proxy + spec attempt; requiresTunnel(). |
Dns |
interface; lookup(host); constant Dns.SYSTEM. |
Dispatcher |
Async concurrency: maxRequests, maxRequestsPerHost, queuedCalls, runningCalls, cancelAll, executorService. |
5.4 Extension-point interfaces & callbacks
| Type | Purpose |
|---|---|
Interceptor / Interceptor.Chain |
Observe/rewrite/retry calls. The chain exposes request(), proceed(), connection(), call(), the resolved timeouts, and with* methods to derive a chain with overridden config/timeouts. Interceptor.invoke { } (Kotlin) builds one from a lambda. |
Authenticator |
Respond to challenges; constants NONE, JAVA_NET_AUTHENTICATOR. |
CookieJar |
Cookie storage; constant NO_COOKIES. |
EventListener / .Factory |
~30 lifecycle callbacks (DNS, connect, secure-connect, request/response headers & body, cache hit/miss, retry/follow-up decisions, etc.); NONE; plus() to combine. |
WebSocket / WebSocket.Factory / WebSocketListener |
WebSocket send/close/cancel and onOpen/onMessage/onClosing/onClosed/onFailure. |
Credentials |
basic(user, pass[, charset]) helper. |
Challenge |
A parsed WWW-Authenticate/Proxy-Authenticate challenge: scheme(), realm(), authParams(), charset(). |
Cookie / .Builder |
RFC 6265 cookie model; parse(), parseAll(), matches(). |
CompressionInterceptor / .DecompressionAlgorithm |
Pluggable response decompression; Gzip is built in. |
TrailersSource |
Source of HTTP trailers; constant EMPTY. |
OkHttp |
OkHttp.VERSION constant; OkHttp.initialize(context) on Android. |
5.5 Feature-module APIs
| Module | Public API |
|---|---|
logging-interceptor |
HttpLoggingInterceptor (Level = NONE/BASIC/HEADERS/BODY; redactHeader, redactQueryParams; pluggable Logger), LoggingEventListener (+ .Factory). |
okhttp-tls |
HeldCertificate (+ .Builder: commonName, addSubjectAlternativeName, validityInterval, serialNumber, certificateAuthority, signedBy, rsa2048/ecdsa256), HandshakeCertificates (+ .Builder: addTrustedCertificate, addPlatformTrustedCertificates, addInsecureHost, heldCertificate), Certificates (PEM encode/decode). |
okhttp-sse |
EventSource, EventSource.Factory, EventSourceListener (onOpen/onEvent/onClosed/onFailure), EventSources.createFactory(...) / processResponse(...). |
okhttp-dnsoverhttps |
DnsOverHttps (+ .Builder: client, url, includeIPv6, post, resolvePrivate/PublicAddresses, bootstrapDnsHosts, systemDns). |
okhttp-brotli |
BrotliInterceptor.INSTANCE, Brotli algorithm. |
okhttp-zstd |
Zstd decompression algorithm. |
okhttp-coroutines |
Call.executeAsync() suspend extension. |
okhttp-urlconnection |
JavaNetAuthenticator, JavaNetCookieJar. |
okhttp-java-net-cookiejar |
okhttp3.java.net.cookiejar.JavaNetCookieJar(CookieHandler). |
mockwebserver3 |
MockWebServer, MockResponse (+ .Builder), RecordedRequest, Dispatcher, QueueDispatcher, SocketEffect, PushPromise. |
6. Caching Internals
The optional Cache is a disk-backed, RFC-9111-aligned response store with browser-like heuristics. It is implemented as the CacheInterceptor within the chain.
Construction: Cache(File directory, long maxSize) or Cache(FileSystem, Path, long maxSize) (Okio file system). One directory per Cache; the directory is exclusively owned.
Behaviour:
- Default freshness for documents without explicit expiry is 10% of the document's age at serve time (based on
Last-Modified); not applied to URLs containing a query. - Three outcomes, observable via
EventListener: Cache Hit (no network), Cache Miss (network used; uncacheable/expired/never-fetched), Conditional Hit (cacheConditionalHitthen a revalidation request; the cached body is reused only on304 Not Modified, so bothcacheResponse()andnetworkResponse()are non-null). - A response is stored only if its body is fully read, canceled, or stalled.
Operations & stats: evictAll(), delete(), urls() iterator (with remove()), flush(), close(), size(), maxSize(), and counters hitCount(), networkCount(), requestCount(), writeSuccessCount(), writeAbortCount().
Warning
Sharing a cache directory between two
Cacheinstances or processes corrupts it. In multi-process apps, give each process its own directory or a single shared client.
7. Security Analysis
OkHttp's threat model centers on protecting transport confidentiality/integrity while maximizing connectivity. Security-relevant subsystems and operational guidance follow.
7.1 TLS posture
- Defaults to
MODERN_TLS+CLEARTEXT; uses the platform TLS stack (or Conscrypt if installed first). TLS versions/cipher suites in eachConnectionSpecevolve per release — historically OkHttp dropped SSL 3.0 (POODLE) and RC4 as attacks emerged. RESTRICTED_TLSis available for stricter compliance environments.- Hostname verification (
OkHostnameVerifier) always runs in addition to certificate trust, even with custom Android trust managers.
Important
Staying current with OkHttp is itself a security control: the library tracks the TLS ecosystem and removes weak primitives. Pin your dependency to a recent 5.x release and update regularly.
7.2 Certificate pinning
CertificatePinner lets you constrain trust to specific public-key hashes, mitigating CA compromise (e.g. the 2011 DigiNotar attack). Pinning is double-edged: an uncoordinated server certificate rotation will hard-fail every pinned client.
Caution
Never deploy certificate pins without your server TLS team's sign-off and a rotation plan. Prefer pinning an intermediate/CA key over a leaf certificate to reduce rotation breakage, and ship backup pins.
7.3 Authentication & credential handling
Authenticatorcallbacks must guard against retry loops by returningnullwhen anAuthorizationheader is already present (see community issue #9477, "Infinite Authentication Loop in Proxy CONNECT Tunnel").Credentials.basicBase64-encodes credentials — Basic auth offers no confidentiality without TLS.HttpUrl.redact()produces a credential-stripped URL safe for logs.
7.4 Logging hygiene
HttpLoggingInterceptor at HEADERS/BODY level will log sensitive headers and payloads. Use redactHeader("Authorization"), redactHeader("Cookie"), and redactQueryParams(...), and never enable BODY logging for authenticated traffic in production.
7.5 Known security-relevant issues (community-reported)
These open/closed issues from the project tracker inform hardening decisions; consult the upstream issue and your installed version before relying on a given behaviour:
| Issue | Topic | Operational note |
|---|---|---|
| #9476 | SSRF via unrestricted redirect following | If you fetch untrusted/user-supplied URLs, consider followRedirects(false) and validate redirect targets in an interceptor. |
| #9477 | Infinite auth loop in proxy CONNECT tunnel | Guard Authenticator against re-attempting with an existing Authorization. |
| #9434 | InsecureAndroidTrustManager MITM concern |
Never use insecure/all-trusting trust managers outside tests. |
| #9472 (closed) | Predictable WebSocket masking keys | Fixed; keep current. |
| #9473 (closed) | Gzip decompression bomb | Bound response sizes for untrusted servers; don't blindly string() arbitrary bodies. |
| #9474 (closed) | CRLF header injection via addUnsafeNonAscii() |
Avoid addUnsafeNonAscii with untrusted header values. |
| #9451 | HttpUrl.isDotDot path-traversal forms |
Relevant if you use OkHttp URL parsing for sandboxing path checks. |
| #9335 | Cache entries not stored correctly on Windows | On Windows, validate cache hit rates; excessive requests may indicate this bug. |
Caution
When fetching attacker-controllable URLs (webhooks, link unfurling, image proxies), treat redirect following and decompression as attack surface: disable or constrain redirects, cap body sizes, and resolve/deny internal IP ranges via a custom
Dnsor network interceptor to prevent SSRF.
8. Operations
8.1 Lifecycle management
OkHttp requires no explicit shutdown — idle dispatcher threads and pooled connections self-release; HTTP/2 daemon threads exit when idle. For aggressive resource reclamation (CLI tools, short-lived workers):
client.dispatcher().executorService().shutdown(); // rejects future calls
client.connectionPool().evictAll();
if (client.cache() != null) client.cache().close();
Warning
After
executorService().shutdown()the client rejects new calls. Aftercache().close(), issuing a call against that cache crashes — close the cache only during teardown.
8.2 Tuning for throughput
- Raise
Dispatcher.maxRequests/maxRequestsPerHostfor high-concurrency async workloads, and back the dispatcher with an executor sized to match. - Keep one shared
OkHttpClient(and thus oneConnectionPool) per process so connections are actually reused. - Prefer HTTP/2 (the default) for many concurrent requests to one host — they multiplex over a single socket.
- Use
pingIntervalon long-lived connections to detect dead peers promptly.
Note
Issue #9443 notes that the dispatcher executor must actually be able to run
maxRequestsconcurrently — a too-small thread pool silently caps real concurrency below the configured maximum.
8.3 Observability
- Metrics: an
EventListener.Factorygives per-call timing for every network phase; sample (e.g. 10%) to bound overhead. Cache hit/miss/conditional events flow through the same listener. - Logging:
LoggingEventListener.Factoryfor phase timings, orHttpLoggingInterceptorfor wire-level request/response logs (redact secrets). - Pool inspection:
connectionPool().connectionCount()/idleConnectionCount()anddispatcher().runningCallsCount()/queuedCallsCount()expose live load.
8.4 Proxy & DNS operations
- Route all traffic through a fixed proxy with
proxy(...), or delegate toproxySelector(...).proxyAuthenticator(...)handles proxy407challenges. - Replace name resolution wholesale with a custom
Dns, or useDnsOverHttpsfor encrypted resolution (configure a bootstrap client and DoH endpoint).
9. Code Quality, Patterns & Conventions
- Kotlin-first, Java-friendly. Core is Kotlin with
@JvmName/@JvmStatic/@JvmOverloadsandCompanion-object factories so Java callers get idiomatic APIs (Headers.of(...),MediaType.get(...)). - Immutability + builders. Value types are immutable; the only mutation path is a
Builder.newBuilder()enables cheap, copy-on-write derivation that shares pools. - Deprecation discipline. Property-style getters that replaced old method-style accessors are retained as
@Deprecated(level = ERROR)-deprecated_*shims (visible throughout the.apifiles) to preserve binary compatibility while steering callers tovalaccessors. - Binary-compatibility validation. Checked-in
api/*.apisignature files are enforced in CI, preventing accidental public-API breakage. - Internal encapsulation. Everything under
okhttp3.internalis non-public by convention (the open issue #9435 tracks formalizing this asinternalor promoting parts to API). Do not depend on internal classes. - KMP source sets.
commonJvmAndroidholds shared JVM/Android code;androidMainandjvmMaincarry platform specifics, allowing one codebase to target both. - Multiplatform value semantics. Inline-class-backed durations appear in the
.apias mangled names (e.g.callTimeout-LRDsOJo) — these are KotlinDurationoverloads, not separate Java API.
10. Testing Strategy
OkHttp dogfoods its own MockWebServer and maintains an unusually broad test matrix:
| Suite | Validates |
|---|---|
mockwebserver3 + unit tests |
Protocol behaviour against a real scriptable server (delays, throttling, chunking, HTTP/2 push, TLS handshake failures, 100-Continue, WebSockets). |
android-test, android-test-app, regression-test |
On-device/emulator instrumentation (e.g. ALPN, SNI, Let's Encrypt trust). |
container-tests |
Behaviour inside containers. |
okhttp-osgi-tests |
OSGi bundle compatibility. |
maven-tests |
Consuming OkHttp via Maven + the BOM version range. |
native-image-tests |
GraalVM native-image compatibility (reachability metadata under META-INF/native-image). |
okhttp-hpacktests |
HTTP/2 HPACK header-compression conformance (shared test cases). |
samples/tlssurvey, samples/compare |
TLS ecosystem surveys and cross-client comparison. |
fuzzing |
Fuzz harnesses for parsers. |
Tip
For consumers,
MockWebServeris the recommended test tool: it exercises the genuine network stack rather than mocking the client. Pair withokhttp-tlsHeldCertificateto test HTTPS paths without external CAs.
11. Integration Patterns
- Retrofit / higher-level clients. OkHttp is the standard transport under Retrofit; share one
OkHttpClientand add cross-cutting concerns (auth, logging, metrics) as interceptors. java.netinterop.okhttp-urlconnectionandokhttp-java-net-cookiejarbridgejava.net.Authenticatorandjava.net.CookieHandler/CookieManagerinto OkHttp.- JSON. Stream
ResponseBody.source()into Moshi/Gson/kotlinx.serialization rather than buffering withstring(). - Coroutines.
okhttp-coroutinesexecuteAsync()integrates cleanly with structured concurrency and cancellation. - R8/ProGuard. See
docs/features/r8_proguard.md; OkHttp ships consumer rules, but verify keep rules for reflective TLS/platform code on Android release builds.
12. Quick Reference
Maven coordinates: com.squareup.okhttp3:okhttp:5.x (core) · :okhttp-bom:5.x (version alignment).
Defaults at a glance:
| Setting | Default |
|---|---|
| connect / read / write timeout | 10 s each |
| call timeout | none (0) |
| ping interval | disabled (0) |
| WebSocket close timeout | 60 s |
| protocols | HTTP/2, then HTTP/1.1 |
| connection specs | MODERN_TLS, CLEARTEXT |
| fast fallback (Happy Eyeballs) | enabled |
| retry on connection failure | enabled |
| follow redirects / SSL redirects | enabled |
| dispatcher maxRequests / perHost | 64 / 5 |
| cookie jar | none |
| cache | none |
| authenticator / proxy authenticator | none |
| min WebSocket message to compress | 1024 bytes |
Minimal client + request:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://example.com").build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.code());
System.out.println(response.body().string());
}
Production-leaning client:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(Duration.ofSeconds(10))
.callTimeout(Duration.ofSeconds(30))
.pingInterval(Duration.ofSeconds(30))
.cache(new Cache(cacheDir, 50L * 1024 * 1024))
.addInterceptor(authInterceptor)
.eventListenerFactory(metricsFactory)
.build();
Operational commands:
client.connectionPool().evictAll(); // drop pooled sockets
client.dispatcher().cancelAll(); // cancel all async calls
client.dispatcher().executorService().shutdown(); // stop accepting calls
client.cache().close(); // close the cache (teardown only)