File: README.md
OkHttp
See the project website for documentation and APIs.
HTTP is the way modern applications network. Itβs how we exchange data & media. Doing HTTP
efficiently makes your stuff load faster and saves bandwidth.
OkHttp is an HTTP client thatβs efficient by default:
- HTTP/2 support allows all requests to the same host to share a socket.
- Connection pooling reduces request latency (if HTTP/2 isnβt available).
- Transparent GZIP shrinks download sizes.
- Response caching avoids the network completely for repeat requests.
OkHttp perseveres when the network is troublesome: it will silently recover from common connection
problems. If your service has multiple IP addresses, OkHttp will attempt alternate addresses if the
first connect fails. This is necessary for IPv4+IPv6 and services hosted in redundant data
centers. OkHttp supports modern TLS features (TLS 1.3, ALPN, certificate pinning). It can be
configured to fall back for broad connectivity.
Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It
supports both synchronous blocking calls and async calls with callbacks.
A well behaved user agent
OkHttp follows modern HTTP specifications such as
- HTTP Semantics - RFC 9110
- HTTP Caching- RFC 9111
- HTTP/1.1 - RFC 9112
- HTTP/2 - RFC 9113
- Websockets - RFC 6455
- SSE - Server-sent events
Where the spec is ambiguous, OkHttp follows modern user agents such as popular Browsers or common HTTP Libraries.
OkHttp is principled and avoids being overly configurable, especially when such configuration is
to workaround a buggy server, test invalid scenarios or that contradict the relevant RFC.
Other HTTP libraries exist that fill that gap allowing extensive customisation including potentially
invalid requests.
Example Limitations
- Does not allow GET with a body.
- Cache is not an interface with alternative implementations.
Get a URL
This program downloads a URL and prints its contents as a string. Full source.
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}Post to a Server
This program posts data to a service. Full source.
public static final MediaType JSON = MediaType.get("application/json");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}Further examples are on the OkHttp Recipes page.
Requirements
OkHttp works on Android 5.0+ (API level 21+) and Java 8+.
On Android, OkHttp uses AndroidX Startup. If you disable the initializer in the manifest,
then apps are responsible for calling OkHttp.initialize(applicationContext) in Application.onCreate.
OkHttp depends on Okio for high-performance I/O and the Kotlin standard library. Both are small libraries with strong backward-compatibility.
We highly recommend you keep OkHttp up-to-date. As with auto-updating web browsers, staying current
with HTTPS clients is an important defense against potential security problems. We
track the dynamic TLS ecosystem and adjust OkHttp to improve connectivity and
security.
OkHttp uses your platform's built-in TLS implementation. On Java platforms OkHttp also supports
Conscrypt, which integrates BoringSSL with Java. OkHttp will use Conscrypt if it is
the first security provider:
Security.insertProviderAt(Conscrypt.newProvider(), 1);The OkHttp 3.12.x branch supports Android 2.3+ (API level 9+) and Java 7+. These platforms lack
support for TLS 1.2 and should not be used.
Releases
Our change log has release history.
The latest release is available on Maven Central.
implementation("com.squareup.okhttp3:okhttp:5.5.0")Snapshot builds are available. R8 and ProGuard rules are available.
Also, we have a bill of materials (BOM) available to help you keep OkHttp artifacts up to date and be sure about version compatibility.
dependencies {
// define a BOM and its version
implementation(platform("com.squareup.okhttp3:okhttp-bom:5.5.0"))
// define any required OkHttp artifacts without version
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")
}Maven and JVM Projects
OkHttp is published as a Kotlin Multiplatform project. While Gradle handles this automatically,
Maven projects must select between okhttp-jvm and okhttp-android. The okhttp artifact will be empty in
Maven projects.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-bom</artifactId>
<version>5.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement><dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-jvm</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver3</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
</dependency>MockWebServer
OkHttp includes a library for testing HTTP, HTTPS, and HTTP/2 clients.
The latest release is available on Maven Central.
testImplementation("com.squareup.okhttp3:mockwebserver3:5.5.0")MockWebServer is used for firstly for internal testing, and for basic testing of apps using OkHttp client.
It is not a full featured HTTP testing library that is developed standalone. It is not being actively developed
for new features. As such you might find your needs outgrow MockWebServer and you may which to use a
more full featured testing library such as MockServer.
GraalVM Native Image
Building your native images with GraalVM should work automatically.
See the okcurl module for an example build.
$ ./gradlew okcurl:nativeImage
$ ./okcurl/build/graal/okcurl https://httpbin.org/getJava Modules
OkHttp (5.2+) implements Java 9 Modules.
With this in place Java builds should fail if apps attempt to use internal packages.
error: package okhttp3.internal.platform is not visible
okhttp3.internal.platform.Platform.get();
^
(package okhttp3.internal.platform is declared in module okhttp3,
which does not export it to module com.bigco.sdk)The stable public API is based on the list of defined modules:
- okhttp3
- okhttp3.brotli
- okhttp3.coroutines
- okhttp3.dnsoverhttps
- okhttp3.java.net.cookiejar
- okhttp3.logging
- okhttp3.sse
- okhttp3.tls
- okhttp3.urlconnection
- mockwebserver3
- mockwebserver3.junit4
- mockwebserver3.junit5
License
Copyright 2019 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.File: android-test/src/androidTest/README.md
Android Test
A gradle module for running Android instrumentation tests on a device or emulator.
- Add an Emulator named
pixel5, if you don't already have one
$ sdkmanager --install "system-images;android-29;google_apis;x86"
$ echo "no" | avdmanager --verbose create avd --force --name "pixel5" --device "pixel" --package "system-images;android-29;google_apis;x86" --tag "google_apis" --abi "x86"- Run an Emulator using Android Studio or from command line.
$ emulator -no-window -no-snapshot-load @pixel5- Turn on logs with logcat
$ adb logcat '*:E' OkHttp:D Http2:D TestRunner:D TaskRunner:D OkHttpTest:D GnssHAL_GnssInterface:F DeviceStateChecker:F memtrack:F
...
01-01 12:53:32.811 10999 11089 D OkHttp : [49 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://1.1.1.1/dns-query?dns=AAABAAABAAAAAAAAA3d3dwhmYWNlYm9vawNjb20AABwAAQ}
01-01 12:53:32.811 10999 11089 D OkHttp : [49 ms] responseBodyStart
01-01 12:53:32.811 10999 11089 D OkHttp : [49 ms] responseBodyEnd: byteCount=128
01-01 12:53:32.811 10999 11089 D OkHttp : [49 ms] connectionReleased
01-01 12:53:32.811 10999 11089 D OkHttp : [49 ms] callEnd
01-01 12:53:32.816 10999 11090 D OkHttp : [54 ms] responseHeadersStart
01-01 12:53:32.816 10999 11090 D OkHttp : [54 ms] responseHeadersEnd: Response{protocol=h2, code=200, message=, url=https://1.1.1.1/dns-query?dns=AAABAAABAAAAAAAAA3d3dwhmYWNlYm9vawNjb20AAAEAAQ}
01-01 12:53:32.817 10999 11090 D OkHttp : [55 ms] responseBodyStart
01-01 12:53:32.818 10999 11090 D OkHttp : [56 ms] responseBodyEnd: byteCount=128
01-01 12:53:32.818 10999 11090 D OkHttp : [56 ms] connectionReleased
01-01 12:53:32.818 10999 11090 D OkHttp : [56 ms] callEnd- Run tests using gradle
$ ANDROID_SDK_ROOT=/Users/myusername/Library/Android/sdk ./gradlew :android-test:connectedCheck -PandroidBuild=true
...
> Task :android-test:connectedDebugAndroidTest
...
11:55:40 V/InstrumentationResultParser: Time: 13.271
11:55:40 V/InstrumentationResultParser:
11:55:40 V/InstrumentationResultParser: OK (12 tests)
...
11:55:40 I/XmlResultReporter: XML test result file generated at /Users/myusername/workspace/okhttp/android-test/build/outputs/androidTest-results/connected/TEST-pixel3a-Q(AVD) - 10-android-test-.xml. Total tests 13, passed 11, assumption_failure 1, ignored 1,
...
BUILD SUCCESSFUL in 1m 30s
63 actionable tasks: 61 executed, 2 up-to-daten.b. use ANDROID_SERIAL=emulator-5554 or similar if you need to select between devices.
File: docs/features/caching.md
Caching
OkHttp implements an optional, off by default, Cache. OkHttp aims for RFC correct and
pragmatic caching behaviour, following common real-world browser like Firefox/Chrome and
server behaviour when ambiguous.
Basic Usage
private val client: OkHttpClient = OkHttpClient.Builder()
.cache(Cache(
directory = File(application.cacheDir, "http_cache"),
// $0.05 worth of phone storage in 2020
maxSize = 50L * 1024L * 1024L // 50 MiB
))
.build()EventListener events
Cache Events are exposed via the EventListener API. Typical scenarios are below.
Cache Hit
In the ideal scenario the cache can fulfill the request without any conditional call to the network.
This will skip the normal events such as DNS, connecting to the network, and downloading the response body.
As recommended by the HTTP RFC the max age of a document is defaulted to 10% of the
document's age at the time it was served based on "Last-Modified". Default expiration dates aren't used for URIs
containing a query.
- CallStart
- CacheHit
- CallEnd
Cache Miss
Under a cache miss the normal request events are seen but an additional event shows the presence of the cache.
Cache Miss will be typical if the item has not been read from the network, is uncacheable, or is past it's
lifetime based on Response cache headers.
- CallStart
- CacheMiss
- ProxySelectStart
- ... Standard Events ...
- CallEnd
Conditional Cache Hit
When cache flags require checking the cache results are still valid an early cacheConditionalHit event is
received followed by a cache hit or miss. Critically in the cache hit scenario the server wonβt send the response body.
The response will have non-null cacheResponse and networkResponse. The cacheResponse will be used as the top level
response only if the response code is HTTP/1.1 304 Not Modified.
- CallStart
- CacheConditionalHit
- ConnectionAcquired
- ... Standard Events...
- ResponseBodyEnd (0 bytes)
- CacheHit
- ConnectionReleased
- CallEnd
Cache directory
The cache directory must be exclusively owned by a single instance.
Deleting the cache when it is no longer needed can be done. However this may delete the purpose of the cache
which is designed to persist between app restarts.
cache.delete()Pruning the Cache
Pruning the entire Cache to clear space temporarily can be done using evictAll.
cache.evictAll()Removing individual items can be done using the urls iterator.
This would be typical after a user initiates a force refresh by a pull to refresh type action.
val urlIterator = cache.urls()
while (urlIterator.hasNext()) {
if (urlIterator.next().startsWith("https://www.google.com/")) {
urlIterator.remove()
}
}Troubleshooting
- Valid cacheable responses are not being cached
Make sure you are reading responses fully as unless they are read fully, cancelled or stalled Responses will not be cached.
Overriding normal cache behaviour
See Cache documentation.
File: docs/features/calls.md
Calls
The HTTP clientβs job is to accept your request and produce its response. This is simple in theory but it gets tricky in practice.
[Requests](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-request/)
Each HTTP request contains a URL, a method (like GET or POST), and a list of headers. Requests may also contain a body: a data stream of a specific content type.
[Responses](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-response/)
The response answers the request with a code (like 200 for success or 404 for not found), headers, and its own optional body.
Rewriting Requests
When you provide OkHttp with an HTTP request, youβre describing the request at a high-level: βfetch me this URL with these headers.β For correctness and efficiency, OkHttp rewrites your request before transmitting it.
OkHttp may add headers that are absent from the original request, including Content-Length, Transfer-Encoding, User-Agent, Host, Connection, and Content-Type. It will add an Accept-Encoding header for transparent response compression unless the header is already present. If youβve got cookies, OkHttp will add a Cookie header with them.
Some requests will have a cached response. When this cached response isnβt fresh, OkHttp can do a conditional GET to download an updated response if itβs newer than whatβs cached. This requires headers like If-Modified-Since and If-None-Match to be added.
Rewriting Responses
If transparent compression was used, OkHttp will drop the corresponding response headers Content-Encoding and Content-Length because they donβt apply to the decompressed response body.
If a conditional GET was successful, responses from the network and cache are merged as directed by the spec.
Follow-up Requests
When your requested URL has moved, the webserver will return a response code like 302 to indicate the documentβs new URL. OkHttp will follow the redirect to retrieve a final response.
If the response issues an authorization challenge, OkHttp will ask the Authenticator (if one is configured) to satisfy the challenge. If the authenticator supplies a credential, the request is retried with that credential included.
Retrying Requests
Sometimes connections fail: either a pooled connection was stale and disconnected, or the webserver itself couldnβt be reached. OkHttp will retry the request with a different route if one is available.
[Calls](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-call/)
With rewrites, redirects, follow-ups and retries, your simple request may yield many requests and responses. OkHttp uses Call to model the task of satisfying your request through however many intermediate requests and responses are necessary. Typically this isnβt many! But itβs comforting to know that your code will continue to work if your URLs are redirected or if you failover to an alternate IP address.
Calls are executed in one of two ways:
- Synchronous: your thread blocks until the response is readable.
- Asynchronous: you enqueue the request on any thread, and get called back on another thread when the response is readable.
Calls can be canceled from any thread. This will fail the call if it hasnβt yet completed! Code that is writing the request body or reading the response body will suffer an IOException when its call is canceled.
Dispatch
For synchronous calls, you bring your own thread and are responsible for managing how many simultaneous requests you make. Too many simultaneous connections wastes resources; too few harms latency.
For asynchronous calls, Dispatcher implements policy for maximum simultaneous requests. You can set maximums per-webserver (default is 5), and overall (default is 64).
File: docs/features/connections.md
Connections
Although you provide only the URL, OkHttp plans its connection to your webserver using three types: URL, Address, and Route.
[URLs](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-http-url/)
URLs (like https://github.com/lysine-dev/okhttp) are fundamental to HTTP and the Internet. In addition to being a universal, decentralized naming scheme for everything on the web, they also specify how to access web resources.
URLs are abstract:
- They specify that the call may be plaintext (
http) or encrypted (https), but not which cryptographic algorithms should be used. Nor do they specify how to verify the peer's certificates (the HostnameVerifier) or which certificates can be trusted (the SSLSocketFactory). - They don't specify whether a specific proxy server should be used or how to authenticate with that proxy server.
They're also concrete: each URL identifies a specific path (like /lysine-dev/okhttp) and query (like ?q=sharks&lang=en). Each webserver hosts many URLs.
[Addresses](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-address/)
Addresses specify a webserver (like github.com) and all of the static configuration necessary to connect to that server: the port number, HTTPS settings, and preferred network protocols (like HTTP/2).
URLs that share the same address may also share the same underlying TCP socket connection. Sharing a connection has substantial performance benefits: lower latency, higher throughput (due to TCP slow start) and conserved battery. OkHttp uses a ConnectionPool that automatically reuses HTTP/1.x connections and multiplexes HTTP/2 connections.
In OkHttp some fields of the address come from the URL (scheme, hostname, port) and the rest come from the OkHttpClient.
[Routes](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-route/)
Routes supply the dynamic information necessary to actually connect to a webserver. This is the specific IP address to attempt (as discovered by a DNS query), the exact proxy server to use (if a ProxySelector is in use), and which version of TLS to negotiate (for HTTPS connections).
There may be many routes for a single address. For example, a webserver that is hosted in multiple datacenters may yield multiple IP addresses in its DNS response.
In limited situations OkHttp will retry a route if connecting fails:
- When making an HTTPS connection through an HTTP proxy, the proxy may issue an authentication challenge. OkHttp will call the proxy authenticator and try again.
- When making TLS connections with multiple connection specs, these are attempted in sequence until the TLS handshake succeeds.
[Connections](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-connection/)
When you request a URL with OkHttp, here's what it does:
- It uses the URL and configured OkHttpClient to create an address. This address specifies how we'll connect to the webserver.
- It attempts to retrieve a connection with that address from the connection pool.
- If it doesn't find a connection in the pool, it selects a route to attempt. This usually means making a DNS request to get the server's IP addresses. It then selects a TLS version and proxy server if necessary.
- If it's a new route, it connects by building either a direct socket connection, a TLS tunnel (for HTTPS over an HTTP proxy), or a direct TLS connection. It does TLS handshakes as necessary. This step may be retried for tunnel challenges and TLS handshake failures.
- It sends the HTTP request and reads the response.
If there's a problem with the connection, OkHttp will select another route and try again. This allows OkHttp to recover when a subset of a server's addresses are unreachable. It's also useful when a pooled connection is stale or if the attempted TLS version is unsupported.
Once the response has been received, the connection will be returned to the pool so it can be reused for a future request. Connections are evicted from the pool after a period of inactivity.
[Fast Fallback](https://lysine.dev/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/fast-fallback)
Since version 5.0, OkHttpClient supports fast fallback, which is our implementation of Happy Eyeballs RFC 6555.
With fast fallback, OkHttp attempts to connect to multiple web servers concurrently. It keeps whichever route connects first and cancels all of the others. Its rules are:
- Prefer to alternate IP addresses from different address families, (IPv6 / IPv4), starting with IPv6.
- Don't start a new attempt until 250 ms after the most recent attempt was started.
- Keep whichever TCP connection succeeds first and cancel all the others.
- Race TCP only. Only attempt a TLS handshake on the winning TCP connection.
If the winner of the TCP handshake race fails to succeed in a TLS handshake, the process is restarted with the remaining routes.
File: docs/features/events.md
Events
Events allow you to capture metrics on your applicationβs HTTP calls. Use events to monitor:
- The size and frequency of the HTTP calls your application makes. If youβre making too many calls, or your calls are too large, you should know about it!
- The performance of these calls on the underlying network. If the networkβs performance isnβt sufficient, you need to either improve the network or use less of it.
EventListener
Subclass EventListener and override methods for the events you are interested in. In a successful HTTP call with no redirects or retries the sequence of events is described by this flow.
Hereβs a sample event listener that prints each event with a timestamp.
class PrintingEventListener extends EventListener {
private long callStartNanos;
private void printEvent(String name) {
long nowNanos = System.nanoTime();
if (name.equals("callStart")) {
callStartNanos = nowNanos;
}
long elapsedNanos = nowNanos - callStartNanos;
System.out.printf("%.3f %s%n", elapsedNanos / 1000000000d, name);
}
@Override public void callStart(Call call) {
printEvent("callStart");
}
@Override public void callEnd(Call call) {
printEvent("callEnd");
}
@Override public void dnsStart(Call call, String domainName) {
printEvent("dnsStart");
}
@Override public void dnsEnd(Call call, String domainName, List inetAddressList) {
printEvent("dnsEnd");
}
...
}We make a couple calls:
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
System.out.println("REQUEST 1 (new connection)");
try (Response response = client.newCall(request).execute()) {
// Consume and discard the response body.
response.body().source().readByteString();
}
System.out.println("REQUEST 2 (pooled connection)");
try (Response response = client.newCall(request).execute()) {
// Consume and discard the response body.
response.body().source().readByteString();
}And the listener prints the corresponding events:
REQUEST 1 (new connection)
0.000 callStart
0.010 dnsStart
0.017 dnsEnd
0.025 connectStart
0.117 secureConnectStart
0.586 secureConnectEnd
0.586 connectEnd
0.587 connectionAcquired
0.588 requestHeadersStart
0.590 requestHeadersEnd
0.591 responseHeadersStart
0.675 responseHeadersEnd
0.676 responseBodyStart
0.679 responseBodyEnd
0.679 connectionReleased
0.680 callEnd
REQUEST 2 (pooled connection)
0.000 callStart
0.001 connectionAcquired
0.001 requestHeadersStart
0.001 requestHeadersEnd
0.002 responseHeadersStart
0.082 responseHeadersEnd
0.082 responseBodyStart
0.082 responseBodyEnd
0.083 connectionReleased
0.083 callEndNotice how no connect events are fired for the second call. It reused the connection from the first request for dramatically better performance.
EventListener.Factory
In the preceding example we used a field, callStartNanos, to track the elapsed time of each event. This is handy, but it wonβt work if multiple calls are executing concurrently. To accommodate this, use a Factory to create a new EventListener instance for each Call. This allows each listener to keep call-specific state.
This sample factory creates a unique ID for each call and uses that ID to differentiate calls in log messages.
class PrintingEventListener extends EventListener {
public static final Factory FACTORY = new Factory() {
final AtomicLong nextCallId = new AtomicLong(1L);
@Override public EventListener create(Call call) {
long callId = nextCallId.getAndIncrement();
System.out.printf("%04d %s%n", callId, call.request().url());
return new PrintingEventListener(callId, System.nanoTime());
}
};
final long callId;
final long callStartNanos;
public PrintingEventListener(long callId, long callStartNanos) {
this.callId = callId;
this.callStartNanos = callStartNanos;
}
private void printEvent(String name) {
long elapsedNanos = System.nanoTime() - callStartNanos;
System.out.printf("%04d %.3f %s%n", callId, elapsedNanos / 1000000000d, name);
}
@Override public void callStart(Call call) {
printEvent("callStart");
}
@Override public void callEnd(Call call) {
printEvent("callEnd");
}
...
}We can use this listener to race a pair of concurrent HTTP requests:
Request washingtonPostRequest = new Request.Builder()
.url("https://www.washingtonpost.com/")
.build();
client.newCall(washingtonPostRequest).enqueue(new Callback() {
...
});
Request newYorkTimesRequest = new Request.Builder()
.url("https://www.nytimes.com/")
.build();
client.newCall(newYorkTimesRequest).enqueue(new Callback() {
...
});Running this race over home WiFi shows the Times (0002) completes just slightly sooner than the Post (0001):
0001 https://www.washingtonpost.com/
0001 0.000 callStart
0002 https://www.nytimes.com/
0002 0.000 callStart
0002 0.010 dnsStart
0001 0.013 dnsStart
0001 0.022 dnsEnd
0002 0.019 dnsEnd
0001 0.028 connectStart
0002 0.025 connectStart
0002 0.072 secureConnectStart
0001 0.075 secureConnectStart
0001 0.386 secureConnectEnd
0002 0.390 secureConnectEnd
0002 0.400 connectEnd
0001 0.403 connectEnd
0002 0.401 connectionAcquired
0001 0.404 connectionAcquired
0001 0.406 requestHeadersStart
0002 0.403 requestHeadersStart
0001 0.414 requestHeadersEnd
0002 0.411 requestHeadersEnd
0002 0.412 responseHeadersStart
0001 0.415 responseHeadersStart
0002 0.474 responseHeadersEnd
0002 0.475 responseBodyStart
0001 0.554 responseHeadersEnd
0001 0.555 responseBodyStart
0002 0.554 responseBodyEnd
0002 0.554 connectionReleased
0002 0.554 callEnd
0001 0.624 responseBodyEnd
0001 0.624 connectionReleased
0001 0.624 callEndThe EventListener.Factory also makes it possible to limit metrics to a subset of calls. This one captures metrics on a random 10%:
class MetricsEventListener extends EventListener {
private static final Factory FACTORY = new Factory() {
@Override public EventListener create(Call call) {
if (Math.random() < 0.10) {
return new MetricsEventListener(call);
} else {
return EventListener.NONE;
}
}
};
...
}Events with Failures
When an operation fails, a failure method is called. This is connectFailed() for failures while building a connection to the server, and callFailed() when the HTTP call fails permanently. When a failure happens it is possible that a start event wonβt have a corresponding end event.
Events with Retries and Follow-Ups
OkHttp is resilient and can automatically recover from some connectivity failures. In this case, the connectFailed() event is not terminal and not followed by callFailed(). Event listeners will receive multiple events of the same type when retries are attempted.
A single HTTP call may require follow-up requests to be made to handle authentication challenges, redirects, and HTTP-layer timeouts. In such cases multiple connections, requests, and responses may be attempted. Follow-ups are another reason a single call may trigger multiple events of the same type.
Availability
Events is available as a public API in OkHttp 3.11. Future releases may introduce new event types; you will need to override the corresponding methods to handle them.
File: docs/features/https.md
HTTPS
OkHttp attempts to balance two competing concerns:
- Connectivity to as many hosts as possible. That includes advanced hosts that run the latest versions of boringssl and less out of date hosts running older versions of OpenSSL.
- Security of the connection. This includes verification of the remote webserver with certificates and the privacy of data exchanged with strong ciphers.
When negotiating a connection to an HTTPS server, OkHttp needs to know which TLS versions and cipher suites to offer. A client that wants to maximize connectivity would include obsolete TLS versions and weak-by-design cipher suites. A strict client that wants to maximize security would be limited to only the latest TLS version and strongest cipher suites.
Specific security vs. connectivity decisions are implemented by ConnectionSpec. OkHttp includes four built-in connection specs:
RESTRICTED_TLSis a secure configuration, intended to meet stricter compliance requirements.MODERN_TLSis a secure configuration that connects to modern HTTPS servers.COMPATIBLE_TLSis a secure configuration that connects to secureβbut not currentβHTTPS servers.CLEARTEXTis an insecure configuration that is used forhttp://URLs.
These loosely follow the model set in Google Cloud Policies. We track changes to this policy.
By default, OkHttp will attempt a MODERN_TLS connection. However by configuring the client connectionSpecs you can allow a fall back to COMPATIBLE_TLS connection if the modern configuration fails.
OkHttpClient client = new OkHttpClient.Builder()
.connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
.build();The TLS versions and cipher suites in each spec can change with each release. For example, in OkHttp 2.2 we dropped support for SSL 3.0 in response to the POODLE attack. And in OkHttp 2.3 we dropped support for RC4. As with your desktop web browser, staying up-to-date with OkHttp is the best way to stay secure.
You can build your own connection spec with a custom set of TLS versions and cipher suites. For example, this configuration is limited to three highly-regarded cipher suites. Its drawback is that it requires Android 5.0+ and a similarly current webserver.
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectionSpecs(Collections.singletonList(spec))
.build();Debugging TLS Handshake Failures
The TLS handshake requires clients and servers to share a common TLS version and cipher suite. This
depends on the JVM or Android version, OkHttp version, and web server configuration. If there is no
common cipher suite and TLS version, your call will fail like this:
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x7f2719a89e80:
Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake
failure (external/openssl/ssl/s23_clnt.c:770 0x7f2728a53ea0:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)You can check a web server's configuration using Qualys SSL Labs. OkHttp's TLS
configuration history is tracked here.
Applications expected to be installed on older Android devices should consider adopting the
Google Play Servicesβ ProviderInstaller. This will increase security for users
and increase connectivity with web servers.
Certificate Pinning ([.kt][CertificatePinningKotlin], [.java][CertificatePinningJava])
By default, OkHttp trusts the certificate authorities of the host platform. This strategy maximizes connectivity, but it is subject to certificate authority attacks such as the 2011 DigiNotar attack. It also assumes your HTTPS serversβ certificates are signed by a certificate authority.
Use CertificatePinner to restrict which certificates and certificate authorities are trusted. Certificate pinning increases security, but limits your server teamβs abilities to update their TLS certificates. Do not use certificate pinning without the blessing of your serverβs TLS administrator!
=== ":material-language-kotlin: Kotlin"
```kotlin
private val client = OkHttpClient.Builder()
.certificatePinner(
CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.build())
.build()
fun run() {
val request = Request.Builder()
.url("https://publicobject.com/robots.txt")
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
for (certificate in response.handshake!!.peerCertificates) {
println(CertificatePinner.pin(certificate))
}
}
}
```=== ":material-language-java: Java"
```java
private final OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(
new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.build())
.build();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/robots.txt")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
for (Certificate certificate : response.handshake().peerCertificates()) {
System.out.println(CertificatePinner.pin(certificate));
}
}
}
```Customizing Trusted Certificates ([.kt][CustomTrustKotlin], [.java][CustomTrustJava])
The full code sample shows how to replace the host platformβs certificate authorities with your own set. As above, do not use custom certificates without the blessing of your serverβs TLS administrator!
=== ":material-language-kotlin: Kotlin"
```kotlin
private val client: OkHttpClient
init {
val trustManager = trustManagerForCertificates(trustedCertificatesInputStream())
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(trustManager), null)
val sslSocketFactory = sslContext.socketFactory
client = OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.build()
}
fun run() {
val request = Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
for ((name, value) in response.headers) {
println("$name: $value")
}
println(response.body!!.string())
}
}
/**
* Returns an input stream containing one or more certificate PEM files. This implementation just
* embeds the PEM files in Java strings; most applications will instead read this from a resource
* file that gets bundled with the application.
*/
private fun trustedCertificatesInputStream(): InputStream {
... // Full source omitted. See sample.
}
private fun trustManagerForCertificates(inputStream: InputStream): X509TrustManager {
... // Full source omitted. See sample.
}
```=== ":material-language-java: Java"
```java
private final OkHttpClient client;
public CustomTrust() {
X509TrustManager trustManager;
SSLSocketFactory sslSocketFactory;
try {
trustManager = trustManagerForCertificates(trustedCertificatesInputStream());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());
}
private InputStream trustedCertificatesInputStream() {
... // Full source omitted. See sample.
}
public SSLContext sslContextForTrustedCertificates(InputStream in) {
... // Full source omitted. See sample.
}
```File: docs/features/interceptors.md
Interceptors
Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls. Here's a simple interceptor that logs the outgoing request and the incoming response.
class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}A call to chain.proceed(request) is a critical part of each interceptorβs implementation. This simple-looking method is where all the HTTP work happens, producing a response to satisfy the request. If chain.proceed(request) is being called more than once previous response bodies must be closed.
Interceptors can be chained. Suppose you have both a compressing interceptor and a checksumming interceptor: you'll need to decide whether data is compressed and then checksummed, or checksummed and then compressed. OkHttp uses lists to track interceptors, and interceptors are called in order.
Application Interceptors
Interceptors are registered as either application or network interceptors. We'll use the LoggingInterceptor defined above to show the difference.
Register an application interceptor by calling addInterceptor() on OkHttpClient.Builder:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())
.build();
Request request = new Request.Builder()
.url("http://www.publicobject.com/helloworld.txt")
.header("User-Agent", "OkHttp Example")
.build();
Response response = client.newCall(request).execute();
response.body().close();The URL http://www.publicobject.com/helloworld.txt redirects to https://publicobject.com/helloworld.txt, and OkHttp follows this redirect automatically. Our application interceptor is called once and the response returned from chain.proceed() has the redirected response:
INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example
INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-aliveWe can see that we were redirected because response.request().url() is different from request.url(). The two log statements log two different URLs.
Network Interceptors
Registering a network interceptor is quite similar. Call addNetworkInterceptor() instead of addInterceptor():
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new LoggingInterceptor())
.build();
Request request = new Request.Builder()
.url("http://www.publicobject.com/helloworld.txt")
.header("User-Agent", "OkHttp Example")
.build();
Response response = client.newCall(request).execute();
response.body().close();When we run this code, the interceptor runs twice. Once for the initial request to http://www.publicobject.com/helloworld.txt, and another for the redirect to https://publicobject.com/helloworld.txt.
INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}
User-Agent: OkHttp Example
Host: www.publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip
INFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://publicobject.com/helloworld.txt
INFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}
User-Agent: OkHttp Example
Host: publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzip
INFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-aliveThe network requests also contain more data, such as the Accept-Encoding: gzip header added by OkHttp to advertise support for response compression. The network interceptor's Chain has a non-null Connection that can be used to interrogate the IP address and TLS configuration that were used to connect to the webserver.
Choosing between application and network interceptors
Each interceptor chain has relative merits.
Application interceptors
- Don't need to worry about intermediate responses like redirects and retries.
- Are always invoked once, even if the HTTP response is served from the cache.
- Observe the application's original intent. Unconcerned with OkHttp-injected headers like
If-None-Match. - Permitted to short-circuit and not call
Chain.proceed(). - Permitted to retry and make multiple calls to
Chain.proceed(). - Can adjust Call timeouts using withConnectTimeout, withReadTimeout, withWriteTimeout.
Network Interceptors
- Able to operate on intermediate responses like redirects and retries.
- Not invoked for cached responses that short-circuit the network.
- Observe the data just as it will be transmitted over the network.
- Access to the
Connectionthat carries the request.
Rewriting Requests
Interceptors can add, remove, or replace request headers. They can also transform the body of those requests that have one. For example, you can use an application interceptor to add request body compression if you're connecting to a webserver known to support it.
/** This interceptor compresses the HTTP request body. Many webservers can't handle this! */
final class GzipRequestInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
return chain.proceed(originalRequest);
}
Request compressedRequest = originalRequest.newBuilder()
.header("Content-Encoding", "gzip")
.method(originalRequest.method(), gzip(originalRequest.body()))
.build();
return chain.proceed(compressedRequest);
}
private RequestBody gzip(final RequestBody body) {
return new RequestBody() {
@Override public MediaType contentType() {
return body.contentType();
}
@Override public long contentLength() {
return -1; // We don't know the compressed length in advance!
}
@Override public void writeTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
body.writeTo(gzipSink);
gzipSink.close();
}
};
}
}Rewriting Responses
Symmetrically, interceptors can rewrite response headers and transform the response body. This is generally more dangerous than rewriting request headers because it may violate the webserver's expectations!
If you're in a tricky situation and prepared to deal with the consequences, rewriting response headers is a powerful way to work around problems. For example, you can fix a server's misconfigured Cache-Control response header to enable better response caching:
/** Dangerous interceptor that rewrites the server's cache-control header. */
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=60")
.build();
}
};Typically this approach works best when it complements a corresponding fix on the webserver!
File: docs/features/r8_proguard.md
R8 / ProGuard
If you use OkHttp as a dependency in an Android project which uses R8 as a default compiler you
don't have to do anything. The specific rules are already bundled into the JAR which can be
interpreted by R8 automatically.
If you, however, don't use R8 you have to apply the rules from this file. You might
also need rules from Okio which is a dependency of this library.