diff --git a/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt b/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt index 6801154b35c7..0f918a6cd396 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/http/CallServerInterceptor.kt @@ -18,8 +18,10 @@ package okhttp3.internal.http import java.io.IOException import java.net.ProtocolException import okhttp3.Interceptor +import okhttp3.Protocol import okhttp3.Response import okhttp3.internal.EMPTY_RESPONSE +import okhttp3.internal.connection.Exchange import okhttp3.internal.http2.ConnectionShutdownException import okio.buffer @@ -103,9 +105,8 @@ class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor { .receivedResponseAtMillis(System.currentTimeMillis()) .build() var code = response.code - if (code == 100) { - // Server sent a 100-continue even though we did not request one. Try again to read the - // actual response status. + + if (shouldIgnoreAndWaitForRealResponse(code, exchange)) { responseBuilder = exchange.readResponseHeaders(expectContinue = false)!! if (invokeStartEvent) { exchange.responseHeadersStart() @@ -148,4 +149,15 @@ class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor { throw e } } + + private fun shouldIgnoreAndWaitForRealResponse(code: Int, exchange: Exchange): 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 + + // Early Hints (103) but not supported yet in OkHttp + code == 103 -> true + + else -> false + } } diff --git a/okhttp/src/main/kotlin/okhttp3/internal/http/StatusLine.kt b/okhttp/src/main/kotlin/okhttp3/internal/http/StatusLine.kt index dc28d2f40401..e47acb1794ba 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/http/StatusLine.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/http/StatusLine.kt @@ -46,6 +46,7 @@ class StatusLine( /** RFC 7540, Section 9.1.2. Retry these if the exchange used connection coalescing. */ const val HTTP_MISDIRECTED_REQUEST = 421 const val HTTP_CONTINUE = 100 + const val HTTP_EARLY_HINTS = 103 fun get(response: Response): StatusLine { return StatusLine(response.protocol, response.code, response.message) diff --git a/okhttp/src/main/kotlin/okhttp3/internal/http1/Http1ExchangeCodec.kt b/okhttp/src/main/kotlin/okhttp3/internal/http1/Http1ExchangeCodec.kt index 87f516a69a75..a573b6986ff6 100644 --- a/okhttp/src/main/kotlin/okhttp3/internal/http1/Http1ExchangeCodec.kt +++ b/okhttp/src/main/kotlin/okhttp3/internal/http1/Http1ExchangeCodec.kt @@ -33,6 +33,7 @@ import okhttp3.internal.http.ExchangeCodec import okhttp3.internal.http.RequestLine import okhttp3.internal.http.StatusLine import okhttp3.internal.http.StatusLine.Companion.HTTP_CONTINUE +import okhttp3.internal.http.StatusLine.Companion.HTTP_EARLY_HINTS import okhttp3.internal.http.promisesBody import okhttp3.internal.http.receiveHeaders import okhttp3.internal.skipAll @@ -193,6 +194,11 @@ class Http1ExchangeCodec( state = STATE_READ_RESPONSE_HEADERS responseBuilder } + statusLine.code == HTTP_EARLY_HINTS -> { + // Early Hints will mean a second header are coming. + state = STATE_READ_RESPONSE_HEADERS + responseBuilder + } else -> { state = STATE_OPEN_RESPONSE_BODY responseBuilder