Передача кодирования в чанках с ohttp только дает полный результат - PullRequest
0 голосов
/ 06 ноября 2018

Я пытаюсь получить некоторую информацию о конечной точке чанков и поэтому планирую распечатать то, что сервер отправляет мне чанк за чанк. Я не смог этого сделать, поэтому написал тест, чтобы проверить, работает ли OkHttp / Retrofit так, как я ожидаю.

Следующий тест должен доставить некоторые куски на консоль, но все, что я получаю, это полный ответ.

Я немного потерял то, что мне не хватает, чтобы даже заставить MockWebServer из OkHttp3 отправлять мне куски.

Я нашел эту запись о проблеме дооснащения, но ответ для меня немного неоднозначен: Ответ кодированной передачи с переброской по частям

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 байт):

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

1 Ответ

0 голосов
/ 07 ноября 2018

OkHttp Mockserver выполняет порцию данных, однако похоже, что LoggingInterceptor ждет, пока весь буфер порций не заполнится, и отображает его.

Из этого хорошего резюме о потоковой передаче HTTP :

Использование Transfer-Encoding: chunked - это то, что позволяет осуществлять потоковую передачу в рамках одного запроса или ответа. Это означает, что данные передаются в виде фрагментов и не влияют на представление контента.

Имея это в виду, мы имеем дело с 1 «запросом / ответом», что означает, что нам придется выполнить наш поиск фрагментов, прежде чем получить полный ответ. Затем помещаем каждый кусок в наш собственный буфер, и все это на 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.

...