elastic-apm-node
Version:
The official Elastic APM agent for Node.js
92 lines (76 loc) • 2.58 kB
JavaScript
/*
* 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.
*/
;
const url = require('url');
var symbols = require('../symbols');
function normalizeSlash(value) {
return value[0] === '/' ? value : '/' + value;
}
function excludeRoot(value) {
return value !== '/';
}
function join(parts) {
if (!parts) return;
return parts.filter(excludeRoot).map(normalizeSlash).join('') || '/';
}
// This works for both express AND restify
function routePath(route) {
if (!route) return;
return route.path || (route.regexp && route.regexp.source);
}
function getStackPath(req) {
var stack = req[symbols.expressMountStack];
return join(stack);
}
// This function is also able to extract the path from a Restify request as
// it's storing the route name on req.route.path as well
function getPathFromRequest(req, useBase, usePathAsTransactionName) {
if (req[symbols.staticFile]) {
return 'static file';
}
var path = getStackPath(req);
var route = routePath(req.route);
if (route) {
return path ? join([path, route]) : route;
} else if (path && (path !== '/' || useBase)) {
return path;
}
if (usePathAsTransactionName) {
// `req.url` is the "request-target" from the HTTP request line
// (https://www.rfc-editor.org/rfc/rfc7230#section-3.1.1). The special-case
// handling for leading '/' is because this:
// GET //foo/bar HTTP/1.1
// Host: example.com
// should result in:
// { host: 'example.com', pathname: '//foo/bar' }
// and *not*:
// { host: 'foo', pathname: '/bar' }
//
// Assuming 'http://' for the `base` URL is fine, because we don't use the
// protocol.
let base;
try {
// Host header may contain invalid characters therefore the URL
// parsing will fail and break the app. This try block is to avoid it
// Ref: https://github.com/elastic/apm-agent-nodejs/issues/3874
const url = new url.URL('http://' + (req.headers && req.headers.host));
base = 'http://' + url.hostname;
} catch (err) {
base = 'http://undefined';
}
// We may receive invalid chars in the path also but the URL
// constructor escapes them without throwing.
const parsed = req.url.startsWith('/')
? new url.URL(base + req.url)
: new url.URL(req.url, base);
return parsed && parsed.pathname;
}
}
module.exports = {
getPathFromRequest,
getStackPath,
routePath,
};