dio_redirect_interceptor 2.0.0 copy "dio_redirect_interceptor: ^2.0.0" to clipboard
dio_redirect_interceptor: ^2.0.0 copied to clipboard

A custom interceptor for Dio HTTP client to handle redirects manually.

2.0.0 #

Security and correctness release. The redirect request is now built and dispatched differently, so behaviour changes for existing users — see below.

Security #

  • Credential headers (authorization, cookie, proxy-authorization) are no longer forwarded when a redirect crosses to another origin. Previously the headers on the Dio instance were sent to whatever host the Location header named, so an open redirect on the server — or a tampered response — leaked the token. Set stripCredentialsOnCrossOrigin: false to restore the old behaviour.
  • An https request is no longer redirected to http by default. Pass allowProtocolDowngrade: true to permit it.
  • Redirects to schemes other than http and https are refused.
  • Location is resolved with RFC 3986 reference resolution. The previous location.contains("://") test treated any value containing :// anywhere as an absolute URL, so Location: /login?next=https://example.com/ was returned verbatim and produced a request against an unintended target.
  • A response carrying more than one Location header is refused instead of silently following a folded value.

Added #

  • RequestOptions.redirectContext, holding a RedirectContext on every redirected request, so an interceptor that attaches credentials can tell which hop it is running on and skip signing it. It exposes originalUri, previousUri, currentUri, count, and three ways of asking about the origin — hasLeftOriginalOrigin (sticky once the chain leaves the origin the caller asked for, the conservative choice for a credential), hopCrossedOrigin (this hop alone, the rule browsers apply), and isOnOriginalOrigin (the return leg of an a → identity provider → a login).
  • RequestOptions.rawUri and RequestOptions.redirectCount extensions.

Fixed #

  • An exception raised while inspecting a response — from a RedirectCallback, a RedirectValidator, or a non-bool value under extra['followRedirects'] — used to leave the request future hanging forever, because dio discards the future a response interceptor returns and waits on the handler alone. All of it now runs inside the guard and fails the request instead.

  • A FormData body is cloned before being replayed on a 307/308. FormData finalises itself on first use, so a redirected multipart upload previously failed with StateError: The FormData has already been finalized.

  • A 303 no longer forwards the payload when it happens to leave the method alone, as RFC 9110 requires.

  • The redirected request carries a mutable queryParameters map, so an interceptor that writes into it — an api-key or signing interceptor — no longer throws UnsupportedError from the second hop onwards.

  • transformTimeout is carried across hops.

  • content-encoding, content-md5, transfer-encoding and expect are dropped along with the body, not just content-length and content-type.

  • The final response is handed to the interceptors that follow this one exactly once. It used to be forwarded again at every nesting level, so a logging or caching interceptor registered after this one fired three times for a two-hop redirect chain.

  • Errors synthesized by this interceptor — a refused downgrade, a malformed or duplicated Location, the hop limit — are delivered to the error interceptors that follow it. An error raised by the nested request is not re-delivered, since it already traversed the chain once.

  • The method and body of the original request are preserved across 307 and 308 redirects. Every redirect previously became a bodyless GET. 303 turns everything but HEAD into GET, and 301/302 rewrite only POST, matching browsers and curl.

  • The headers of the original request are carried across the redirect. They were dropped entirely before, leaving only the headers of the Dio instance.

  • CancelToken, onSendProgress and onReceiveProgress are carried across the redirect, so cancelling a redirected request works.

  • Errors raised while handling a redirect are surfaced as DioException with the cause in error, instead of escaping as a bare exception.

  • Following a redirect no longer mutates the extra map of the options the caller passed in, so a reused Options no longer accumulates redirectCount.

  • followRedirects and validateStatus no longer have to be configured by hand. The interceptor sets followRedirects: false on the requests it handles so the adapter cannot swallow a hop, and widens a validateStatus that would reject redirect statuses — otherwise forcing the first would turn every redirect into a badResponse error. A redirect that is not followed (past maxRedirectCount, or declined by RedirectValidator) is still judged by the caller's own validateStatus. A validateStatus that already accepts 301, 302, 303, 307 and 308 is left untouched.

Known limitations #

  • Each hop re-enters the whole interceptor chain, so onRequest of every interceptor runs once per hop, and interceptors registered after this one never observe the 3xx hops. See "Interceptor ordering" in the README.
  • Cross-origin credential stripping only covers the headers this interceptor carries. Another interceptor that injects credentials in onRequest runs again on each hop and would re-add them; guard it with options.redirectContext.

1.0.1 #

  • Update dart version to ">=3.0.0 <4.0.0"

1.0.0 #

  • Initial version.
3
likes
160
points
349
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A custom interceptor for Dio HTTP client to handle redirects manually.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

dio

More

Packages that depend on dio_redirect_interceptor