Skip to content

Commit 003ab3f

Browse files
committed
attach both headers to outgoing requests instead of just sentry-trace header
1 parent e16cd35 commit 003ab3f

File tree

4 files changed

+58
-28
lines changed

4 files changed

+58
-28
lines changed

packages/node/src/integrations/http.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getCurrentHub } from '@sentry/core';
2-
import { Integration, Span } from '@sentry/types';
2+
import { Integration, Span, TraceHeaders } from '@sentry/types';
33
import { fill, logger, parseSemver } from '@sentry/utils';
44
import * as http from 'http';
55
import * as https from 'https';
@@ -115,9 +115,9 @@ function _createWrappedRequestMethodFactory(
115115
op: 'request',
116116
});
117117

118-
const sentryTraceHeader = span.toTraceparent();
119-
logger.log(`[Tracing] Adding sentry-trace header to outgoing request: ${sentryTraceHeader}`);
120-
requestOptions.headers = { ...requestOptions.headers, 'sentry-trace': sentryTraceHeader };
118+
const traceHeaders = span.getTraceHeaders();
119+
logger.log(`[Tracing] Adding sentry-trace and tracestate headers to outgoing request.`);
120+
requestOptions.headers = { ...requestOptions.headers, ...(traceHeaders as TraceHeaders) };
121121
}
122122
}
123123

packages/node/test/integrations/http.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,23 @@ describe('tracing', () => {
6464
expect((spans[0] as Transaction).name).toEqual('dogpark');
6565
});
6666

67-
it('attaches the sentry-trace header to outgoing non-sentry requests', async () => {
67+
it('attaches tracing headers to outgoing non-sentry requests', async () => {
6868
nock('http://dogs.are.great')
6969
.get('/')
7070
.reply(200);
7171

7272
createTransactionOnScope();
7373

7474
const request = http.get('http://dogs.are.great/');
75-
const sentryTraceHeader = request.getHeader('sentry-trace') as string;
75+
const sentryTraceHeader = request.getHeader('sentry-trace');
76+
const tracestateHeader = request.getHeader('tracestate');
7677

7778
expect(sentryTraceHeader).toBeDefined();
78-
expect(TRACEPARENT_REGEXP.test(sentryTraceHeader)).toBe(true);
79+
expect(tracestateHeader).toBeDefined();
80+
expect(TRACEPARENT_REGEXP.test(sentryTraceHeader as string)).toBe(true);
7981
});
8082

81-
it("doesn't attach the sentry-trace header to outgoing sentry requests", () => {
83+
it("doesn't attach tracing headers to outgoing sentry requests", () => {
8284
nock('http://squirrelchasers.ingest.sentry.io')
8385
.get('/api/12312012/store/')
8486
.reply(200);
@@ -87,7 +89,9 @@ describe('tracing', () => {
8789

8890
const request = http.get('http://squirrelchasers.ingest.sentry.io/api/12312012/store/');
8991
const sentryTraceHeader = request.getHeader('sentry-trace');
92+
const tracestateHeader = request.getHeader('tracestate');
9093

9194
expect(sentryTraceHeader).not.toBeDefined();
95+
expect(tracestateHeader).not.toBeDefined();
9296
});
9397
});

packages/tracing/src/browser/request.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,14 @@ export function fetchCallback(
195195
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
196196
if (typeof headers.append === 'function') {
197197
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
198-
headers.append('sentry-trace', span.toTraceparent());
198+
headers.append(Object.entries(span.getTraceHeaders()));
199199
} else if (Array.isArray(headers)) {
200-
headers = [...headers, ['sentry-trace', span.toTraceparent()]];
200+
headers = [...headers, ...Object.entries(span.getTraceHeaders())];
201201
} else {
202-
headers = { ...headers, 'sentry-trace': span.toTraceparent() };
202+
headers = { ...headers, ...span.getTraceHeaders() };
203203
}
204204
} else {
205-
headers = { 'sentry-trace': span.toTraceparent() };
205+
headers = span.getTraceHeaders();
206206
}
207207
options.headers = headers;
208208
}
@@ -261,7 +261,11 @@ export function xhrCallback(
261261

262262
if (handlerData.xhr.setRequestHeader) {
263263
try {
264-
handlerData.xhr.setRequestHeader('sentry-trace', span.toTraceparent());
264+
const sentryHeaders = span.getTraceHeaders();
265+
handlerData.xhr.setRequestHeader('sentry-trace', sentryHeaders['sentry-trace']);
266+
if (sentryHeaders.tracestate) {
267+
handlerData.xhr.setRequestHeader('tracestate', sentryHeaders.tracestate);
268+
}
265269
} catch (_) {
266270
// Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
267271
}

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

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ import {
1313
import { addExtensionMethods } from '../../src/hubextensions';
1414
import * as tracingUtils from '../../src/utils';
1515

16+
// This is a normal base64 regex, modified to reflect that fact that we strip the trailing = or == off
17+
const stripped_base64 = '([a-zA-Z0-9+/]{4})*([a-zA-Z0-9+/]{2,3})?';
18+
19+
const TRACESTATE_HEADER_REGEX = new RegExp(
20+
`sentry=(${stripped_base64})` + // our part of the header - should be the only part or at least the first part
21+
`(,\\w+=\\w+)*`, // any number of copies of a comma followed by `name=value`
22+
);
23+
1624
beforeAll(() => {
1725
addExtensionMethods();
1826
// @ts-ignore need to override global Request because it's not in the jest environment (even with an
@@ -63,7 +71,7 @@ describe('callbacks', () => {
6371
const fetchHandlerData: FetchData = {
6472
args: ['http://dogs.are.great/', {}],
6573
fetchData: { url: 'http://dogs.are.great/', method: 'GET' },
66-
startTimestamp: 1356996072000,
74+
startTimestamp: 2012112120121231,
6775
};
6876
const xhrHandlerData: XHRData = {
6977
xhr: {
@@ -78,18 +86,27 @@ describe('callbacks', () => {
7886
// setRequestHeader: XMLHttpRequest.prototype.setRequestHeader,
7987
setRequestHeader,
8088
},
81-
startTimestamp: 1353501072000,
89+
startTimestamp: 2012112120121231,
8290
};
83-
const endTimestamp = 1356996072000;
91+
const endTimestamp = 2013041520130908;
8492

8593
beforeAll(() => {
86-
hub = new Hub(new BrowserClient({ tracesSampleRate: 1 }));
94+
hub = new Hub(
95+
new BrowserClient({
96+
dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012',
97+
environment: 'dogpark',
98+
release: 'off.leash.park',
99+
100+
tracesSampleRate: 1,
101+
}),
102+
);
87103
makeMain(hub);
88104
});
89105

90106
beforeEach(() => {
91-
transaction = hub.startTransaction({ name: 'organizations/users/:userid', op: 'pageload' }) as Transaction;
107+
transaction = hub.startTransaction({ name: 'meetNewDogFriend', op: 'wag.tail' }) as Transaction;
92108
hub.configureScope(scope => scope.setSpan(transaction));
109+
jest.clearAllMocks();
93110
});
94111

95112
describe('fetchCallback()', () => {
@@ -117,20 +134,17 @@ describe('callbacks', () => {
117134
expect(spans).toEqual({});
118135
});
119136

120-
it('does not add fetch request headers if tracing is disabled', () => {
137+
it('does not add tracing headers if tracing is disabled', () => {
121138
hasTracingEnabled.mockReturnValueOnce(false);
122139

123140
// make a local copy so the global one doesn't get mutated
124-
const handlerData: FetchData = {
125-
args: ['http://dogs.are.great/', {}],
126-
fetchData: { url: 'http://dogs.are.great/', method: 'GET' },
127-
startTimestamp: 1353501072000,
128-
};
141+
const handlerData = { ...fetchHandlerData };
129142

130143
fetchCallback(handlerData, alwaysCreateSpan, {});
131144

132145
const headers = (handlerData.args[1].headers as Record<string, string>) || {};
133146
expect(headers['sentry-trace']).not.toBeDefined();
147+
expect(headers['tracestate']).not.toBeDefined();
134148
});
135149

136150
it('creates and finishes fetch span on active transaction', () => {
@@ -185,8 +199,15 @@ describe('callbacks', () => {
185199
expect(newSpan!.status).toBe(SpanStatus.fromHttpCode(404));
186200
});
187201

188-
it('adds sentry-trace header to fetch requests', () => {
189-
// TODO
202+
it('adds tracing headers to fetch requests', () => {
203+
// make a local copy so the global one doesn't get mutated
204+
const handlerData = { ...fetchHandlerData };
205+
206+
fetchCallback(handlerData, alwaysCreateSpan, {});
207+
208+
const headers = (handlerData.args[1].headers as Record<string, string>) || {};
209+
expect(headers['sentry-trace']).toBeDefined();
210+
expect(headers['tracestate']).toBeDefined();
190211
});
191212
});
192213

@@ -207,21 +228,22 @@ describe('callbacks', () => {
207228
expect(spans).toEqual({});
208229
});
209230

210-
it('does not add xhr request headers if tracing is disabled', () => {
231+
it('does not add tracing headers if tracing is disabled', () => {
211232
hasTracingEnabled.mockReturnValueOnce(false);
212233

213234
xhrCallback(xhrHandlerData, alwaysCreateSpan, {});
214235

215236
expect(setRequestHeader).not.toHaveBeenCalled();
216237
});
217238

218-
it('adds sentry-trace header to XHR requests', () => {
239+
it('adds tracing headers to XHR requests', () => {
219240
xhrCallback(xhrHandlerData, alwaysCreateSpan, {});
220241

221242
expect(setRequestHeader).toHaveBeenCalledWith(
222243
'sentry-trace',
223244
expect.stringMatching(tracingUtils.TRACEPARENT_REGEXP),
224245
);
246+
expect(setRequestHeader).toHaveBeenCalledWith('tracestate', expect.stringMatching(TRACESTATE_HEADER_REGEX));
225247
});
226248

227249
it('creates and finishes XHR span on active transaction', () => {

0 commit comments

Comments
 (0)