History

Rebuild the stamped auth request with an explicit body

Rhys Sullivan committed 1 month ago

Commit d486c2b1 parent 0ff9d4f2 files · +57 −1Browse files

Unified · Split

2 files
  1. apps/host-selfhost/src/auth/client-ip.test.ts +32 −0
  2. apps/host-selfhost/src/auth/client-ip.ts +25 −1
apps/host-selfhost/src/auth/client-ip.test.ts+32−0
@@ -93,6 +93,38 @@ describe("makeClientIpStamper without a trusted proxy", () => { · 92 unmodified lines
93 expect(out.headers.get("x-forwarded-for")).toBe("203.0.113.9");93 expect(out.headers.get("x-forwarded-for")).toBe("203.0.113.9");
94 expect(await out.json()).toEqual({ email: "a@example.com", password: "pw" });94 expect(await out.json()).toEqual({ email: "a@example.com", password: "pw" });
95 });95 });
96
97 // The Vite dev middleware builds the request over a stream the runtime did
98 // not create itself (`Readable.toWeb(req)`); the stamped request must still
99 // deliver it in full.
100 it("passes a streamed body through", async () => {
101 const payload = JSON.stringify({ email: "a@example.com", password: "pw" });
102 const init: RequestInit & { duplex?: "half" } = {
103 method: "POST",
104 headers: { "content-type": "application/json" },
105 body: new ReadableStream<Uint8Array>({
106 start(controller) {
107 controller.enqueue(new TextEncoder().encode(payload));
108 controller.close();
109 },
110 }),
111 duplex: "half",
112 };
113 const streamed = new Request("https://host.example/api/auth/sign-in/email", init);
114 const out = stamp(streamed, Option.some("198.51.100.4"));
115 expect(out.headers.get(CLIENT_IP_HEADER)).toBe("198.51.100.4");
116 expect(await out.text()).toBe(payload);
117 });
118
119 it("stamps a bodiless request without inventing a body", () => {
120 const out = stamp(
121 new Request("https://host.example/api/auth/get-session", { method: "GET" }),
122 Option.some("198.51.100.4"),
123 );
124 expect(out.method).toBe("GET");
125 expect(out.body).toBeNull();
126 expect(out.headers.get(CLIENT_IP_HEADER)).toBe("198.51.100.4");
127 });
96});128});
97 129
98describe("makeClientIpStamper behind a trusted proxy", () => {130describe("makeClientIpStamper behind a trusted proxy", () => {
apps/host-selfhost/src/auth/client-ip.ts+25−1
@@ -107,6 +107,30 @@ export const clientIpAddressOptions = ( · 106 unmodified lines
107 }107 }
108 : { ipAddressHeaders: [CLIENT_IP_HEADER] };108 : { ipAddressHeaders: [CLIENT_IP_HEADER] };
109 109
110/**
111 * The request with its headers replaced, everything else passed through.
112 *
113 * Deliberately NOT `new Request(request, { headers })`: Bun's copy constructor
114 * never delivers a body that is not one of its own native streams, and the
115 * Vite dev middleware hands the handler `Readable.toWeb(req)`, so every
116 * sign-in POST hung until the client gave up (the production Bun.serve path
117 * was unaffected). Rebuilding from the parts with the body passed explicitly
118 * behaves the same on both paths; `duplex: "half"` is what the Fetch spec
119 * requires when a request body is a stream.
120 */
121const withHeaders = (request: Request, headers: Headers): Request => {
122 const init: RequestInit & { duplex?: "half" } = {
123 method: request.method,
124 headers,
125 signal: request.signal,
126 };
127 if (request.body) {
128 init.body = request.body;
129 init.duplex = "half";
130 }
131 return new Request(request.url, init);
132};
133
110export interface ClientIpStamperOptions {134export interface ClientIpStamperOptions {
111 /** Where the one-time unconfigured-proxy warning goes. Defaults to console.warn. */135 /** Where the one-time unconfigured-proxy warning goes. Defaults to console.warn. */
112 readonly warn?: (message: string) => void;136 readonly warn?: (message: string) => void;
@@ -153,6 +177,6 @@ export const makeClientIpStamper = ( · 40 unmodified lines
153 warn(unconfiguredProxyWarning(hint));177 warn(unconfiguredProxyWarning(hint));
154 }178 }
155 }179 }
156 return new Request(request, { headers });180 return withHeaders(request, headers);
157 };181 };
158};182};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t