UNPKG

6.29 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const e = require("express");
4const onFinished = require("on-finished");
5const prometheus_extended_gauge_1 = require("prometheus-extended-gauge");
6const promBundle = require("express-prom-bundle");
7const xmlparser = require("express-xml-bodyparser");
8const BaseSingletonDefinition_1 = require("../ioc/objectdefinition/BaseSingletonDefinition");
9const NewrelicUtil_1 = require("../newrelic/NewrelicUtil");
10const LogManager_1 = require("../log/LogManager");
11const WebRoutingInspector_1 = require("./WebRoutingInspector");
12const HttpError_1 = require("./HttpError");
13const ContentNegotiationMiddleware_1 = require("./ContentNegotiationMiddleware");
14const logger = LogManager_1.LogManager.getLogger(__filename);
15const activeRequestsGauge = new prometheus_extended_gauge_1.ExtendedGauge({
16 max: true,
17 min: false,
18 average: true,
19 bucketSizeMillis: 1000,
20 numBuckets: 60,
21 help: 'Number of active http requests being processed',
22 name: 'http_active_requests',
23});
24class RouteRegisterUtil {
25 constructor() {
26 this.routesToRegister = [];
27 }
28 doRegister() {
29 this.routesToRegister.forEach((route) => {
30 logger.info(`Registering route from ${route.objectName}: ${route.verb.toUpperCase()} ${route.path} -> ${route.methodName}`);
31 this.express[route.verb](route.path, (req, res) => {
32 return this[route.instanceProperty][route.methodName](req, res);
33 });
34 });
35 }
36}
37exports.RouteRegisterUtil = RouteRegisterUtil;
38/**
39 * Client errors do not need to be sent to NewRelic so create clientErrorMiddleware
40 * to send response if the error is 4xx. clientErrorMiddleware needs to be registered
41 * before errorMiddleware.
42 * @param err
43 * @param req
44 * @param res
45 * @param next
46 */
47exports.clientErrorMiddleware = (err, req, res, next) => {
48 let more = [];
49 if (err instanceof HttpError_1.default && err.statusCode && err.statusCode >= 400 && err.statusCode < 500) {
50 res.status(err.getStatusCode()).send({ message: err.message });
51 }
52 else if (err.failedValidation) {
53 more = err.results && err.results.errors;
54 res.status(400).send({ message: err.message });
55 }
56 else {
57 return next(err); // Give back to express to handle
58 }
59 logger.error({ err, more }); // log 4xx errors and swagger errors
60};
61exports.errorMiddleware = (err, req, res, next) => {
62 if (res.headersSent) {
63 return next(err); // Give back to express to handle
64 }
65 logger.error(err);
66 const data = { message: err.message };
67 if (err instanceof HttpError_1.default && err.statusCode) {
68 if (err.statusCode >= 500) {
69 NewrelicUtil_1.NewrelicUtil.noticeError(err);
70 }
71 res.status(err.getStatusCode()).send(data);
72 }
73 else {
74 NewrelicUtil_1.NewrelicUtil.noticeError(err);
75 res.status(500).send(data);
76 }
77};
78class WebPlugin {
79 // private expressProvider = () => new e();
80 constructor(options = {}) {
81 this.options = options;
82 this.name = 'WebPlugin';
83 }
84 willStart(app, pluginContext) {
85 const express = e();
86 express.set('trust proxy', true); // stop redirecting to http internally https://expressjs.com/en/guide/behind-proxies.html
87 pluginContext.set(WebPlugin.CONTEXT_APP_KEY, express);
88 const context = app.getContext();
89 express.use(promBundle({
90 includeMethod: true,
91 buckets: [0.003, 0.03, 0.1, 0.3, 0.5, 1.5, 10],
92 autoregister: false,
93 }));
94 // Add middleware for stats on active requests
95 express.use((req, res, next) => {
96 activeRequestsGauge.inc();
97 onFinished(res, () => activeRequestsGauge.dec());
98 next();
99 });
100 this.registerXmlBodyParser(express);
101 this.registerXmlContentNegotiationMiddleware(express, app.getConfig('app.xmlRoot', ''));
102 // move from didStart to willStart
103 express.use(e.json({ limit: app.getConfig('app.server.maxRequestSize', '10mb') }));
104 express.use(e.urlencoded({ extended: true, limit: app.getConfig('app.server.maxRequestSize', '10mb') }));
105 if (this.options && this.options.staticRoots) {
106 this.options.staticRoots.forEach((root) => {
107 express.use(e.static(root));
108 });
109 }
110 const definition = new BaseSingletonDefinition_1.BaseSingletonDefinition(RouteRegisterUtil);
111 definition.withLazyLoading(false);
112 definition.startFunction('doRegister');
113 definition.setPropertyByValue('express', express);
114 context.registerDefinition(definition);
115 context.addObjectDefinitionInspector(new WebRoutingInspector_1.WebRoutingInspector(definition));
116 }
117 didStart(app, pluginContext) {
118 const express = pluginContext.get(WebPlugin.CONTEXT_APP_KEY);
119 const port = app.getConfig('app.server.port', 10010);
120 express.use(exports.clientErrorMiddleware);
121 // Add error handling middleware as the final middleware.
122 express.use(exports.errorMiddleware);
123 // Start the server
124 const server = express.listen(port, () => {
125 app.logger.info(`Server started at http://localhost:${port}`);
126 });
127 pluginContext.set(WebPlugin.CONTEXT_SERVER_KEY, server);
128 }
129 didStop(app, pluginContext) {
130 const express = pluginContext.get(WebPlugin.CONTEXT_SERVER_KEY);
131 app.logger.info('Shutting down server');
132 if (express) {
133 express.close();
134 }
135 }
136 registerXmlBodyParser(express) {
137 if (this.options && this.options.xmlBodyParserOptions) {
138 express.use(xmlparser(this.options.xmlBodyParserOptions));
139 }
140 }
141 registerXmlContentNegotiationMiddleware(express, xmlRoot) {
142 const negoContentMiddleware = new ContentNegotiationMiddleware_1.ContentNegotiationMiddleware(xmlRoot);
143 const xmlMiddleware = negoContentMiddleware.getMiddleware();
144 if (xmlMiddleware) {
145 express.use(xmlMiddleware);
146 }
147 }
148}
149WebPlugin.CONTEXT_APP_KEY = 'WebPlugin/APP';
150WebPlugin.CONTEXT_SERVER_KEY = 'WebPlugin/SERVER';
151exports.default = WebPlugin;
152//# sourceMappingURL=WebPlugin.js.map
\No newline at end of file