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

add support for redacted headers #3721

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/strong-taxis-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/platform": patch
---

feat: Add Redacted support to headers
30 changes: 30 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Watch",
"type": "shell",
"command": "pnpm check -w",
"options": {
"cwd": "${workspaceRoot}"
},
"group": {
"kind": "build",
"isDefault": true
},
"isBackground": true,
"presentation": {
"group": "watch-build"
},
"problemMatcher": [
{
"base": "$tsc-watch",
"fileLocation": [
"relative",
"${workspaceRoot}",
],
}
]
}
]
}
16 changes: 10 additions & 6 deletions packages/platform-browser/src/internal/httpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const makeXMLHttpRequest = Client.make((request, url, signal, fiber) =>
}, { once: true })
xhr.open(request.method, url.toString(), true)
xhr.responseType = fiber.getFiberRef(currentXHRResponseType)
Object.entries(request.headers).forEach(([k, v]) => {
Object.entries(Headers.unredact(request.headers)).forEach(([k, v]) => {
xhr.setRequestHeader(k, v)
})
return Effect.zipRight(
Expand All @@ -60,7 +60,9 @@ const makeXMLHttpRequest = Client.make((request, url, signal, fiber) =>
const onChange = () => {
if (!sent && xhr.readyState >= 2) {
sent = true
resume(Effect.succeed(new ClientResponseImpl(request, xhr)))
resume(
Effect.succeed(new ClientResponseImpl(request, xhr, fiber.getFiberRef(Headers.currentRedactedNames)))
)
}
}
xhr.onreadystatechange = onChange
Expand Down Expand Up @@ -127,7 +129,8 @@ export abstract class IncomingMessageImpl<E> extends Inspectable.Class

constructor(
readonly source: XMLHttpRequest,
readonly onError: (error: unknown) => E
readonly onError: (error: unknown) => E,
private readonly redactedKeys: string | RegExp | ReadonlyArray<string | RegExp>
) {
super()
this[IncomingMessage.TypeId] = IncomingMessage.TypeId
Expand All @@ -147,7 +150,7 @@ export abstract class IncomingMessageImpl<E> extends Inspectable.Class
const parser = HeaderParser.make()
const result = parser(encoder.encode(this._rawHeaderString + "\r\n"), 0)
this._rawHeaders = result._tag === "Headers" ? result.headers : undefined
const parsed = result._tag === "Headers" ? Headers.fromInput(result.headers) : Headers.empty
const parsed = result._tag === "Headers" ? Headers.fromInput(result.headers, this.redactedKeys) : Headers.empty
return this._headers = parsed
}

Expand Down Expand Up @@ -289,15 +292,16 @@ class ClientResponseImpl extends IncomingMessageImpl<Error.ResponseError> implem

constructor(
readonly request: ClientRequest.HttpClientRequest,
source: XMLHttpRequest
source: XMLHttpRequest,
redactedKeys: string | RegExp | ReadonlyArray<string | RegExp>
) {
super(source, (cause) =>
new Error.ResponseError({
request,
response: this,
reason: "Decode",
cause
}))
}), redactedKeys)
this[ClientResponse.TypeId] = ClientResponse.TypeId
}

Expand Down
29 changes: 19 additions & 10 deletions packages/platform-bun/src/internal/httpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import * as Config from "effect/Config"
import * as Deferred from "effect/Deferred"
import * as Effect from "effect/Effect"
import * as Exit from "effect/Exit"
import * as FiberRef from "effect/FiberRef"
import * as FiberSet from "effect/FiberSet"
import { pipe } from "effect/Function"
import * as Inspectable from "effect/Inspectable"
Expand Down Expand Up @@ -88,11 +89,17 @@ export const make = (
Effect.async<never>((_) => {
function handler(request: Request, server: BunServer) {
return new Promise<Response>((resolve, _reject) => {
const fiber = runFork(Effect.provideService(
app,
ServerRequest.HttpServerRequest,
new ServerRequestImpl(request, resolve, removeHost(request.url), server)
))
const fiber = runFork(
FiberRef.get(Headers.currentRedactedNames).pipe(
Effect.flatMap((keys) =>
Effect.provideService(
app,
ServerRequest.HttpServerRequest,
new ServerRequestImpl(request, resolve, removeHost(request.url), server, keys)
)
)
)
)
request.signal.addEventListener("abort", () => {
runFork(fiber.interruptAsFork(Error.clientAbortFiberId))
}, { once: true })
Expand Down Expand Up @@ -124,7 +131,7 @@ const makeResponse = (
status?: number
statusText?: string
} = {
headers: new globalThis.Headers(response.headers),
headers: new globalThis.Headers(Headers.unredact(response.headers)),
status: response.status
}

Expand Down Expand Up @@ -216,6 +223,7 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS
public resolve: (response: Response) => void,
readonly url: string,
private bunServer: BunServer,
private redactedKeys: string | RegExp | ReadonlyArray<string | RegExp>,
public headersOverride?: Headers.Headers,
private remoteAddressOverride?: string
) {
Expand All @@ -242,6 +250,7 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS
this.resolve,
options.url ?? this.url,
this.bunServer,
this.redactedKeys,
options.headers ?? this.headersOverride,
options.remoteAddress ?? this.remoteAddressOverride
)
Expand All @@ -256,7 +265,7 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS
return this.remoteAddressOverride ? Option.some(this.remoteAddressOverride) : Option.none()
}
get headers(): Headers.Headers {
this.headersOverride ??= Headers.fromInput(this.source.headers)
this.headersOverride ??= Headers.fromInput(this.source.headers, this.redactedKeys)
return this.headersOverride
}

Expand All @@ -265,7 +274,7 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS
if (this.cachedCookies) {
return this.cachedCookies
}
return this.cachedCookies = Cookies.parseHeader(this.headers.cookie ?? "")
return this.cachedCookies = Cookies.parseHeader(Headers.unredactHeader(this.headers.cookie) ?? "")
}

get stream(): Stream.Stream<Uint8Array, Error.RequestError> {
Expand Down Expand Up @@ -345,13 +354,13 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS
return this.multipartEffect
}
this.multipartEffect = Effect.runSync(Effect.cached(
MultipartNode.persisted(Readable.fromWeb(this.source.body! as any), this.headers)
MultipartNode.persisted(Readable.fromWeb(this.source.body! as any), Headers.unredact(this.headers))
))
return this.multipartEffect
}

get multipartStream(): Stream.Stream<Multipart.Part, Multipart.MultipartError> {
return MultipartNode.stream(Readable.fromWeb(this.source.body! as any), this.headers)
return MultipartNode.stream(Readable.fromWeb(this.source.body! as any), Headers.unredact(this.headers))
}

private arrayBufferEffect: Effect.Effect<ArrayBuffer, Error.RequestError> | undefined
Expand Down
17 changes: 12 additions & 5 deletions packages/platform-node/src/internal/httpClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Cookies from "@effect/platform/Cookies"
import * as Headers from "@effect/platform/Headers"
import type * as Body from "@effect/platform/HttpBody"
import * as Client from "@effect/platform/HttpClient"
import * as Error from "@effect/platform/HttpClientError"
Expand All @@ -7,6 +8,7 @@ import * as ClientResponse from "@effect/platform/HttpClientResponse"
import * as IncomingMessage from "@effect/platform/HttpIncomingMessage"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as FiberRef from "effect/FiberRef"
import { pipe } from "effect/Function"
import * as Layer from "effect/Layer"
import type * as Scope from "effect/Scope"
Expand Down Expand Up @@ -60,20 +62,24 @@ const fromAgent = (agent: NodeClient.HttpAgent): Client.HttpClient =>
Https.request(url, {
agent: agent.https,
method: request.method,
headers: request.headers,
headers: Headers.unredact(request.headers),
signal
}) :
Http.request(url, {
agent: agent.http,
method: request.method,
headers: request.headers,
headers: Headers.unredact(request.headers),
signal
})
return pipe(
Effect.zipRight(sendBody(nodeRequest, request, request.body), waitForResponse(nodeRequest, request), {
concurrent: true
}),
Effect.map((_) => new ClientResponseImpl(request, _))
Effect.flatMap((_) =>
FiberRef.get(Headers.currentRedactedNames).pipe(
Effect.map((redactedKeys) => new ClientResponseImpl(request, _, redactedKeys))
)
)
)
})

Expand Down Expand Up @@ -188,15 +194,16 @@ class ClientResponseImpl extends HttpIncomingMessageImpl<Error.ResponseError>

constructor(
readonly request: ClientRequest.HttpClientRequest,
source: Http.IncomingMessage
source: Http.IncomingMessage,
redactedKeys: string | RegExp | ReadonlyArray<string | RegExp>
) {
super(source, (cause) =>
new Error.ResponseError({
request,
response: this,
reason: "Decode",
cause
}))
}), redactedKeys)
this[ClientResponse.TypeId] = ClientResponse.TypeId
}

Expand Down
13 changes: 9 additions & 4 deletions packages/platform-node/src/internal/httpClientUndici.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const make = (dispatcher: Undici.Dispatcher): Client.HttpClient =>
...options,
signal,
method: request.method,
headers: request.headers,
headers: Headers.unredact(request.headers),
origin: url.origin,
path: url.pathname + url.search + url.hash,
body,
Expand All @@ -69,7 +69,11 @@ export const make = (dispatcher: Undici.Dispatcher): Client.HttpClient =>
})
})
),
Effect.map((response) => new ClientResponseImpl(request, response))
Effect.flatMap((response) =>
FiberRef.get(Headers.currentRedactedNames).pipe(
Effect.map((redactedKeys) => new ClientResponseImpl(request, response, redactedKeys))
)
)
)
})

Expand Down Expand Up @@ -101,7 +105,8 @@ class ClientResponseImpl extends Inspectable.Class implements ClientResponse.Htt

constructor(
readonly request: ClientRequest.HttpClientRequest,
readonly source: Undici.Dispatcher.ResponseData
readonly source: Undici.Dispatcher.ResponseData,
protected redactedKeys: string | RegExp | ReadonlyArray<string | RegExp>
) {
super()
this[IncomingMessage.TypeId] = IncomingMessage.TypeId
Expand Down Expand Up @@ -130,7 +135,7 @@ class ClientResponseImpl extends Inspectable.Class implements ClientResponse.Htt
}

get headers(): Headers.Headers {
return Headers.fromInput(this.source.headers)
return Headers.fromInput(this.source.headers, this.redactedKeys)
}

get remoteAddress(): Option.Option<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ export abstract class HttpIncomingMessageImpl<E> extends Inspectable.Class
constructor(
readonly source: Http.IncomingMessage,
readonly onError: (error: unknown) => E,
protected readonly redactedKeys: string | RegExp | ReadonlyArray<string | RegExp>,
readonly remoteAddressOverride?: string
) {
super()
this[IncomingMessage.TypeId] = IncomingMessage.TypeId
}

get headers() {
return Headers.fromInput(this.source.headers as any)
return Headers.fromInput(this.source.headers as any, this.redactedKeys)
}

get remoteAddress() {
Expand Down
3 changes: 2 additions & 1 deletion packages/platform-node/src/internal/httpPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export const make = Platform.make({
headers: Headers.merge(
headers,
Headers.unsafeFromRecord({
"content-type": headers["content-type"] ?? Mime.getType(file.name) ?? "application/octet-stream",
"content-type": Headers.unredactHeader(headers["content-type"]) ?? Mime.getType(file.name) ??
"application/octet-stream",
"content-length": file.size.toString()
})
),
Expand Down
Loading
Loading