@nestjs/platform-fastify
Version:
Nest - modern, fast, powerful node.js web framework (@platform-fastify)
219 lines (218 loc) • 6.81 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.fastifyMiddie = fastifyMiddie;
const fastify_plugin_1 = require("fastify-plugin");
const url_sanitizer_1 = require("find-my-way/lib/url-sanitizer");
const path_to_regexp_1 = require("path-to-regexp");
const reusify = require("reusify");
/**
* A clone of `@fastify/middie` engine https://github.com/fastify/middie
* with an extra vulnerability fix. Path is now decoded before matching to
* avoid bypassing middleware with encoded characters.
*/
function middie(complete) {
const middlewares = [];
const pool = reusify(Holder);
return {
use,
run,
};
function use(url, f) {
if (f === undefined) {
f = url;
url = null;
}
let regexp;
if (typeof url === 'string') {
const pathRegExp = (0, path_to_regexp_1.pathToRegexp)(sanitizePrefixUrl(url), {
end: false,
});
regexp = pathRegExp.regexp;
}
if (Array.isArray(f)) {
for (const val of f) {
middlewares.push({ regexp, fn: val });
}
}
else {
middlewares.push({ regexp, fn: f });
}
return this;
}
function run(req, res, ctx) {
if (!middlewares.length) {
complete(null, req, res, ctx);
return;
}
req.originalUrl = req.url;
const holder = pool.get();
holder.req = req;
holder.res = res;
holder.url = sanitizeUrl(req.url);
holder.context = ctx;
holder.done();
}
function Holder() {
this.req = null;
this.res = null;
this.url = null;
this.context = null;
this.i = 0;
const that = this;
this.done = function (err) {
const req = that.req;
const res = that.res;
const url = that.url;
const context = that.context;
const i = that.i++;
req.url = req.originalUrl;
if (res.finished === true || res.writableEnded === true) {
cleanup();
return;
}
if (err || middlewares.length === i) {
complete(err, req, res, context);
cleanup();
}
else {
const { fn, regexp } = middlewares[i];
if (regexp) {
// Decode URL before matching to avoid bypassing middleware
const decodedUrl = (0, url_sanitizer_1.safeDecodeURI)(url).path;
const result = regexp.exec(decodedUrl);
if (result) {
req.url = req.url.replace(result[0], '');
if (req.url[0] !== '/')
req.url = '/' + req.url;
fn(req, res, that.done);
}
else {
that.done();
}
}
else {
fn(req, res, that.done);
}
}
};
function cleanup() {
that.req = null;
that.res = null;
that.context = null;
that.i = 0;
pool.release(that);
}
}
}
function sanitizeUrl(url) {
for (let i = 0, len = url.length; i < len; i++) {
const charCode = url.charCodeAt(i);
if (charCode === 63 || charCode === 35) {
return url.slice(0, i);
}
}
return url;
}
function sanitizePrefixUrl(url) {
if (url === '/')
return '';
if (url[url.length - 1] === '/')
return url.slice(0, -1);
return url;
}
const kMiddlewares = Symbol('fastify-middie-middlewares');
const kMiddie = Symbol('fastify-middie-instance');
const kMiddieHasMiddlewares = Symbol('fastify-middie-has-middlewares');
const supportedHooksWithPayload = [
'onError',
'onSend',
'preParsing',
'preSerialization',
];
const supportedHooksWithoutPayload = [
'onRequest',
'onResponse',
'onTimeout',
'preHandler',
'preValidation',
];
const supportedHooks = [
...supportedHooksWithPayload,
...supportedHooksWithoutPayload,
];
function fastifyMiddie(fastify, options, next) {
fastify.decorate('use', use);
fastify[kMiddlewares] = [];
fastify[kMiddieHasMiddlewares] = false;
fastify[kMiddie] = middie(onMiddieEnd);
const hook = options.hook || 'onRequest';
if (!supportedHooks.includes(hook)) {
next(new Error(`The hook "${hook}" is not supported by fastify-middie`));
return;
}
fastify
.addHook(hook, supportedHooksWithPayload.includes(hook)
? runMiddieWithPayload
: runMiddie)
.addHook('onRegister', onRegister);
function use(path, fn) {
if (typeof path === 'string') {
const prefix = this.prefix;
path = prefix + (path === '/' && prefix.length > 0 ? '' : path);
}
this[kMiddlewares].push([path, fn]);
if (fn == null) {
this[kMiddie].use(path);
}
else {
this[kMiddie].use(path, fn);
}
this[kMiddieHasMiddlewares] = true;
return this;
}
function runMiddie(req, reply, next) {
if (this[kMiddieHasMiddlewares]) {
const raw = req.raw;
raw.id = req.id;
raw.hostname = req.hostname;
raw.protocol = req.protocol;
raw.ip = req.ip;
raw.ips = req.ips;
raw.log = req.log;
req.raw.query = req.query;
reply.raw.log = req.log;
if (req.body !== undefined)
req.raw.body = req.body;
this[kMiddie].run(req.raw, reply.raw, next);
}
else {
next();
}
}
function runMiddieWithPayload(req, reply, _payload, next) {
runMiddie.bind(this)(req, reply, next);
}
function onMiddieEnd(err, _req, _res, next) {
next(err);
}
function onRegister(instance) {
const middlewares = instance[kMiddlewares].slice();
instance[kMiddlewares] = [];
instance[kMiddie] = middie(onMiddieEnd);
instance[kMiddieHasMiddlewares] = false;
instance.decorate('use', use);
for (const middleware of middlewares) {
instance.use(...middleware);
}
}
next();
}
/**
* A clone of `@fastify/middie` engine https://github.com/fastify/middie
* with an extra vulnerability fix. Path is now decoded before matching to
* avoid bypassing middleware with encoded characters.
*/
exports.default = (0, fastify_plugin_1.default)(fastifyMiddie, {
fastify: '5.x',
name: '@fastify/middie',
});