使用 okhttp 分块的传输编码仅提供完整结果 [英] Transfer Encoding chunked with okhttp only delivers full result

查看:118
本文介绍了使用 okhttp 分块的传输编码仅提供完整结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试获得有关分块端点的一些见解,因此计划打印服务器逐块发送给我的内容.我没有这样做,所以我写了一个测试,看看 OkHttp/Retrofit 是否按我的预期工作.

I am trying to get some insights on a chunked endpoint and therefore planned to print what the server sends me chunk by chunk. I failed to do so so I wrote a test to see if OkHttp/Retrofit are working as I expect it.

以下测试应该向控制台提供一些数据块,但我得到的只是完整响应.

The following test should deliver some chunks to the console but all I get is the full response.

我有点失去了我所缺少的东西,甚至让 OkHttp3 的 MockWebServer 向我发送块.

I am a bit lost what I am missing to even make the MockWebServer of OkHttp3 sending me chunks.

我发现了这个改造问题条目,但答案对我来说有点模棱两可:分块传输编码响应

I found this retrofit issue entry but the answer is a bit ambiguous for me: 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$2 execute
INFO: MockWebServer[49293] starting to accept connections
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer$3 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$2 acceptConnections
INFO: MockWebServer[49293] done accepting connections: Socket closed

我希望更像(每 5 个字节剪切"一次正文):

I expected to be more like (body "cut" every 5 bytes):

Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer$2 execute
INFO: MockWebServer[49293] starting to accept connections
Nov 06, 2018 4:08:15 PM okhttp3.mockwebserver.MockWebServer$3 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$2 acceptConnections
INFO: MockWebServer[49293] done accepting connections: Socket closed

推荐答案

OkHttp Mockserver 确实对数据进行分块,但是看起来 LoggingInterceptor 会等待整个分块缓冲区已满,然后才显示它.

The OkHttp Mockserver does chunk the data, however it looks like the LoggingInterceptor waits until the whole chunks buffer is full then it displays it.

来自这个关于 HTTP 流的精彩摘要:

传输编码的使用:分块允许在单个请求或响应中流式传输.这意味着数据以分块的方式传输,并且不会影响内容的表示.

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 个请求/响应",这意味着我们必须在获得整个响应之前进行块检索.然后将每个块推送到我们自己的缓冲区中,所有这些都在 OkHttp 网络拦截器上.

With that in mind, we are dealing with 1 "request / response", which means we'll have to do our chunks retrieval before getting the entire response. Then pushing each chunk in our own buffer, all that on an OkHttp network interceptor.

以下是上述 NetworkInterceptor 的示例:

Here is an example of said 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 客户端注册这个网络拦截器.

Then of course we register this Network Interceptor on the OkHttp client.

这篇关于使用 okhttp 分块的传输编码仅提供完整结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆