### MockWebServer complete example (Java/Kotlin) Source: https://github.com/lysine-dev/okhttp/blob/main/mockwebserver/README.md Complete example of using MockWebServer: enqueue responses, start the server, exercise application code, verify recorded requests, and shut down. Responses are returned in the order enqueued. Instances cannot be reused after close(). ```java public void test() throws Exception { // Create a MockWebServer. These are lean enough that you can create a new // instance for every unit test. MockWebServer server = new MockWebServer(); // Schedule some responses. server.enqueue(new MockResponse.Builder() .body("hello, world!") .build()); server.enqueue(new MockResponse.Builder() .body("sup, bra?") .build()); server.enqueue(new MockResponse.Builder() .body("yo dog") .build()); // Start the server. server.start(); // Ask the server for its URL. You'll need this to make HTTP requests. HttpUrl baseUrl = server.url("/v1/chat/"); // Exercise your application code, which should make those HTTP requests. // Responses are returned in the same order that they are enqueued. Chat chat = new Chat(baseUrl); chat.loadMore(); assertEquals("hello, world!", chat.messages()); chat.loadMore(); chat.loadMore(); assertEquals("" + "hello, world!\n" + "sup, bra?\n" + "yo dog", chat.messages()); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request1 = server.takeRequest(); assertEquals("/v1/chat/messages/", request1.getUrl().encodedPath()); assertNotNull(request1.getHeaders().get("Authorization")); RecordedRequest request2 = server.takeRequest(); assertEquals("/v1/chat/messages/2", request2.getUrl().encodedPath()); RecordedRequest request3 = server.takeRequest(); assertEquals("/v1/chat/messages/3", request3.getUrl().encodedPath()); // Shut down the server. Instances cannot be reused. server.close(); } ``` ```kotlin fun test() { // Create a MockWebServer. These are lean enough that you can create a new // instance for every unit test. val server = MockWebServer() // Schedule some responses. server.enqueue(MockResponse(body = "hello, world!")) server.enqueue(MockResponse(body = "sup, bra?")) server.enqueue(MockResponse(body = "yo dog")) // Start the server. server.start() // Ask the server for its URL. You'll need this to make HTTP requests. val baseUrl = server.url("/v1/chat/") // Exercise your application code, which should make those HTTP requests. // Responses are returned in the same order that they are enqueued. val chat = Chat(baseUrl) chat.loadMore() assertEquals("hello, world!", chat.messages()) chat.loadMore() chat.loadMore() assertEquals("" + "hello, world!\n" + "sup, bra?\n" + "yo dog", chat.messages()) // Optional: confirm that your app made the HTTP requests you were expecting. val request1 = server.takeRequest() assertEquals("/v1/chat/messages/", request1.url.encodedPath) assertNotNull(request1.headers["Authorization"]) val request2 = server.takeRequest() assertEquals("/v1/chat/messages/2", request2.url.encodedPath) val request3 = server.takeRequest() assertEquals("/v1/chat/messages/3", request3.url.encodedPath) // Shut down the server. Instances cannot be reused. server.close() } ``` -------------------------------- ### Configure local.properties for Gradle Source: https://github.com/lysine-dev/okhttp/blob/main/CONTRIBUTING.md Example local.properties file for Gradle setup, specifying the Android SDK path and enabling Gradle caching. ```properties $ cat local.properties sdk.dir=PATH_TO_ANDROID_HOME/sdk org.gradle.caching=true ``` -------------------------------- ### TaskRunner logging output Source: https://github.com/lysine-dev/okhttp/blob/main/docs/contribute/debug_logging.md Example output of TaskRunner logging, showing task enqueues, starts, and finishes. ```text [2020-01-01 00:00:00] Q10000 scheduled after 0 µs: OkHttp ConnectionPool [2020-01-01 00:00:00] Q10000 starting : OkHttp ConnectionPool [2020-01-01 00:00:00] Q10000 run again after 300 s : OkHttp ConnectionPool [2020-01-01 00:00:00] Q10000 finished run in 1 ms: OkHttp ConnectionPool [2020-01-01 00:00:00] Q10001 scheduled after 0 µs: OkHttp squareup.com applyAndAckSettings [2020-01-01 00:00:00] Q10001 starting : OkHttp squareup.com applyAndAckSettings [2020-01-01 00:00:00] Q10003 scheduled after 0 µs: OkHttp squareup.com onSettings [2020-01-01 00:00:00] Q10003 starting : OkHttp squareup.com onSettings [2020-01-01 00:00:00] Q10001 finished run in 3 ms: OkHttp squareup.com applyAndAckSettings [2020-01-01 00:00:00] Q10003 finished run in 528 µs: OkHttp squareup.com onSettings [2020-01-01 00:00:00] Q10000 scheduled after 0 µs: OkHttp ConnectionPool [2020-01-01 00:00:00] Q10000 starting : OkHttp ConnectionPool [2020-01-01 00:00:00] Q10000 run again after 300 s : OkHttp ConnectionPool [2020-01-01 00:00:00] Q10000 finished run in 739 µs: OkHttp ConnectionPool ``` -------------------------------- ### Build and run okcurl native image Source: https://github.com/lysine-dev/okhttp/blob/main/README.md Commands to build and run the okcurl example as a GraalVM native image. The first command builds the native image, the second runs it with a URL argument. ```shell $ ./gradlew okcurl:nativeImage $ ./okcurl/build/graal/okcurl https://httpbin.org/get ``` -------------------------------- ### Install Conscrypt security provider Source: https://github.com/lysine-dev/okhttp/blob/main/docs/changelogs/changelog_3x.md Install Conscrypt as the preferred security provider before creating an OkHttpClient. OkHttp will then use Conscrypt automatically. This is not needed on Android because Conscrypt is already the bundled security provider. ```java Security.insertProviderAt( new org.conscrypt.OpenSSLProvider(), 1); ``` -------------------------------- ### Create pixel5 emulator with sdkmanager and avdmanager Source: https://github.com/lysine-dev/okhttp/blob/main/android-test/src/androidTest/README.md Install the Android 29 system image and create an AVD named pixel5. The echo "no" answers the prompt about creating a custom hardware profile. ```bash $ 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" ``` -------------------------------- ### Use @StartStop with MockWebServer Source: https://github.com/lysine-dev/okhttp/blob/main/mockwebserver-junit5/README.md Annotate a MockWebServer field with @StartStop to automatically start and shut down the server for each test. ```java class MyTest { @StartStop public final MockWebServer server = new MockWebServer(); @Test void test() { ... } } ``` -------------------------------- ### CertificatePinner with wildcard hostnames Source: https://github.com/lysine-dev/okhttp/blob/main/docs/changelogs/changelog_2x.md Configure certificate pinning for both the top-level domain and the wildcard subdomain to ensure full coverage. This example pins multiple certificates for publicobject.com and *.publicobject.com. ```java client.setCertificatePinner(new CertificatePinner.Builder() .add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=") .add("*.publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=") .add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=") .add("*.publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=") .add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=") .add("*.publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=") .add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=") .add("*.publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=") .build()); ``` -------------------------------- ### Synchronous Get (Kotlin) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Downloads a file, prints its headers, and prints the response body as a string. The string() method is convenient for small documents; avoid it for bodies larger than 1 MiB to prevent memory issues. ```kotlin private val client = OkHttpClient() 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()) } } ``` -------------------------------- ### Custom Trust in Java Source: https://github.com/lysine-dev/okhttp/blob/main/docs/features/https.md Java example that builds an OkHttpClient with a custom TrustManager and SSLContext, then makes a request to https://publicobject.com/helloworld.txt. The helper methods trustedCertificatesInputStream() and sslContextForTrustedCertificates() are omitted in the source. ```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. } ``` -------------------------------- ### Synchronous Get (Java) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Downloads a file, prints its headers, and prints the response body as a string. The string() method is convenient for small documents; avoid it for bodies larger than 1 MiB to prevent memory issues. ```java private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { Request request = new Request.Builder() .url("https://publicobject.com/helloworld.txt") .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); Headers responseHeaders = response.headers(); for (int i = 0; i < responseHeaders.size(); i++) { System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i)); } System.out.println(response.body().string()); } } ``` -------------------------------- ### Enable Brotli compression with BrotliInterceptor Source: https://github.com/lysine-dev/okhttp/blob/main/docs/changelogs/changelog_4x.md Install the BrotliInterceptor on an OkHttpClient to enable Brotli compression, which compresses 5-20% smaller than gzip. The okhttp-brotli module has a dependency on Google's Brotli decoder (95 KiB). ```kotlin val client = OkHttpClient.Builder() .addInterceptor(BrotliInterceptor) .build() ``` -------------------------------- ### Handle authentication with Authenticator in Java Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Java example showing how to implement an Authenticator that supplies credentials for 401 responses. It uses Response.challenges() and Credentials.basic() to build the Authorization header, and returns null to skip retries when credentials were already attempted or after an application-defined attempt limit (using the responseCount() method). ```java private final OkHttpClient client; public Authenticate() { client = new OkHttpClient.Builder() .authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { if (response.request().header("Authorization") != null) { return null; // Give up, we've already attempted to authenticate. } System.out.println("Authenticating for response: " + response); System.out.println("Challenges: " + response.challenges()); String credential = Credentials.basic("jesse", "password1"); return response.request().newBuilder() .header("Authorization", credential) .build(); } }) .build(); } public void run() throws Exception { Request request = new Request.Builder() .url("http://publicobject.com/secrets/hellosecret.txt") .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } } ``` ```java if (credential.equals(response.request().header("Authorization"))) { return null; // If we already failed with these credentials, don't retry. } ``` ```java if (responseCount(response) >= 3) { return null; // If we've failed 3 times, give up. } ``` ```java private int responseCount(Response response) { int result = 1; while ((response = response.priorResponse()) != null) { result++; } return result; } ``` -------------------------------- ### HTTP/2 frame logging output Source: https://github.com/lysine-dev/okhttp/blob/main/docs/contribute/debug_logging.md Example output of HTTP/2 frame logging, showing inbound (<<) and outbound (>>) frames. ```text [2020-01-01 00:00:00] >> CONNECTION 505249202a20485454502f322e300d0a0d0a534d0d0a0d0a [2020-01-01 00:00:00] >> 0x00000000 6 SETTINGS [2020-01-01 00:00:00] >> 0x00000000 4 WINDOW_UPDATE [2020-01-01 00:00:00] >> 0x00000003 47 HEADERS END_STREAM|END_HEADERS [2020-01-01 00:00:00] << 0x00000000 6 SETTINGS [2020-01-01 00:00:00] << 0x00000000 0 SETTINGS ACK [2020-01-01 00:00:00] << 0x00000000 4 WINDOW_UPDATE [2020-01-01 00:00:00] >> 0x00000000 0 SETTINGS ACK [2020-01-01 00:00:00] << 0x00000003 322 HEADERS END_HEADERS [2020-01-01 00:00:00] << 0x00000003 288 DATA [2020-01-01 00:00:00] << 0x00000003 0 DATA END_STREAM [2020-01-01 00:00:00] << 0x00000000 8 GOAWAY [2020-01-01 00:00:00] << 0x00000000 8 GOAWAY ``` -------------------------------- ### Configure timeouts with OkHttpClient.Builder (Java) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Configure connect, write, and read timeouts on an OkHttpClient. Timeouts fail a call when its peer is unreachable, covering client connectivity, server availability, or anything between. This example sets 10-second connect/write timeouts and a 30-second read timeout. ```java private final OkHttpClient client; public ConfigureTimeouts() throws Exception { client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); } public void run() throws Exception { Request request = new Request.Builder() .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay. .build(); try (Response response = client.newCall(request).execute()) { System.out.println("Response completed: " + response); } } ``` -------------------------------- ### Per-call configuration with OkHttpClient.newBuilder() Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Use OkHttpClient.newBuilder() to create a copy of the client that shares the same connection pool, dispatcher, and configuration, then override readTimeout for a single call. The example makes two requests to http://httpbin.org/delay/1, one with a 500 ms timeout and one with a 3000 ms timeout, and prints success or failure for each. ```kotlin private val client = OkHttpClient() fun run() { val request = Request.Builder() .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay. .build() // Copy to customize OkHttp for this request. val client1 = client.newBuilder() .readTimeout(500, TimeUnit.MILLISECONDS) .build() try { client1.newCall(request).execute().use { response -> println("Response 1 succeeded: $response") } } catch (e: IOException) { println("Response 1 failed: $e") } // Copy to customize OkHttp for this request. val client2 = client.newBuilder() .readTimeout(3000, TimeUnit.MILLISECONDS) .build() try { client2.newCall(request).execute().use { response -> println("Response 2 succeeded: $response") } } catch (e: IOException) { println("Response 2 failed: $e") } } ``` ```java private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { Request request = new Request.Builder() .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay. .build(); // Copy to customize OkHttp for this request. OkHttpClient client1 = client.newBuilder() .readTimeout(500, TimeUnit.MILLISECONDS) .build(); try (Response response = client1.newCall(request).execute()) { System.out.println("Response 1 succeeded: " + response); } catch (IOException e) { System.out.println("Response 1 failed: " + e); } // Copy to customize OkHttp for this request. OkHttpClient client2 = client.newBuilder() .readTimeout(3000, TimeUnit.MILLISECONDS) .build(); try (Response response = client2.newCall(request).execute()) { System.out.println("Response 2 succeeded: " + response); } catch (IOException e) { System.out.println("Response 2 failed: " + e); } } ``` -------------------------------- ### Get a URL with OkHttp Source: https://github.com/lysine-dev/okhttp/blob/main/README.md Downloads a URL and prints its contents as a string. Uses a synchronous call with a try-with-resources block to close the response. ```java 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(); } } ``` -------------------------------- ### Handle authentication with Authenticator in Kotlin Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Kotlin example showing how to implement an Authenticator that supplies credentials for 401 responses. It uses Response.challenges() and Credentials.basic() to build the Authorization header, and returns null to skip retries when credentials were already attempted or after an application-defined attempt limit (using the responseCount extension). ```kotlin private val client = OkHttpClient.Builder() .authenticator(object : Authenticator { @Throws(IOException::class) override fun authenticate(route: Route?, response: Response): Request? { if (response.request.header("Authorization") != null) { return null // Give up, we've already attempted to authenticate. } println("Authenticating for response: $response") println("Challenges: ${response.challenges()}") val credential = Credentials.basic("jesse", "password1") return response.request.newBuilder() .header("Authorization", credential) .build() } }) .build() fun run() { val request = Request.Builder() .url("http://publicobject.com/secrets/hellosecret.txt") .build() } ``` ```kotlin if (credential == response.request.header("Authorization")) { return null // If we already failed with these credentials, don't retry. } ``` ```kotlin if (response.responseCount >= 3) { return null // If we've failed 3 times, give up. } ``` ```kotlin val Response.responseCount: Int get() = generateSequence(this) { it.priorResponse }.count() ``` -------------------------------- ### Custom Trust in Kotlin Source: https://github.com/lysine-dev/okhttp/blob/main/docs/features/https.md Kotlin example that builds an OkHttpClient with a custom TrustManager and SSLContext, then makes a request to https://publicobject.com/helloworld.txt. The helper methods trustedCertificatesInputStream() and trustManagerForCertificates() are omitted in the source. ```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. } ``` -------------------------------- ### Posting a File as Request Body with OkHttp (Kotlin/Java) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Uses a file as the request body by calling file.asRequestBody(MEDIA_TYPE_MARKDOWN) in Kotlin or RequestBody.create(MEDIA_TYPE_MARKDOWN, file) in Java. The example POSTs README.md to https://api.github.com/markdown/raw and prints the response. This is the simplest way to upload a file with OkHttp. ```kotlin private val client = OkHttpClient() fun run() { val file = File("README.md") val request = Request.Builder() .url("https://api.github.com/markdown/raw") .post(file.asRequestBody(MEDIA_TYPE_MARKDOWN)) .build() client.newCall(request).execute().use { response -> if (!response.isSuccessful) throw IOException("Unexpected code $response") println(response.body!!.string()) } } companion object { val MEDIA_TYPE_MARKDOWN = "text/x-markdown; charset=utf-8".toMediaType() } ``` ```java public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8"); private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { File file = new File("README.md"); Request request = new Request.Builder() .url("https://api.github.com/markdown/raw") .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)) .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } } ``` -------------------------------- ### Application interceptor log output Source: https://github.com/lysine-dev/okhttp/blob/main/docs/features/interceptors.md Log output from the application interceptor example. Shows the interceptor is called once and the response URL differs from the request URL due to automatic redirect. ```text 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-alive ``` -------------------------------- ### Configure timeouts with OkHttpClient.Builder (Kotlin) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Configure connect, write, read, and full call timeouts on an OkHttpClient. Timeouts fail a call when its peer is unreachable, covering client connectivity, server availability, or anything between. This example sets 5-second connect/write/read timeouts and a 10-second call timeout. ```kotlin private val client: OkHttpClient = OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .writeTimeout(5, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS) .callTimeout(10, TimeUnit.SECONDS) .build() fun run() { val request = Request.Builder() .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay. .build() client.newCall(request).execute().use { response -> println("Response completed: $response") } } ``` -------------------------------- ### Post Streaming Request Body with OkHttp (Kotlin/Java) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Streams a request body directly into an Okio BufferedSink, generating content as it is written. The example POSTs a markdown list of factors to https://api.github.com/markdown/raw and prints the response. Use this when you need to generate a request body on the fly without buffering the entire content in memory. ```kotlin private val client = OkHttpClient() fun run() { val requestBody = object : RequestBody() { override fun contentType() = MEDIA_TYPE_MARKDOWN override fun writeTo(sink: BufferedSink) { sink.writeUtf8("Numbers\n") sink.writeUtf8("-------\n") for (i in 2..997) { sink.writeUtf8(String.format(" * $i = ${factor(i)}\n")) } } private fun factor(n: Int): String { for (i in 2 until n) { val x = n / i if (x * i == n) return "${factor(x)} × $i" } return n.toString() } } val request = Request.Builder() .url("https://api.github.com/markdown/raw") .post(requestBody) .build() client.newCall(request).execute().use { response -> if (!response.isSuccessful) throw IOException("Unexpected code $response") println(response.body!!.string()) } } companion object { val MEDIA_TYPE_MARKDOWN = "text/x-markdown; charset=utf-8".toMediaType() } ``` ```java public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8"); private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { RequestBody requestBody = new RequestBody() { @Override public MediaType contentType() { return MEDIA_TYPE_MARKDOWN; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeUtf8("Numbers\n"); sink.writeUtf8("-------\n"); for (int i = 2; i <= 997; i++) { sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i))); } } private String factor(int n) { for (int i = 2; i < n; i++) { int x = n / i; if (x * i == n) return factor(x) + " × " + i; } return Integer.toString(n); } }; Request request = new Request.Builder() .url("https://api.github.com/markdown/raw") .post(requestBody) .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } } ``` -------------------------------- ### Upload Progress with ProgressRequestBody and ProgressListener Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Upload a file to Imgur and report progress as the request body is written. The Kotlin and Java versions are equivalent implementations of the same example. The code wraps the request body with ProgressRequestBody and uses a ProgressListener to print bytes written and percentage done; it prints "completed" when done and "content-length: unknown" when the length is -1. ```kotlin class UploadProgress { companion object { private const val IMGUR_CLIENT_ID = "9199fdef135c122" private val MEDIA_TYPE_PNG = "image/png".toMediaType() @JvmStatic fun main(args: Array) { UploadProgress().run() } } private val client = OkHttpClient() @Throws(Exception::class) fun run() { val progressListener = object : ProgressListener { private var firstUpdate = true override fun update(bytesWritten: Long, contentLength: Long, done: Boolean) { if (done) { println("completed") } else { if (firstUpdate) { firstUpdate = false if (contentLength == -1L) { println("content-length: unknown") } else { println("content-length: $contentLength") } } println(bytesWritten) if (contentLength != -1L) { println("${100 * bytesWritten / contentLength}% done") } } } } val file = File("docs/images/logo-square.png") val requestBody: RequestBody = file.asRequestBody(MEDIA_TYPE_PNG) val request = Request.Builder().header("Authorization", "Client-ID $IMGUR_CLIENT_ID") .url("https://api.imgur.com/3/image") .post(ProgressRequestBody(requestBody, progressListener)).build() client.newCall(request).execute().use { response -> if (!response.isSuccessful) throw IOException("Unexpected code $response") println(response.body.string()) } } private class ProgressRequestBody( private val delegate: RequestBody, private val progressListener: ProgressListener ) : RequestBody() { override fun contentType() = delegate.contentType() @Throws(IOException::class) override fun contentLength(): Long = delegate.contentLength() @Throws(IOException::class) override fun writeTo(sink: BufferedSink) { val forwardingSink = object : ForwardingSink(sink) { private var totalBytesWritten: Long = 0 private var completed = false override fun write(source: Buffer, byteCount: Long) { super.write(source, byteCount) totalBytesWritten += byteCount progressListener.update(totalBytesWritten, contentLength(), completed) } override fun close() { super.close() if (!completed) { completed = true progressListener.update(totalBytesWritten, contentLength(), completed) } } } val bufferedSink = forwardingSink.buffer() delegate.writeTo(bufferedSink) bufferedSink.flush() } } fun interface ProgressListener { fun update(bytesWritten: Long, contentLength: Long, done: Boolean) } } ``` ```java public final class UploadProgress { private static final String IMGUR_CLIENT_ID = "9199fdef135c122"; private static final MediaType MEDIA_TYPE_PNG = MediaType.get("image/png"); private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { final ProgressListener progressListener = new ProgressListener() { boolean firstUpdate = true; @Override public void update(long bytesWritten, long contentLength, boolean done) { if (done) { System.out.println("completed"); } else { if (firstUpdate) { firstUpdate = false; if (contentLength == -1) { System.out.println("content-length: unknown"); } else { System.out.format("content-length: %d\n", contentLength); } } System.out.println(bytesWritten); if (contentLength != -1) { System.out.format("%d%% done\n", (100 * bytesWritten) / contentLength); } } } }; ``` -------------------------------- ### PrintingEventListener class that prints events with timestamps Source: https://github.com/lysine-dev/okhttp/blob/main/docs/features/events.md Subclass EventListener and override methods for events of interest. This sample prints each event with a timestamp using System.nanoTime(). The callStart event initializes the start time. Override methods like callStart, callEnd, dnsStart, dnsEnd, and others to capture specific events. ```java 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"); } ... } ``` -------------------------------- ### Remove individual cache entries by URL prefix Source: https://github.com/lysine-dev/okhttp/blob/main/docs/features/caching.md Iterates over cached URLs and removes entries that start with a given prefix, useful for a force-refresh action. The example removes all URLs starting with "https://www.google.com/". ```java val urlIterator = cache.urls() while (urlIterator.hasNext()) { if (urlIterator.next().startsWith("https://www.google.com/")) { urlIterator.remove() } } ``` -------------------------------- ### Configure OkHttpClient with DnsOverHttps Source: https://github.com/lysine-dev/okhttp/blob/main/okhttp-dnsoverhttps/README.md Builds a DnsOverHttps instance using a bootstrap client with a cache, sets the DNS-over-HTTPS URL and bootstrap DNS hosts, then creates a new OkHttpClient that uses this DNS resolver. The bootstrap client is reused for the final client. ```kotlin val appCache = Cache(File("cacheDir", "okhttpcache"), 10 * 1024 * 1024) val bootstrapClient = OkHttpClient.Builder().cache(appCache).build() val dns = DnsOverHttps.Builder().client(bootstrapClient) .url("https://dns.google/dns-query".toHttpUrl()) .bootstrapDnsHosts(InetAddress.getByName("8.8.4.4"), InetAddress.getByName("8.8.8.8")) .build() val client = bootstrapClient.newBuilder().dns(dns).build() ``` -------------------------------- ### Run native image tests with Gradle Source: https://github.com/lysine-dev/okhttp/blob/main/native-image-tests/README.md Run OkHttp's test suite inside a GraalVM native image. The -PgraalBuild=true property enables the native build, and --info provides detailed logging. Execute from the project root. ```bash ./gradlew -PgraalBuild=true --info native-image-tests:nativeTest ``` -------------------------------- ### Asynchronous Get (Java) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Downloads a file on a worker thread and gets called back when the response is readable. The callback is made after response headers are ready; reading the response body may still block. OkHttp does not offer asynchronous APIs to receive a response body in parts. ```java private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { try (ResponseBody responseBody = response.body()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); Headers responseHeaders = response.headers(); for (int i = 0, size = responseHeaders.size(); i < size; i++) { System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i)); } System.out.println(responseBody.string()); } } }); } ``` -------------------------------- ### Run emulator from command line Source: https://github.com/lysine-dev/okhttp/blob/main/android-test/src/androidTest/README.md Launch the pixel5 emulator headless without loading a snapshot. Use this to run instrumentation tests on a device or emulator. ```bash $ emulator -no-window -no-snapshot-load @pixel5 ``` -------------------------------- ### Asynchronous Get (Kotlin) Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Downloads a file on a worker thread and gets called back when the response is readable. The callback is made after response headers are ready; reading the response body may still block. OkHttp does not offer asynchronous APIs to receive a response body in parts. ```kotlin private val client = OkHttpClient() fun run() { val request = Request.Builder() .url("http://publicobject.com/helloworld.txt") .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { e.printStackTrace() } override fun onResponse(call: Call, response: Response) { response.use { if (!response.isSuccessful) throw IOException("Unexpected code $response") for ((name, value) in response.headers) { println("$name: $value") } println(response.body!!.string()) } } }) } ``` -------------------------------- ### Update versions, tag release, and prepare next development version Source: https://github.com/lysine-dev/okhttp/blob/main/docs/releasing.md Updates version strings in the Gradle conventions file and README files, commits the release, tags it, pushes, then updates to the next snapshot version and pushes again. Requires the RELEASE_VERSION and NEXT_VERSION environment variables to be set. ```bash sed -i "" \ "s/version = \".*\"/version = \"$RELEASE_VERSION\"/g" \ build-logic/src/main/kotlin/okhttp.base-conventions.gradle.kts sed -i "" \ "s/\"com.squareup.okhttp3:\([^\:]*\):[^\"]*\"/\"com.squareup.okhttp3:\1:$RELEASE_VERSION\"/g" \ `find . -name "README.md"` sed -i "" \ "s/\/com.squareup.okhttp3\/\([^\:]*\)\/[^\/]*\//\/com.squareup.okhttp3\/\1\/$RELEASE_VERSION\//g" \ `find . -name "README.md"` git commit -am "Prepare for release $RELEASE_VERSION." git tag -a parent-$RELEASE_VERSION -m "Version $RELEASE_VERSION" git push && git push --tags sed -i "" \ "s/version = \".*\"/version = \"$NEXT_VERSION\"/g" \ build-logic/src/main/kotlin/okhttp.base-conventions.gradle.kts git commit -am "Prepare next development version." git push ``` -------------------------------- ### Configure server certificates for HTTPS Source: https://github.com/lysine-dev/okhttp/blob/main/okhttp-tls/README.md Builds a HandshakeCertificates instance for the server, holding the localhost certificate, and configures a MockWebServer to use HTTPS with the resulting SSLSocketFactory. ```java HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder() .heldCertificate(localhostCertificate) .build(); MockWebServer server = new MockWebServer(); server.useHttps(serverCertificates.sslSocketFactory(), false); ``` -------------------------------- ### Declare MockWebServerRule with @Rule in Java Source: https://github.com/lysine-dev/okhttp/blob/main/mockwebserver-junit4/README.md Declare a field with @Rule to get a MockWebServer instance that is shut down automatically after the test. ```java @Rule public final MockWebServerRule serverRule = new MockWebServerRule(); ``` -------------------------------- ### Configure OkHttpClient cache and inspect cacheResponse/networkResponse Source: https://github.com/lysine-dev/okhttp/blob/main/docs/recipes.md Configure an OkHttpClient with a Cache and make two identical requests to demonstrate that the second response is served from cache. Requires a private cache directory and a size limit; multiple caches on the same directory will corrupt the cache. Use CacheControl.FORCE_NETWORK to bypass the cache and CacheControl.FORCE_CACHE to force a cached response, which returns 504 Unsatisfiable Request if the network is needed. ```kotlin private val client: OkHttpClient = OkHttpClient.Builder() .cache(Cache( directory = cacheDirectory, maxSize = 10L * 1024L * 1024L // 10 MiB )) .build() fun run() { val request = Request.Builder() .url("http://publicobject.com/helloworld.txt") .build() val response1Body = client.newCall(request).execute().use { if (!it.isSuccessful) throw IOException("Unexpected code $it") println("Response 1 response: $it") println("Response 1 cache response: ${it.cacheResponse}") println("Response 1 network response: ${it.networkResponse}") return@use it.body!!.string() } val response2Body = client.newCall(request).execute().use { if (!it.isSuccessful) throw IOException("Unexpected code $it") println("Response 2 response: $it") println("Response 2 cache response: ${it.cacheResponse}") println("Response 2 network response: ${it.networkResponse}") return@use it.body!!.string() } println("Response 2 equals Response 1? " + (response1Body == response2Body)) } ``` ```java private final OkHttpClient client; public CacheResponse(File cacheDirectory) throws Exception { int cacheSize = 10 * 1024 * 1024; // 10 MiB Cache cache = new Cache(cacheDirectory, cacheSize); client = new OkHttpClient.Builder() .cache(cache) .build(); } public void run() throws Exception { Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build(); String response1Body; try (Response response1 = client.newCall(request).execute()) { if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1); response1Body = response1.body().string(); System.out.println("Response 1 response: " + response1); System.out.println("Response 1 cache response: " + response1.cacheResponse()); System.out.println("Response 1 network response: " + response1.networkResponse()); } String response2Body; try (Response response2 = client.newCall(request).execute()) { if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2); response2Body = response2.body().string(); System.out.println("Response 2 response: " + response2); System.out.println("Response 2 cache response: " + response2.cacheResponse()); System.out.println("Response 2 network response: " + response2.networkResponse()); } System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body)); } ``` -------------------------------- ### Output of event listener for two requests Source: https://github.com/lysine-dev/okhttp/blob/main/docs/features/events.md This is the output printed by the listener for the two requests. The first request shows a full sequence including DNS, connect, and secure connect events. The second request omits connect events because it reuses the pooled connection, demonstrating performance benefits. ```text 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 callEnd ``` -------------------------------- ### Enable OkHttp debug logging Source: https://github.com/lysine-dev/okhttp/blob/main/docs/contribute/debug_logging.md Enable OkHttp debug logging for HTTP/2 and TaskRunner features. Paste the OkHttpDebugLogging.kt file into your project first. ```kotlin OkHttpDebugLogging.enableHttp2() OkHttpDebugLogging.enableTaskRunner() ``` -------------------------------- ### Run OkCurl locally Source: https://github.com/lysine-dev/okhttp/blob/main/okcurl/README.md Run OkCurl locally. Requires GRAALVM_HOME to be set. ```bash ./okcurl ```