UNPKG

rjweb-server

Version:

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

299 lines (298 loc) 10.9 kB
/// <reference types="node" /> /// <reference types="node" /> /// <reference types="node" /> import InternalRequestContext from "../../types/internal/classes/RequestContext"; import { HttpContext } from "../../types/implementation/contexts/http"; import { Content, ParsedBody, RatelimitInfos } from "../../types/global"; import Base from "./Base"; import { ZodResponse } from "../../types/internal"; import { z } from "zod"; import { Writable } from "stream"; export default class HttpRequestContext<Context extends Record<any, any> = {}> extends Base<Context> { private rawContext; private abort; constructor(context: InternalRequestContext, rawContext: HttpContext, abort: AbortSignal); /** * The Type of this Request * @since 5.7.0 */ readonly type: 'http' | 'ws'; /** * HTTP Status Codes Enum * @since 9.0.0 */ $status: Readonly<{ CONTINUE: 100; SWITCHING_PROTOCOLS: 101; PROCESSING: 102; EARLY_HINTS: 103; OK: 200; CREATED: 201; ACCEPTED: 202; NON_AUTHORATIVE_INFORMATION: 203; NO_CONTENT: 204; RESET_CONTENT: 205; PARTIAL_CONTENT: 206; MULTI_STATUS: 207; ALREADY_REPORTED: 208; IM_USED: 226; MULTIPLE_CHOICES: 300; MOVED_PERMANENTLY: 301; FOUND: 302; SEE_OTHER: 303; NOT_MODIFIED: 304; USE_PROXY: 305; TEMPORARY_REDIRECT: 307; PERMANENT_REDIRECT: 308; BAD_REQUEST: 400; UNAUTHORIZED: 401; PAYMENT_REQUIRED: 402; FORBIDDEN: 403; NOT_FOUND: 404; METHOD_NOT_ALLOWED: 405; NOT_ACCEPTABLE: 406; PROXY_AUTHENTICATION_REQUIRED: 407; REQUEST_TIMEOUT: 408; CONFLICT: 409; GONE: 410; LENGTH_REQUIRED: 411; PRECONDITION_FAILED: 412; PAYLOAD_TOO_LARGE: 413; URI_TOO_LONG: 414; UNSUPPORTED_MEDIA_TYPE: 415; RANGE_NOT_SATISFIABLE: 416; EXPECTATION_FAILED: 417; IM_A_TEAPOT: 418; MISDIRECTED_REQUEST: 421; UNPROCESSABLE_ENTITY: 422; LOCKED: 423; FAILED_DEPENDENCY: 424; TOO_EARLY: 425; UPGRADE_REQUIRED: 426; PRECONDITION_REQUIRED: 428; TOO_MANY_REQUESTS: 429; REQUEST_HEADER_FIELDS_TOO_LARGE: 431; UNAVAILABLE_FOR_LEGAL_REASONS: 451; INTERNAL_SERVER_ERROR: 500; NOT_IMPLEMENTED: 501; BAD_GATEWAY: 502; SERVICE_UNAVAILABLE: 503; GATEWAY_TIMEOUT: 504; HTTP_VERSION_NOT_SUPPORTED: 505; VARIANT_ALSO_NEGOTIATES: 506; INSUFFICIENT_STORAGE: 507; LOOP_DETECTED: 508; BANDWIDTH_LIMIT_EXCEEDED: 509; NOT_EXTENDED: 510; NETWORK_AUTHENTICATION_REQUIRED: 511; }>; /** * HTTP Abort Controller (please use to decrease server load) * @since 9.0.0 */ $abort(callback?: () => void): boolean; /** * The Request Body (JSON and Urlencoding Automatically parsed if enabled) * @since 0.4.0 */ body(): Promise<ParsedBody>; /** * The HTTP Body Type * @since 9.0.0 */ bodyType(): Promise<'raw' | 'json' | 'url-encoded'>; /** * The Raw Request Body * @since 5.5.2 */ rawBody(encoding: BufferEncoding): Promise<string>; /** * The Raw Request Body as Buffer * @since 8.1.4 */ rawBodyBytes(): Promise<Buffer>; /** * Bind the Body using Zod * * This uses `.body` internally so no binary data * @example * ``` * const [ data, error ] = await ctr.bindBody((z) => z.object({ * name: z.string().max(24), * gender: z.union([ z.literal('male'), z.literal('female') ]) * })) * * if (!data) return ctr.status((s) => s.BAD_REQUEST).print(error.toString()) * * ctr.print('Everything valid! 👍') * ctr.printPart(` * your name is ${data.name} * and you are a ${data.gender} * `) * ``` * @since 8.8.0 */ bindBody<Schema extends z.ZodTypeAny>(schema: Schema | ((z: typeof import('zod')) => Schema)): Promise<ZodResponse<Schema>>; /** * 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(ctr.$status.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; /** * Clear the active Ratelimit of the Client * * This Clears the currently active Ratelimit (on this endpoint) of the Client, remember: * you cant call this in a normal endpoint if the max hits are already reached since well... * they are already reached. * @since 8.6.0 */ clearRateLimit(): this; /** * Skips counting the request to the Client IPs Rate limit (if there is one) * * When a specific IP makes a request to an endpoint under a ratelimit, the maxhits will be * increased instantly to prevent bypassing the rate limit by spamming requests faster than the host can * handle. When this function is called, the server removes the set hit again. * @since 8.6.0 */ skipRateLimit(): this; /** * Get Infos about the current Ratelimit * * This will get all information about the currently applied ratelimit * to the endpoint. If none is active, will return `null`. * @since 8.6.0 */ getRateLimit(): RatelimitInfos | 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(ctr.$status.IM_A_TEAPOT).print('Im a Teapot, mate!') * ``` * @since 0.0.2 */ status(code: 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!' * }, 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, prettify?: boolean): this; /** * Print a Message to the client (without resetting the previous message state) * * This will turn your response into a chunked response, this means that you cannot * add headers or cookies after this function has been called. This function is useful * if you want to add content to the response body without resetting the previous content. * And when you manually want to print massive amounts of data to the client without having * to store it in memory. * @example * ``` * const file = fs.createReadStream('./10GB.bin') * * ctr.printChunked((print) => new Promise<void>((end) => { * file.on('data', (chunk) => { * file.pause() * print(chunk) * .then(() => file.resume()) * }) * * file.on('end', () => { * end() * }) * })) * ``` * @since 8.2.0 */ printChunked<Callback extends ((print: (content: Content) => Promise<void>) => Promise<any>) | null>(callback: Callback): Callback extends null ? Writable : 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?: { /** * The Name of the File (if not set, the basename of the file will be used) * * Only applied if the `content-disposition` header is not set and options.download is true * @default path.basename(file) * @since 9.1.4 */ name?: string; /** * Whether to download the file or display it in the browser * @default ctr.headers.get('content-type') === 'application/octet-stream' * @since 9.1.4 */ download?: boolean; /** * 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; }): this; }