diff options
Diffstat (limited to 'std/http/server.ts')
| -rw-r--r-- | std/http/server.ts | 62 |
1 files changed, 50 insertions, 12 deletions
diff --git a/std/http/server.ts b/std/http/server.ts index 0bd5d06b5..b8a41379f 100644 --- a/std/http/server.ts +++ b/std/http/server.ts @@ -12,14 +12,6 @@ import { deferred, Deferred, MuxAsyncIterator } from "../util/async.ts"; const encoder = new TextEncoder(); -function bufWriter(w: Writer): BufWriter { - if (w instanceof BufWriter) { - return w; - } else { - return new BufWriter(w); - } -} - export function setContentLength(r: Response): void { if (!r.headers) { r.headers = new Headers(); @@ -30,16 +22,16 @@ export function setContentLength(r: Response): void { // typeof r.body === "string" handled in writeResponse. if (r.body instanceof Uint8Array) { const bodyLength = r.body.byteLength; - r.headers.append("Content-Length", bodyLength.toString()); + r.headers.set("content-length", bodyLength.toString()); } else { - r.headers.append("Transfer-Encoding", "chunked"); + r.headers.set("transfer-encoding", "chunked"); } } } } async function writeChunkedBody(w: Writer, r: Reader): Promise<void> { - const writer = bufWriter(w); + const writer = BufWriter.create(w); for await (const chunk of toAsyncIterator(r)) { if (chunk.byteLength <= 0) continue; @@ -53,13 +45,54 @@ async function writeChunkedBody(w: Writer, r: Reader): Promise<void> { const endChunk = encoder.encode("0\r\n\r\n"); await writer.write(endChunk); } +const kProhibitedTrailerHeaders = [ + "transfer-encoding", + "content-length", + "trailer" +]; + +/** write trailer headers to writer. it mostly should be called after writeResponse */ +export async function writeTrailers( + w: Writer, + headers: Headers, + trailers: Headers +): Promise<void> { + const trailer = headers.get("trailer"); + if (trailer === null) { + throw new Error('response headers must have "trailer" header field'); + } + 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}"` + ); + } + const writer = BufWriter.create(w); + const trailerHeaderFields = trailer + .split(",") + .map(s => s.trim().toLowerCase()); + for (const f of trailerHeaderFields) { + assert( + !kProhibitedTrailerHeaders.includes(f), + `"${f}" is prohibited for trailer header` + ); + } + 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.flush(); +} export async function writeResponse(w: Writer, r: Response): Promise<void> { const protoMajor = 1; const protoMinor = 1; const statusCode = r.status || 200; const statusText = STATUS_TEXT.get(statusCode); - const writer = bufWriter(w); + const writer = BufWriter.create(w); if (!statusText) { throw Error("bad status code"); } @@ -97,6 +130,10 @@ export async function writeResponse(w: Writer, r: Response): Promise<void> { } else { await writeChunkedBody(writer, r.body); } + if (r.trailers) { + const t = await r.trailers(); + await writeTrailers(writer, headers, t); + } await writer.flush(); } @@ -572,4 +609,5 @@ export interface Response { status?: number; headers?: Headers; body?: Uint8Array | Reader | string; + trailers?: () => Promise<Headers> | Headers; } |
