@@ -45,68 +45,74 @@ export function addTracePropagationHeadersToFetchRequest(
4545
4646 const { 'sentry-trace' : sentryTrace , baggage, traceparent } = addedHeaders ;
4747
48+ const requestHeaders = Array . isArray ( request . headers ) ? request . headers : stringToArrayHeaders ( request . headers ) ;
49+
4850 // OTel's UndiciInstrumentation calls propagation.inject() which unconditionally
4951 // appends headers to the request. When the user also sets headers via getTraceData(),
5052 // this results in duplicate sentry-trace and baggage entries.
5153 // We clean these up before applying our own logic.
52- _deduplicateHeaders ( request ) ;
54+ _deduplicateArrayHeaders ( requestHeaders ) ;
5355
5456 // We do not want to overwrite existing headers here
5557 // If the core UndiciInstrumentation is registered, it will already have set the headers
5658 // We do not want to add any then
57- if ( Array . isArray ( request . headers ) ) {
58- const requestHeaders = request . headers ;
59-
60- const hasExistingSentryTraceHeader = requestHeaders . includes ( SENTRY_TRACE_HEADER ) ;
59+ const hasExistingSentryTraceHeader = requestHeaders . includes ( SENTRY_TRACE_HEADER ) ;
6160
62- // We do not want to set any headers if we already have an existing sentry-trace header.
63- // This is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values.
64- if ( ! hasExistingSentryTraceHeader ) {
65- if ( sentryTrace ) {
66- requestHeaders . push ( SENTRY_TRACE_HEADER , sentryTrace ) ;
67- }
61+ // We do not want to set any headers if we already have an existing sentry-trace header.
62+ // sentry-trace is still the source of truth, otherwise we risk mixing up baggage and sentry-trace values.
63+ if ( ! hasExistingSentryTraceHeader ) {
64+ if ( sentryTrace ) {
65+ requestHeaders . push ( SENTRY_TRACE_HEADER , sentryTrace ) ;
66+ }
6867
69- if ( traceparent && ! requestHeaders . includes ( 'traceparent' ) ) {
70- requestHeaders . push ( 'traceparent' , traceparent ) ;
71- }
68+ if ( traceparent && ! requestHeaders . includes ( 'traceparent' ) ) {
69+ requestHeaders . push ( 'traceparent' , traceparent ) ;
70+ }
7271
73- // For baggage, we make sure to merge this into a possibly existing header
74- const existingBaggagePos = requestHeaders . findIndex ( header => header === SENTRY_BAGGAGE_HEADER ) ;
75- if ( baggage && existingBaggagePos === - 1 ) {
76- requestHeaders . push ( SENTRY_BAGGAGE_HEADER , baggage ) ;
77- } else if ( baggage ) {
78- // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here
79- const existingBaggage = requestHeaders [ existingBaggagePos + 1 ] ;
80- const merged = mergeBaggageHeaders ( existingBaggage , baggage ) ;
81- if ( merged ) {
82- requestHeaders [ existingBaggagePos + 1 ] = merged ;
83- }
72+ // For baggage, we make sure to merge this into a possibly existing header
73+ const existingBaggagePos = requestHeaders . findIndex ( header => header === SENTRY_BAGGAGE_HEADER ) ;
74+ if ( baggage && existingBaggagePos === - 1 ) {
75+ requestHeaders . push ( SENTRY_BAGGAGE_HEADER , baggage ) ;
76+ } else if ( baggage ) {
77+ // headers in format [key_0, value_0, key_1, value_1, ...], hence the +1 here
78+ const existingBaggage = requestHeaders [ existingBaggagePos + 1 ] ;
79+ const merged = mergeBaggageHeaders ( existingBaggage , baggage ) ;
80+ if ( merged ) {
81+ requestHeaders [ existingBaggagePos + 1 ] = merged ;
8482 }
8583 }
86- } else {
87- // We do not want to overwrite existing header here, if it was already set
88- const hasExistingSentryTraceHeader = request . headers . includes ( `${ SENTRY_TRACE_HEADER } :` ) ;
84+ }
8985
90- if ( ! hasExistingSentryTraceHeader ) {
91- if ( sentryTrace ) {
92- request . headers += `${ SENTRY_TRACE_HEADER } : ${ sentryTrace } \r\n` ;
93- }
86+ if ( ! Array . isArray ( request . headers ) ) {
87+ // For orginal string request headers, we need to wrote them back to the request
88+ request . headers = arrayToStringHeaders ( requestHeaders ) ;
89+ }
90+ }
9491
95- if ( traceparent && ! request . headers . includes ( 'traceparent:' ) ) {
96- request . headers += `traceparent: ${ traceparent } \r\n` ;
92+ function stringToArrayHeaders ( requestHeaders : string ) : string [ ] {
93+ const headersArray = requestHeaders . split ( '\r\n' ) ;
94+ const headers : string [ ] = [ ] ;
95+ for ( const header of headersArray ) {
96+ try {
97+ const [ key , value ] = header . split ( ':' ) . map ( part => part . trim ( ) ) ;
98+ if ( key != null && value != null ) {
99+ headers . push ( key , value ) ;
97100 }
101+ } catch { }
102+ }
103+ return headers ;
104+ }
98105
99- const existingBaggage = request . headers . match ( BAGGAGE_HEADER_REGEX ) ?. [ 1 ] ;
100- if ( baggage && ! existingBaggage ) {
101- request . headers += `${ SENTRY_BAGGAGE_HEADER } : ${ baggage } \r\n` ;
102- } else if ( baggage ) {
103- const merged = mergeBaggageHeaders ( existingBaggage , baggage ) ;
104- if ( merged ) {
105- request . headers = request . headers . replace ( BAGGAGE_HEADER_REGEX , `baggage: ${ merged } \r\n` ) ;
106- }
107- }
108- }
106+ function arrayToStringHeaders ( headers : string [ ] ) : string {
107+ const headerPairs : string [ ] = [ ] ;
108+ for ( let i = 0 ; i < headers . length ; i += 2 ) {
109+ headerPairs . push ( `${ headers [ i ] } : ${ headers [ i + 1 ] } ` ) ;
110+ }
111+ if ( ! headerPairs . length ) {
112+ return '' ;
109113 }
114+
115+ return headerPairs . join ( '\r\n' ) . concat ( '\r\n' ) ;
110116}
111117
112118/**
@@ -115,37 +121,9 @@ export function addTracePropagationHeadersToFetchRequest(
115121 * OTel's UndiciInstrumentation unconditionally appends headers via propagation.inject(),
116122 * which can create duplicates when the user has already set these headers (e.g. via getTraceData()).
117123 * For sentry-trace, we keep the first occurrence (user-set).
118- * For baggage, we merge all occurrences into one to preserve both sentry and non-sentry entries.
124+ * For baggage, we merge all occurrences into one header to preserve non-sentry entries. For Sentry
125+ * entries, we keep the first occurance.
119126 */
120- function _deduplicateHeaders ( request : UndiciRequest ) : void {
121- if ( Array . isArray ( request . headers ) ) {
122- _deduplicateArrayHeaders ( request . headers ) ;
123- } else {
124- const headersArray = request . headers . split ( '\r\n' ) ;
125- const headers : string [ ] = [ ] ;
126- for ( const header of headersArray ) {
127- try {
128- const [ key , value ] = header . split ( ':' ) . map ( part => part . trim ( ) ) ;
129- if ( key != null && value != null ) {
130- headers . push ( key , value ) ;
131- }
132- } catch {
133- continue ;
134- }
135- }
136-
137- _deduplicateArrayHeaders ( headers ) ;
138-
139- const headerPairs : string [ ] = [ ] ;
140- for ( let i = 0 ; i < headers . length ; i += 2 ) {
141- headerPairs . push ( `${ headers [ i ] } : ${ headers [ i + 1 ] } ` ) ;
142- }
143- const concatenated = headerPairs . join ( '\r\n' ) ;
144- if ( concatenated ) {
145- request . headers = concatenated . concat ( '\r\n' ) ;
146- }
147- }
148- }
149127
150128function _deduplicateArrayHeaders ( headers : string [ ] ) : void {
151129 _deduplicateArrayHeader ( headers , SENTRY_TRACE_HEADER ) ;
0 commit comments