与 okhttp 分块的传输编码仅提供完整结果
Transfer Encoding chunked with okhttp only delivers full result
我正在尝试获得有关分块端点的一些见解,因此计划逐块打印服务器发送给我的内容。我没有这样做,所以我写了一个测试来查看 OkHttp/Retrofit 是否按我预期的那样工作。
下面的测试应该向控制台传送一些块,但我得到的只是完整的响应。
我什至让 OkHttp3 的 MockWebServer 向我发送块,我有点迷失了我所缺少的东西。
我找到了这个改造问题条目,但答案对我来说有点模棱两可:Chunked Transfer Encoding Response
class ChunkTest {
@Rule
@JvmField
val rule = RxImmediateSchedulerRule() // custom rule to run Rx synchronously
@Test
fun `test Chunked Response`() {
val mockWebServer = MockWebServer()
mockWebServer.enqueue(getMockChunkedResponse())
val retrofit = Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(OkHttpClient.Builder().build())
.build()
val chunkedApi = retrofit.create(ChunkedApi::class.java)
chunkedApi.getChunked()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
System.out.println(it.string())
}, {
System.out.println(it.message)
})
mockWebServer.shutdown()
}
private fun getMockChunkedResponse(): MockResponse {
val mockResponse = MockResponse()
mockResponse.setHeader("Transfer-Encoding", "chunked")
mockResponse.setChunkedBody("THIS IS A CHUNKED RESPONSE!", 5)
return mockResponse
}
}
interface ChunkedApi {
@Streaming
@GET("/")
fun getChunked(): Flowable<ResponseBody>
}
测试控制台输出:
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer execute
INFO: MockWebServer[49293] starting to accept connections
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer processOneRequest
INFO: MockWebServer[49293] received request: GET / HTTP/1.1 and responded: HTTP/1.1 200 OK
THIS IS A CHUNKED RESPONSE!
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer acceptConnections
INFO: MockWebServer[49293] done accepting connections: Socket closed
我希望更像(正文 "cut" 每 5 个字节):
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer execute
INFO: MockWebServer[49293] starting to accept connections
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer processOneRequest
INFO: MockWebServer[49293] received request: GET / HTTP/1.1 and responded: HTTP/1.1 200 OK
THIS
IS A
CHUNKE
D RESPO
NSE!
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer acceptConnections
INFO: MockWebServer[49293] done accepting connections: Socket closed
OkHttp Mockserver 确实对数据进行分块,但 LoggingInterceptor 似乎会等到整个分块缓冲区已满,然后才会显示它。
来自这个nice summary about HTTP streaming:
The use of Transfer-Encoding: chunked is what allows streaming within a single request or response. This means that the data is transmitted in a chunked manner, and does not impact the representation of the content.
考虑到这一点,我们正在处理 1 "request / response",这意味着我们必须在获得整个响应之前进行块检索。然后将每个块推送到我们自己的缓冲区中,所有这些都在 OkHttp network interceptor
.
上
这里有一个例子 NetworkInterceptor
:
class ChunksInterceptor: Interceptor {
val Utf8Charset = Charset.forName ("UTF-8")
override fun intercept (chain: Interceptor.Chain): Response {
val originalResponse = chain.proceed (chain.request ())
val responseBody = originalResponse.body ()
val source = responseBody!!.source ()
val buffer = Buffer () // We create our own Buffer
// Returns true if there are no more bytes in this source
while (!source.exhausted ()) {
val readBytes = source.read (buffer, Long.MAX_VALUE) // We read the whole buffer
val data = buffer.readString (Utf8Charset)
println ("Read: $readBytes bytes")
println ("Content: \n $data \n")
}
return originalResponse
}
}
那当然我们在OkHttp客户端上注册这个Network Interceptor
我正在尝试获得有关分块端点的一些见解,因此计划逐块打印服务器发送给我的内容。我没有这样做,所以我写了一个测试来查看 OkHttp/Retrofit 是否按我预期的那样工作。
下面的测试应该向控制台传送一些块,但我得到的只是完整的响应。
我什至让 OkHttp3 的 MockWebServer 向我发送块,我有点迷失了我所缺少的东西。
我找到了这个改造问题条目,但答案对我来说有点模棱两可:Chunked Transfer Encoding Response
class ChunkTest {
@Rule
@JvmField
val rule = RxImmediateSchedulerRule() // custom rule to run Rx synchronously
@Test
fun `test Chunked Response`() {
val mockWebServer = MockWebServer()
mockWebServer.enqueue(getMockChunkedResponse())
val retrofit = Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(OkHttpClient.Builder().build())
.build()
val chunkedApi = retrofit.create(ChunkedApi::class.java)
chunkedApi.getChunked()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
System.out.println(it.string())
}, {
System.out.println(it.message)
})
mockWebServer.shutdown()
}
private fun getMockChunkedResponse(): MockResponse {
val mockResponse = MockResponse()
mockResponse.setHeader("Transfer-Encoding", "chunked")
mockResponse.setChunkedBody("THIS IS A CHUNKED RESPONSE!", 5)
return mockResponse
}
}
interface ChunkedApi {
@Streaming
@GET("/")
fun getChunked(): Flowable<ResponseBody>
}
测试控制台输出:
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer execute
INFO: MockWebServer[49293] starting to accept connections
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer processOneRequest
INFO: MockWebServer[49293] received request: GET / HTTP/1.1 and responded: HTTP/1.1 200 OK
THIS IS A CHUNKED RESPONSE!
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer acceptConnections
INFO: MockWebServer[49293] done accepting connections: Socket closed
我希望更像(正文 "cut" 每 5 个字节):
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer execute
INFO: MockWebServer[49293] starting to accept connections
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer processOneRequest
INFO: MockWebServer[49293] received request: GET / HTTP/1.1 and responded: HTTP/1.1 200 OK
THIS
IS A
CHUNKE
D RESPO
NSE!
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer acceptConnections
INFO: MockWebServer[49293] done accepting connections: Socket closed
OkHttp Mockserver 确实对数据进行分块,但 LoggingInterceptor 似乎会等到整个分块缓冲区已满,然后才会显示它。
来自这个nice summary about HTTP streaming:
The use of Transfer-Encoding: chunked is what allows streaming within a single request or response. This means that the data is transmitted in a chunked manner, and does not impact the representation of the content.
考虑到这一点,我们正在处理 1 "request / response",这意味着我们必须在获得整个响应之前进行块检索。然后将每个块推送到我们自己的缓冲区中,所有这些都在 OkHttp network interceptor
.
这里有一个例子 NetworkInterceptor
:
class ChunksInterceptor: Interceptor {
val Utf8Charset = Charset.forName ("UTF-8")
override fun intercept (chain: Interceptor.Chain): Response {
val originalResponse = chain.proceed (chain.request ())
val responseBody = originalResponse.body ()
val source = responseBody!!.source ()
val buffer = Buffer () // We create our own Buffer
// Returns true if there are no more bytes in this source
while (!source.exhausted ()) {
val readBytes = source.read (buffer, Long.MAX_VALUE) // We read the whole buffer
val data = buffer.readString (Utf8Charset)
println ("Read: $readBytes bytes")
println ("Content: \n $data \n")
}
return originalResponse
}
}
那当然我们在OkHttp客户端上注册这个Network Interceptor