/// /// import { Dict } from '../utils/types'; import Route from './route'; import { AppConfig } from './config'; import * as http from 'http'; import * as parseRange from 'range-parser'; /** * The Request class represents an incoming HTTP request (specifically, Node's * IncomingMessage). * * @package runtime * @since 0.1.0 */ export default class Request { /** * A UUID generated unqiue to this request. Useful for tracing a request * through the application. * * @since 0.1.0 */ id: string; /** * The route parser route that was matched * * @since 0.1.0 */ route: Route; /** * The name of the original action that was invoked - useful for error * actions to create helpful debug messages. * * @since 0.1.0 */ _originalAction: string; /** * The underlying HTTP server's IncomingMessage instance * * @since 0.1.0 */ incomingMessage: http.IncomingMessage; /** * A subset of the app config, the `config.server` namespace * * @since 0.1.0 */ config: AppConfig['server']; /** * The uppercase method name for the request, i.e. GET, POST, HEAD * * @since 0.1.0 */ readonly method: string; /** * The requested path name * * @since 0.1.0 */ readonly path: string; /** * The params extracted from the router's dynamic segments * * @since 0.1.0 */ params: any; /** * The query string, parsed into an object * * @since 0.1.0 */ readonly query: Dict; /** * The headers for the incoming request * * @since 0.1.0 */ readonly headers: Dict; /** * Return subdomains as an array. * * Subdomains are the dot-separated parts of the host before the main domain * of the app. By default, the domain of the app is assumed to be the last * two parts of the host. This can be changed by setting * config.server.subdomainOffset * * For example, if the domain is "tobi.ferrets.example.com": If the subdomain * offset is not set, req.subdomains is `["ferrets", "tobi"]`. If the * subdomain offset is 3, req.subdomains is `["tobi"]`. * * @since 0.1.0 */ readonly subdomains: string[]; /** * Return the protocol string "http" or "https" when requested with TLS. When * the "server.trustProxy" setting trusts the socket address, the * "X-Forwarded-Proto" header field will be trusted and used if present. * * If you're running behind a reverse proxy that supplies https for you this * may be enabled. * * @since 0.1.0 */ readonly protocol: 'http' | 'https'; /** * Check if the request was an _XMLHttpRequest_. * * @since 0.1.0 */ readonly xhr: boolean; /** * Parse the "Host" header field to a hostname. * * When the "trust proxy" setting trusts the socket address, the * "X-Forwarded-Host" header field will be trusted. * * @since 0.1.0 */ readonly hostname: string; /** * Return the remote address from the trusted proxy. * * The is the remote address on the socket unless "trust proxy" is set. * * @since 0.1.0 */ readonly ip: string; /** * When "trust proxy" is set, trusted proxy addresses + client. * * For example if the value were "client, proxy1, proxy2" you would receive * the array `["client", "proxy1", "proxy2"]` where "proxy2" is the furthest * down-stream and "proxy1" and "proxy2" were trusted. * * @since 0.1.0 */ readonly ips: string[]; /** * Does this request have a request body? */ readonly hasBody: boolean; constructor(incomingMessage: http.IncomingMessage, serverConfig?: AppConfig['server']); /** * Return request header. * * The `Referrer` header field is special-cased, both `Referrer` and * `Referer` are interchangeable. * * Examples: * * req.get('Content-Type'); // => "text/plain" * * req.get('content-type'); // => "text/plain" * * req.get('Something'); // => undefined * * Aliased as `req.header()`. * @since 0.1.0 */ getHeader(name: string): string; getHeader(name: 'set-cookie' | 'Set-cookie' | 'Set-Cookie'): string[]; /** * Check if the given `type(s)` is acceptable, returning the best match when * true, otherwise `undefined`, in which case you should respond with 406 * "Not Acceptable". * * The `type` value may be a single MIME type string such as * "application/json", an extension name such as "json", a comma-delimited * list such as "json, html, text/plain", an argument list such as `"json", * "html", "text/plain"`, or an array `["json", "html", "text/plain"]`. When * a list or array is given, the _best_ match, if any is returned. * * Examples: * * // Accept: text/html * req.accepts('html'); * // => "html" * * // Accept: text/*, application/json * req.accepts('html'); * // => "html" * req.accepts('text/html'); * // => "text/html" * req.accepts('json, text'); * // => "json" * req.accepts('application/json'); * // => "application/json" * * // Accept: text/*, application/json * req.accepts('image/png'); * req.accepts('png'); * // => undefined * * // Accept: text/*;q=.5, application/json * req.accepts(['html', 'json']); * req.accepts('html', 'json'); * req.accepts('html, json'); * // => "json" * * @since 0.1.0 */ accepts(): string[]; accepts(...type: string[]): string[] | string | false; /** * Check if the given `encoding`s are accepted. * * @since 0.1.0 */ acceptsEncodings(): string[]; acceptsEncodings(...encoding: string[]): string | false; /** * Check if the given `charset`s are acceptable, otherwise you should respond * with 406 "Not Acceptable". * * @since 0.1.0 */ acceptsCharsets(): string[]; acceptsCharsets(...charset: string[]): string | false; /** * Check if the given `lang`s are acceptable, otherwise you should respond * with 406 "Not Acceptable". * * @since 0.1.0 */ acceptsLanguages(): string[]; acceptsLanguages(...lang: string[]): string | false; /** * Parse Range header field, capping to the given `size`. * * Unspecified ranges such as "0-" require knowledge of your resource length. * In the case of a byte range this is of course the total number of bytes. If * the Range header field is not given `undefined` is returned, `-1` when * unsatisfiable, and `-2` when syntactically invalid. * * When ranges are returned, the array has a "type" property which is the type * of range that is required (most commonly, "bytes"). Each array element is * an object with a "start" and "end" property for the portion of the range. * * The "combine" option can be set to `true` and overlapping & adjacent ranges * will be combined into a single range. * * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3" * should respond with 4 users when available, not 3. * * @since 0.1.0 */ range(size: number, options?: parseRange.Options): parseRange.Result | parseRange.Ranges; /** * Check if the incoming request contains the "Content-Type" header field, * and it contains the give mime `type`. * * Examples: * * // With Content-Type: text/html; charset=utf-8 * req.is('html'); * req.is('text/html'); * req.is('text/*'); * // => true * * // When Content-Type is application/json * req.is('json'); * req.is('application/json'); * req.is('application/*'); * // => true * * req.is('html'); * // => false * * @since 0.1.0 */ is(...types: string[]): string | false; }