@jeaks03/overseer
Version:
Just another TypeScript Back-End framework
228 lines • 19.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const standard_responses_1 = require("../misc/standard-responses");
const requisites_1 = require("../core/requisites");
const route_utils_1 = require("./route-utils");
const http_error_1 = require("../errors/http-error");
const redirect_1 = require("./redirect");
const core_error_1 = require("../errors/core-error");
const converter_1 = require("../converters/converter");
class AsyncRequestHandler {
constructor(serverRequest, serverResponse, router) {
this.serverRequest = serverRequest;
this.serverResponse = serverResponse;
this.router = router;
}
static async doHandle(serverRequest, serverResponse, router) {
const handler = new AsyncRequestHandler(serverRequest, serverResponse, router);
await handler.handle();
handler.serverResponse.end();
}
async handle() {
// Handle cross origin headers and OPTIONS requests
this.appendInitialHeaders();
if (route_utils_1.RouteUtils.isMethod(this.serverRequest, 'options')) {
return this.handleCORS();
}
try {
await this.checkAvailableRoute();
}
catch (e) {
if (e.response) {
e.handle(this.serverResponse);
}
else {
http_error_1.HttpError.handleServerError(this.serverResponse, e);
}
}
}
/**
* Handles possible routing scenarios such as: route exists, resource exists or not found
*/
async checkAvailableRoute() {
const route = this.findRoute();
// Handle existing route
if (route) {
this.foundRoute = route;
await this.routeMatched();
return;
}
// If no route then handle public resources
if (route_utils_1.RouteUtils.isMethod(this.serverRequest, 'get') && this.router.resourceMgr.fileOrIndexExists(route_utils_1.RouteUtils.getBaseUrl(this.serverRequest.url))) {
this.router.resourceMgr.handleRequest(route_utils_1.RouteUtils.getBaseUrl(this.serverRequest.url), this.serverResponse);
return;
}
// If no public resource then send 404
this.serverResponse.statusCode = 404;
this.serverResponse.setHeader('Content-Type', 'application/json');
}
/**
* Triggered when a route was found
*/
async routeMatched() {
const rawBody = await this.parseRequest();
const messageConverter = MessageConverterScout.
using(this.serverRequest, this.foundRoute)
.findReadConverter(rawBody);
const body = messageConverter.doRead(rawBody);
const pathInfo = this.generatePathInfo(body);
// Triggers the controller method associated with this route
const output = this.handleRoute(pathInfo);
// Handle redirect
if (output.body instanceof redirect_1.Redirect) {
this.handleRedirect(output.body);
return;
}
// Request is done, write output
this.writeResponse(output);
}
/**
* Triggers the controller method associated with the route
* @param info param to pass to controller method
*/
handleRoute(info) {
try {
return {
body: this.foundRoute.handle(info),
status: this.foundRoute.details.statusCode
};
}
catch (e) {
return this.handleError(e, info);
}
}
/**
* Handles the given error
* @param e the error
* @param info param to pass to error handler method
*/
handleError(e, info) {
let output = {};
if (e instanceof core_error_1.CoreError) {
output.body = e.handle(info);
output.status = e.getStatusCode();
}
else if (e instanceof http_error_1.HttpError) {
output = e.response;
}
else {
console.error(e);
output = standard_responses_1.INTERVAL_SERVER_ERROR;
}
return output;
}
/**
* Generates PathInfo for the controller method to use
*/
generatePathInfo(body) {
return {
body,
raw: {
request: this.serverRequest,
response: this.serverResponse
},
pathParams: route_utils_1.RouteUtils.parsePathParams(this.serverRequest.url, this.foundRoute.details.path),
queryParams: route_utils_1.RouteUtils.parseQueryParams(this.serverRequest.url)
};
}
/**
* Parses the raw http request body to a string promise
*/
parseRequest() {
let requestBody = '';
this.serverRequest.on('data', chunk => requestBody += chunk.toString());
return new Promise((resolve) => this.serverRequest.on('end', () => resolve(requestBody)));
}
/**
* Returns the route associated with the current server request
*/
findRoute() {
const parts = route_utils_1.RouteUtils.getUrlPattern(this.serverRequest.url);
let tree = this.router.routeTree;
for (const part of parts) {
if (!tree[part]) {
return null;
}
tree = tree[part];
}
if (!tree || !tree[this.router.routeSym]) {
return null;
}
return tree[this.router.routeSym][this.serverRequest.method.toLowerCase()] || null;
}
/**
* Adds necessary headers to avoid CORS issues
*/
appendInitialHeaders() {
this.serverResponse.setHeader('Access-Control-Allow-Origin', '*');
this.serverResponse.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS');
}
/**
* Handles redirects
*/
handleRedirect(body) {
this.serverResponse.statusCode = 302;
this.serverResponse.setHeader('Location', body.url);
}
/**
* Adds necessary headers to avoid CORS issues in case of OPTIONS requests
*/
handleCORS() {
this.serverResponse.setHeader('Access-Control-Allow-Headers', `Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With`);
this.serverResponse.statusCode = 200;
}
/**
* Writes to the http response object
* @param output what to write
*/
writeResponse(output) {
const converter = MessageConverterScout
.using(this.serverRequest, this.foundRoute)
.findWriteConverter(output.body);
this.serverResponse.statusCode = output.status;
this.serverResponse.setHeader('Content-Type', converter.getContentType());
this.serverResponse.write(converter.doWrite(output.body));
}
}
exports.AsyncRequestHandler = AsyncRequestHandler;
class MessageConverterScout {
constructor(serverRequest, foundRoute) {
this.serverRequest = serverRequest;
this.foundRoute = foundRoute;
}
static using(serverRequest, foundRoute) {
return new MessageConverterScout(serverRequest, foundRoute);
}
findReadConverter(body) {
const type = this.findContentType(this.serverRequest.headers["content-type"], this.foundRoute.details.consumes);
const converter = requisites_1.Requisites.findAll(converter_1.Converter).find((x) => x.canRead(body, type));
if (!converter) {
throw new http_error_1.HttpError(standard_responses_1.UNSUPPORTED_MEDIA_TYPE);
}
return converter;
}
findWriteConverter(body) {
const type = this.findContentType(this.serverRequest.headers.accept, this.foundRoute.details.produces);
const converter = requisites_1.Requisites.findAll(converter_1.Converter).find((x) => x.canWrite(body, type));
if (!converter) {
throw new http_error_1.HttpError(standard_responses_1.UNSUPPORTED_MEDIA_TYPE);
}
return converter;
}
findContentType(contentTypeHeader, allowedContentTypes) {
const contentTypes = contentTypeHeader;
let requiredContentTypes;
if (contentTypes) {
requiredContentTypes = contentTypes.split(',').map(x => x.trim()).map(x => x.split(';')[0]);
}
let type;
if (!requiredContentTypes || contentTypes.includes('*/*')) {
type = allowedContentTypes[0];
}
else if (requiredContentTypes) {
type = requiredContentTypes.find(ctype => allowedContentTypes.includes(ctype));
}
return type;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXN5bmMtcmVxdWVzdC1oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JvdXRlcy9hc3luYy1yZXF1ZXN0LWhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFFQSxtRUFBMkY7QUFDM0YsbURBQWdEO0FBR2hELCtDQUEyQztBQUMzQyxxREFBaUQ7QUFDakQseUNBQXNDO0FBRXRDLHFEQUFpRDtBQUNqRCx1REFBb0Q7QUFFcEQsTUFBYSxtQkFBbUI7SUFRNUIsWUFBNEIsYUFBOEIsRUFDOUMsY0FBOEIsRUFDOUIsTUFBYztRQUZFLGtCQUFhLEdBQWIsYUFBYSxDQUFpQjtRQUM5QyxtQkFBYyxHQUFkLGNBQWMsQ0FBZ0I7UUFDOUIsV0FBTSxHQUFOLE1BQU0sQ0FBUTtJQUFJLENBQUM7SUFSL0IsTUFBTSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsYUFBOEIsRUFBRSxjQUE4QixFQUFFLE1BQWM7UUFDaEcsTUFBTSxPQUFPLEdBQUcsSUFBSSxtQkFBbUIsQ0FBQyxhQUFhLEVBQUUsY0FBYyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQy9FLE1BQU0sT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3ZCLE9BQU8sQ0FBQyxjQUFjLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDakMsQ0FBQztJQU1PLEtBQUssQ0FBQyxNQUFNO1FBRWhCLG1EQUFtRDtRQUNuRCxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUM1QixJQUFHLHdCQUFVLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsU0FBUyxDQUFDLEVBQUU7WUFDbkQsT0FBTyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7U0FDNUI7UUFFRCxJQUFJO1lBQ0EsTUFBTSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztTQUNwQztRQUFDLE9BQU0sQ0FBQyxFQUFFO1lBQ1AsSUFBRyxDQUFDLENBQUMsUUFBUSxFQUFFO2dCQUNYLENBQUMsQ0FB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