-
Notifications
You must be signed in to change notification settings - Fork 230
/
Copy pathexpress.js
153 lines (128 loc) · 4.78 KB
/
express.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* Copyright Elasticsearch B.V. and other contributors where applicable.
* Licensed under the BSD 2-Clause License; you may not use this file except in
* compliance with the BSD 2-Clause License.
*/
'use strict';
var isError = require('core-util-is').isError;
var semver = require('semver');
var shimmer = require('../shimmer');
var symbols = require('../../symbols');
module.exports = function (express, agent, { version, enabled }) {
if (!enabled) return express;
agent.setFramework({ name: 'express', version, overwrite: false });
if (!semver.satisfies(version, '^4.0.0')) {
agent.logger.debug(
'express version %s not supported - aborting...',
version,
);
return express;
}
// express 5 moves the router methods onto a prototype
var routerProto = semver.satisfies(version, '^5')
? express.Router && express.Router.prototype
: express.Router;
var layerPatchedSymbol = Symbol('layer-patched');
function shouldReport(err) {
if (!agent._conf.captureExceptions) return false;
if (typeof err === 'string') return true;
if (isError(err) && !err[symbols.errorReportedSymbol]) {
err[symbols.errorReportedSymbol] = true;
return true;
}
return false;
}
function safePush(obj, prop, value) {
if (!obj[prop]) obj[prop] = [];
obj[prop].push(value);
}
function patchLayer(layer, layerPath) {
if (!layer[layerPatchedSymbol]) {
layer[layerPatchedSymbol] = true;
agent.logger.debug(
'shimming express.Router.Layer.handle function: %s',
layer.name,
);
shimmer.wrap(layer, 'handle', function (orig) {
let handle;
if (orig.length !== 4) {
handle = function (req, res, next) {
if (!layer.route && layerPath && typeof next === 'function') {
safePush(req, symbols.expressMountStack, layerPath);
arguments[2] = function (nextArg) {
// https://github.com/expressjs/express/blob/4.18.1/lib/router/route.js#L116-L149
// The argument to an Express handler's `next()` can be:
// falsey (call the next handler), 'route' (skip handlers for
// this route), 'router' (skip handlers for this Router), or
// any other value is considered an error (it doesn't have to
// be an instance of Error). For all but the last one, Express
// will consider other routes, so we want to pop the mount
// stack.
if (!nextArg || nextArg === 'route' || nextArg === 'router') {
req[symbols.expressMountStack].pop();
}
return next.apply(this, arguments);
};
}
return orig.apply(this, arguments);
};
} else {
handle = function (err, req, res, next) {
if (shouldReport(err)) {
agent.captureError(err, { request: req });
}
return orig.apply(this, arguments);
};
}
for (const prop in orig) {
if (Object.prototype.hasOwnProperty.call(orig, prop)) {
handle[prop] = orig[prop];
}
}
return handle;
});
}
}
agent.logger.debug('shimming express.Router.use function');
shimmer.wrap(routerProto, 'route', (orig) => {
return function route(path) {
var route = orig.apply(this, arguments);
var layer = this.stack[this.stack.length - 1];
patchLayer(layer, path);
return route;
};
});
shimmer.wrap(routerProto, 'use', (orig) => {
return function use(path) {
var route = orig.apply(this, arguments);
var layer = this.stack[this.stack.length - 1];
patchLayer(layer, typeof path === 'string' && path);
return route;
};
});
agent.logger.debug('shimming express.static function');
shimmer.wrap(express, 'static', function wrapStatic(orig) {
// By the time of this writing, Express adds a `mime` property to the
// `static` function that needs to be copied to the wrapped function.
// Instead of only copying the `mime` function, let's loop over all
// properties in case new properties are added in later versions of
// Express.
for (const prop of Object.keys(orig)) {
agent.logger.debug('copying property %s from express.static', prop);
wrappedStatic[prop] = orig[prop];
}
return wrappedStatic;
function wrappedStatic() {
var origServeStatic = orig.apply(this, arguments);
return function serveStatic(req, res, next) {
req[symbols.staticFile] = true;
return origServeStatic(req, res, nextHook);
function nextHook(err) {
if (!err) req[symbols.staticFile] = false;
return next.apply(this, arguments);
}
};
}
});
return express;
};