@@ -334,109 +334,161 @@ function _addHeaderLines(headers, n) {
334334}
335335
336336
337- // This function is used to help avoid the lowercasing of a field name if it
338- // matches a 'traditional cased' version of a field name. It then returns the
339- // lowercased name to both avoid calling toLowerCase() a second time and to
340- // indicate whether the field was a 'no duplicates' field. If a field is not a
341- // 'no duplicates' field, a `0` byte is prepended as a flag. The one exception
342- // to this is the Set-Cookie header which is indicated by a `1` byte flag, since
343- // it is an 'array' field and thus is treated differently in _addHeaderLines().
337+ // How repeated instances of a header field are combined in the headers
338+ // object. Fields that are not known are joined with ', '.
339+ const kFirstWins = 0 ;
340+ const kJoinComma = 1 ;
341+ const kJoinSemicolon = 2 ;
342+ const kArray = 3 ;
343+
344+ function knownField ( name , merge ) {
345+ return { name, merge } ;
346+ }
347+
348+ // Later values are dropped, unless joinDuplicateHeaders is set.
349+ const kAge = knownField ( 'age' , kFirstWins ) ;
350+ const kHost = knownField ( 'host' , kFirstWins ) ;
351+ const kFrom = knownField ( 'from' , kFirstWins ) ;
352+ const kETag = knownField ( 'etag' , kFirstWins ) ;
353+ const kServer = knownField ( 'server' , kFirstWins ) ;
354+ const kReferer = knownField ( 'referer' , kFirstWins ) ;
355+ const kExpires = knownField ( 'expires' , kFirstWins ) ;
356+ const kLocation = knownField ( 'location' , kFirstWins ) ;
357+ const kUserAgent = knownField ( 'user-agent' , kFirstWins ) ;
358+ const kRetryAfter = knownField ( 'retry-after' , kFirstWins ) ;
359+ const kContentType = knownField ( 'content-type' , kFirstWins ) ;
360+ const kMaxForwards = knownField ( 'max-forwards' , kFirstWins ) ;
361+ const kAuthorization = knownField ( 'authorization' , kFirstWins ) ;
362+ const kLastModified = knownField ( 'last-modified' , kFirstWins ) ;
363+ const kContentLength = knownField ( 'content-length' , kFirstWins ) ;
364+ const kIfModifiedSince = knownField ( 'if-modified-since' , kFirstWins ) ;
365+ const kProxyAuthorization = knownField ( 'proxy-authorization' , kFirstWins ) ;
366+ const kIfUnmodifiedSince = knownField ( 'if-unmodified-since' , kFirstWins ) ;
367+
368+ // Values are joined with ', '.
369+ const kDate = knownField ( 'date' , kJoinComma ) ;
370+ const kVary = knownField ( 'vary' , kJoinComma ) ;
371+ const kOrigin = knownField ( 'origin' , kJoinComma ) ;
372+ const kExpect = knownField ( 'expect' , kJoinComma ) ;
373+ const kAccept = knownField ( 'accept' , kJoinComma ) ;
374+ const kUpgrade = knownField ( 'upgrade' , kJoinComma ) ;
375+ const kIfMatch = knownField ( 'if-match' , kJoinComma ) ;
376+ const kConnection = knownField ( 'connection' , kJoinComma ) ;
377+ const kCacheControl = knownField ( 'cache-control' , kJoinComma ) ;
378+ const kIfNoneMatch = knownField ( 'if-none-match' , kJoinComma ) ;
379+ const kAcceptEncoding = knownField ( 'accept-encoding' , kJoinComma ) ;
380+ const kAcceptLanguage = knownField ( 'accept-language' , kJoinComma ) ;
381+ const kXForwardedFor = knownField ( 'x-forwarded-for' , kJoinComma ) ;
382+ const kContentEncoding = knownField ( 'content-encoding' , kJoinComma ) ;
383+ const kXForwardedHost = knownField ( 'x-forwarded-host' , kJoinComma ) ;
384+ const kTransferEncoding = knownField ( 'transfer-encoding' , kJoinComma ) ;
385+ const kXForwardedProto = knownField ( 'x-forwarded-proto' , kJoinComma ) ;
386+
387+ // Values are joined with '; '.
388+ const kCookie = knownField ( 'cookie' , kJoinSemicolon ) ;
389+
390+ // Values are collected into an array.
391+ const kSetCookie = knownField ( 'set-cookie' , kArray ) ;
392+
393+ // Returns the descriptor of a known field, or the lowercased name of any other
394+ // field. The 'traditional cased' and lowercase spellings of known fields are
395+ // matched first to avoid calling toLowerCase() for them.
344396// TODO: perhaps http_parser could be returning both raw and lowercased versions
345397// of known header names to avoid us having to call toLowerCase() for those
346398// headers.
347399function matchKnownFields ( field , lowercased ) {
348400 switch ( field . length ) {
349401 case 3 :
350- if ( field === 'Age' || field === 'age' ) return 'age' ;
402+ if ( field === 'Age' || field === 'age' ) return kAge ;
351403 break ;
352404 case 4 :
353- if ( field === 'Host' || field === 'host' ) return 'host' ;
354- if ( field === 'From' || field === 'from' ) return 'from' ;
355- if ( field === 'ETag' || field === 'etag' ) return 'etag' ;
356- if ( field === 'Date' || field === 'date' ) return '\u0000date' ;
357- if ( field === 'Vary' || field === 'vary' ) return '\u0000vary' ;
405+ if ( field === 'Host' || field === 'host' ) return kHost ;
406+ if ( field === 'From' || field === 'from' ) return kFrom ;
407+ if ( field === 'ETag' || field === 'etag' ) return kETag ;
408+ if ( field === 'Date' || field === 'date' ) return kDate ;
409+ if ( field === 'Vary' || field === 'vary' ) return kVary ;
358410 break ;
359411 case 6 :
360- if ( field === 'Server' || field === 'server' ) return 'server' ;
361- if ( field === 'Cookie' || field === 'cookie' ) return '\u0002cookie' ;
362- if ( field === 'Origin' || field === 'origin' ) return '\u0000origin' ;
363- if ( field === 'Expect' || field === 'expect' ) return '\u0000expect' ;
364- if ( field === 'Accept' || field === 'accept' ) return '\u0000accept' ;
412+ if ( field === 'Server' || field === 'server' ) return kServer ;
413+ if ( field === 'Cookie' || field === 'cookie' ) return kCookie ;
414+ if ( field === 'Origin' || field === 'origin' ) return kOrigin ;
415+ if ( field === 'Expect' || field === 'expect' ) return kExpect ;
416+ if ( field === 'Accept' || field === 'accept' ) return kAccept ;
365417 break ;
366418 case 7 :
367- if ( field === 'Referer' || field === 'referer' ) return 'referer' ;
368- if ( field === 'Expires' || field === 'expires' ) return 'expires' ;
369- if ( field === 'Upgrade' || field === 'upgrade' ) return '\u0000upgrade' ;
419+ if ( field === 'Referer' || field === 'referer' ) return kReferer ;
420+ if ( field === 'Expires' || field === 'expires' ) return kExpires ;
421+ if ( field === 'Upgrade' || field === 'upgrade' ) return kUpgrade ;
370422 break ;
371423 case 8 :
372424 if ( field === 'Location' || field === 'location' )
373- return 'location' ;
425+ return kLocation ;
374426 if ( field === 'If-Match' || field === 'if-match' )
375- return '\u0000if-match' ;
427+ return kIfMatch ;
376428 break ;
377429 case 10 :
378430 if ( field === 'User-Agent' || field === 'user-agent' )
379- return 'user-agent' ;
431+ return kUserAgent ;
380432 if ( field === 'Set-Cookie' || field === 'set-cookie' )
381- return '\u0001' ;
433+ return kSetCookie ;
382434 if ( field === 'Connection' || field === 'connection' )
383- return '\u0000connection' ;
435+ return kConnection ;
384436 break ;
385437 case 11 :
386438 if ( field === 'Retry-After' || field === 'retry-after' )
387- return 'retry-after' ;
439+ return kRetryAfter ;
388440 break ;
389441 case 12 :
390442 if ( field === 'Content-Type' || field === 'content-type' )
391- return 'content-type' ;
443+ return kContentType ;
392444 if ( field === 'Max-Forwards' || field === 'max-forwards' )
393- return 'max-forwards' ;
445+ return kMaxForwards ;
394446 break ;
395447 case 13 :
396448 if ( field === 'Authorization' || field === 'authorization' )
397- return 'authorization' ;
449+ return kAuthorization ;
398450 if ( field === 'Last-Modified' || field === 'last-modified' )
399- return 'last-modified' ;
451+ return kLastModified ;
400452 if ( field === 'Cache-Control' || field === 'cache-control' )
401- return '\u0000cache-control' ;
453+ return kCacheControl ;
402454 if ( field === 'If-None-Match' || field === 'if-none-match' )
403- return '\u0000if-none-match' ;
455+ return kIfNoneMatch ;
404456 break ;
405457 case 14 :
406458 if ( field === 'Content-Length' || field === 'content-length' )
407- return 'content-length' ;
459+ return kContentLength ;
408460 break ;
409461 case 15 :
410462 if ( field === 'Accept-Encoding' || field === 'accept-encoding' )
411- return '\u0000accept-encoding' ;
463+ return kAcceptEncoding ;
412464 if ( field === 'Accept-Language' || field === 'accept-language' )
413- return '\u0000accept-language' ;
465+ return kAcceptLanguage ;
414466 if ( field === 'X-Forwarded-For' || field === 'x-forwarded-for' )
415- return '\u0000x-forwarded-for' ;
467+ return kXForwardedFor ;
416468 break ;
417469 case 16 :
418470 if ( field === 'Content-Encoding' || field === 'content-encoding' )
419- return '\u0000content-encoding' ;
471+ return kContentEncoding ;
420472 if ( field === 'X-Forwarded-Host' || field === 'x-forwarded-host' )
421- return '\u0000x-forwarded-host' ;
473+ return kXForwardedHost ;
422474 break ;
423475 case 17 :
424476 if ( field === 'If-Modified-Since' || field === 'if-modified-since' )
425- return 'if-modified-since' ;
477+ return kIfModifiedSince ;
426478 if ( field === 'Transfer-Encoding' || field === 'transfer-encoding' )
427- return '\u0000transfer-encoding' ;
479+ return kTransferEncoding ;
428480 if ( field === 'X-Forwarded-Proto' || field === 'x-forwarded-proto' )
429- return '\u0000x-forwarded-proto' ;
481+ return kXForwardedProto ;
430482 break ;
431483 case 19 :
432484 if ( field === 'Proxy-Authorization' || field === 'proxy-authorization' )
433- return 'proxy-authorization' ;
485+ return kProxyAuthorization ;
434486 if ( field === 'If-Unmodified-Since' || field === 'if-unmodified-since' )
435- return 'if-unmodified-since' ;
487+ return kIfUnmodifiedSince ;
436488 break ;
437489 }
438490 if ( lowercased ) {
439- return '\u0000' + field ;
491+ return field ;
440492 }
441493 return matchKnownFields ( field . toLowerCase ( ) , true ) ;
442494}
@@ -447,21 +499,25 @@ function matchKnownFields(field, lowercased) {
447499// multiple values this way. The one exception to this is the Cookie header,
448500// which has multiple values joined with a '; ' instead. If a header's values
449501// cannot be joined in either of these ways, we declare the first instance the
450- // winner and drop the second. Extended header fields (those beginning with
451- // 'x-') are always joined.
502+ // winner and drop the second. Fields that are not known are always joined.
452503IncomingMessage . prototype . _addHeaderLine = _addHeaderLine ;
453504function _addHeaderLine ( field , value , dest ) {
454- field = matchKnownFields ( field ) ;
455- const flag = field . charCodeAt ( 0 ) ;
456- if ( flag === 0 || flag === 2 ) {
457- field = field . slice ( 1 ) ;
505+ const known = matchKnownFields ( field ) ;
506+ let merge = kJoinComma ;
507+ if ( typeof known === 'string' ) {
508+ field = known ;
509+ } else {
510+ field = known . name ;
511+ merge = known . merge ;
512+ }
513+ if ( merge === kJoinComma || merge === kJoinSemicolon ) {
458514 // Make a delimited list
459515 if ( typeof dest [ field ] === 'string' ) {
460- dest [ field ] += ( flag === 0 ? ', ' : '; ' ) + value ;
516+ dest [ field ] += ( merge === kJoinComma ? ', ' : '; ' ) + value ;
461517 } else {
462518 dest [ field ] = value ;
463519 }
464- } else if ( flag === 1 ) {
520+ } else if ( merge === kArray ) {
465521 // Array header -- only Set-Cookie at the moment
466522 if ( dest [ 'set-cookie' ] !== undefined ) {
467523 dest [ 'set-cookie' ] . push ( value ) ;
0 commit comments