1
1
import type { Client , HandlerDataFetch , Scope , Span , SpanOrigin } from '@sentry/types' ;
2
2
import {
3
3
BAGGAGE_HEADER_NAME ,
4
+ SENTRY_BAGGAGE_KEY_PREFIX ,
4
5
dynamicSamplingContextToSentryBaggageHeader ,
5
6
generateSentryTraceHeader ,
6
7
isInstanceOf ,
@@ -122,7 +123,7 @@ export function addTracingHeadersToFetchRequest(
122
123
request : string | unknown , // unknown is actually type Request but we can't export DOM types from this package,
123
124
client : Client ,
124
125
scope : Scope ,
125
- options : {
126
+ fetchOptionsObj : {
126
127
headers ?:
127
128
| {
128
129
[ key : string ] : string [ ] | string | undefined ;
@@ -145,25 +146,53 @@ export function addTracingHeadersToFetchRequest(
145
146
) ;
146
147
147
148
const headers =
148
- options . headers ||
149
+ fetchOptionsObj . headers ||
149
150
( typeof Request !== 'undefined' && isInstanceOf ( request , Request ) ? ( request as Request ) . headers : undefined ) ;
150
151
151
152
if ( ! headers ) {
152
153
return { 'sentry-trace' : sentryTraceHeader , baggage : sentryBaggageHeader } ;
153
154
} else if ( typeof Headers !== 'undefined' && isInstanceOf ( headers , Headers ) ) {
154
155
const newHeaders = new Headers ( headers as Headers ) ;
155
156
156
- newHeaders . append ( 'sentry-trace' , sentryTraceHeader ) ;
157
+ newHeaders . set ( 'sentry-trace' , sentryTraceHeader ) ;
157
158
158
159
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
+ }
162
174
}
163
175
164
176
return newHeaders as PolymorphicRequestHeaders ;
165
177
} 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
+ ] ;
167
196
168
197
if ( sentryBaggageHeader ) {
169
198
// 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(
174
203
return newHeaders as PolymorphicRequestHeaders ;
175
204
} else {
176
205
const existingBaggageHeader = 'baggage' in headers ? headers . baggage : undefined ;
177
- const newBaggageHeaders : string [ ] = [ ] ;
206
+ let newBaggageHeaders : string [ ] = [ ] ;
178
207
179
208
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 === '' ) ;
181
214
} else if ( existingBaggageHeader ) {
182
- newBaggageHeaders . push ( existingBaggageHeader ) ;
215
+ newBaggageHeaders . push ( stripBaggageHeaderOfSentryBaggageValues ( existingBaggageHeader ) ) ;
183
216
}
184
217
185
218
if ( sentryBaggageHeader ) {
@@ -221,3 +254,13 @@ function endSpan(span: Span, handlerData: HandlerDataFetch): void {
221
254
}
222
255
span . end ( ) ;
223
256
}
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