rjweb-server
Version:
Easy and Robust Way to create a Web Server with Many Easy-to-use Features in NodeJS
185 lines (184 loc) • 6.3 kB
JavaScript
import Middleware from "../../../classes/Middleware";
import RuntimeError from "../../../classes/RuntimeError";
import URLObject from "../../../classes/URLObject";
import ValueCollection from "../../../classes/ValueCollection";
export default class RequestContext {
constructor(context, middlewares, server, global) {
this.context = context;
this.middlewares = middlewares;
this.server = server;
this.executeSelf = () => true;
this.middlewareData = {};
this.started = performance.now();
this.aborted = false;
/**
* The Params of the URL Path
* @since 9.0.0
*/ this.params = new ValueCollection();
/**
* The Cookies of the Request
* @since 9.0.0
*/ this.cookies = null;
/**
* The Queries of the Request
* @since 9.0.0
*/ this.queries = null;
/**
* The Fragments of the Request
* @since 9.0.0
*/ this.fragments = null;
/**
* The Error this request encountered
* @since 9.0.0
*/ this.error = null;
/**
* Whether the EndFn was called
* @since 9.0.0
*/ this.endFn = false;
/**
* Whether the Request is chunked
* @since 9.0.0
*/ this.chunked = false;
/**
* The File to print at the end of the request
* @warn Only used in static routes, do not use in other routes
* @since 9.0.0
*/ this.file = null;
/**
* Body / Ws Message Data
* @since 9.0.0
*/ this.body = {
chunks: [],
callbacks: [],
raw: null,
parsed: '',
awaiting: false,
type: 'raw'
};
/**
* The Route, if found
* @since 9.0.0
*/ this.route = null;
/**
* The Response Data
* @since 9.0.0
*/ this.response = {
status: 200,
statusText: null,
headers: new ValueCollection(),
cookies: new ValueCollection(),
content: Buffer.allocUnsafe(0),
prettify: false
};
this.url = new URLObject(context.path(), context.method());
this.type = context.type();
this.global = global;
this.headers = context.getHeaders();
this.ip = {
isProxied: false,
value: context.clientIP(),
port: context.clientPort()
};
}
/**
* Set Code to execute at the end end of the request,
* return a boolean describing if you want the normal request end to proceed too
* @since 9.0.0
*/ setExecuteSelf(callback) {
const old = this.executeSelf;
this.executeSelf = async () => {
const result = await Promise.resolve(old());
if (!result)
return false;
return await Promise.resolve(callback());
};
return this;
}
/**
* Handle an Error, automatically sets status and calls callback
* @since 9.0.0
*/ handleError(error, cause) {
this.error = new RuntimeError(cause, error);
return this.error;
}
/**
* Initiate Request Abortion
* @since 9.0.0
*/ async abort(ctr) {
if (this.aborted)
return true;
if (!ctr)
this.global.logger.debug(`Aborted Request on ${this.url.method} ${this.url.href}`);
if (ctr) {
for (let i = 0; i < this.middlewares.length; i++) {
const middleware = this.middlewares[i];
if (middleware.finishCallbacks.httpRequest)
try {
await Promise.resolve(middleware.finishCallbacks.httpRequest(middleware.config, this.server, this, ctr, this.elapsed()));
}
catch { }
}
if (this.global.finishHandlers.httpRequest)
try {
await Promise.resolve(this.global.finishHandlers.httpRequest(ctr, this.elapsed()));
}
catch { }
}
this.aborted = !ctr;
return false;
}
/**
* Retrieve Middleware Data
* @since 9.0.0
*/ data(middleware) {
const key = middleware instanceof Middleware ? middleware['name'] : 'use' in middleware ? middleware.use().infos.name : middleware.infos.name;
if (!this.middlewareData[key])
this.middlewareData[key] = {};
return this.middlewareData[key];
}
/**
* Await the Body
* @since 9.0.0
*/ async awaitBody(ctr, concat = true) {
if (this.url.method === 'GET')
return Buffer.allocUnsafe(0);
if (this.body.awaiting)
return new Promise((resolve) => this.body.callbacks.push(resolve));
if (this.body.raw)
return this.body.raw;
this.body.awaiting = true;
await this.context.onBodyChunk(async (chunk, isLast) => {
const buffer = Buffer.from(chunk);
if (this.context.aborted().aborted)
return this.abort();
if (this.route?.type === 'http' && !this.route.data.onRawBody && concat)
this.body.chunks.push(buffer);
else if (this.route?.type === 'http')
try {
if (!this.endFn)
await Promise.resolve(this.route.data.onRawBody?.(ctr, () => this.endFn = true, buffer, isLast));
}
catch (err) {
this.handleError(err, 'http.handle.onRawBody');
}
if (isLast && concat) {
this.body.raw = Buffer.concat(this.body.chunks);
this.body.chunks.length = 0;
for (const callback of this.body.callbacks) {
callback(this.body.raw);
}
this.body.callbacks.length = 0;
}
});
return this.body.raw;
}
/**
* Get the ms elapsed since the request started
* @since 9.0.0
*/ elapsed(reset = false) {
const ms = performance.now() - this.started;
if (reset)
this.started = performance.now();
return ms;
}
}