11import type { Client , HandlerDataFetch , Scope , Span , SpanOrigin } from '@sentry/types' ;
22import {
33 BAGGAGE_HEADER_NAME ,
4+ SENTRY_BAGGAGE_KEY_PREFIX ,
45 dynamicSamplingContextToSentryBaggageHeader ,
56 generateSentryTraceHeader ,
67 isInstanceOf ,
@@ -122,7 +123,7 @@ export function addTracingHeadersToFetchRequest(
122123 request : string | unknown , // unknown is actually type Request but we can't export DOM types from this package,
123124 client : Client ,
124125 scope : Scope ,
125- options : {
126+ fetchOptionsObj : {
126127 headers ?:
127128 | {
128129 [ key : string ] : string [ ] | string | undefined ;
@@ -145,25 +146,53 @@ export function addTracingHeadersToFetchRequest(
145146 ) ;
146147
147148 const headers =
148- options . headers ||
149+ fetchOptionsObj . headers ||
149150 ( typeof Request !== 'undefined' && isInstanceOf ( request , Request ) ? ( request as Request ) . headers : undefined ) ;
150151
151152 if ( ! headers ) {
152153 return { 'sentry-trace' : sentryTraceHeader , baggage : sentryBaggageHeader } ;
153154 } else if ( typeof Headers !== 'undefined' && isInstanceOf ( headers , Headers ) ) {
154155 const newHeaders = new Headers ( headers as Headers ) ;
155156
156- newHeaders . append ( 'sentry-trace' , sentryTraceHeader ) ;
157+ newHeaders . set ( 'sentry-trace' , sentryTraceHeader ) ;
157158
158159 if ( sentryBaggageHeader ) {
159- // If the same header is appended multiple times the browser will merge the values into a single request header.
160- // Its therefore safe to simply push a "baggage" entry, even though there might already be another baggage header.
161- newHeaders . append ( BAGGAGE_HEADER_NAME , sentryBaggageHeader ) ;
160+ const prevBaggageHeader = newHeaders . get ( BAGGAGE_HEADER_NAME ) ;
161+ if ( prevBaggageHeader ) {
162+ const prevHeaderStrippedFromSentryBaggage = stripBaggageHeaderOfSentryBaggageValues ( prevBaggageHeader ) ;
163+ newHeaders . set (
164+ BAGGAGE_HEADER_NAME ,
165+ // If there are non-sentry entries (i.e. if the stripped string is non-empty/truthy) combine the stripped header and sentry baggage header
166+ // otherwise just set the sentry baggage header
167+ prevHeaderStrippedFromSentryBaggage
168+ ? `${ prevHeaderStrippedFromSentryBaggage } ,${ sentryBaggageHeader } `
169+ : sentryBaggageHeader ,
170+ ) ;
171+ } else {
172+ newHeaders . set ( BAGGAGE_HEADER_NAME , sentryBaggageHeader ) ;
173+ }
162174 }
163175
164176 return newHeaders as PolymorphicRequestHeaders ;
165177 } else if ( Array . isArray ( headers ) ) {
166- const newHeaders = [ ...headers , [ 'sentry-trace' , sentryTraceHeader ] ] ;
178+ const newHeaders = [
179+ ...headers
180+ // Remove any existing sentry-trace headers
181+ . filter ( header => {
182+ return ! ( Array . isArray ( header ) && header [ 0 ] === 'sentry-trace' ) ;
183+ } )
184+ // Get rid of previous sentry baggage values in baggage header
185+ . map ( header => {
186+ if ( Array . isArray ( header ) && header [ 0 ] === BAGGAGE_HEADER_NAME && typeof header [ 1 ] === 'string' ) {
187+ const [ headerName , headerValue , ...rest ] = header ;
188+ return [ headerName , stripBaggageHeaderOfSentryBaggageValues ( headerValue ) , ...rest ] ;
189+ } else {
190+ return header ;
191+ }
192+ } ) ,
193+ // Attach the new sentry-trace header
194+ [ 'sentry-trace' , sentryTraceHeader ] ,
195+ ] ;
167196
168197 if ( sentryBaggageHeader ) {
169198 // If there are multiple entries with the same key, the browser will merge the values into a single request header.
@@ -174,12 +203,16 @@ export function addTracingHeadersToFetchRequest(
174203 return newHeaders as PolymorphicRequestHeaders ;
175204 } else {
176205 const existingBaggageHeader = 'baggage' in headers ? headers . baggage : undefined ;
177- const newBaggageHeaders : string [ ] = [ ] ;
206+ let newBaggageHeaders : string [ ] = [ ] ;
178207
179208 if ( Array . isArray ( existingBaggageHeader ) ) {
180- newBaggageHeaders . push ( ...existingBaggageHeader ) ;
209+ newBaggageHeaders = existingBaggageHeader
210+ . map ( headerItem =>
211+ typeof headerItem === 'string' ? stripBaggageHeaderOfSentryBaggageValues ( headerItem ) : headerItem ,
212+ )
213+ . filter ( headerItem => headerItem === '' ) ;
181214 } else if ( existingBaggageHeader ) {
182- newBaggageHeaders . push ( existingBaggageHeader ) ;
215+ newBaggageHeaders . push ( stripBaggageHeaderOfSentryBaggageValues ( existingBaggageHeader ) ) ;
183216 }
184217
185218 if ( sentryBaggageHeader ) {
@@ -221,3 +254,13 @@ function endSpan(span: Span, handlerData: HandlerDataFetch): void {
221254 }
222255 span . end ( ) ;
223256}
257+
258+ function stripBaggageHeaderOfSentryBaggageValues ( baggageHeader : string ) : string {
259+ return (
260+ baggageHeader
261+ . split ( ',' )
262+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
263+ . filter ( baggageEntry => ! baggageEntry . split ( '=' ) [ 0 ] ! . startsWith ( SENTRY_BAGGAGE_KEY_PREFIX ) )
264+ . join ( ',' )
265+ ) ;
266+ }
0 commit comments