@@ -40,8 +40,7 @@ import {
4040 getUrlQuery ,
4141 filterCollectedUrl ,
4242 filterCollectedUrlQuery ,
43- _INTERNAL_shouldFilterDataKey ,
44- _INTERNAL_FILTERED_VALUE ,
43+ httpHeadersToSpanAttributes ,
4544} from '@sentry/core' ;
4645import { addFetchRequestBreadcrumb , addTracePropagationHeadersToFetchRequest } from '../../utils/outgoingFetchRequest' ;
4746import {
@@ -314,20 +313,21 @@ function onRequestHeaders(config: NodeFetchOptions, { request, socket }: Request
314313
315314 // After hooks have been processed (which may modify request headers)
316315 // we can collect the headers based on the configuration
317- if ( config . headersToSpanAttributes ?. requestHeaders ) {
316+ const client = getClient ( ) ;
317+ if ( config . headersToSpanAttributes ?. requestHeaders && client ) {
318318 const headersToAttribs = new Set ( config . headersToSpanAttributes . requestHeaders . map ( n => n . toLowerCase ( ) ) ) ;
319319 const headersMap = parseRequestHeaders ( request ) ;
320320
321+ const allowlisted : Record < string , string | string [ ] > = { } ;
321322 for ( const [ name , value ] of headersMap . entries ( ) ) {
322323 if ( headersToAttribs . has ( name ) ) {
323- // An allowlist entry does not exempt a header from the denylist.
324- spanAttributes [ `http.request.header.${ name } ` ] = _INTERNAL_shouldFilterDataKey ( name , true )
325- ? [ _INTERNAL_FILTERED_VALUE ]
326- : Array . isArray ( value )
327- ? value
328- : [ value ] ;
324+ allowlisted [ name ] = value ;
329325 }
330326 }
327+
328+ // An entry in `headersToSpanAttributes` does not exempt a header from the `dataCollection`
329+ // filtering, so the allowlisted subset goes through the same pipeline as any other header.
330+ Object . assign ( spanAttributes , httpHeadersToSpanAttributes ( allowlisted , client . getDataCollectionOptions ( ) ) ) ;
331331 }
332332
333333 span . setAttributes ( spanAttributes ) ;
@@ -360,30 +360,31 @@ function onResponseHeaders(config: NodeFetchOptions, { request, response }: Resp
360360 ( ) => undefined ,
361361 ) ;
362362
363- if ( config . headersToSpanAttributes ?. responseHeaders ) {
363+ const client = getClient ( ) ;
364+ if ( config . headersToSpanAttributes ?. responseHeaders && client ) {
364365 const headersToAttribs = new Set < string > ( ) ;
365366 config . headersToSpanAttributes ?. responseHeaders . forEach ( name => headersToAttribs . add ( name . toLowerCase ( ) ) ) ;
366367
368+ const allowlisted : Record < string , string [ ] > = { } ;
367369 for ( let idx = 0 ; idx < response . headers . length ; idx = idx + 2 ) {
368370 const nameBuf = response . headers [ idx ] ;
369371 const valueBuf = response . headers [ idx + 1 ] ;
370372 if ( nameBuf === undefined || valueBuf === undefined ) {
371373 continue ;
372374 }
373375 const name = nameBuf . toString ( ) . toLowerCase ( ) ;
374- const value = valueBuf ;
375376
376377 if ( headersToAttribs . has ( name ) ) {
377- const attrName = `http.response.header.${ name } ` ;
378- if ( _INTERNAL_shouldFilterDataKey ( name , true ) ) {
379- spanAttributes [ attrName ] = [ _INTERNAL_FILTERED_VALUE ] ;
380- } else if ( ! Object . prototype . hasOwnProperty . call ( spanAttributes , attrName ) ) {
381- spanAttributes [ attrName ] = [ value . toString ( ) ] ;
382- } else {
383- ( spanAttributes [ attrName ] as string [ ] ) . push ( value . toString ( ) ) ;
384- }
378+ ( allowlisted [ name ] ??= [ ] ) . push ( valueBuf . toString ( ) ) ;
385379 }
386380 }
381+
382+ // An entry in `headersToSpanAttributes` does not exempt a header from the `dataCollection`
383+ // filtering, so the allowlisted subset goes through the same pipeline as any other header.
384+ Object . assign (
385+ spanAttributes ,
386+ httpHeadersToSpanAttributes ( allowlisted , client . getDataCollectionOptions ( ) , 'response' ) ,
387+ ) ;
387388 }
388389
389390 span . setAttributes ( spanAttributes ) ;
0 commit comments