diff --git a/okhttp/api/okhttp.api b/okhttp/api/okhttp.api index fcc0709091a4..d0a7b2805ddd 100644 --- a/okhttp/api/okhttp.api +++ b/okhttp/api/okhttp.api @@ -1076,6 +1076,7 @@ public class okhttp3/Request$Builder { public fun patch (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder; public fun post (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder; public fun put (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder; + public fun query (Lokhttp3/RequestBody;)Lokhttp3/Request$Builder; public fun removeHeader (Ljava/lang/String;)Lokhttp3/Request$Builder; public fun tag (Ljava/lang/Class;Ljava/lang/Object;)Lokhttp3/Request$Builder; public fun tag (Ljava/lang/Object;)Lokhttp3/Request$Builder; diff --git a/okhttp/src/main/kotlin/okhttp3/Cache.kt b/okhttp/src/main/kotlin/okhttp3/Cache.kt index 69d79cd9394f..31d534eb26b8 100644 --- a/okhttp/src/main/kotlin/okhttp3/Cache.kt +++ b/okhttp/src/main/kotlin/okhttp3/Cache.kt @@ -228,11 +228,12 @@ class Cache internal constructor( return null } - if (requestMethod != "GET") { - // Don't cache non-GET responses. We're technically allowed to cache HEAD requests and some - // POST requests, but the complexity of doing so is high and the benefit is low. - return null - } + if (requestMethod != "GET" && requestMethod != "QUERY") { + // Don't cache non-GET and non-QUERY responses. We're technically allowed to cache HEAD + // requests and some POST requests, but the complexity of doing so is high and the benefit + // is low. + return null + } if (response.hasVaryAll()) { return null @@ -739,7 +740,7 @@ class Cache internal constructor( private const val ENTRY_BODY = 1 private const val ENTRY_COUNT = 2 - @JvmStatic + @JvmStatic fun key(url: HttpUrl): String = url.toString().encodeUtf8().md5().hex() @Throws(IOException::class) diff --git a/okhttp/src/main/kotlin/okhttp3/Request.kt b/okhttp/src/main/kotlin/okhttp3/Request.kt index b781bcb2a6ff..34a0a42ef4b6 100644 --- a/okhttp/src/main/kotlin/okhttp3/Request.kt +++ b/okhttp/src/main/kotlin/okhttp3/Request.kt @@ -33,6 +33,7 @@ import okhttp3.internal.commonMethod import okhttp3.internal.commonPatch import okhttp3.internal.commonPost import okhttp3.internal.commonPut +import okhttp3.internal.commonQuery import okhttp3.internal.commonRemoveHeader import okhttp3.internal.commonTag import okhttp3.internal.commonToString @@ -276,6 +277,8 @@ class Request internal constructor(builder: Builder) { open fun patch(body: RequestBody): Builder = commonPatch(body) + open fun query(body: RequestBody): Builder = commonQuery(body) + open fun method( method: String, body: RequestBody?, diff --git a/okhttp/src/main/kotlin/okhttp3/internal/-RequestCommon.kt b/okhttp/src/main/kotlin/okhttp3/internal/-RequestCommon.kt index 0cbe9a09fe36..a774dfb6f178 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/-RequestCommon.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/-RequestCommon.kt @@ -96,6 +96,8 @@ fun Request.Builder.commonPut(body: RequestBody): Request.Builder = method("PUT" fun Request.Builder.commonPatch(body: RequestBody): Request.Builder = method("PATCH", body) +fun Request.Builder.commonQuery(body: RequestBody): Request.Builder = method("QUERY", body) + fun Request.Builder.commonMethod( method: String, body: RequestBody?, diff --git a/okhttp/src/test/java/okhttp3/CacheTest.kt b/okhttp/src/test/java/okhttp3/CacheTest.kt index 1400aadaeb68..4fce896c0b52 100644 --- a/okhttp/src/test/java/okhttp3/CacheTest.kt +++ b/okhttp/src/test/java/okhttp3/CacheTest.kt @@ -48,6 +48,7 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.internal.addHeaderLenient import okhttp3.internal.cacheGet +import okhttp3.internal.commonEmptyRequestBody import okhttp3.internal.platform.Platform.Companion.get import okhttp3.java.net.cookiejar.JavaNetCookieJar import okhttp3.testing.PlatformRule @@ -172,6 +173,7 @@ class CacheTest { private fun assertCached( shouldWriteToCache: Boolean, responseCode: Int, + method: String = "GET", ) { var expectedResponseCode = responseCode server = MockWebServer() @@ -212,6 +214,7 @@ class CacheTest { val request = Request.Builder() .url(server.url("/")) + .method(method, null) .build() val response = client.newCall(request).execute() assertThat(response.code).isEqualTo(expectedResponseCode) @@ -475,6 +478,63 @@ class CacheTest { assertThat(recordedRequest3.sequenceNumber).isEqualTo(2) } + @Test + fun getAndQueryRedirectToCachedResultIndependently() { + //GET responses + server.enqueue( + MockResponse.Builder() + .addHeader("Cache-Control: max-age=60") + .body("ABC") + .build(), + ) + server.enqueue( + MockResponse.Builder() + .code(HttpURLConnection.HTTP_MOVED_PERM) + .addHeader("Location: /foo") + .build(), + ) + //QUERY responses + server.enqueue( + MockResponse.Builder() + .addHeader("Cache-Control: max-age=60") + .body("DEF") + .build(), + ) + server.enqueue( + MockResponse.Builder() + .code(HttpURLConnection.HTTP_MOVED_PERM) + .addHeader("Location: /baz") + .build(), + ) + + val request1 = Request.Builder().url(server.url("/foo")).get().build() + val response1 = client.newCall(request1).execute() + assertThat(response1.body.string()).isEqualTo("ABC") + val recordedRequest1 = server.takeRequest() + assertThat(recordedRequest1.requestLine).isEqualTo("GET /foo HTTP/1.1") + assertThat(recordedRequest1.sequenceNumber).isEqualTo(0) + val request2 = Request.Builder().url(server.url("/bar")).get().build() + val response2 = client.newCall(request2).execute() + assertThat(response2.body.string()).isEqualTo("ABC") + val recordedRequest2 = server.takeRequest() + assertThat(recordedRequest2.requestLine).isEqualTo("GET /bar HTTP/1.1") + assertThat(recordedRequest2.sequenceNumber).isEqualTo(1) + + val request3 = Request.Builder().url(server.url("/baz")).query(commonEmptyRequestBody).build() + val response3 = client.newCall(request3).execute() + assertThat(response3.body.string()).isEqualTo("DEF") + val recordedRequest3 = server.takeRequest() + assertThat(recordedRequest3.requestLine).isEqualTo("QUERY /baz HTTP/1.1") + assertThat(recordedRequest3.sequenceNumber).isEqualTo(2) + val request4 = Request.Builder().url(server.url("/bar")).query(commonEmptyRequestBody).build() + val response4 = client.newCall(request4).execute() + assertThat(response4.body.string()).isEqualTo("DEF") + val recordedRequest4 = server.takeRequest() + assertThat(recordedRequest4.requestLine).isEqualTo("QUERY /bar HTTP/1.1") + assertThat(recordedRequest4.sequenceNumber).isEqualTo(3) + + } + @Test fun secureResponseCachingAndRedirects() { server.useHttps(handshakeCertificates.sslSocketFactory()) diff --git a/okhttp/src/test/java/okhttp3/RequestTest.kt b/okhttp/src/test/java/okhttp3/RequestTest.kt index f5c3a2a30784..c3151a1eaf2b 100644 --- a/okhttp/src/test/java/okhttp3/RequestTest.kt +++ b/okhttp/src/test/java/okhttp3/RequestTest.kt @@ -210,6 +210,10 @@ class RequestTest { val patch = Request.Builder().url("http://localhost/api").patch(body).build() assertThat(patch.method).isEqualTo("PATCH") assertThat(patch.body).isEqualTo(body) + + val query = Request.Builder().url("http://localhost/api").query(body).build() + assertThat(query.method).isEqualTo("QUERY") + assertThat(query.body).isEqualTo(body) } @Test