summaryrefslogtreecommitdiff
path: root/std/http/_io.ts
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2020-06-04 03:32:27 +0100
committerGitHub <noreply@github.com>2020-06-03 22:32:27 -0400
commit97d876f6db55f5f32ac06155dcb4823e1c636215 (patch)
tree0171130f480548663608dd9440e8bf37fff68c5f /std/http/_io.ts
parent9bd5c08d5a2a9cdd2649dd24a4d6b26d9af2a4c4 (diff)
fix(std/http): Don't use assert() for user input validation (#6092)
Diffstat (limited to 'std/http/_io.ts')
-rw-r--r--std/http/_io.ts84
1 files changed, 49 insertions, 35 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"));