diff --git a/.changeset/tricky-parents-relate.md b/.changeset/tricky-parents-relate.md new file mode 100644 index 0000000000..c0f271f2bb --- /dev/null +++ b/.changeset/tricky-parents-relate.md @@ -0,0 +1,5 @@ +--- +"@effect/platform": patch +--- + +add HttpClient.followRedirects api diff --git a/packages/platform-node/test/HttpClient.test.ts b/packages/platform-node/test/HttpClient.test.ts index 5b151bdf25..628fdb347a 100644 --- a/packages/platform-node/test/HttpClient.test.ts +++ b/packages/platform-node/test/HttpClient.test.ts @@ -58,6 +58,20 @@ const JsonPlaceholderLive = Layer.effect(JsonPlaceholder, makeJsonPlaceholder) expect(response).toContain("Google") }).pipe(Effect.provide(layer))) + it.effect("google followRedirects", () => + Effect.gen(function*(_) { + const client = (yield* HttpClient.HttpClient).pipe( + HttpClient.followRedirects() + ) + const response = yield* _( + HttpClientRequest.get("http://google.com/"), + client, + Effect.flatMap((_) => _.text), + Effect.scoped + ) + expect(response).toContain("Google") + }).pipe(Effect.provide(layer))) + it.effect("google stream", () => Effect.gen(function*(_) { const client = yield* _(HttpClient.HttpClient) diff --git a/packages/platform/src/HttpClient.ts b/packages/platform/src/HttpClient.ts index d0d365d2ac..1240abcb73 100644 --- a/packages/platform/src/HttpClient.ts +++ b/packages/platform/src/HttpClient.ts @@ -465,6 +465,15 @@ export const withCookiesRef: { (self: HttpClient.WithResponse, ref: Ref): HttpClient.WithResponse } = internal.withCookiesRef +/** + * @since 1.0.0 + * @category redirects + */ +export const followRedirects: { + (maxRedirects?: number | undefined): (self: HttpClient.WithResponse) => HttpClient.WithResponse + (self: HttpClient.WithResponse, maxRedirects?: number | undefined): HttpClient.WithResponse +} = internal.followRedirects + /** * @since 1.0.0 * @category fiber refs diff --git a/packages/platform/src/internal/httpClient.ts b/packages/platform/src/internal/httpClient.ts index b4a5caf4a2..bad41db7c2 100644 --- a/packages/platform/src/internal/httpClient.ts +++ b/packages/platform/src/internal/httpClient.ts @@ -748,3 +748,41 @@ export const withCookiesRef = dual< )) ) ) + +/** @internal */ +export const followRedirects = dual< + ( + maxRedirects?: number | undefined + ) => (self: Client.HttpClient.WithResponse) => Client.HttpClient.WithResponse, + ( + self: Client.HttpClient.WithResponse, + maxRedirects?: number | undefined + ) => Client.HttpClient.WithResponse +>((args) => isClient(args[0]), ( + self: Client.HttpClient.WithResponse, + maxRedirects?: number | undefined +): Client.HttpClient.WithResponse => + make( + (request) => { + const loop = ( + request: ClientRequest.HttpClientRequest, + redirects: number + ): Effect.Effect => + Effect.flatMap( + self.execute(Effect.succeed(request)), + (response) => + response.status >= 300 && response.status < 400 && response.headers.location && + redirects < (maxRedirects ?? 10) + ? loop( + internalRequest.setUrl( + request, + response.headers.location + ), + redirects + 1 + ) + : Effect.succeed(response) + ) + return Effect.flatMap(request, (request) => loop(request, 0)) + }, + self.preprocess + ))