From 01170c151958ebb93cc54139648330686748d8f4 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Sat, 15 Jul 2023 12:04:09 +0100 Subject: [PATCH 1/3] Don't hang on 103 --- .../internal/http/CallServerInterceptor.kt | 20 ++++++++++--------- .../okhttp3/internal/http2/Http2Stream.kt | 7 ++++++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt b/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt index 32c21282da01..60161e921c85 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt @@ -148,16 +148,18 @@ class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor { } } - private fun shouldIgnoreAndWaitForRealResponse(code: Int): Boolean = when { - // Server sent a 100-continue even though we did not request one. Try again to read the - // actual response status. - code == 100 -> true + companion object { + fun shouldIgnoreAndWaitForRealResponse(code: Int): Boolean = when { + // Server sent a 100-continue even though we did not request one. Try again to read the + // actual response status. + code == 100 -> true - // Handle Processing (102) & Early Hints (103) and any new codes without failing - // 100 and 101 are the exceptions with different meanings - // But Early Hints not currently exposed - code in (102 until 200) -> true + // Handle Processing (102) & Early Hints (103) and any new codes without failing + // 100 and 101 are the exceptions with different meanings + // But Early Hints not currently exposed + code in (102 until 200) -> true - else -> false + else -> false + } } } diff --git a/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt b/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt index e8a6d7499d10..1ec8f4ee7c0b 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt @@ -18,6 +18,8 @@ package okhttp3.internal.http2 import okhttp3.Headers import okhttp3.internal.EMPTY_HEADERS import okhttp3.internal.assertThreadDoesntHoldLock +import okhttp3.internal.http.CallServerInterceptor +import okhttp3.internal.http.CallServerInterceptor.Companion.shouldIgnoreAndWaitForRealResponse import okhttp3.internal.notifyAll import okhttp3.internal.toHeaderList import okhttp3.internal.wait @@ -283,7 +285,10 @@ class Http2Stream internal constructor( val open: Boolean synchronized(this) { if (!hasResponseHeaders || !inFinished) { - hasResponseHeaders = true + val status = headers[Header.RESPONSE_STATUS_UTF8]?.toIntOrNull() + if (status == null || !shouldIgnoreAndWaitForRealResponse(status)) { + hasResponseHeaders = true + } headersQueue += headers } else { this.source.trailers = headers From aa6f1d75b3a62e58e71241f6eef78e732c1ddcd9 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Sat, 15 Jul 2023 12:24:08 +0100 Subject: [PATCH 2/3] Manual test --- .../java/okhttp3/InformationalResponseCodeTest.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/okhttp/src/test/java/okhttp3/InformationalResponseCodeTest.kt b/okhttp/src/test/java/okhttp3/InformationalResponseCodeTest.kt index 46bcd26d86d1..f0a55007591f 100644 --- a/okhttp/src/test/java/okhttp3/InformationalResponseCodeTest.kt +++ b/okhttp/src/test/java/okhttp3/InformationalResponseCodeTest.kt @@ -30,19 +30,22 @@ class InformationalResponseCodeTest { recordFrames = true } - private var client = clientTestRule.newClient() + private var client = clientTestRule.newClientBuilder() + .followRedirects(false) + .build() @Test fun test103() { // Pretend we are curl so cloudflare will send a 103 val request = Request.Builder() - .url("https://tradingstrategy.ai") - .header("user-agent", "curl/7.85.0") + .url("https://theornateoracle.com/cart.php?action=add&product_id=456") + .header("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) " + + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36") .build() val response = client.newCall(request).execute() - assertThat(response.code).isEqualTo(200) + assertThat(response.code).isEqualTo(302) assertThat(response.protocol).isEqualTo(Protocol.HTTP_2) response.close() From 15ebeee7464f25af210a1426969d26433c8f14c7 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Sat, 22 Jul 2023 21:09:11 +0100 Subject: [PATCH 3/3] Avoid hanging on takeHeaders (incorrect 103 handling) when response body is empty. --- .../src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt b/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt index 1ec8f4ee7c0b..f625d02a7c03 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt @@ -260,12 +260,12 @@ class Http2Stream internal constructor( if (this.errorCode != null) { return false } - if (source.finished && sink.finished) { - return false - } this.errorCode = errorCode this.errorException = errorException notifyAll() + if (source.finished && sink.finished) { + return false + } } connection.removeStream(id) return true