Skip to content

Commit 6f4c045

Browse files
authored
fix(node): Ensure correct URL is passed to ignoreIncomingRequests callback (#12929)
Fix an oversight in our Node `httpIntegration`. It looks like we assumed that the `request` object being passed to `ignoreIncomingRequestHook` and `ignoreOutgoingRequestHook` was of the same type. However, it's not: - `request` is of type `IncomingMessage` in `ignoreIncomingRequestHook` - `request` is of type `RequestOptions` in `ignoreOutgoingRequestHook` fix the bug by simply taking the request.url property instead and adds integration tests to properly test the two options.
1 parent 707afd6 commit 6f4c045

File tree

5 files changed

+153
-6
lines changed

5 files changed

+153
-6
lines changed

dev-packages/node-integration-tests/src/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ export function loggingTransport(_options: BaseTransportOptions): Transport {
2020

2121
/**
2222
* Starts an express server and sends the port to the runner
23+
* @param app Express app
24+
* @param port Port to start the app on. USE WITH CAUTION! By default a random port will be chosen.
25+
* Setting this port to something specific is useful for local debugging but dangerous for
26+
* CI/CD environments where port collisions can cause flakes!
2327
*/
24-
export function startExpressServerAndSendPortToRunner(app: Express): void {
25-
const server = app.listen(0, () => {
28+
export function startExpressServerAndSendPortToRunner(app: Express, port: number | undefined = undefined): void {
29+
const server = app.listen(port || 0, () => {
2630
const address = server.address() as AddressInfo;
2731

2832
// eslint-disable-next-line no-console
29-
console.log(`{"port":${address.port}}`);
33+
console.log(`{"port":${port || address.port}}`);
3034
});
3135
}
3236

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
2+
const Sentry = require('@sentry/node');
3+
4+
Sentry.init({
5+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
transport: loggingTransport,
9+
10+
integrations: [
11+
Sentry.httpIntegration({
12+
ignoreIncomingRequests: url => {
13+
return url.includes('/liveness');
14+
},
15+
}),
16+
],
17+
});
18+
19+
// express must be required after Sentry is initialized
20+
const express = require('express');
21+
const cors = require('cors');
22+
const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');
23+
24+
const app = express();
25+
26+
app.use(cors());
27+
28+
app.get('/test', (_req, res) => {
29+
res.send({ response: 'response 1' });
30+
});
31+
32+
app.get('/liveness', (_req, res) => {
33+
res.send({ response: 'liveness' });
34+
});
35+
36+
Sentry.setupExpressErrorHandler(app);
37+
38+
startExpressServerAndSendPortToRunner(app);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
2+
const Sentry = require('@sentry/node');
3+
const http = require('http');
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
release: '1.0',
8+
tracesSampleRate: 1.0,
9+
transport: loggingTransport,
10+
11+
integrations: [
12+
Sentry.httpIntegration({
13+
ignoreOutgoingRequests: url => {
14+
return url.includes('example.com');
15+
},
16+
}),
17+
],
18+
});
19+
20+
// express must be required after Sentry is initialized
21+
const express = require('express');
22+
const cors = require('cors');
23+
const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');
24+
25+
const app = express();
26+
27+
app.use(cors());
28+
29+
app.get('/test', (_req, response) => {
30+
http
31+
.request('http://example.com/', res => {
32+
res.on('data', () => {});
33+
res.on('end', () => {
34+
response.send({ response: 'done' });
35+
});
36+
})
37+
.end();
38+
});
39+
40+
Sentry.setupExpressErrorHandler(app);
41+
42+
startExpressServerAndSendPortToRunner(app);

dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,59 @@ describe('httpIntegration', () => {
7575
.start(done)
7676
.makeRequest('get', '/test');
7777
});
78+
79+
test("doesn't create a root span for incoming requests ignored via `ignoreIncomingRequests`", done => {
80+
const runner = createRunner(__dirname, 'server-ignoreIncomingRequests.js')
81+
.expect({
82+
transaction: {
83+
contexts: {
84+
trace: {
85+
span_id: expect.any(String),
86+
trace_id: expect.any(String),
87+
data: {
88+
url: expect.stringMatching(/\/test$/),
89+
'http.response.status_code': 200,
90+
},
91+
op: 'http.server',
92+
status: 'ok',
93+
},
94+
},
95+
transaction: 'GET /test',
96+
},
97+
})
98+
.start(done);
99+
100+
runner.makeRequest('get', '/liveness'); // should be ignored
101+
runner.makeRequest('get', '/test');
102+
});
103+
104+
test("doesn't create child spans for outgoing requests ignored via `ignoreOutgoingRequests`", done => {
105+
const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js')
106+
.expect({
107+
transaction: {
108+
contexts: {
109+
trace: {
110+
span_id: expect.any(String),
111+
trace_id: expect.any(String),
112+
data: {
113+
url: expect.stringMatching(/\/test$/),
114+
'http.response.status_code': 200,
115+
},
116+
op: 'http.server',
117+
status: 'ok',
118+
},
119+
},
120+
transaction: 'GET /test',
121+
spans: [
122+
expect.objectContaining({ op: 'middleware.express', description: 'query' }),
123+
expect.objectContaining({ op: 'middleware.express', description: 'expressInit' }),
124+
expect.objectContaining({ op: 'middleware.express', description: 'corsMiddleware' }),
125+
expect.objectContaining({ op: 'request_handler.express', description: '/test' }),
126+
],
127+
},
128+
})
129+
.start(done);
130+
131+
runner.makeRequest('get', '/test');
132+
});
78133
});

packages/node/src/integrations/http.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,20 @@ interface HttpOptions {
3434
/**
3535
* Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`.
3636
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
37+
*
38+
* The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request.
39+
* For example: `'https://someService.com/users/details?id=123'`
3740
*/
3841
ignoreOutgoingRequests?: (url: string) => boolean;
3942

4043
/**
4144
* Do not capture spans or breadcrumbs for incoming HTTP requests to URLs where the given callback returns `true`.
4245
* This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.
46+
*
47+
* The `urlPath` param consists of the URL path and query string (if any) of the incoming request.
48+
* For example: `'/users/details?id=123'`
4349
*/
44-
ignoreIncomingRequests?: (url: string) => boolean;
50+
ignoreIncomingRequests?: (urlPath: string) => boolean;
4551

4652
/**
4753
* Additional instrumentation options that are passed to the underlying HttpInstrumentation.
@@ -103,7 +109,9 @@ export const instrumentHttp = Object.assign(
103109
},
104110

105111
ignoreIncomingRequestHook: request => {
106-
const url = getRequestUrl(request);
112+
// request.url is the only property that holds any information about the url
113+
// it only consists of the URL path and query string (if any)
114+
const urlPath = request.url;
107115

108116
const method = request.method?.toUpperCase();
109117
// We do not capture OPTIONS/HEAD requests as transactions
@@ -112,7 +120,7 @@ export const instrumentHttp = Object.assign(
112120
}
113121

114122
const _ignoreIncomingRequests = _httpOptions.ignoreIncomingRequests;
115-
if (_ignoreIncomingRequests && _ignoreIncomingRequests(url)) {
123+
if (urlPath && _ignoreIncomingRequests && _ignoreIncomingRequests(urlPath)) {
116124
return true;
117125
}
118126

0 commit comments

Comments
 (0)