summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--std/http/_io.ts84
-rw-r--r--std/http/_io_test.ts25
2 files changed, 61 insertions, 48 deletions
diff --git a/std/http/_io.ts b/std/http/_io.ts
index 6b3796c5b..82954ccee 100644
--- a/std/http/_io.ts
+++ b/std/http/_io.ts
@@ -118,27 +118,37 @@ function isProhibidedForTrailer(key: string): boolean {
return s.has(key.toLowerCase());
}
-/**
- * Read trailer headers from reader and append values to headers.
- * "trailer" field will be deleted.
- * */
+/** Read trailer headers from reader and append values to headers. "trailer"
+ * field will be deleted. */
export async function readTrailers(
headers: Headers,
r: BufReader
): Promise<void> {
- const headerKeys = parseTrailer(headers.get("trailer"));
- if (!headerKeys) return;
+ const trailers = parseTrailer(headers.get("trailer"));
+ if (trailers == null) return;
+ const trailerNames = [...trailers.keys()];
const tp = new TextProtoReader(r);
const result = await tp.readMIMEHeader();
- assert(result !== null, "trailer must be set");
+ if (result == null) {
+ throw new Deno.errors.InvalidData("Missing trailer header.");
+ }
+ const undeclared = [...result.keys()].filter(
+ (k) => !trailerNames.includes(k)
+ );
+ if (undeclared.length > 0) {
+ throw new Deno.errors.InvalidData(
+ `Undeclared trailers: ${Deno.inspect(undeclared)}.`
+ );
+ }
for (const [k, v] of result) {
- if (!headerKeys.has(k)) {
- throw new Error("Undeclared trailer field");
- }
- headerKeys.delete(k);
headers.append(k, v);
}
- assert(Array.from(headerKeys).length === 0, "Missing trailers");
+ const missingTrailers = trailerNames.filter((k) => !result.has(k));
+ if (missingTrailers.length > 0) {
+ throw new Deno.errors.InvalidData(
+ `Missing trailers: ${Deno.inspect(missingTrailers)}.`
+ );
+ }
headers.delete("trailer");
}
@@ -146,16 +156,17 @@ function parseTrailer(field: string | null): Headers | undefined {
if (field == null) {
return undefined;
}
- const keys = field.split(",").map((v) => v.trim().toLowerCase());
- if (keys.length === 0) {
- throw new Error("Empty trailer");
+ const trailerNames = field.split(",").map((v) => v.trim().toLowerCase());
+ if (trailerNames.length === 0) {
+ throw new Deno.errors.InvalidData("Empty trailer header.");
}
- for (const key of keys) {
- if (isProhibidedForTrailer(key)) {
- throw new Error(`Prohibited field for trailer`);
- }
+ const prohibited = trailerNames.filter((k) => isProhibidedForTrailer(k));
+ if (prohibited.length > 0) {
+ throw new Deno.errors.InvalidData(
+ `Prohibited trailer names: ${Deno.inspect(prohibited)}.`
+ );
}
- return new Headers(keys.map((key) => [key, ""]));
+ return new Headers(trailerNames.map((key) => [key, ""]));
}
export async function writeChunkedBody(
@@ -176,7 +187,8 @@ export async function writeChunkedBody(
await writer.write(endChunk);
}
-/** write trailer headers to writer. it mostly should be called after writeResponse */
+/** Write trailer headers to writer. It should mostly should be called after
+ * `writeResponse()`. */
export async function writeTrailers(
w: Deno.Writer,
headers: Headers,
@@ -184,29 +196,31 @@ export async function writeTrailers(
): Promise<void> {
const trailer = headers.get("trailer");
if (trailer === null) {
- throw new Error('response headers must have "trailer" header field');
+ throw new TypeError("Missing trailer header.");
}
const transferEncoding = headers.get("transfer-encoding");
if (transferEncoding === null || !transferEncoding.match(/^chunked/)) {
- throw new Error(
- `trailer headers is only allowed for "transfer-encoding: chunked": got "${transferEncoding}"`
+ throw new TypeError(
+ `Trailers are only allowed for "transfer-encoding: chunked", got "transfer-encoding: ${transferEncoding}".`
);
}
const writer = BufWriter.create(w);
- const trailerHeaderFields = trailer
- .split(",")
- .map((s) => s.trim().toLowerCase());
- for (const f of trailerHeaderFields) {
- assert(
- !isProhibidedForTrailer(f),
- `"${f}" is prohibited for trailer header`
+ const trailerNames = trailer.split(",").map((s) => s.trim().toLowerCase());
+ const prohibitedTrailers = trailerNames.filter((k) =>
+ isProhibidedForTrailer(k)
+ );
+ if (prohibitedTrailers.length > 0) {
+ throw new TypeError(
+ `Prohibited trailer names: ${Deno.inspect(prohibitedTrailers)}.`
);
}
+ const undeclared = [...trailers.keys()].filter(
+ (k) => !trailerNames.includes(k)
+ );
+ if (undeclared.length > 0) {
+ throw new TypeError(`Undeclared trailers: ${Deno.inspect(undeclared)}.`);
+ }
for (const [key, value] of trailers) {
- assert(
- trailerHeaderFields.includes(key),
- `Not trailer header field: ${key}`
- );
await writer.write(encoder.encode(`${key}: ${value}\r\n`));
}
await writer.write(encoder.encode("\r\n"));
diff --git a/std/http/_io_test.ts b/std/http/_io_test.ts
index 3e74e365d..3b385d013 100644
--- a/std/http/_io_test.ts
+++ b/std/http/_io_test.ts
@@ -1,5 +1,4 @@
import {
- AssertionError,
assertThrowsAsync,
assertEquals,
assert,
@@ -105,8 +104,8 @@ test("readTrailer should throw if undeclared headers found in trailer", async ()
async () => {
await readTrailers(h, new BufReader(new Buffer(encode(trailer))));
},
- Error,
- "Undeclared trailer field"
+ Deno.errors.InvalidData,
+ `Undeclared trailers: [ "`
);
}
});
@@ -120,8 +119,8 @@ test("readTrailer should throw if trailer contains prohibited fields", async ()
async () => {
await readTrailers(h, new BufReader(new Buffer()));
},
- Error,
- "Prohibited field for trailer"
+ Deno.errors.InvalidData,
+ `Prohibited trailer names: [ "`
);
}
});
@@ -145,15 +144,15 @@ test("writeTrailer should throw", async () => {
() => {
return writeTrailers(w, new Headers(), new Headers());
},
- Error,
- 'must have "trailer"'
+ TypeError,
+ "Missing trailer header."
);
await assertThrowsAsync(
() => {
return writeTrailers(w, new Headers({ trailer: "deno" }), new Headers());
},
- Error,
- "only allowed"
+ TypeError,
+ `Trailers are only allowed for "transfer-encoding: chunked", got "transfer-encoding: null".`
);
for (const f of ["content-length", "trailer", "transfer-encoding"]) {
await assertThrowsAsync(
@@ -164,8 +163,8 @@ test("writeTrailer should throw", async () => {
new Headers({ [f]: "1" })
);
},
- AssertionError,
- "prohibited"
+ TypeError,
+ `Prohibited trailer names: [ "`
);
}
await assertThrowsAsync(
@@ -176,8 +175,8 @@ test("writeTrailer should throw", async () => {
new Headers({ node: "js" })
);
},
- AssertionError,
- "Not trailer"
+ TypeError,
+ `Undeclared trailers: [ "node" ].`
);
});