Skip to content

Fix redirect handling in KtorImageFetcher (#859)#939

Merged
skydoves merged 1 commit into
mainfrom
fix/859-redirect-cookie-loop
Jul 2, 2026
Merged

Fix redirect handling in KtorImageFetcher (#859)#939
skydoves merged 1 commit into
mainfrom
fix/859-redirect-cookie-loop

Conversation

@skydoves

@skydoves skydoves commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #859. LandscapistImage failed to load images whose URL performs a redirect, reporting SendCountExceedException: Max send count 20 exceeded even though only one redirect was involved. A browser loads the same URL fine.

Two issues in KtorImageFetcher caused this:

  • No cookie store. Some CDNs set a cookie on the first response and redirect to a target that requires it. Without retaining the cookie, the target keeps redirecting, looping until the send-count limit is hit. Browsers retain cookies automatically, which is exactly why the URL works in a browser but not here. Now install(HttpCookies) retains the cookie across redirect hops.
  • maxSendCount semantics. It was set to maxRedirects, conflating the total number of request dispatches (original + redirects + retries) with the redirect count. That under-allowed redirects by one, and before that setting existed the client used Ktor's default of 20 (matching the reported error). It is now maxRedirects + 1, so the original send plus maxRedirects hops are permitted.

The shared client configuration is extracted into configureForImageLoading so create() and tests build an identical client regardless of engine.

Tests

Adds MockEngine-based regression tests in KtorImageFetcherTest:

  • cookieGatedRedirectResolvesInsteadOfLooping — reproduces SendCountExceedException: Max send count 20 exceeded #859; a cookie-gated self-redirect now resolves to success.
  • redirectChainWithinMaxRedirectsSucceeds — a chain within maxRedirects is followed.
  • redirectChainBeyondMaxRedirectsFails — a longer chain fails with SendCountExceedException instead of looping forever.

All pass on landscapist-core:desktopTest; native and wasm test targets compile.

Notes

  • Adds ktor-client-mock as a commonTest dependency.
  • The default User-Agent is left unchanged; some servers gate on it, but it remains overridable via NetworkConfig.userAgent.

Summary by CodeRabbit

  • Bug Fixes

    • Improved image loading through redirects, including better handling of cookies so redirected requests resolve more reliably.
    • Fixed redirect behavior so requests can complete within the expected redirect limit without looping unnecessarily.
  • Tests

    • Added coverage for cookie-based redirects and redirect chains to help prevent regressions.

LandscapistImage failed to load images whose URL performs a redirect,
reporting `SendCountExceedException: Max send count 20 exceeded` even
though only one redirect was involved.

Two issues in KtorImageFetcher caused this:

- No cookie store was installed. Some CDNs set a cookie on the first
  response and redirect to a target that requires it. Without retaining
  the cookie the target keeps redirecting, looping until the send-count
  limit is hit. Browsers retain cookies automatically, which is why the
  same URL loads in a browser. Install HttpCookies to match that.

- maxSendCount was set to maxRedirects, conflating the total number of
  request dispatches (original + redirects + retries) with the redirect
  count. That both under-allowed redirects (off-by-one) and, before the
  setting existed, left Ktor's default of 20. Set it to maxRedirects + 1
  so the original send plus maxRedirects hops are permitted.

The shared client configuration is extracted into configureForImageLoading
so create() and tests build an identical client. Adds MockEngine-based
regression tests covering the cookie-gated redirect, the maxRedirects
boundary, and failure beyond the limit.
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 66b91037-e997-44b7-9eed-5cf2d5ced672

📥 Commits

Reviewing files that changed from the base of the PR and between 7187dec and 581c124.

📒 Files selected for processing (4)
  • gradle/libs.versions.toml
  • landscapist-core/build.gradle.kts
  • landscapist-core/src/commonMain/kotlin/com/skydoves/landscapist/core/network/KtorImageFetcher.kt
  • landscapist-core/src/commonTest/kotlin/com/skydoves/landscapist/core/network/KtorImageFetcherTest.kt

Walkthrough

The Ktor client configuration in KtorImageFetcher is refactored into a reusable extension that installs HttpCookies for cross-redirect cookie persistence and increases HttpSend's maxSendCount to maxRedirects + 1. A ktor-mock test dependency and a new test suite validate redirect and cookie handling.

Changes

Ktor redirect and cookie fix

Layer / File(s) Summary
HttpClient configuration with cookie support
landscapist-core/src/commonMain/kotlin/com/skydoves/landscapist/core/network/KtorImageFetcher.kt
Extracts client setup into an internal configureForImageLoading extension, installs HttpCookies, and changes maxSendCount from maxRedirects to maxRedirects + 1.
Test dependency and redirect/cookie test suite
gradle/libs.versions.toml, landscapist-core/build.gradle.kts, landscapist-core/src/commonTest/kotlin/com/skydoves/landscapist/core/network/KtorImageFetcherTest.kt
Adds a ktor-mock library entry and test dependency, and introduces KtorImageFetcherTest with tests for cookie-gated redirects, redirect chains within limits, and chains exceeding maxRedirects.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • skydoves/landscapist#860: Both PRs modify KtorImageFetcher.kt's Ktor HttpClient redirect handling by configuring HttpSend's max send/redirect count.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant KtorImageFetcher
  participant HttpClient
  participant Server
  Client->>KtorImageFetcher: fetch(url)
  KtorImageFetcher->>HttpClient: create() with configureForImageLoading
  HttpClient->>Server: initial request
  Server-->>HttpClient: redirect + Set-Cookie
  HttpClient->>HttpClient: persist cookie (HttpCookies)
  HttpClient->>Server: retry with cookie (maxSendCount = maxRedirects + 1)
  Server-->>HttpClient: image bytes
  HttpClient-->>KtorImageFetcher: FetchResult
  KtorImageFetcher-->>Client: image bytes
Loading

Poem

A redirect once looped and looped in vain,
Twenty tries, then failure's stinging pain.
Now cookies stick, one hop more we allow,
The bunny hops through — image loads right now! 🐇
Hooray, no more chasing my own tail! 🥕

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/859-redirect-cookie-loop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@skydoves skydoves merged commit 8592603 into main Jul 2, 2026
3 of 4 checks passed
@skydoves skydoves deleted the fix/859-redirect-cookie-loop branch July 2, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SendCountExceedException: Max send count 20 exceeded

1 participant