@tunframework/tun
Version:
tun framework for node with typescript
484 lines (462 loc) • 16.5 kB
TypeScript
/// <reference types="node" />
import { EventEmitter } from 'events';
import { IncomingHttpHeaders } from 'http';
import { IncomingMessage } from 'http';
import { ListenOptions } from 'net';
import { OutgoingHttpHeaders } from 'http';
import { Server } from 'http';
import { ServerResponse } from 'http';
import { Socket } from 'net';
import { Stream } from 'stream';
import { URL as URL_2 } from 'url';
import { URLSearchParams as URLSearchParams_2 } from 'url';
export declare function compose<Context>(composables: TunComposable<Context>[]): (ctx: Context, next?: TunComposable<Context> | undefined) => Promise<any>;
/**
* Wrapped file for formidable
*/
declare interface File_2 {
size: number;
path: string;
name: string;
type: string;
lastModifiedDate?: Date;
hash?: string;
toJSON(): Object;
}
export { File_2 as File }
/* Excluded from this release type: _getRequestCookieByName */
export declare class HttpError extends Error {
status: number;
constructor({ status, message, error }: HttpErrorOptions);
}
export declare interface HttpErrorOptions {
status?: number;
message?: string;
error?: Error;
}
/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
*/
export declare const HttpMethod: {
readonly GET: "GET";
readonly HEAD: "HEAD";
readonly POST: "POST";
readonly PUT: "PUT";
readonly DELETE: "DELETE";
readonly CONNECT: "CONNECT";
readonly OPTIONS: "OPTIONS";
readonly TRACE: "TRACE";
readonly PATCH: "PATCH";
};
/**
* @see https://stackoverflow.com/questions/50784444/add-description-attribute-to-enum-and-read-this-description-in-typescript
*/
export declare const HttpStatus: {
readonly CONTINUE: 100;
readonly SWITCHING_PROTOCOLS: 101;
readonly PROCESSING: 102;
readonly OK: 200;
readonly CREATED: 201;
readonly ACCEPTED: 202;
readonly NON_AUTHORITATIVE_INFORMATION: 203;
readonly NO_CONTENT: 204;
readonly RESET_CONTENT: 205;
readonly PARTIAL_CONTENT: 206;
readonly MULTI_STATUS: 207;
readonly ALREADY_REPORTED: 208;
readonly IM_USED: 226;
readonly MULTIPLE_CHOICES: 300;
readonly MOVED_PERMANENTLY: 301;
readonly FOUND: 302;
readonly SEE_OTHER: 303;
readonly NOT_MODIFIED: 304;
readonly USE_PROXY: 305;
readonly TEMPORARY_REDIRECT: 307;
readonly PERMANENT_REDIRECT: 308;
readonly BAD_REQUEST: 400;
readonly UNAUTHORIZED: 401;
readonly PAYMENT_REQUIRED: 402;
readonly FORBIDDEN: 403;
readonly NOT_FOUND: 404;
readonly METHOD_NOT_ALLOWED: 405;
readonly NOT_ACCEPTABLE: 406;
readonly PROXY_AUTHENTICATION_REQUIRED: 407;
readonly REQUEST_TIMEOUT: 408;
readonly CONFLICT: 409;
readonly GONE: 410;
readonly LENGTH_REQUIRED: 411;
readonly PRECONDITION_FAILED: 412;
readonly PAYLOAD_TOO_LARGE: 413;
readonly URI_TOO_LONG: 414;
readonly UNSUPPORTED_MEDIA_TYPE: 415;
readonly RANGE_NOT_SATISFIABLE: 416;
readonly EXPECTATION_FAILED: 417;
readonly IM_A_TEAPOT: 418;
readonly UNPROCESSABLE_ENTITY: 422;
readonly LOCKED: 423;
readonly FAILED_DEPENDENCY: 424;
readonly UPGRADE_REQUIRED: 426;
readonly PRECONDITION_REQUIRED: 428;
readonly TOO_MANY_REQUESTS: 429;
readonly REQUEST_HEADER_FIELDS_TOO_LARGE: 431;
readonly INTERNAL_SERVER_ERROR: 500;
readonly NOT_IMPLEMENTED: 501;
readonly BAD_GATEWAY: 502;
readonly SERVICE_UNAVAILABLE: 503;
readonly GATEWAY_TIMEOUT: 504;
readonly HTTP_VERSION_NOT_SUPPORTED: 505;
readonly VARIANT_ALSO_NEGOTIATES: 506;
readonly INSUFFICIENT_STORAGE: 507;
readonly LOOP_DETECTED: 508;
readonly NOT_EXTENDED: 510;
readonly NETWORK_AUTHENTICATION_REQUIRED: 511;
};
/**
* Map for ranslate `HttpStatus`
*/
export declare const HttpStatusMessage: Record<number, string>;
export declare const mimeExtMap: Record<string, string>;
export declare const RAW_REQUEST: unique symbol;
export declare const RAW_RESPONSE: unique symbol;
/* Excluded from this release type: _stringifyTunCookie */
export declare class TunApplication extends EventEmitter {
middleware: TunComposable<TunContext>[];
mountObj: Record<string, any>;
constructor();
use(fn: TunComposable<TunContext> | TunComposable<TunContext>): this;
mount(name: string, obj: any): void;
callback(): (_req: IncomingMessage, _res: ServerResponse) => Promise<void>;
listen(option?: ListenOptions): Server;
}
export declare type TunComposable<Context> = (ctx: Context, next: () => Promise<any>) => Promise<any> | void;
export declare class TunContext {
req: TunRequest;
res: TunResponse;
/**
* sth you may need to store and access
*/
state: Record<string, any>;
constructor(_req: IncomingMessage, _res: ServerResponse);
/**
* shorthand for `res.body`
*/
get body(): string | Object | any[] | Stream | Buffer | null;
set body(val: string | Object | any[] | Stream | Buffer | null);
getRequestCookie(name: string): string | undefined;
}
export declare interface TunCookie {
name: string;
value: string;
domain?: string;
path?: string;
maxAge?: number;
expires?: Date;
httpOnly?: boolean;
secure?: boolean;
}
export declare class TunRequest {
#private;
[RAW_REQUEST]: IncomingMessage;
/**
* formdata fields
* @see [bodyparser](https://github.com/tunframework/tun-bodyparser)
*/
fields: Record<string, string>;
_fields: Record<string, string | Array<string>>;
/**
* formdata files
* @see [bodyparser](https://github.com/tunframework/tun-bodyparser)
*/
files: Record<string, File_2>;
_files: Record<string, File_2 | File_2[]>;
/**
* request body
* @see [bodyparser](https://github.com/tunframework/tun-bodyparser)
*/
body: any;
/**
* @example "/product/abc" matches "/product/:id", ctx.req.slugs.id === "abc"
* @see [rest-router](https://github.com/tunframework/tun-rest-router)
*/
slugs: Record<string, any>;
constructor(req: IncomingMessage, options?: TunRequestOptions);
get header(): IncomingHttpHeaders;
set header(val: IncomingHttpHeaders);
get headers(): IncomingHttpHeaders;
set headers(val: IncomingHttpHeaders);
/**
*
* http methods
*
* refers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
*
* Get/Set request method, useful for implementing middleware such as methodOverride().
*
*/
get method(): "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH";
set method(val: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH");
/**
* Return request Content-Length as a number when preqent, or undefined.
*/
get length(): number | undefined;
get url(): string;
set url(val: string);
get originalUrl(): string | undefined;
/**
* Get origin of URL, include protocol and host.
* @example http://example.com
*/
get origin(): string | undefined;
/**
* Get full request URL, include protocol, host and url.
* @example http://example.com/foo/bar?q=1
*/
get href(): string;
get path(): string;
set path(val: string);
get querystring(): string;
set querystring(val: string);
get search(): string;
set search(val: string);
/**
* Get host (hostname:port) when present.
* Supports X-Forwarded-Host when app.proxy is true,
* otherwise Host is used.
*/
get host(): string | undefined;
/**
* Get hostname when present.
* Supports X-Forwarded-Host when app.proxy is true,
* otherwise Host is used.
*
* //// ignore: If host is IPv6, delegates parsing to WHATWG URL API, Note This may impact performance.
*/
get hostname(): string;
/**
* Get WHATWG parsed URL object.
*
* refers: https://nodebeginner.org/blog/post/nodejs-tutorial-whatwg-url-parser/
*
* url模块现在(node 8.0+)提供了一个额外的实现,它实现了标准化的WHATWG URL API,使得Node.js的url-parsing代码与Web浏览器解析URL的方式相同
*
* 原有的 'url.parse()' Nodejs 被认为
*/
get URL(): URL_2;
get type(): string;
/**
* Get request charset when present, or undefined:
* @example "utf-8"
*/
get charset(): string | undefined;
get query(): URLSearchParams_2;
set query(val: URLSearchParams_2);
get protocol(): string | string[];
get secure(): boolean;
/**
* Request remote address. Supports X-Forwarded-For when app.proxy is true.
*/
get ip(): string | undefined;
/**
* When X-Forwarded-For is present and app.proxy is enabled an array of these ips is returned,
* ordered from upstream to downstream.
* When disabled an empty array is returned.
*
* refers: https://support.stackpath.com/hc/en-us/articles/360021658292-Getting-Real-Client-IPs-with-X-Forwarded-For
*/
get ips(): (string | undefined)[];
/**
* 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 app.subdomainOffset.
*
* For example, if the domain is "tobi.ferrets.example.com": If app.subdomainOffset is not set,
* ctx.subdomains is ["ferrets", "tobi"].
* If app.subdomainOffset is 3, ctx.subdomains is ["tobi"].
*/
get subdomains(): string[];
/**
* Check if the incoming request contains the "Content-Type" header field,
* and it contains any of the give mime types.
* If there is no request body, null is returned.
* If there is no content type, or the match fails false is returned.
* Otherwise, it returns the matching content-type.
*/
is(...types: string[]): string | false;
/**
* Check if the given type(s) is acceptable,
* returning the best match when true, otherwise false.
* The type value may be one or more mime type string such as "application/json",
* the extension name such as "json",
* or an array ["json", "html", "text/plain"].
*
* refers: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept
*/
accepts(...types: string[]): string | false;
/**
* Check if encodings are acceptable,
* returning the best match when true, otherwise false.
* Note that you should include identity as one of the encodings!
*
* // Accept-Encoding: gzip
* ctx.acceptsEncodings('gzip', 'deflate', 'identity');
* // "gzip"
*
* ctx.acceptsEncodings(['gzip', 'deflate', 'identity']);
* // "gzip"
*
* Note that the identity encoding (which means no encoding)
* could be unacceptable if the client explicitly sends identity;q=0.
* Although this is an edge case,
* you should still handle the case where this method returns false.
*/
acceptsEncodings(...encodings: Array<string | string[]>): string | boolean | string[];
/**
* Check if charsets are acceptable,
* returning the best match when true, otherwise false.
*/
acceptsCharsets(...charsets: string[]): string | boolean;
/**
* Check if langs are acceptable,
* returning the best match when true,
* otherwise false.
*
* When no arguments are given all accepted languages are returned as an array
*
* refers: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Language
* @returns return wheather (given) languages acceptable, or all accepted languages (when no arguments are given)
*/
acceptsLanguages(...langs: Array<string | string[]>): string | boolean | string[];
/**
* Check if the request is idempotent.
*
* refers: https://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html
*/
idempotent(): boolean;
get socket(): Socket;
/**
* Return request header.
*
* field lower-kebad-case-field-name
*/
get(field: string): string | string[] | undefined;
}
export declare interface TunRequestOptions {
proxy?: boolean;
subdomainOffset?: number;
}
export declare class TunResponse {
#private;
[RAW_REQUEST]: IncomingMessage;
[RAW_RESPONSE]: ServerResponse;
cookies: TunCookie[];
constructor(req: IncomingMessage, res: ServerResponse);
get header(): OutgoingHttpHeaders;
get headers(): OutgoingHttpHeaders;
get socket(): Socket | null;
get status(): number;
set status(val: number);
/**
* Get response status message.
* By default, it is associated with response.status.
*/
get message(): string;
set message(val: string);
set length(val: number);
/**
* Return response Content-Length as a number when present,
* or deduce from ctx.body when possible, or undefined.
*/
get length(): number;
get body(): string | Buffer | Stream | Object | Array<any> | null;
set body(val: string | Buffer | Stream | Object | Array<any> | null);
/**
* Get a response header field value with case-insensitive field.
*
* e.g. const etag = ctx.response.get('ETag');
*/
get(field: string): string | number | string[] | undefined;
/**
* Set response header field to value:
*
* e.g. `ctx.request.set('Cache-Control', 'no-cache');`
* ```
* ctx.request.set({
* 'Etag': '1234',
* 'Last-Modified': date
* })
* ```
*/
set(field: string, value: string | number | string[]): void;
/**
* Append additional header field with value val.
*/
append(field: string, value: string | number | string[]): void;
/**
* Remove header field.
*/
remove(field: string): void;
/**
* Get request Content-Type void of parameters such as "charset".
*/
get type(): {
mineType?: keyof typeof mimeExtMap;
suffixType?: string;
rawType?: string;
};
set type(val: {
mineType?: keyof typeof mimeExtMap;
suffixType?: string;
rawType?: string;
});
/**
* Very similar to ctx.request.is().
* Check whether the response type is one of the supplied types.
* This is particularly useful for creating middleware that manipulate responses.
*/
is(...types: string[]): string | false;
/**
* Perform a [302] redirect to url.
*
* The string "back" is special-cased to provide Referrer support,
* when Referrer is not present alt or "/" is used.
*/
redirect(url: string, alt: string): void;
/**
* 设置 响应的 附件下载 Content-Disposition 头,并设置相应 Content-Type 头
*
* refers:
* https://github.com/jshttp/content-disposition
* https://github.com/jshttp/content-disposition/blob/master/index.js
*/
attachment(filename: string): void;
get headerSent(): boolean;
/**
* Return '' if header not exists
*
* Set the Last-Modified header as an appropriate UTC string.
* You can either set it as a Date or date string.
*/
get lastModified(): Date | string;
set lastModified(date: Date | string);
/**
* Set the ETag of a response including the wrapped "s.
* Note that there is no corresponding response.etag getter.
*
* e.g. `crypto.createHash('md5').update(ctx.body).digest('hex')`
*/
get etag(): string;
set etag(val: string);
/**
* 对向CDN缓存服务等 响应 说明区分某个字段的值做两份缓存(gzip|identity)?
*
* refers: https://imququ.com/post/vary-header-in-http.html
*/
vary(field: string): void;
/**
* Flush any set headers, and begin the body.
*/
flushHeaders(): void;
}
export { }