From d711677b0efe6e73858aae2ecc59ae99a69eac05 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Tue, 5 Nov 2024 06:50:55 +0000 Subject: [PATCH 1/2] Fix: Handle multiple 1xx responses (#8569) This change updates the client to handle multiple 1xx (Processing etc) responses from the server. The client now waits for the final response with a status code other than 1xx before proceeding. From https://datatracker.ietf.org/doc/html/rfc7231#section-6.2 > A client MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one. A user agent MAY ignore unexpected 1xx responses. fixes #8568 (cherry picked from commit f1e6d01aea30844f0ddc7394208ec13d59d5e6f8) --- .../main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt b/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt index 32c21282da01..7efe8111c208 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt @@ -104,7 +104,7 @@ class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor { .build() var code = response.code - if (shouldIgnoreAndWaitForRealResponse(code)) { + while (shouldIgnoreAndWaitForRealResponse(code)) { responseBuilder = exchange.readResponseHeaders(expectContinue = false)!! if (invokeStartEvent) { exchange.responseHeadersStart() From e8be0aab1321dd0109e5289121df04121668bf47 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Mon, 5 May 2025 22:14:24 +0100 Subject: [PATCH 2/2] Add Test --- okhttp/src/test/java/okhttp3/CallTest.java | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/okhttp/src/test/java/okhttp3/CallTest.java b/okhttp/src/test/java/okhttp3/CallTest.java index 4c01db100d9e..a712a97d57a0 100644 --- a/okhttp/src/test/java/okhttp3/CallTest.java +++ b/okhttp/src/test/java/okhttp3/CallTest.java @@ -2795,6 +2795,51 @@ public void cancelWhileRequestHeadersAreSent_HTTP_2() throws Exception { expect100ContinueTimesOutWithoutContinue(); } + @Test public void serverRespondsWithProcessingMultiple() throws Exception { + MockResponse response = new MockResponse(); + for (int i = 0; i < 10; i++) { + response.addInformationalResponse(new MockResponse().setResponseCode(102)); + } + server.enqueue(response); + + Request request = new Request.Builder() + .url(server.url("/")) + .post(RequestBody.create("abc", MediaType.get("text/plain"))) + .build(); + + executeSynchronously(request) + .assertCode(200) + .assertSuccessful(); + + RecordedRequest recordedRequest = server.takeRequest(); + assertThat(recordedRequest.getBody().readUtf8()).isEqualTo("abc"); + } + + @Test public void serverReturnsMultiple100ContinuesHttp2() throws Exception { + enableProtocol(Protocol.HTTP_2); + serverReturnsMultiple100Continues(); + } + + @Test public void serverReturnsMultiple100Continues() throws Exception { + MockResponse response = new MockResponse(); + for (int i = 0; i < 3; i++) { + response.addInformationalResponse(new MockResponse().setResponseCode(100)); + } + server.enqueue(response); + + Request request = new Request.Builder() + .url(server.url("/")) + .post(RequestBody.create("abc", MediaType.get("text/plain"))) + .build(); + + executeSynchronously(request) + .assertCode(200) + .assertSuccessful(); + + RecordedRequest recordedRequest = server.takeRequest(); + assertThat(recordedRequest.getBody().readUtf8()).isEqualTo("abc"); + } + @Test public void serverRespondsWithUnsolicited100Continue() throws Exception { server.enqueue(new MockResponse() .setSocketPolicy(SocketPolicy.CONTINUE_ALWAYS));