@cldn/web-ts
Version:
Class-based Node.js web server
212 lines (211 loc) • 8.9 kB
TypeScript
import { IP } from "@cldn/ip";
import { Multipart } from "multipart-ts";
import http from "node:http";
import stream from "node:stream";
import { Authorisation } from "./auth/Authorisation.js";
import { Permission } from "./auth/index.js";
import { Server } from "./Server.js";
/**
* An incoming HTTP request from a connected client.
*/
export declare class Request<A> {
/**
* The request method.
*/
readonly method: Request.Method;
/**
* The original request address, as sent by the last peer. The {@link !URL} `protocol` is always set to `http:` and
* the `host` (and related) are always taken from the `HOST` environment variable or defaulted to `localhost`.
*
* If basic authentication is available to this request via headers, the `username` and `password` fields are
* available in the {@link URL} object.
*/
readonly originalUrl: Readonly<URL>;
/**
* The address requested by the client (first peer). If the request originated from a trusted proxy, this address
* will be constructed based on protocol and host provided by the proxy. If the proxy does not specify protocol,
* `http:` will be used as a default. If the proxy does not specify host (or the proxy is not trusted), will use the
* `Host` request header. If that is not specified either, will use the `HOST` environment variable or default to
* `localhost`.
*
* In short, this is, as closely as possible, the exact URL address the client requested.
*
* If basic authentication is available to this request via headers, the `username` and `password` fields are
* available in the {@link URL} object.
*/
readonly url: Readonly<URL>;
/**
* The request headers.
*/
readonly headers: Readonly<Headers>;
/**
* Request body readable stream.
*/
readonly bodyStream: stream.Readable;
/**
* The IP address of the request sender (last peer). This might be a proxy.
*/
readonly originalIp: IP;
/**
* IP address of client (first peer). If the request originated from a trusted proxy, this will be the client IP
* indicated by the proxy. Otherwise, if the proxy specifies no client IP, or the proxy is untrusted, this will be
* the proxy IP and equivalent to {@link Request#originalIp}.
*/
readonly ip: IP;
/**
* The {@link Server} from which this request was received.
*/
readonly server: Server<A>;
/**
* The components of the request URL path name.
*/
readonly pathComponents: ReadonlyArray<string>;
/**
* The parsed request cookies from the {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cookie|Cookie} request header.
*/
readonly cookies: ReadonlyMap<string, string>;
/**
* Construct a new Request.
* @param method See {@link Request#method}.
* @param originalUrl See {@link Request#originalUrl}.
* @param url See {@link Request#url}.
* @param headers See {@link Request#headers}.
* @param bodyStream See {@link Request#bodyStream}.
* @param originalIp See {@link Request#originalIp}.
* @param ip See {@link Request#ip}.
* @param server See {@link Request#server}.
* @throws {@link !URIError} If the request URL path name contains an invalid URI escape sequence.
*/
constructor(method: Request<A>["method"], originalUrl: Request<A>["originalUrl"], url: Request<A>["url"], headers: Request<A>["headers"], bodyStream: Request<A>["bodyStream"], originalIp: Request<A>["originalIp"], ip: Request<A>["ip"], server: Request<A>["server"]);
/**
* Create a new Request from a Node.js incoming HTTP request.
* @throws {@link Request.BadUrlError} If the request URL is invalid.
* @throws {@link Request.SocketClosedError} If the request socket was closed before the request could be handled.
*/
static incomingMessage<A>(incomingMessage: http.IncomingMessage, server: Server<A>): Request<A>;
/**
* Extract client IP, protocol, and host, from the information provided by a trusted proxy.
* @param headers The HTTP headers sent by a trusted proxy.
*/
private static getClientInfoFromTrustedProxy;
/**
* Attempt to obtain authorisation for this request with one of the {@link Server}’s {@link Authenticator}s.
* @returns `null` if the request lacks authorisation information.
*/
getAuthorisation(): Promise<Authorisation<A> | null>;
/**
* Returns a boolean value that declares whether the body has been read yet.
*/
bodyUsed(): boolean;
/**
* Returns a promise that resolves with an ArrayBuffer representation of the request body.
* @throws {@link Request.BodyAlreadyConsumedError} If the request body has already been consumed.
*/
arrayBuffer(): Promise<ArrayBuffer>;
/**
* Returns a promise that resolves with a Blob representation of the request body.
* @throws {@link Request.BodyAlreadyConsumedError} If the request body has already been consumed.
*/
blob(): Promise<Blob>;
/**
* Returns a promise that resolves with a Uint8Array representation of the request body.
* @throws {@link Request.BodyAlreadyConsumedError} If the request body has already been consumed.
*/
bytes(): Promise<Uint8Array>;
/**
* Returns a promise that resolves with a FormData representation of the request body.
* @throws {@link Request.BodyAlreadyConsumedError} If the request body has already been consumed.
* @throws {@link !TypeError} If the request body cannot be parsed as multipart.
*/
formData(): Promise<FormData>;
/**
* Returns a promise that resolves with the result of parsing the request body as JSON.
* @throws {@link Request.BodyAlreadyConsumedError} If the request body has already been consumed.
* @throws {@link !SyntaxError} If the request body cannot be parsed as JSON.
*/
json(): Promise<unknown>;
/**
* Returns a promise that resolves with a FormData representation of the request body.
* @throws {@link Request.BodyAlreadyConsumedError} If the request body has already been consumed.
* @throws {@link !TypeError} If the request body cannot be parsed as multipart.
*/
multipart(): Promise<Multipart>;
/**
* Returns a promise that resolves with a text representation of the request body.
* @throws {@link Request.BodyAlreadyConsumedError} If the request body has already been consumed.
*/
text(): Promise<string>;
/**
* Require that authorisation can be obtained from this request.
* @throws {@link ThrowableResponse} of {@link ServerErrorRegistry.ErrorCodes.UNAUTHORISED} if authorisation cannot
* be obtained.
*/
auth(): Promise<Authorisation<A>>;
/**
* Require that authorisation can be obtained from this request and that the given (requested) permission(s) are
* ALL within the scope of the authorisation.
* @param permissions The requested permissions.
* @throws {@link ThrowableResponse} of {@link ServerErrorRegistry.ErrorCodes.UNAUTHORISED} if authorisation cannot
* be obtained.
* @throws {@link ThrowableResponse} of {@link ServerErrorRegistry.ErrorCodes.NO_PERMISSION} if the authorisation
* lacks any of the requested permissions.
*/
auth(...permissions: [Permission, ...Permission[]]): Promise<Authorisation<A>>;
}
export declare namespace Request {
class BadUrlError extends Error {
readonly path: string | undefined;
constructor(path: string | undefined);
}
/**
* The request body has already been consumed.
*/
class BodyAlreadyConsumedError extends Error {
constructor();
}
/**
* HTTP request methods.
*/
const enum Method {
ACL = "ACL",
BIND = "BIND",
CHECKOUT = "CHECKOUT",
CONNECT = "CONNECT",
COPY = "COPY",
DELETE = "DELETE",
GET = "GET",
HEAD = "HEAD",
LINK = "LINK",
LOCK = "LOCK",
"M-SEARCH" = "M-SEARCH",
MERGE = "MERGE",
MKACTIVITY = "MKACTIVITY",
MKCALENDAR = "MKCALENDAR",
MKCOL = "MKCOL",
MOVE = "MOVE",
NOTIFY = "NOTIFY",
OPTIONS = "OPTIONS",
PATCH = "PATCH",
POST = "POST",
PROPFIND = "PROPFIND",
PROPPATCH = "PROPPATCH",
PURGE = "PURGE",
PUT = "PUT",
REBIND = "REBIND",
REPORT = "REPORT",
SEARCH = "SEARCH",
SOURCE = "SOURCE",
SUBSCRIBE = "SUBSCRIBE",
TRACE = "TRACE",
UNBIND = "UNBIND",
UNLINK = "UNLINK",
UNLOCK = "UNLOCK",
UNSUBSCRIBE = "UNSUBSCRIBE"
}
/**
* Socket closed by peer.
*/
class SocketClosedError extends Error {
constructor();
}
}