Skip to content

Commit 63e43e6

Browse files
authored
fix(tracing): Make shouldAttachHeaders not fall back to default values (#6238)
This patch fixes a bug in our `shouldAttachHeaders` function that determines internally if outgoing requests should have headers attached or not. Previously, we assigned the default values to `tracingOrigins` as well as `tracePropagationTargets` and overwrote these defaults, if users provided one (or (unlikely) both) options. In `shouldAttachHeaders`, we then first tested `tracingOrigins` for a match and in case there was no match, we'd test `tracePropagationTargets`. This, however, also meant that if users specified `tracingOrigins` and an URL didn't match them, there was still a chance that headers would be attached - in case the url matched the `tracePropagationTargets`' default values. With this fix, we don't assign default values to either option but check for definedness of them and only if they're both undefined, well fall back to defaults. ### Example: url: `/api/test/123` tracingOrigins: `[]` tracePropTargets: `['localhost', /^\//]` // default values `shouldAttachHeaders` returned `true` but should have returned `false`. This PR fixes this behaviour. In case, both options are defined, we prefer `tracePropagationTargets` over `tracingOrigins`, as defined in the [dev specification](https://develop.sentry.dev/sdk/performance/#example). Furthermore, this patch extracts the `shouldAttachHeaders` function by wrapping it in a factory function so that it's easier to test this behaviour. And it adds some tests to verify correct behaviour. We can get rid of some code around this in v8 when we finally remove `tracingOrigins`. Side-note: Because we're exporting `defaultRequestInstrumentationOptions` as public API, I decided to leave the optional value assignment in place but to simply not take the default values from it anymore in `instrumentOutgoingRequests` (see [aed40a6](aed40a6)).
1 parent 2f37d32 commit 63e43e6

File tree

2 files changed

+82
-9
lines changed

2 files changed

+82
-9
lines changed

packages/tracing/src/browser/request.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import {
1010

1111
import { getActiveTransaction, hasTracingEnabled } from '../utils';
1212

13-
// TODO (v8): Remove `tracingOrigins`
14-
export const DEFAULT_TRACING_ORIGINS = ['localhost', /^\//];
1513
export const DEFAULT_TRACE_PROPAGATION_TARGETS = ['localhost', /^\//];
1614

1715
/** Options for Request Instrumentation */
@@ -107,39 +105,61 @@ type PolymorphicRequestHeaders =
107105
export const defaultRequestInstrumentationOptions: RequestInstrumentationOptions = {
108106
traceFetch: true,
109107
traceXHR: true,
110-
tracingOrigins: DEFAULT_TRACING_ORIGINS,
108+
// TODO (v8): Remove this property
109+
tracingOrigins: DEFAULT_TRACE_PROPAGATION_TARGETS,
111110
tracePropagationTargets: DEFAULT_TRACE_PROPAGATION_TARGETS,
112111
};
113112

114113
/** Registers span creators for xhr and fetch requests */
115114
export function instrumentOutgoingRequests(_options?: Partial<RequestInstrumentationOptions>): void {
116115
// eslint-disable-next-line deprecation/deprecation
117116
const { traceFetch, traceXHR, tracingOrigins, tracePropagationTargets, shouldCreateSpanForRequest } = {
118-
...defaultRequestInstrumentationOptions,
117+
traceFetch: defaultRequestInstrumentationOptions.traceFetch,
118+
traceXHR: defaultRequestInstrumentationOptions.traceXHR,
119119
..._options,
120120
};
121121

122122
const shouldCreateSpan =
123123
typeof shouldCreateSpanForRequest === 'function' ? shouldCreateSpanForRequest : (_: string) => true;
124124

125-
const shouldAttachHeaders = (url: string): boolean =>
126-
stringMatchesSomePattern(url, tracingOrigins) || stringMatchesSomePattern(url, tracePropagationTargets);
125+
const shouldAttachHeadersWithTargets = (url: string): boolean =>
126+
shouldAttachHeaders(url, tracingOrigins, tracePropagationTargets);
127127

128128
const spans: Record<string, Span> = {};
129129

130130
if (traceFetch) {
131131
addInstrumentationHandler('fetch', (handlerData: FetchData) => {
132-
fetchCallback(handlerData, shouldCreateSpan, shouldAttachHeaders, spans);
132+
fetchCallback(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans);
133133
});
134134
}
135135

136136
if (traceXHR) {
137137
addInstrumentationHandler('xhr', (handlerData: XHRData) => {
138-
xhrCallback(handlerData, shouldCreateSpan, shouldAttachHeaders, spans);
138+
xhrCallback(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans);
139139
});
140140
}
141141
}
142142

143+
/**
144+
* A function that determines whether to attach tracing headers to a request.
145+
* This was extracted from `instrumentOutgoingRequests` to make it easier to test shouldAttachHeaders.
146+
* We only export this fuction for testing purposes.
147+
*
148+
* TODO (v8): Remove `tracingOrigins` which should drastically simplify this function.
149+
*/
150+
export function shouldAttachHeaders(
151+
url: string,
152+
tracePropagationTargets: (string | RegExp)[] | undefined,
153+
tracingOrigins: (string | RegExp)[] | undefined,
154+
): boolean {
155+
// TODO (v8): Replace the entire code below with this one-liner:
156+
// return stringMatchesSomePattern(url, tracePropagationTargets || DEFAULT_TRACE_PROPAGATION_TARGETS);
157+
if (tracePropagationTargets || tracingOrigins) {
158+
return stringMatchesSomePattern(url, tracePropagationTargets || tracingOrigins);
159+
}
160+
return stringMatchesSomePattern(url, DEFAULT_TRACE_PROPAGATION_TARGETS);
161+
}
162+
143163
/**
144164
* Create and track fetch request spans
145165
*/

packages/tracing/test/browser/request.test.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ import { Hub, makeMain } from '@sentry/core';
33
import * as utils from '@sentry/utils';
44

55
import { Span, spanStatusfromHttpCode, Transaction } from '../../src';
6-
import { fetchCallback, FetchData, instrumentOutgoingRequests, xhrCallback, XHRData } from '../../src/browser/request';
6+
import {
7+
fetchCallback,
8+
FetchData,
9+
instrumentOutgoingRequests,
10+
shouldAttachHeaders,
11+
xhrCallback,
12+
XHRData,
13+
} from '../../src/browser/request';
714
import { addExtensionMethods } from '../../src/hubextensions';
815
import * as tracingUtils from '../../src/utils';
916
import { getDefaultBrowserClientOptions } from '../testutils';
@@ -383,3 +390,49 @@ describe('callbacks', () => {
383390
});
384391
});
385392
});
393+
394+
// TODO (v8): Adapt these tests once we remove `tracingOrigins`
395+
describe('[pre-v8]: shouldAttachHeaders', () => {
396+
describe('prefer `tracePropagationTargets` over `tracingOrigins`', () => {
397+
it('should return `true` if the url matches the new tracePropagationTargets', () => {
398+
expect(shouldAttachHeaders('http://example.com', ['example.com'], undefined)).toBe(true);
399+
});
400+
401+
it('should return `false` if tracePropagationTargets array is empty', () => {
402+
expect(shouldAttachHeaders('http://localhost:3000/test', [], ['localhost'])).toBe(false);
403+
});
404+
405+
it("should return `false` if tracePropagationTargets array doesn't match", () => {
406+
expect(shouldAttachHeaders('http://localhost:3000/test', ['example.com'], ['localhost'])).toBe(false);
407+
});
408+
});
409+
410+
describe('tracingOrigins backwards compatibility (tracePropagationTargets not defined)', () => {
411+
it('should return `true` if the url matches tracingOrigns', () => {
412+
expect(shouldAttachHeaders('http://example.com', undefined, ['example.com'])).toBe(true);
413+
});
414+
415+
it('should return `false` if tracePropagationTargets array is empty', () => {
416+
expect(shouldAttachHeaders('http://localhost:3000/test', undefined, [])).toBe(false);
417+
});
418+
419+
it("should return `false` if tracePropagationTargets array doesn't match", () => {
420+
expect(shouldAttachHeaders('http://localhost:3000/test', undefined, ['example.com'])).toBe(false);
421+
});
422+
});
423+
424+
describe('should fall back to defaults if no options are specified', () => {
425+
it.each([
426+
'/api/test',
427+
'http://localhost:3000/test',
428+
'http://somewhere.com/test/localhost/123',
429+
'http://somewhere.com/test?url=localhost:3000&test=123',
430+
])('return `true` for urls matching defaults (%s)', url => {
431+
expect(shouldAttachHeaders(url, undefined, undefined)).toBe(true);
432+
});
433+
434+
it.each(['notmydoman/api/test', 'example.com'])('return `false` for urls not matching defaults (%s)', url => {
435+
expect(shouldAttachHeaders(url, undefined, undefined)).toBe(false);
436+
});
437+
});
438+
});

0 commit comments

Comments
 (0)