Skip to content

Commit c401b12

Browse files
committed
fix(platform): document and surface redirect behavior for HttpClient.followRedirects
Fixes #6231
1 parent e71ba68 commit c401b12

4 files changed

Lines changed: 35 additions & 4 deletions

File tree

‎.changeset/fuzzy-chairs-taste.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@effect/platform": patch
3+
---
4+
5+
Allow `HttpClient.followRedirects` to take effect on `FetchHttpClient` by exposing `redirect` through the requestInit FiberRef and documenting the interaction.

‎packages/platform/src/FetchHttpClient.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import * as internal from "./internal/fetchHttpClient.js"
1313
export class Fetch extends Context.Tag(internal.fetchTagKey)<Fetch, typeof globalThis.fetch>() {}
1414

1515
/**
16+
* The `RequestInit` options provided to `fetch`.
17+
*
18+
* Set `redirect` to `"manual"` when using `HttpClient.followRedirects`, so that
19+
* fetch does not follow redirects before the client decorator can observe them.
20+
*
1621
* @since 1.0.0
1722
* @category tags
1823
*/

‎packages/platform/src/HttpClient.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,10 @@ export const withCookiesRef: {
621621
/**
622622
* Follows HTTP redirects up to a specified number of times.
623623
*
624+
* When used with `FetchHttpClient`, provide `FetchHttpClient.RequestInit` with
625+
* `{ redirect: "manual" }` so that `fetch` exposes redirect responses to this
626+
* decorator instead of following them internally.
627+
*
624628
* @since 1.0.0
625629
* @category redirects
626630
*/

‎packages/platform/test/HttpClient.test.ts‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,16 +295,33 @@ describe("HttpClient", () => {
295295

296296
it.effect("followRedirects", () =>
297297
Effect.gen(function*() {
298+
const requests: Array<{ readonly url: string; readonly init: RequestInit | undefined }> = []
299+
const fetch: typeof globalThis.fetch = (input, init) => {
300+
const url = typeof input === "string" || input instanceof URL ? input.toString() : input.url
301+
requests.push({ url, init })
302+
if (requests.length === 1) {
303+
return Promise.resolve(new Response(null, {
304+
status: 302,
305+
headers: { location: "/redirected" }
306+
}))
307+
}
308+
return Promise.resolve(new Response("ok"))
309+
}
298310
const defaultClient = yield* HttpClient.HttpClient
299311
const client = defaultClient.pipe(HttpClient.followRedirects())
300312

301-
const response = yield* client.get("https://google.com/")
302-
strictEqual(response.request.url, "https://www.google.com/")
313+
const response = yield* client.get("https://example.com/")
314+
strictEqual(response.request.url, "https://example.com/redirected")
315+
strictEqual(requests.length, 2)
316+
strictEqual(requests[0]!.url, "https://example.com/")
317+
strictEqual(requests[0]!.init?.redirect, "manual")
318+
strictEqual(requests[1]!.url, "https://example.com/redirected")
319+
strictEqual(requests[1]!.init?.redirect, "manual")
303320
}).pipe(
304-
it.flakyTest,
305321
Effect.provide(FetchHttpClient.layer),
322+
Effect.provideService(FetchHttpClient.Fetch, fetch),
306323
Effect.provideService(FetchHttpClient.RequestInit, { redirect: "manual" })
307-
), 30000)
324+
))
308325

309326
describe("retryTransient", () => {
310327
const makeTestClient = (status: number) => {

0 commit comments

Comments
 (0)