Skip to content
New issue

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

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

Already on GitHub? Sign in 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in 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