Skip to content

Commit 9d65b96

Browse files
committed
Allow proxy middleware to reuse body from memo
1 parent adbaaed commit 9d65b96

File tree

4 files changed

+37
-14
lines changed

4 files changed

+37
-14
lines changed

body.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ export class Body {
8989
return this.#request?.bodyUsed ?? !!this.#used;
9090
}
9191

92+
/** Return the body to be reused as BodyInit. */
93+
async init(): Promise<BodyInit | null> {
94+
if (!this.has) {
95+
return null;
96+
}
97+
return await this.#memo ?? this.stream;
98+
}
99+
92100
/** Reads a body to the end and resolves with the value as an
93101
* {@linkcode ArrayBuffer} */
94102
async arrayBuffer(): Promise<ArrayBuffer> {

middleware/proxy.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,26 @@ Deno.test({
266266
await mw(ctx, next);
267267
},
268268
});
269+
270+
Deno.test({
271+
name: "proxy - consumed body",
272+
async fn() {
273+
async function fetch(request: Request): Promise<Response> {
274+
const body = await request.text();
275+
assertEquals(body, "hello world");
276+
return new Response(body);
277+
}
278+
279+
const mw = proxy("https://oakserver.github.io/", { fetch });
280+
const stream = ReadableStream.from([new TextEncoder().encode("hello world")]);
281+
const ctx = createMockContext({
282+
method: "POST",
283+
path: "/oak/FAQ",
284+
body: stream,
285+
});
286+
const next = createMockNext();
287+
288+
assertEquals(await ctx.request.body.text(), "hello world");
289+
await mw(ctx, next);
290+
},
291+
});

middleware/proxy.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ async function createRequest<
152152
}
153153
url.search = ctx.request.url.search;
154154

155-
const body = getBodyInit(ctx);
155+
const body = await ctx.request.body?.init() ?? null;
156156
const headers = new Headers(ctx.request.headers);
157157
if (optHeaders) {
158158
if (typeof optHeaders === "function") {
@@ -195,19 +195,6 @@ async function createRequest<
195195
return request;
196196
}
197197

198-
function getBodyInit<
199-
R extends string,
200-
P extends RouteParams<R>,
201-
S extends State,
202-
>(
203-
ctx: Context<S> | RouterContext<R, P, S>,
204-
): BodyInit | null {
205-
if (!ctx.request.hasBody) {
206-
return null;
207-
}
208-
return ctx.request.body.stream;
209-
}
210-
211198
function iterableHeaders(
212199
headers: HeadersInit,
213200
): IterableIterator<[string, string]> {

testing.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
type ErrorStatus,
1717
SecureCookieMap,
1818
} from "./deps.ts";
19+
import { Body } from "./body.ts";
1920
import type { RouteParams, RouterContext } from "./router.ts";
2021
import type { Request } from "./request.ts";
2122
import { Response } from "./response.ts";
@@ -67,6 +68,7 @@ export interface MockContextOptions<
6768
path?: string;
6869
state?: S;
6970
headers?: [string, string][];
71+
body?: ReadableStream;
7072
}
7173

7274
/** Allows external parties to modify the context state. */
@@ -90,6 +92,7 @@ export function createMockContext<
9092
state,
9193
app = createMockApp(state),
9294
headers: requestHeaders,
95+
body = undefined,
9396
}: MockContextOptions<R> = {},
9497
): RouterContext<R, P, S> {
9598
function createMockRequest(): Request {
@@ -120,6 +123,8 @@ export function createMockContext<
120123
search: undefined,
121124
searchParams: new URLSearchParams(),
122125
url: new URL(path, "http://localhost/"),
126+
hasBody: !!body,
127+
body: body ? new Body({ headers, getBody: () => body }) : undefined,
123128
} as any;
124129
}
125130

0 commit comments

Comments
 (0)