import { ServerRequest } from "./deps.ts";import { preferredEncodings } from "./encoding.ts";import httpErrors from "./httpError.ts";import { isMediaType } from "./isMediaType.ts";import { preferredMediaTypes } from "./mediaType.ts";import { HTTPMethods } from "./types.ts";
export enum BodyType { JSON = "json", Form = "form", Text = "text", Undefined = "undefined"}
export type Body = | { type: BodyType.JSON; value: any } | { type: BodyType.Form; value: URLSearchParams } | { type: BodyType.Text; value: string } | { type: BodyType.Undefined; value: undefined };
const decoder = new TextDecoder();
const jsonTypes = ["json", "application/*+json", "application/csp-report"];const formTypes = ["urlencoded"];const textTypes = ["text"];
export class Request { private _body?: Body; private _path: string; private _rawBodyPromise?: Promise<Uint8Array>; private _search?: string; private _searchParams: URLSearchParams; private _serverRequest: ServerRequest;
get hasBody(): boolean { return ( this.headers.get("transfer-encoding") !== null || !!parseInt(this.headers.get("content-length") || "") ); }
get headers(): Headers { return this._serverRequest.headers; }
get method(): HTTPMethods { return this._serverRequest.method as HTTPMethods; }
get path(): string { return this._path; }
get search(): string | undefined { return this._search; }
get searchParams(): URLSearchParams { return this._searchParams; }
get serverRequest(): ServerRequest { return this._serverRequest; }
get url(): string { return this._serverRequest.url; }
constructor(serverRequest: ServerRequest) { this._serverRequest = serverRequest; const [path, search] = serverRequest.url.split("?"); this._path = path; this._search = search ? `?${search}` : undefined; this._searchParams = new URLSearchParams(search); }
accepts(): string[]; accepts(...types: string[]): string | undefined; accepts(...types: string[]): string | string[] | undefined { const acceptValue = this._serverRequest.headers.get("Accept"); if (!acceptValue) { return; } if (types.length) { return preferredMediaTypes(acceptValue, types)[0]; } return preferredMediaTypes(acceptValue); }
acceptsCharsets(): string[]; acceptsCharsets(...charsets: string[]): string | undefined; acceptsCharsets(...charsets: string[]): string[] | string | undefined { return undefined; }
acceptsEncodings(): string[]; acceptsEncodings(...encodings: string[]): string | undefined; acceptsEncodings(...encodings: string[]): string[] | string | undefined { const acceptEncodingValue = this._serverRequest.headers.get( "Accept-Encoding" ); if (!acceptEncodingValue) { return; } if (encodings.length) { return preferredEncodings(acceptEncodingValue, encodings)[0]; } return preferredEncodings(acceptEncodingValue); }
acceptsLanguages(): string[]; acceptsLanguages(...langs: string[]): string | undefined; acceptsLanguages(...langs: string[]): string[] | string | undefined { return undefined; }
async body(): Promise<Body> { if (this._body) { return this._body; } const encoding = this.headers.get("content-encoding") || "identity"; if (encoding !== "identity") { throw new httpErrors.UnsupportedMediaType( `Unsupported content-encoding: ${encoding}` ); } if (!this.hasBody) { return { type: BodyType.Undefined, value: undefined }; } const contentType = this.headers.get("content-type"); if (contentType) { const rawBody = await (this._rawBodyPromise || (this._rawBodyPromise = this._serverRequest.body())); const str = decoder.decode(rawBody); if (isMediaType(contentType, jsonTypes)) { return (this._body = { type: BodyType.JSON, value: JSON.parse(str) }); } else if (isMediaType(contentType, formTypes)) { return (this._body = { type: BodyType.Form, value: new URLSearchParams(str) }); } else if (isMediaType(contentType, textTypes)) { return (this._body = { type: BodyType.Text, value: str }); } } throw new httpErrors.UnsupportedMediaType( contentType ? `Unsupported content-type: ${contentType}` : "Missing content-type" ); }}