Skip to content

Commit 1288020

Browse files
committed
merge incoming baggage header with possibly modified 3rd party headers
1 parent e7a6e84 commit 1288020

File tree

2 files changed

+63
-11
lines changed

2 files changed

+63
-11
lines changed

packages/tracing/src/browser/request.ts

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
/* eslint-disable max-lines */
2+
import { Baggage } from '@sentry/types';
13
import {
24
addInstrumentationHandler,
35
BAGGAGE_HEADER_NAME,
6+
createBaggage,
7+
getThirdPartyBaggage,
48
isInstanceOf,
59
isMatchingPattern,
10+
parseBaggageString,
611
serializeBaggage,
712
} from '@sentry/utils';
813

@@ -76,6 +81,7 @@ export interface XHRData {
7681
};
7782
__sentry_xhr_span_id__?: string;
7883
setRequestHeader?: (key: string, val: string) => void;
84+
getRequestHeader?: (key: string) => string;
7985
__sentry_own_request__?: boolean;
8086
};
8187
startTimestamp: number;
@@ -90,6 +96,7 @@ type PolymorphicRequestHeaders =
9096
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9197
[key: string]: any;
9298
append: (key: string, value: string) => void;
99+
get: (key: string) => string;
93100
};
94101

95102
export const defaultRequestInstrumentationOptions: RequestInstrumentationOptions = {
@@ -209,24 +216,32 @@ function addTracingHeaders(
209216
if (isInstanceOf(request, Request)) {
210217
headers = (request as Request).headers;
211218
}
212-
// TODO do we have to merge third-party-created added baggage headers with the ones we got form incoming req?
213-
// const baggageContent = (request as Request).headers.get(BAGGAGE_HEADER_NAME) || '';
214-
const baggageContent = serializeBaggageFromSpan(span);
219+
const incomingBaggage = span.getBaggage();
215220

216221
if (headers) {
217222
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
218223
if (typeof headers.append === 'function') {
219224
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
220225
headers.append('sentry-trace', span.toTraceparent());
221226
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
222-
headers.append(BAGGAGE_HEADER_NAME, baggageContent);
227+
headers.append(BAGGAGE_HEADER_NAME, mergeAndSerializeBaggage(incomingBaggage, headers.get(BAGGAGE_HEADER_NAME)));
223228
} else if (Array.isArray(headers)) {
224-
headers = [...headers, ['sentry-trace', span.toTraceparent()], [BAGGAGE_HEADER_NAME, baggageContent]];
229+
const [, headerBaggageString] = headers.find(([key, _]) => key === BAGGAGE_HEADER_NAME);
230+
headers = [
231+
...headers,
232+
['sentry-trace', span.toTraceparent()],
233+
[BAGGAGE_HEADER_NAME, mergeAndSerializeBaggage(incomingBaggage, headerBaggageString)],
234+
];
225235
} else {
226-
headers = { ...headers, 'sentry-trace': span.toTraceparent(), baggage: baggageContent };
236+
headers = {
237+
...headers,
238+
'sentry-trace': span.toTraceparent(),
239+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
240+
baggage: mergeAndSerializeBaggage(incomingBaggage, headers.baggage),
241+
};
227242
}
228243
} else {
229-
headers = { 'sentry-trace': span.toTraceparent(), baggage: baggageContent };
244+
headers = { 'sentry-trace': span.toTraceparent(), baggage: mergeAndSerializeBaggage(incomingBaggage) };
230245
}
231246
return headers;
232247
}
@@ -285,15 +300,44 @@ export function xhrCallback(
285300
if (handlerData.xhr.setRequestHeader) {
286301
try {
287302
handlerData.xhr.setRequestHeader('sentry-trace', span.toTraceparent());
288-
handlerData.xhr.setRequestHeader(BAGGAGE_HEADER_NAME, serializeBaggageFromSpan(span));
303+
304+
const headerBaggageString =
305+
handlerData.xhr.getRequestHeader && handlerData.xhr.getRequestHeader(BAGGAGE_HEADER_NAME);
306+
307+
handlerData.xhr.setRequestHeader(
308+
BAGGAGE_HEADER_NAME,
309+
mergeAndSerializeBaggage(span.getBaggage(), headerBaggageString),
310+
);
289311
} catch (_) {
290312
// Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
291313
}
292314
}
293315
}
294316
}
295317

296-
function serializeBaggageFromSpan(span: Span): string {
297-
const baggage = span.getBaggage();
298-
return (baggage && serializeBaggage(baggage)) || '';
318+
/**
319+
* Merges the baggage header we saved from the incoming request (or meta tag) with
320+
* a possibly created or modified baggage header by a third party that's been added
321+
* to the outgoing request header.
322+
*
323+
* In case @param headerBaggage exists, we can safely add the the 3rd party part of @param headerBaggage
324+
* with our @param incomingBaggage. This is possible because if we modified anything beforehand,
325+
* it would only affect parts of the sentry baggage (@see Baggage interface).
326+
*
327+
* @param incomingBaggage the baggage header of the incoming request that might contain sentry entries
328+
* @param headerBaggageString possibly existing baggage header string added from a third party to request headers
329+
*
330+
* @return a merged and serialized baggage string to be propagated with the outgoing request
331+
*/
332+
function mergeAndSerializeBaggage(incomingBaggage?: Baggage, headerBaggageString?: string): string {
333+
if (!incomingBaggage && !headerBaggageString) {
334+
return '';
335+
}
336+
337+
const headerBaggage = (headerBaggageString && parseBaggageString(headerBaggageString)) || undefined;
338+
const thirdPartyHeaderBaggage = headerBaggage && getThirdPartyBaggage(headerBaggage);
339+
340+
const finalBaggage = createBaggage((incomingBaggage && incomingBaggage[0]) || {}, thirdPartyHeaderBaggage || '');
341+
342+
return serializeBaggage(finalBaggage);
299343
}

packages/utils/src/baggage.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ export function isBaggageEmpty(baggage: Baggage): boolean {
3636
return Object.keys(baggage[0]).length === 0;
3737
}
3838

39+
/**
40+
* Returns 3rd party baggage string of @param baggage
41+
* @param baggage
42+
*/
43+
export function getThirdPartyBaggage(baggage: Baggage): string {
44+
return baggage[1];
45+
}
46+
3947
/** Serialize a baggage object */
4048
export function serializeBaggage(baggage: Baggage): string {
4149
return Object.keys(baggage[0]).reduce((prev, key: keyof BaggageObj) => {

0 commit comments

Comments
 (0)