Skip to content

Commit 61a8735

Browse files
New Response.peekTrailers() API (#8921)
* New Response.peekTrailers() API This restores behavior we had in OkHttp 4.x that I broke when I changed Response.trailers() to block until trailers were available. Closes: #8916 * apiDump --------- Co-authored-by: Jesse Wilson <[email protected]>
1 parent 6684401 commit 61a8735

File tree

13 files changed

+217
-34
lines changed

13 files changed

+217
-34
lines changed

okhttp/api/android/okhttp.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,7 @@ public final class okhttp3/Response : java/io/Closeable {
11141114
public final fun networkResponse ()Lokhttp3/Response;
11151115
public final fun newBuilder ()Lokhttp3/Response$Builder;
11161116
public final fun peekBody (J)Lokhttp3/ResponseBody;
1117+
public final fun peekTrailers ()Lokhttp3/Headers;
11171118
public final fun priorResponse ()Lokhttp3/Response;
11181119
public final fun protocol ()Lokhttp3/Protocol;
11191120
public final fun receivedResponseAtMillis ()J
@@ -1219,11 +1220,16 @@ public abstract interface class okhttp3/TrailersSource {
12191220
public static final field Companion Lokhttp3/TrailersSource$Companion;
12201221
public static final field EMPTY Lokhttp3/TrailersSource;
12211222
public abstract fun get ()Lokhttp3/Headers;
1223+
public fun peek ()Lokhttp3/Headers;
12221224
}
12231225

12241226
public final class okhttp3/TrailersSource$Companion {
12251227
}
12261228

1229+
public final class okhttp3/TrailersSource$DefaultImpls {
1230+
public static fun peek (Lokhttp3/TrailersSource;)Lokhttp3/Headers;
1231+
}
1232+
12271233
public abstract interface class okhttp3/WebSocket {
12281234
public abstract fun cancel ()V
12291235
public abstract fun close (ILjava/lang/String;)Z

okhttp/api/jvm/okhttp.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,7 @@ public final class okhttp3/Response : java/io/Closeable {
11141114
public final fun networkResponse ()Lokhttp3/Response;
11151115
public final fun newBuilder ()Lokhttp3/Response$Builder;
11161116
public final fun peekBody (J)Lokhttp3/ResponseBody;
1117+
public final fun peekTrailers ()Lokhttp3/Headers;
11171118
public final fun priorResponse ()Lokhttp3/Response;
11181119
public final fun protocol ()Lokhttp3/Protocol;
11191120
public final fun receivedResponseAtMillis ()J
@@ -1219,11 +1220,16 @@ public abstract interface class okhttp3/TrailersSource {
12191220
public static final field Companion Lokhttp3/TrailersSource$Companion;
12201221
public static final field EMPTY Lokhttp3/TrailersSource;
12211222
public abstract fun get ()Lokhttp3/Headers;
1223+
public fun peek ()Lokhttp3/Headers;
12221224
}
12231225

12241226
public final class okhttp3/TrailersSource$Companion {
12251227
}
12261228

1229+
public final class okhttp3/TrailersSource$DefaultImpls {
1230+
public static fun peek (Lokhttp3/TrailersSource;)Lokhttp3/Headers;
1231+
}
1232+
12271233
public abstract interface class okhttp3/WebSocket {
12281234
public abstract fun cancel ()V
12291235
public abstract fun close (ILjava/lang/String;)Z

okhttp/src/commonJvmAndroid/kotlin/okhttp3/Response.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,26 @@ class Response internal constructor(
192192
@Throws(IOException::class)
193193
fun trailers(): Headers = trailersSource.get()
194194

195+
/**
196+
* Returns the trailers after the HTTP response, if they are available to read immediately. Unlike
197+
* [trailers], this doesn't block if the trailers are not immediately available, and instead
198+
* returns null.
199+
*
200+
* This will typically return null until [ResponseBody.source] has buffered the last byte of the
201+
* response body. Call `body.source().request(1024 * 1024)` to block until either that's done, or
202+
* 1 MiB of response data is loaded into memory. (You could use any size here, though large values
203+
* risk exhausting memory.)
204+
*
205+
* This returns an empty value if the trailers are available, but have no data.
206+
*
207+
* It is not safe to call this concurrently with code that is processing the response body.
208+
*
209+
* @throws IOException if the trailers cannot be loaded, such as if the network connection is
210+
* dropped.
211+
*/
212+
@Throws(IOException::class)
213+
fun peekTrailers(): Headers? = trailersSource.peek()
214+
195215
/**
196216
* Peeks up to [byteCount] bytes from the response body and returns them as a new response
197217
* body. If fewer than [byteCount] bytes are in the response body, the full response body is

okhttp/src/commonJvmAndroid/kotlin/okhttp3/TrailersSource.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,20 @@ import okio.IOException
2727
* This interface is for test and production code that creates [Response] instances without making
2828
* an HTTP call to a remote server.
2929
*/
30-
fun interface TrailersSource {
30+
interface TrailersSource {
31+
@Throws(IOException::class)
32+
fun peek(): Headers? = null
33+
3134
@Throws(IOException::class)
3235
fun get(): Headers
3336

3437
companion object {
3538
@JvmField
36-
val EMPTY: TrailersSource = TrailersSource { Headers.EMPTY }
39+
val EMPTY: TrailersSource =
40+
object : TrailersSource {
41+
override fun peek() = Headers.EMPTY
42+
43+
override fun get() = Headers.EMPTY
44+
}
3745
}
3846
}

okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/Exchange.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ class Exchange(
141141
}
142142

143143
@Throws(IOException::class)
144-
fun trailers(): Headers = codec.trailers()
144+
fun peekTrailers(): Headers? = codec.peekTrailers()
145145

146146
@Throws(SocketException::class)
147147
fun newWebSocketStreams(): RealWebSocket.Streams {

okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/CallServerInterceptor.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ package okhttp3.internal.http
1717

1818
import java.io.IOException
1919
import java.net.ProtocolException
20+
import okhttp3.Headers
2021
import okhttp3.Interceptor
2122
import okhttp3.Response
23+
import okhttp3.TrailersSource
2224
import okhttp3.internal.connection.Exchange
2325
import okhttp3.internal.http2.ConnectionShutdownException
2426
import okhttp3.internal.skipAll
@@ -134,13 +136,19 @@ class CallServerInterceptor(
134136
response
135137
.newBuilder()
136138
.body(responseBody)
137-
.trailers {
138-
val source = responseBody.source()
139-
if (source.isOpen) {
140-
source.skipAll()
141-
}
142-
exchange.trailers()
143-
}.build()
139+
.trailers(
140+
object : TrailersSource {
141+
override fun peek() = exchange.peekTrailers()
142+
143+
override fun get(): Headers {
144+
val source = responseBody.source()
145+
if (source.isOpen) {
146+
source.skipAll()
147+
}
148+
return peek() ?: error("null trailers after exhausting response body?!")
149+
}
150+
},
151+
).build()
144152
}
145153
if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
146154
"close".equals(response.header("Connection"), ignoreCase = true)

okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http/ExchangeCodec.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ interface ExchangeCodec {
6666
@Throws(IOException::class)
6767
fun openResponseBodySource(response: Response): Source
6868

69-
/** Returns the trailers after the HTTP response. May be empty. */
69+
/** Returns the trailers after the HTTP response if they're ready. May be empty. */
7070
@Throws(IOException::class)
71-
fun trailers(): Headers
71+
fun peekTrailers(): Headers?
7272

7373
/**
7474
* Cancel this stream. Resources held by this stream will be cleaned up, though not synchronously.

okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http1/Http1ExchangeCodec.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,14 @@ class Http1ExchangeCodec(
147147
}
148148
}
149149

150-
override fun trailers(): Headers {
150+
override fun peekTrailers(): Headers? {
151151
if (trailers === TRAILERS_RESPONSE_BODY_TRUNCATED) {
152152
throw IOException("Trailers cannot be read because the response body was truncated")
153153
}
154-
return trailers ?: error("state: $state")
154+
check(state == STATE_READING_RESPONSE_BODY || state == STATE_CLOSED) {
155+
"Trailers cannot be read because the state is $state"
156+
}
157+
return trailers
155158
}
156159

157160
override fun flushRequest() {

okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2ExchangeCodec.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class Http2ExchangeCodec(
115115

116116
override fun openResponseBodySource(response: Response): Source = stream!!.source
117117

118-
override fun trailers(): Headers = stream!!.trailers()
118+
override fun peekTrailers(): Headers? = stream!!.peekTrailers()
119119

120120
override fun cancel() {
121121
canceled = true

okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Stream.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,18 @@ class Http2Stream internal constructor(
168168
}
169169

170170
/**
171-
* Returns the trailers. It is only safe to call this once the source stream has been completely
172-
* exhausted.
171+
* Returns the trailers if they're immediately available.
173172
*/
174173
@Throws(IOException::class)
175-
fun trailers(): Headers {
174+
fun peekTrailers(): Headers? {
176175
withLock {
177176
if (source.finished && source.receiveBuffer.exhausted() && source.readBuffer.exhausted()) {
178177
return source.trailers ?: Headers.EMPTY
179178
}
180179
if (errorCode != null) {
181180
throw errorException ?: StreamResetException(errorCode!!)
182181
}
183-
throw IllegalStateException("too early; can't read the trailers yet")
182+
return null
184183
}
185184
}
186185

0 commit comments

Comments
 (0)