1+ /* eslint-disable max-lines */
2+ import { Baggage } from '@sentry/types' ;
13import {
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
95102export 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}
0 commit comments