From c5394822587610ba67e534713c21119147b7cd74 Mon Sep 17 00:00:00 2001 From: Andre_601 <11576465+Andre601@users.noreply.github.com> Date: Tue, 15 Sep 2020 18:28:35 +0200 Subject: [PATCH] Update and improve recipes page in docs (#6270) * Add PymdownX tabbed * Update recipes.md * fix invalid tab names * Fix additional tabs and publish * remove site directory --- docs/caching.md | 2 +- docs/https.md | 248 ++--- docs/recipes.md | 1617 +++++++++++++++++---------------- docs/upgrading_to_okhttp_4.md | 2 +- mkdocs.yml | 1 + 5 files changed, 944 insertions(+), 926 deletions(-) diff --git a/docs/caching.md b/docs/caching.md index 9cc7e9c8d..b6787fc14 100644 --- a/docs/caching.md +++ b/docs/caching.md @@ -7,7 +7,7 @@ server behaviour when ambiguous. # Basic Usage -```Kotlin tab= +```kotlin private val client: OkHttpClient = OkHttpClient.Builder() .cache(Cache( directory = File(application.cacheDir, "http_cache"), diff --git a/docs/https.md b/docs/https.md index ff7b1b983..df1f2bc9c 100644 --- a/docs/https.md +++ b/docs/https.md @@ -70,137 +70,139 @@ By default, OkHttp trusts the certificate authorities of the host platform. This Use [CertificatePinner](http://square.github.io/okhttp/4.x/okhttp/okhttp3/-certificate-pinner/) 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!** -```Kotlin tab= - 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)) +=== "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)) + } + } } - } - } -``` - -```Java tab= - 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)); + ``` +=== "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!** -```Kotlin tab= - 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") +=== "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() } - - 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. - } -``` - -```Java tab= - 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. - } -``` + + 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. + } + ``` +=== "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. + } + ``` [CustomTrustJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java [CustomTrustKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/CustomTrust.kt diff --git a/docs/recipes.md b/docs/recipes.md index a58f4749f..108deb76f 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -9,107 +9,109 @@ Download a file, print its headers, and print its response body as a string. The `string()` method on response body is convenient and efficient for small documents. But if the response body is large (greater than 1 MiB), avoid `string()` because it will load the entire document into memory. In that case, prefer to process the body as a stream. -```Kotlin tab= - 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") +=== "Kotlin" + ```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()) + } } - - println(response.body!!.string()) - } - } -``` - -```Java tab= - 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)); + ``` +=== "Java" + ```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()); + } } - - System.out.println(response.body().string()); - } - } -``` + ``` ### Asynchronous Get ([.kt][AsynchronousGetKotlin], [.java][AsynchronousGetJava]) Download a file on a worker thread, and get called back when the response is readable. The callback is made after the response headers are ready. Reading the response body may still block. OkHttp doesn't currently offer asynchronous APIs to receive a response body in parts. -```Kotlin tab= - 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") +=== "Kotlin" + ```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() } - - println(response.body!!.string()) - } - } - }) - } -``` - -```Java tab= - 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)); + + 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()) + } } - - System.out.println(responseBody.string()); - } + }) } - }); - } -``` + ``` +=== "Java" + ```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()); + } + } + }); + } + ``` ### Accessing Headers ([.kt][AccessHeadersKotlin], [.java][AccessHeadersJava]) @@ -121,361 +123,368 @@ When reading response a header, use `header(name)` to return the _last_ occurren To visit all headers, use the `Headers` class which supports access by index. -```Kotlin tab= - private val client = OkHttpClient() - - fun run() { - val request = Request.Builder() - .url("https://api.github.com/repos/square/okhttp/issues") - .header("User-Agent", "OkHttp Headers.java") - .addHeader("Accept", "application/json; q=0.5") - .addHeader("Accept", "application/vnd.github.v3+json") - .build() - - client.newCall(request).execute().use { response -> - if (!response.isSuccessful) throw IOException("Unexpected code $response") - - println("Server: ${response.header("Server")}") - println("Date: ${response.header("Date")}") - println("Vary: ${response.headers("Vary")}") - } - } -``` - -```Java tab= - private final OkHttpClient client = new OkHttpClient(); - - public void run() throws Exception { - Request request = new Request.Builder() - .url("https://api.github.com/repos/square/okhttp/issues") - .header("User-Agent", "OkHttp Headers.java") - .addHeader("Accept", "application/json; q=0.5") - .addHeader("Accept", "application/vnd.github.v3+json") - .build(); - - try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); - - System.out.println("Server: " + response.header("Server")); - System.out.println("Date: " + response.header("Date")); - System.out.println("Vary: " + response.headers("Vary")); - } - } -``` +=== "Kotlin" + ```kotlin + private val client = OkHttpClient() + + fun run() { + val request = Request.Builder() + .url("https://api.github.com/repos/square/okhttp/issues") + .header("User-Agent", "OkHttp Headers.java") + .addHeader("Accept", "application/json; q=0.5") + .addHeader("Accept", "application/vnd.github.v3+json") + .build() + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) throw IOException("Unexpected code $response") + + println("Server: ${response.header("Server")}") + println("Date: ${response.header("Date")}") + println("Vary: ${response.headers("Vary")}") + } + } + ``` + +=== "Java" + ```java + private final OkHttpClient client = new OkHttpClient(); + + public void run() throws Exception { + Request request = new Request.Builder() + .url("https://api.github.com/repos/square/okhttp/issues") + .header("User-Agent", "OkHttp Headers.java") + .addHeader("Accept", "application/json; q=0.5") + .addHeader("Accept", "application/vnd.github.v3+json") + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + System.out.println("Server: " + response.header("Server")); + System.out.println("Date: " + response.header("Date")); + System.out.println("Vary: " + response.headers("Vary")); + } + } + ``` ### Posting a String ([.kt][PostStringKotlin], [.java][PostStringJava]) Use an HTTP POST to send a request body to a service. This example posts a markdown document to a web service that renders markdown as HTML. Because the entire request body is in memory simultaneously, avoid posting large (greater than 1 MiB) documents using this API. -```Kotlin tab= - private val client = OkHttpClient() - - fun run() { - val postBody = """ - |Releases - |-------- - | - | * _1.0_ May 6, 2013 - | * _1.1_ June 15, 2013 - | * _1.2_ August 11, 2013 - |""".trimMargin() - - val request = Request.Builder() - .url("https://api.github.com/markdown/raw") - .post(postBody.toRequestBody(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 tab= - 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 { - String postBody = "" - + "Releases\n" - + "--------\n" - + "\n" - + " * _1.0_ May 6, 2013\n" - + " * _1.1_ June 15, 2013\n" - + " * _1.2_ August 11, 2013\n"; - - Request request = new Request.Builder() - .url("https://api.github.com/markdown/raw") - .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody)) - .build(); - - try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); - - System.out.println(response.body().string()); - } - } -``` +=== "Kotlin" + ```kotlin + private val client = OkHttpClient() + + fun run() { + val postBody = """ + |Releases + |-------- + | + | * _1.0_ May 6, 2013 + | * _1.1_ June 15, 2013 + | * _1.2_ August 11, 2013 + |""".trimMargin() + + val request = Request.Builder() + .url("https://api.github.com/markdown/raw") + .post(postBody.toRequestBody(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" + ```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 { + String postBody = "" + + "Releases\n" + + "--------\n" + + "\n" + + " * _1.0_ May 6, 2013\n" + + " * _1.1_ June 15, 2013\n" + + " * _1.2_ August 11, 2013\n"; + + Request request = new Request.Builder() + .url("https://api.github.com/markdown/raw") + .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody)) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + System.out.println(response.body().string()); + } + } + ``` ### Post Streaming ([.kt][PostStreamingKotlin], [.java][PostStreamingJava]) Here we `POST` a request body as a stream. The content of this request body is being generated as it's being written. This example streams directly into the [Okio](https://github.com/square/okio) buffered sink. Your programs may prefer an `OutputStream`, which you can get from `BufferedSink.outputStream()`. -```Kotlin tab= - 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")) +=== "Kotlin" + ```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()) } } - - 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() + + companion object { + val MEDIA_TYPE_MARKDOWN = "text/x-markdown; charset=utf-8".toMediaType() } - } - - 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 tab= - 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))); + ``` +=== "Java" + ```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()); } } - - 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()); - } - } -``` + ``` ### Posting a File ([.kt][PostFileKotlin], [.java][PostFileJava]) It's easy to use a file as a request body. -```Kotlin tab= - 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 tab= - 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()); - } - } -``` +=== "Kotlin" + ```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" + ```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()); + } + } + ``` ### Posting form parameters ([.kt][PostFormKotlin], [.java][PostFormJava]) Use `FormBody.Builder` to build a request body that works like an HTML `
` tag. Names and values will be encoded using an HTML-compatible form URL encoding. -```Kotlin tab= - private val client = OkHttpClient() - - fun run() { - val formBody = FormBody.Builder() - .add("search", "Jurassic Park") - .build() - val request = Request.Builder() - .url("https://en.wikipedia.org/w/index.php") - .post(formBody) - .build() - - client.newCall(request).execute().use { response -> - if (!response.isSuccessful) throw IOException("Unexpected code $response") - - println(response.body!!.string()) - } - } -``` - -```Java tab= - private final OkHttpClient client = new OkHttpClient(); - - public void run() throws Exception { - RequestBody formBody = new FormBody.Builder() - .add("search", "Jurassic Park") - .build(); - Request request = new Request.Builder() - .url("https://en.wikipedia.org/w/index.php") - .post(formBody) - .build(); - - try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); - - System.out.println(response.body().string()); - } - } -``` +=== "Kotlin" + ```kotlin + private val client = OkHttpClient() + + fun run() { + val formBody = FormBody.Builder() + .add("search", "Jurassic Park") + .build() + val request = Request.Builder() + .url("https://en.wikipedia.org/w/index.php") + .post(formBody) + .build() + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) throw IOException("Unexpected code $response") + + println(response.body!!.string()) + } + } + ``` +=== "Java" + ```java + private final OkHttpClient client = new OkHttpClient(); + + public void run() throws Exception { + RequestBody formBody = new FormBody.Builder() + .add("search", "Jurassic Park") + .build(); + Request request = new Request.Builder() + .url("https://en.wikipedia.org/w/index.php") + .post(formBody) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + System.out.println(response.body().string()); + } + } + ``` ### Posting a multipart request ([.kt][PostMultipartKotlin], [.java][PostMultipartJava]) `MultipartBody.Builder` can build sophisticated request bodies compatible with HTML file upload forms. Each part of a multipart request body is itself a request body, and can define its own headers. If present, these headers should describe the part body, such as its `Content-Disposition`. The `Content-Length` and `Content-Type` headers are added automatically if they're available. -```Kotlin tab= - private val client = OkHttpClient() - - fun run() { - // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image - val requestBody = MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("title", "Square Logo") - .addFormDataPart("image", "logo-square.png", - File("docs/images/logo-square.png").asRequestBody(MEDIA_TYPE_PNG)) - .build() - - val request = Request.Builder() - .header("Authorization", "Client-ID $IMGUR_CLIENT_ID") - .url("https://api.imgur.com/3/image") - .post(requestBody) - .build() - - client.newCall(request).execute().use { response -> - if (!response.isSuccessful) throw IOException("Unexpected code $response") - - println(response.body!!.string()) - } - } - - companion object { - /** - * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running - * these examples, please request your own client ID! https://api.imgur.com/oauth2 - */ - private val IMGUR_CLIENT_ID = "9199fdef135c122" - private val MEDIA_TYPE_PNG = "image/png".toMediaType() - } -``` - -```Java tab= - /** - * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running - * these examples, please request your own client ID! https://api.imgur.com/oauth2 - */ - private static final String IMGUR_CLIENT_ID = "..."; - private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); - - private final OkHttpClient client = new OkHttpClient(); - - public void run() throws Exception { - // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image - RequestBody requestBody = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("title", "Square Logo") - .addFormDataPart("image", "logo-square.png", - RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png"))) - .build(); - - Request request = new Request.Builder() - .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID) - .url("https://api.imgur.com/3/image") - .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()); - } - } -``` +=== "Kotlin" + ```kotlin + private val client = OkHttpClient() + + fun run() { + // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image + val requestBody = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("title", "Square Logo") + .addFormDataPart("image", "logo-square.png", + File("docs/images/logo-square.png").asRequestBody(MEDIA_TYPE_PNG)) + .build() + + val request = Request.Builder() + .header("Authorization", "Client-ID $IMGUR_CLIENT_ID") + .url("https://api.imgur.com/3/image") + .post(requestBody) + .build() + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) throw IOException("Unexpected code $response") + + println(response.body!!.string()) + } + } + + companion object { + /** + * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running + * these examples, please request your own client ID! https://api.imgur.com/oauth2 + */ + private val IMGUR_CLIENT_ID = "9199fdef135c122" + private val MEDIA_TYPE_PNG = "image/png".toMediaType() + } + ``` +=== "Java" + ```java + /** + * The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running + * these examples, please request your own client ID! https://api.imgur.com/oauth2 + */ + private static final String IMGUR_CLIENT_ID = "..."; + private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); + + private final OkHttpClient client = new OkHttpClient(); + + public void run() throws Exception { + // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image + RequestBody requestBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("title", "Square Logo") + .addFormDataPart("image", "logo-square.png", + RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png"))) + .build(); + + Request request = new Request.Builder() + .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID) + .url("https://api.imgur.com/3/image") + .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()); + } + } + ``` ### Parse a JSON Response With Moshi ([.kt][ParseResponseWithMoshiKotlin], [.java][ParseResponseWithMoshiJava]) @@ -483,63 +492,64 @@ Use `FormBody.Builder` to build a request body that works like an HTML `` Note that `ResponseBody.charStream()` uses the `Content-Type` response header to select which charset to use when decoding the response body. It defaults to `UTF-8` if no charset is specified. -```Kotlin tab= - private val client = OkHttpClient() - private val moshi = Moshi.Builder().build() - private val gistJsonAdapter = moshi.adapter(Gist::class.java) - - fun run() { - val request = Request.Builder() - .url("https://api.github.com/gists/c2a7c39532239ff261be") - .build() - client.newCall(request).execute().use { response -> - if (!response.isSuccessful) throw IOException("Unexpected code $response") - - val gist = gistJsonAdapter.fromJson(response.body!!.source()) - - for ((key, value) in gist!!.files!!) { - println(key) - println(value.content) +=== "Kotlin" + ```kotlin + private val client = OkHttpClient() + private val moshi = Moshi.Builder().build() + private val gistJsonAdapter = moshi.adapter(Gist::class.java) + + fun run() { + val request = Request.Builder() + .url("https://api.github.com/gists/c2a7c39532239ff261be") + .build() + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) throw IOException("Unexpected code $response") + + val gist = gistJsonAdapter.fromJson(response.body!!.source()) + + for ((key, value) in gist!!.files!!) { + println(key) + println(value.content) + } + } } - } - } - - @JsonClass(generateAdapter = true) - data class Gist(var files: Map?) - - @JsonClass(generateAdapter = true) - data class GistFile(var content: String?) -``` - -```Java tab= - private final OkHttpClient client = new OkHttpClient(); - private final Moshi moshi = new Moshi.Builder().build(); - private final JsonAdapter gistJsonAdapter = moshi.adapter(Gist.class); - - public void run() throws Exception { - Request request = new Request.Builder() - .url("https://api.github.com/gists/c2a7c39532239ff261be") - .build(); - try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); - - Gist gist = gistJsonAdapter.fromJson(response.body().source()); - - for (Map.Entry entry : gist.files.entrySet()) { - System.out.println(entry.getKey()); - System.out.println(entry.getValue().content); + + @JsonClass(generateAdapter = true) + data class Gist(var files: Map?) + + @JsonClass(generateAdapter = true) + data class GistFile(var content: String?) + ``` +=== "Java" + ```java + private final OkHttpClient client = new OkHttpClient(); + private final Moshi moshi = new Moshi.Builder().build(); + private final JsonAdapter gistJsonAdapter = moshi.adapter(Gist.class); + + public void run() throws Exception { + Request request = new Request.Builder() + .url("https://api.github.com/gists/c2a7c39532239ff261be") + .build(); + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + + Gist gist = gistJsonAdapter.fromJson(response.body().source()); + + for (Map.Entry entry : gist.files.entrySet()) { + System.out.println(entry.getKey()); + System.out.println(entry.getValue().content); + } + } } - } - } - - static class Gist { - Map files; - } - - static class GistFile { - String content; - } -``` + + static class Gist { + Map files; + } + + static class GistFile { + String content; + } + ``` ### Response Caching ([.kt][CacheResponseKotlin], [.java][CacheResponseJava]) @@ -549,81 +559,82 @@ It is an error to have multiple caches accessing the same cache directory simult Response caching uses HTTP headers for all configuration. You can add request headers like `Cache-Control: max-stale=3600` and OkHttp's cache will honor them. Your webserver configures how long responses are cached with its own response headers, like `Cache-Control: max-age=9600`. There are cache headers to force a cached response, force a network response, or force the network response to be validated with a conditional GET. -```Kotlin tab= - 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 tab= - 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)); - } -``` +=== "Kotlin" + ```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" + ```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)); + } + ``` To prevent a response from using the cache, use [`CacheControl.FORCE_NETWORK`](http://square.github.io/okhttp/4.x/okhttp/okhttp3/-cache-control/-f-o-r-c-e_-n-e-t-w-o-r-k/). To prevent it from using the network, use [`CacheControl.FORCE_CACHE`](http://square.github.io/okhttp/4.x/okhttp/okhttp3/-cache-control/-f-o-r-c-e_-c-a-c-h-e/). Be warned: if you use `FORCE_CACHE` and the response requires the network, OkHttp will return a `504 Unsatisfiable Request` response. @@ -631,182 +642,185 @@ To prevent a response from using the cache, use [`CacheControl.FORCE_NETWORK`](h Use `Call.cancel()` to stop an ongoing call immediately. If a thread is currently writing a request or reading a response, it will receive an `IOException`. Use this to conserve the network when a call is no longer necessary; for example when your user navigates away from an application. Both synchronous and asynchronous calls can be canceled. -```Kotlin tab= - private val executor = Executors.newScheduledThreadPool(1) - private val client = OkHttpClient() - - fun run() { - val request = Request.Builder() - .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay. - .build() - - val startNanos = System.nanoTime() - val call = client.newCall(request) - - // Schedule a job to cancel the call in 1 second. - executor.schedule({ - System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f) - call.cancel() - System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f) - }, 1, TimeUnit.SECONDS) - - System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f) - try { - call.execute().use { response -> - System.out.printf("%.2f Call was expected to fail, but completed: %s%n", - (System.nanoTime() - startNanos) / 1e9f, response) +=== "Kotlin" + ```kotlin + private val executor = Executors.newScheduledThreadPool(1) + private val client = OkHttpClient() + + fun run() { + val request = Request.Builder() + .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay. + .build() + + val startNanos = System.nanoTime() + val call = client.newCall(request) + + // Schedule a job to cancel the call in 1 second. + executor.schedule({ + System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f) + call.cancel() + System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f) + }, 1, TimeUnit.SECONDS) + + System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f) + try { + call.execute().use { response -> + System.out.printf("%.2f Call was expected to fail, but completed: %s%n", + (System.nanoTime() - startNanos) / 1e9f, response) + } + } catch (e: IOException) { + System.out.printf("%.2f Call failed as expected: %s%n", + (System.nanoTime() - startNanos) / 1e9f, e) + } } - } catch (e: IOException) { - System.out.printf("%.2f Call failed as expected: %s%n", - (System.nanoTime() - startNanos) / 1e9f, e) - } - } -``` - -```Java tab= - private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); - private final OkHttpClient client = new OkHttpClient(); - - 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(); - - final long startNanos = System.nanoTime(); - final Call call = client.newCall(request); - - // Schedule a job to cancel the call in 1 second. - executor.schedule(new Runnable() { - @Override public void run() { - System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f); - call.cancel(); - System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f); + ``` +=== "Java" + ```java + private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + private final OkHttpClient client = new OkHttpClient(); + + 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(); + + final long startNanos = System.nanoTime(); + final Call call = client.newCall(request); + + // Schedule a job to cancel the call in 1 second. + executor.schedule(new Runnable() { + @Override public void run() { + System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f); + call.cancel(); + System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f); + } + }, 1, TimeUnit.SECONDS); + + System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f); + try (Response response = call.execute()) { + System.out.printf("%.2f Call was expected to fail, but completed: %s%n", + (System.nanoTime() - startNanos) / 1e9f, response); + } catch (IOException e) { + System.out.printf("%.2f Call failed as expected: %s%n", + (System.nanoTime() - startNanos) / 1e9f, e); + } } - }, 1, TimeUnit.SECONDS); - - System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f); - try (Response response = call.execute()) { - System.out.printf("%.2f Call was expected to fail, but completed: %s%n", - (System.nanoTime() - startNanos) / 1e9f, response); - } catch (IOException e) { - System.out.printf("%.2f Call failed as expected: %s%n", - (System.nanoTime() - startNanos) / 1e9f, e); - } - } -``` + ``` ### Timeouts ([.kt][ConfigureTimeoutsKotlin], [.java][ConfigureTimeoutsJava]) Use timeouts to fail a call when its peer is unreachable. Network partitions can be due to client connectivity problems, server availability problems, or anything between. OkHttp supports connect, write, read, and full call timeouts. -```Kotlin tab= - 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") - } - } -``` - -```Java tab= - 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); - } - } -``` +=== "Kotlin" + ```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") + } + } + ``` +=== "Java" + ```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 ([.kt][PerCallSettingsKotlin], [.java][PerCallSettingsJava]) All the HTTP client configuration lives in `OkHttpClient` including proxy settings, timeouts, and caches. When you need to change the configuration of a single call, call `OkHttpClient.newBuilder()`. This returns a builder that shares the same connection pool, dispatcher, and configuration with the original client. In the example below, we make one request with a 500 ms timeout and another with a 3000 ms timeout. -```Kotlin tab= - 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") +=== "Kotlin" + ```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") + } } - } 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") + ``` +=== "Java" + ```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); + } } - } catch (e: IOException) { - println("Response 2 failed: $e") - } - } -``` - -```Java tab= - 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); - } - } -``` + ``` ### Handling authentication ([.kt][AuthenticateKotlin], [.java][AuthenticateJava]) @@ -814,100 +828,101 @@ OkHttp can automatically retry unauthenticated requests. When a response is `401 Use `Response.challenges()` to get the schemes and realms of any authentication challenges. When fulfilling a `Basic` challenge, use `Credentials.basic(username, password)` to encode the request header. -```Kotlin tab= - 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() - - client.newCall(request).execute().use { response -> - if (!response.isSuccessful) throw IOException("Unexpected code $response") - - println(response.body!!.string()) - } - } -``` - -```Java tab= - 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. +=== "Kotlin" + ```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() } - - 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()); - } - } -``` - -To avoid making many retries when authentication isn't working, you can return null to give up. For example, you may want to skip the retry when these exact credentials have already been attempted: - -```java - if (credential.equals(response.request().header("Authorization"))) { - return null; // If we already failed with these credentials, don't retry. - } -``` - -You may also skip the retry when you’ve hit an application-defined attempt limit: - -```java - if (responseCount(response) >= 3) { - return null; // If we've failed 3 times, give up. - } -``` - -This above code relies on this `responseCount()` method: - -```java - private int responseCount(Response response) { - int result = 1; - while ((response = response.priorResponse()) != null) { - result++; - } - return result; - } -``` + }) + .build() + + fun run() { + val request = Request.Builder() + .url("http://publicobject.com/secrets/hellosecret.txt") + .build() + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) throw IOException("Unexpected code $response") + + println(response.body!!.string()) + } + } + ``` +=== "Java" + ```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()); + } + } + ``` + + To avoid making many retries when authentication isn't working, you can return null to give up. For example, you may want to skip the retry when these exact credentials have already been attempted: + + ```java + if (credential.equals(response.request().header("Authorization"))) { + return null; // If we already failed with these credentials, don't retry. + } + ``` + + You may also skip the retry when you’ve hit an application-defined attempt limit: + + ```java + if (responseCount(response) >= 3) { + return null; // If we've failed 3 times, give up. + } + ``` + + This above code relies on this `responseCount()` method: + + ```java + private int responseCount(Response response) { + int result = 1; + while ((response = response.priorResponse()) != null) { + result++; + } + return result; + } + ``` [SynchronousGetJava]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/SynchronousGet.java [SynchronousGetKotlin]: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/kt/SynchronousGet.kt diff --git a/docs/upgrading_to_okhttp_4.md b/docs/upgrading_to_okhttp_4.md index 0d3c53917..ad17bc3fb 100644 --- a/docs/upgrading_to_okhttp_4.md +++ b/docs/upgrading_to_okhttp_4.md @@ -242,7 +242,7 @@ OkHttp 4’s minimum requirements are Java 8+ and Android 5+. These requirements Here’s what you need in `build.gradle` to target Java 8 byte code for Kotlin, Java, and Android plugins respectively. -``` +```groovy compileKotlin { kotlinOptions { jvmTarget = "1.8" diff --git a/mkdocs.yml b/mkdocs.yml index 9c7705c2f..2da82aea1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -35,6 +35,7 @@ markdown_extensions: - pymdownx.smartsymbols - pymdownx.superfences - pymdownx.tilde + - pymdownx.tabbed - tables nav: