UNPKG

rjweb-server

Version:

Easy and Robust Way to create a Web Server with Many Easy-to-use Features in NodeJS

260 lines (259 loc) 9.45 kB
/// <reference types="node" /> /// <reference types="node" /> import { HttpRequest, HttpResponse } from "@rjweb/uws"; import Status from "../../misc/statusEnum"; import Server from "../server"; import { LocalContext } from "../../types/context"; import { Content } from "../../functions/parseContent"; import HTMLBuilder from "../HTMLBuilder"; import { Readable } from "stream"; import Base from "./Base"; export declare const toArrayBuffer: (buffer: Buffer) => ArrayBuffer; export default class HTTPRequest<Context extends Record<any, any> = {}, Body = unknown, Path extends string = '/'> extends Base<Context, Path> { /** * Initializes a new Instance of a Web Context * @since 7.0.0 */ constructor(controller: Server<any, any>, localContext: LocalContext, req: HttpRequest, res: HttpResponse, type: 'http' | 'upgrade'); /** * The Type of this Request * @since 5.7.0 */ readonly type: 'http' | 'upgrade'; /** * The Raw HTTP Server Req Variable * @since 0.2.2 */ readonly rawReq: HttpRequest; /** * The Raw HTTP Server Res Variable * @since 0.2.2 */ readonly rawRes: HttpResponse; /** * The Type of the HTTP Body * @since 7.8.0 */ get bodyType(): LocalContext['body']['type']; /** * The Request Body (JSON Automatically parsed if enabled) * @since 0.4.0 */ get body(): Body; /** * The Raw Request Body * @since 5.5.2 */ get rawBody(): string; /** * The Raw Request Body as Buffer * @since 8.1.4 */ get rawBodyBytes(): Buffer; /** * HTTP WWW-Authentication Checker * * This will validate the Authorization Header using the WWW-Authentication Standard, * you can choose between `basic` and `digest` authentication, in most cases `digest` * should be used unless you are using an outdated client or want to test easily. * When not matching any user the method will return `null` and the request should be * ended with a `Status.UNAUTHORIZED` (401) status code. * @example * ``` * const user = ctr.wwwAuth('basic', 'Access this Page.', { // Automatically adds www-authenticate header * bob: '123!', * rotvproHD: 'password' * }) * * if (!user) return ctr.status((s) => s.UNAUTHORIZED).print('Invalid credentials') * * ctr.print('You authenticated with user:', user) * ``` * @since 8.0.0 */ wwwAuth<Users extends Record<string, string>>(type: 'basic' | 'digest', reason: string, users: Users): keyof Users | null; /** * The Request Status to Send * * This will set the status of the request that the client will recieve, by default * the status will be `200`, the server will not change this value unless calling the * `.redirect()` method. If you want to add a custom message to the status you can provide * a second argument that sets that, for RFC documented codes this will automatically be * set but can be overridden, the mapping is provided by `http.STATUS_CODES` * @example * ``` * ctr.status(401).print('Unauthorized') * * // or * ctr.status(666, 'The Devil').print('The Devil') * * // or * ctr.status((c) => c.IM_A_TEAPOT).print('Im a Teapot, mate!') * ``` * @since 0.0.2 */ status(code: number | ((codes: typeof Status) => number), message?: string): this; /** * Redirect a Client to another URL * * This will set the location header and the status to either to 301 or 302 depending * on whether the server should tell the browser that the page has permanently moved * or temporarily. Obviously this will only work correctly if the client supports the * 30x Statuses combined with the location header. * @example * ``` * ctr.redirect('https://example.com', 'permanent') // Will redirect to that URL * ``` * @since 2.8.5 */ redirect(location: string, type?: 'temporary' | 'permanent'): this; /** * Print a Message to the Client (automatically Formatted) * * This Message will be the one actually sent to the client, nothing * can be "added" to the content using this function, it can only be replaced using `.print()` * To add content to the response body, use `.printPart()` instead. * @example * ``` * ctr.print({ * message: 'this is json!' * }) * * // content will be `{"message":"this is json!"}` * * /// or * * ctr.print({ * message: 'this is json!' * }, { * prettify: true * }) * * // content will be `{\n "message": "this is json!"\n}` * * /// or * * ctr.print('this is text!') * // content will be `this is text!` * ``` * @since 0.0.2 */ print(content: Content, options?: { /** * Whether to prettify output (currently just JSONs) * @default false * @since 6.2.0 */ prettify?: boolean; }): this; /** * Print a Message to the client (without resetting the previous message state) * * This will add content to the current response body, if being called without `.print()` * before, the response body will be only this, basically the first call is the same as `.print()`. * this could be used when for example you want to loop over an array asynchronously without some * `await Promise.all(array.map(async() => ...))` voodo magic. Basically just call `.printPart()` * after finishing an iteration. * @example * ``` * ctr.printPart('hi') * ctr.printPart(' ') * ctr.printPart('mate') * * // content will be `hi mate` * ``` * @since 8.2.0 */ printPart(content: Content, options?: { /** * Whether to prettify end output (currently just JSONs) * @default false * @since 8.2.0 */ prettify?: boolean; }): this; /** * Print a Message made using the HTML Builder & Formatter * * This will set the http response body to an automatically generated html template * defined by the callback function. This also allows some quality of life features such * as `.every()` to change your html every x miliseconds without writing the frontend js * manually. * @example * ``` * const userInput = '<script>alert("xss!!!!")</script>' * * ctr.printHTML((html) => html * .t('head', {}, (t) => t * .t('title', {}, (t) => t * .escaped(userInput) // no xss attack because of .escaped() * ) * ) * .t('body', {}, (t) => t * .t( * 'h1', * { style: { color: 'red' } }, * (t) => t * .raw('Hello world matey!') * ) * ) * ) * ``` * @since 6.6.0 */ printHTML(callback: (html: HTMLBuilder) => HTMLBuilder, options?: { /** * The HTML Language to show at the top html tag * @default "en" * @since 6.6.0 */ htmlLanguage?: string; }): this; /** * Print the Content of a File to the Client * * This will print a file to the client using transfer encoding chunked and * if `addTypes` is enabled automatically add some content types based on the * file extension. This function wont respect any other http response body set by * `.print()` or any other normal print as this overwrites the custom ctx execution * function. * @example * ``` * ctr.printFile('./profile.png', { * addTypes: true // Automatically add Content types * }) * ``` * @since 0.6.3 */ printFile(file: string, options?: { /** * Whether some Content Type Headers will be added automatically * @default true * @since 2.2.0 */ addTypes?: boolean; /** * Whether to compress this File * @default true * @since 7.9.0 */ compress?: boolean; /** * Whether to Cache the sent Files after accessed once (only renew after restart) * @default false * @since 2.2.0 */ cache?: boolean; }): this; /** * Print the `data` event of a Stream to the Client * * This will print the `data` event of a stream to the client and makes the connection * stay alive until the stream is closed or the client disconnects. Best usecase of this is * probably Server Side Events for something like a front page as websockets can be quite * expensive. Remember to set the correct content type header when doing that. * @example * ``` * const fileStream = fs.createReadStream('./profile.png') * ctr.printStream(fileStream) * * // in this case though just use ctr.printFile since it does exactly this * ``` * @since 4.3.0 */ printStream(stream: Readable, options?: { /** * Whether to end the Request after the Stream finishes * @default true * @since 4.3.5 */ endRequest?: boolean; /** * Whether to prettify output (currently just JSONs) * @default false * @since 7.4.0 */ prettify?: boolean; /** * Whether to Destroy the Stream when the Request gets aborted * @default true * @since 4.3.5 */ destroyAbort?: boolean; }): this; }