@@ -119,6 +119,146 @@ function _addHeaderLines(headers, n) {
119119}
120120
121121
122+ // This function is used to help avoid the lowercasing of a field name if it
123+ // matches a 'traditional cased' version of a field name. It then returns the
124+ // lowercased name to both avoid calling toLowerCase() a second time and to
125+ // indicate whether the field was a 'no duplicates' field. If a field is not a
126+ // 'no duplicates' field, a `0` byte is prepended as a flag. The one exception
127+ // to this is the Set-Cookie header which is indicated by a `1` byte flag, since
128+ // it is an 'array' field and thus is treated differently in _addHeaderLines().
129+ // TODO: perhaps http_parser could be returning both raw and lowercased versions
130+ // of known header names to avoid us having to call toLowerCase() for those
131+ // headers.
132+ /* eslint-disable max-len */
133+ // 'array' header list is taken from:
134+ // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
135+ /* eslint-enable max-len */
136+ function matchKnownFields ( field ) {
137+ var low = false ;
138+ while ( true ) {
139+ switch ( field ) {
140+ case 'Content-Type' :
141+ case 'content-type' :
142+ return 'content-type' ;
143+ case 'Content-Length' :
144+ case 'content-length' :
145+ return 'content-length' ;
146+ case 'User-Agent' :
147+ case 'user-agent' :
148+ return 'user-agent' ;
149+ case 'Referer' :
150+ case 'referer' :
151+ return 'referer' ;
152+ case 'Host' :
153+ case 'host' :
154+ return 'host' ;
155+ case 'Authorization' :
156+ case 'authorization' :
157+ return 'authorization' ;
158+ case 'Proxy-Authorization' :
159+ case 'proxy-authorization' :
160+ return 'proxy-authorization' ;
161+ case 'If-Modified-Since' :
162+ case 'if-modified-since' :
163+ return 'if-modified-since' ;
164+ case 'If-Unmodified-Since' :
165+ case 'if-unmodified-since' :
166+ return 'if-unmodified-since' ;
167+ case 'From' :
168+ case 'from' :
169+ return 'from' ;
170+ case 'Location' :
171+ case 'location' :
172+ return 'location' ;
173+ case 'Max-Forwards' :
174+ case 'max-forwards' :
175+ return 'max-forwards' ;
176+ case 'Retry-After' :
177+ case 'retry-after' :
178+ return 'retry-after' ;
179+ case 'ETag' :
180+ case 'etag' :
181+ return 'etag' ;
182+ case 'Last-Modified' :
183+ case 'last-modified' :
184+ return 'last-modified' ;
185+ case 'Server' :
186+ case 'server' :
187+ return 'server' ;
188+ case 'Age' :
189+ case 'age' :
190+ return 'age' ;
191+ case 'Expires' :
192+ case 'expires' :
193+ return 'expires' ;
194+ case 'Set-Cookie' :
195+ case 'set-cookie' :
196+ return '\u0001' ;
197+ // The fields below are not used in _addHeaderLine(), but they are common
198+ // headers where we can avoid toLowerCase() if the mixed or lower case
199+ // versions match the first time through.
200+ case 'Transfer-Encoding' :
201+ case 'transfer-encoding' :
202+ return '\u0000transfer-encoding' ;
203+ case 'Date' :
204+ case 'date' :
205+ return '\u0000date' ;
206+ case 'Connection' :
207+ case 'connection' :
208+ return '\u0000connection' ;
209+ case 'Cache-Control' :
210+ case 'cache-control' :
211+ return '\u0000cache-control' ;
212+ case 'Vary' :
213+ case 'vary' :
214+ return '\u0000vary' ;
215+ case 'Content-Encoding' :
216+ case 'content-encoding' :
217+ return '\u0000content-encoding' ;
218+ case 'Cookie' :
219+ case 'cookie' :
220+ return '\u0000cookie' ;
221+ case 'Origin' :
222+ case 'origin' :
223+ return '\u0000origin' ;
224+ case 'Upgrade' :
225+ case 'upgrade' :
226+ return '\u0000upgrade' ;
227+ case 'Expect' :
228+ case 'expect' :
229+ return '\u0000expect' ;
230+ case 'If-Match' :
231+ case 'if-match' :
232+ return '\u0000if-match' ;
233+ case 'If-None-Match' :
234+ case 'if-none-match' :
235+ return '\u0000if-none-match' ;
236+ case 'Accept' :
237+ case 'accept' :
238+ return '\u0000accept' ;
239+ case 'Accept-Encoding' :
240+ case 'accept-encoding' :
241+ return '\u0000accept-encoding' ;
242+ case 'Accept-Language' :
243+ case 'accept-language' :
244+ return '\u0000accept-language' ;
245+ case 'X-Forwarded-For' :
246+ case 'x-forwarded-for' :
247+ return '\u0000x-forwarded-for' ;
248+ case 'X-Forwarded-Host' :
249+ case 'x-forwarded-host' :
250+ return '\u0000x-forwarded-host' ;
251+ case 'X-Forwarded-Proto' :
252+ case 'x-forwarded-proto' :
253+ return '\u0000x-forwarded-proto' ;
254+ default :
255+ if ( low )
256+ return '\u0000' + field ;
257+ field = field . toLowerCase ( ) ;
258+ low = true ;
259+ }
260+ }
261+ }
122262// Add the given (field, value) pair to the message
123263//
124264// Per RFC2616, section 4.2 it is acceptable to join multiple instances of the
@@ -128,51 +268,27 @@ function _addHeaderLines(headers, n) {
128268// always joined.
129269IncomingMessage . prototype . _addHeaderLine = _addHeaderLine ;
130270function _addHeaderLine ( field , value , dest ) {
131- field = field . toLowerCase ( ) ;
132- switch ( field ) {
133- // Array headers:
134- case 'set-cookie' :
135- if ( dest [ field ] !== undefined ) {
136- dest [ field ] . push ( value ) ;
137- } else {
138- dest [ field ] = [ value ] ;
139- }
140- break ;
141-
142- /* eslint-disable max-len */
143- // list is taken from:
144- // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
145- /* eslint-enable max-len */
146- case 'content-type' :
147- case 'content-length' :
148- case 'user-agent' :
149- case 'referer' :
150- case 'host' :
151- case 'authorization' :
152- case 'proxy-authorization' :
153- case 'if-modified-since' :
154- case 'if-unmodified-since' :
155- case 'from' :
156- case 'location' :
157- case 'max-forwards' :
158- case 'retry-after' :
159- case 'etag' :
160- case 'last-modified' :
161- case 'server' :
162- case 'age' :
163- case 'expires' :
164- // drop duplicates
165- if ( dest [ field ] === undefined )
166- dest [ field ] = value ;
167- break ;
168-
169- default :
170- // make comma-separated list
171- if ( typeof dest [ field ] === 'string' ) {
172- dest [ field ] += ', ' + value ;
173- } else {
174- dest [ field ] = value ;
175- }
271+ field = matchKnownFields ( field ) ;
272+ var flag = field . charCodeAt ( 0 ) ;
273+ if ( flag === 0 ) {
274+ field = field . slice ( 1 ) ;
275+ // Make comma-separated list
276+ if ( typeof dest [ field ] === 'string' ) {
277+ dest [ field ] += ', ' + value ;
278+ } else {
279+ dest [ field ] = value ;
280+ }
281+ } else if ( flag === 1 ) {
282+ // Array header -- only Set-Cookie at the moment
283+ if ( dest [ 'set-cookie' ] !== undefined ) {
284+ dest [ 'set-cookie' ] . push ( value ) ;
285+ } else {
286+ dest [ 'set-cookie' ] = [ value ] ;
287+ }
288+ } else {
289+ // Drop duplicates
290+ if ( dest [ field ] === undefined )
291+ dest [ field ] = value ;
176292 }
177293}
178294
0 commit comments