@@ -165,25 +165,24 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
165165 } ) ;
166166 }
167167
168- /**
169- * Yield an object { key, value } for each header in the request. Skips
170- * likely-invalid headers. Multi-valued headers are passed through.
171- */
172- private * parseRequestHeaders (
173- request : UndiciRequest
174- ) : Generator < { key : string ; value : string | string [ ] } > {
168+ private parseRequestHeaders ( request : UndiciRequest ) {
169+ const result = new Map < string , string | string [ ] > ( ) ;
170+
175171 if ( Array . isArray ( request . headers ) ) {
176172 // headers are an array [k1, v2, k2, v2] (undici v6+)
173+ // values could be string or a string[] for multiple values
177174 for ( let i = 0 ; i < request . headers . length ; i += 2 ) {
178175 const key = request . headers [ i ] ;
179- if ( typeof key !== 'string' ) {
180- // Shouldn't happen, but the types don't know that, and let's be safe
181- continue ;
176+ const value = request . headers [ i + 1 ] ;
177+
178+ // Key should always be a string, but the types don't know that, and let's be safe
179+ if ( typeof key === 'string' ) {
180+ result . set ( key . toLowerCase ( ) , value ) ;
182181 }
183- yield { key, value : request . headers [ i + 1 ] } ;
184182 }
185183 } else if ( typeof request . headers === 'string' ) {
186184 // headers are a raw string (undici v5)
185+ // headers could be repeated in several lines for multiple values
187186 const headers = request . headers . split ( '\r\n' ) ;
188187 for ( const line of headers ) {
189188 if ( ! line ) {
@@ -194,11 +193,20 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
194193 // Invalid header? Probably this can't happen, but again let's be safe.
195194 continue ;
196195 }
197- const key = line . substring ( 0 , colonIndex ) ;
196+ const key = line . substring ( 0 , colonIndex ) . toLowerCase ( ) ;
198197 const value = line . substring ( colonIndex + 1 ) . trim ( ) ;
199- yield { key, value } ;
198+ const allValues = result . get ( key ) ;
199+
200+ if ( allValues && Array . isArray ( allValues ) ) {
201+ allValues . push ( value ) ;
202+ } else if ( allValues ) {
203+ result . set ( key , [ allValues , value ] ) ;
204+ } else {
205+ result . set ( key , value ) ;
206+ }
200207 }
201208 }
209+ return result ;
202210 }
203211
204212 // This is the 1st message we receive for each request (fired after request creation). Here we will
@@ -254,17 +262,14 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
254262 }
255263
256264 // Get user agent from headers
257- for ( const { key, value } of this . parseRequestHeaders ( request ) ) {
258- if ( key . toLowerCase ( ) === 'user-agent' ) {
259- // user-agent should only appear once per the spec, but the library doesn't
260- // prevent passing it multiple times, so we handle that to be safe.
261- // we will pick last value set
262- const userAgent = Array . isArray ( value )
263- ? value [ value . length - 1 ]
264- : value ;
265- attributes [ SemanticAttributes . USER_AGENT_ORIGINAL ] = userAgent ;
266- break ;
267- }
265+ const headersMap = this . parseRequestHeaders ( request ) ;
266+ const userAgentValues = headersMap . get ( 'user-agent' ) ;
267+
268+ if ( userAgentValues ) {
269+ const userAgent = Array . isArray ( userAgentValues )
270+ ? userAgentValues [ userAgentValues . length - 1 ]
271+ : userAgentValues ;
272+ attributes [ SemanticAttributes . USER_AGENT_ORIGINAL ] = userAgent ;
268273 }
269274
270275 // Get attributes from the hook if present
@@ -357,9 +362,9 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
357362 const headersToAttribs = new Set (
358363 config . headersToSpanAttributes . requestHeaders . map ( n => n . toLowerCase ( ) )
359364 ) ;
365+ const headersMap = this . parseRequestHeaders ( request ) ;
360366
361- for ( const { key, value } of this . parseRequestHeaders ( request ) ) {
362- const name = key . toLowerCase ( ) ;
367+ for ( const [ name , value ] of headersMap . entries ( ) ) {
363368 if ( headersToAttribs . has ( name ) ) {
364369 const attrValue = Array . isArray ( value ) ? value . join ( ', ' ) : value ;
365370 spanAttributes [ `http.request.header.${ name } ` ] = attrValue . trim ( ) ;
0 commit comments