Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Duplicate network request for images with chunked response #1543

Closed
Fayth opened this issue Nov 24, 2022 · 4 comments
Closed

Duplicate network request for images with chunked response #1543

Fayth opened this issue Nov 24, 2022 · 4 comments
Labels
help wanted Issues that are up for grabs + are good candidates for community PRs

Comments

@Fayth
Copy link

Fayth commented Nov 24, 2022

Describe the bug
Scenario:

  • image caching disabled in coil
  • requesting image via url (HttpUrlFetcher)
  • server returns valid chunked response (doesn't contain Content-Length header)

results in two requests to given url (despite the first one being successful)

To Reproduce

val request = ImageRequest.Builder(context)
            .data("https://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx")
            .memoryCachePolicy(CachePolicy.DISABLED)
            .diskCachePolicy(CachePolicy.DISABLED)
            .build()
context.imageLoader.execute(request)

Logs/Screenshots
The cause seems to be the verification in https://github.com/coil-kt/coil/blob/main/coil-base/src/main/java/coil/fetch/HttpUriFetcher.kt#L95 which relies on explicit content length being provided in response. In the scenario above the length isn't known (okhttp reports it as -1)

Version
2.2.2

It might be enough to allow contentLength() == -1 in the check mentioned above (it seems to fix the problem in my case) or possibly also peeking the response body buffer to make sure it's not empty

@colinrtwhite colinrtwhite added the help wanted Issues that are up for grabs + are good candidates for community PRs label Nov 27, 2022
@colinrtwhite
Copy link
Member

@Fayth Does OkHttp have the same behaviour for that URL? Coil's HttpUriFetcher code is based off of OkHttp's behaviour and tries to adhere pretty closely to it.

@Fayth
Copy link
Author

Fayth commented Nov 28, 2022

I haven't used "raw" okhttp much, but from what I tried, there's only one GET request when using this sample request:

val request: Request = Request.Builder()
            .url(url)
            .cacheControl(CacheControl.FORCE_NETWORK)
            .build()
OkHttpClient().newCall(request).execute().use { response -> return response.body?.string() }

Looking through okhttp source, I think the only place where it uses content length in higher-level logic is ensuring there's no body for 204/205 responses.

@ferinagy
Copy link

ferinagy commented Jul 4, 2024

Hello, we just ran into this for 2.6.0. I tried to create a failing test in HttpUriFetcherTest:

@Test
fun `chunked response`() = runTestAsync {
    val url = server.url(IMAGE).toString()

    server.enqueueChunkedImage(IMAGE)
    server.enqueueChunkedImage(IMAGE)

    val fetcher = newFetcher(url, Options(context, diskCachePolicy = CachePolicy.DISABLED))
    fetcher.fetch()

    assertEquals(1, server.requestCount)
}

fun MockWebServer.enqueueChunkedImage(image: String) {
    val buffer = Buffer()
    val context = ApplicationProvider.getApplicationContext<Context>()
    context.assets.open(image).source().buffer().readAll(buffer)
    val response = MockResponse().setBody(buffer).removeHeader("Content-Length")
    enqueue(response)
}

I also tried it with current 3.0.0 on main and there it does not happen as the real size of the response is checked instead of the Content-length header.

val responseBodyBuffer = responseBody.readBuffer()
if (responseBodyBuffer.size > 0) {

However, as 3.0 is still in alpha, we'd prefer to have a fix in 2.x. I am open to creating a PR if there is a chance to get it merged.

@colinrtwhite
Copy link
Member

This is fixed in 2.7.0.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
help wanted Issues that are up for grabs + are good candidates for community PRs
Projects
None yet
Development

No branches or pull requests

3 participants