@@ -11,18 +11,8 @@ const checkIsHttpToken = common._checkIsHttpToken;
11
11
const checkInvalidHeaderChar = common . _checkInvalidHeaderChar ;
12
12
13
13
const CRLF = common . CRLF ;
14
- const trfrEncChunkExpression = common . chunkExpression ;
15
14
const debug = common . debug ;
16
15
17
- const upgradeExpression = / ^ U p g r a d e $ / i;
18
- const transferEncodingExpression = / ^ T r a n s f e r - E n c o d i n g $ / i;
19
- const contentLengthExpression = / ^ C o n t e n t - L e n g t h $ / i;
20
- const dateExpression = / ^ D a t e $ / i;
21
- const expectExpression = / ^ E x p e c t $ / i;
22
- const trailerExpression = / ^ T r a i l e r $ / i;
23
- const connectionExpression = / ^ C o n n e c t i o n $ / i;
24
- const connCloseExpression = / ( ^ | \W ) c l o s e ( \W | $ ) / i;
25
- const connUpgradeExpression = / ( ^ | \W ) u p g r a d e ( \W | $ ) / i;
26
16
27
17
const automaticHeaders = {
28
18
connection : true ,
@@ -31,6 +21,10 @@ const automaticHeaders = {
31
21
date : true
32
22
} ;
33
23
24
+ var RE_FIELDS = new RegExp ( '^(?:Connection|Transfer-Encoding|Content-Length|' +
25
+ 'Date|Expect|Trailer|Upgrade)$' , 'i' ) ;
26
+ var RE_CONN_VALUES = / (?: ^ | \W ) c l o s e | u p g r a d e (?: $ | \W ) / ig;
27
+ var RE_TE_CHUNKED = common . chunkExpression ;
34
28
35
29
var dateCache ;
36
30
function utcDate ( ) {
@@ -83,7 +77,6 @@ function OutgoingMessage() {
83
77
this . connection = null ;
84
78
this . _header = null ;
85
79
this . _headers = null ;
86
- this . _headerNames = { } ;
87
80
88
81
this . _onPendingData = null ;
89
82
}
@@ -198,57 +191,72 @@ function _storeHeader(firstLine, headers) {
198
191
// firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
199
192
// in the case of response it is: 'HTTP/1.1 200 OK\r\n'
200
193
var state = {
201
- sentConnectionHeader : false ,
202
- sentConnectionUpgrade : false ,
203
- sentContentLengthHeader : false ,
204
- sentTransferEncodingHeader : false ,
205
- sentDateHeader : false ,
206
- sentExpect : false ,
207
- sentTrailer : false ,
208
- sentUpgrade : false ,
209
- messageHeader : firstLine
194
+ connection : false ,
195
+ connUpgrade : false ,
196
+ contLen : false ,
197
+ te : false ,
198
+ date : false ,
199
+ expect : false ,
200
+ trailer : false ,
201
+ upgrade : false ,
202
+ header : firstLine
210
203
} ;
211
204
212
- var i ;
213
- var j ;
214
205
var field ;
206
+ var key ;
215
207
var value ;
216
- if ( headers instanceof Array ) {
217
- for ( i = 0 ; i < headers . length ; ++ i ) {
208
+ var i ;
209
+ var j ;
210
+ if ( headers === this . _headers ) {
211
+ for ( key in headers ) {
212
+ var entry = headers [ key ] ;
213
+ field = entry [ 0 ] ;
214
+ value = entry [ 1 ] ;
215
+
216
+ if ( value instanceof Array ) {
217
+ for ( j = 0 ; j < value . length ; j ++ ) {
218
+ storeHeader ( this , state , field , value [ j ] , false ) ;
219
+ }
220
+ } else {
221
+ storeHeader ( this , state , field , value , false ) ;
222
+ }
223
+ }
224
+ } else if ( headers instanceof Array ) {
225
+ for ( i = 0 ; i < headers . length ; i ++ ) {
218
226
field = headers [ i ] [ 0 ] ;
219
227
value = headers [ i ] [ 1 ] ;
220
228
221
229
if ( value instanceof Array ) {
222
230
for ( j = 0 ; j < value . length ; j ++ ) {
223
- storeHeader ( this , state , field , value [ j ] ) ;
231
+ storeHeader ( this , state , field , value [ j ] , true ) ;
224
232
}
225
233
} else {
226
- storeHeader ( this , state , field , value ) ;
234
+ storeHeader ( this , state , field , value , true ) ;
227
235
}
228
236
}
229
237
} else if ( headers ) {
230
238
var keys = Object . keys ( headers ) ;
231
- for ( i = 0 ; i < keys . length ; ++ i ) {
239
+ for ( i = 0 ; i < keys . length ; i ++ ) {
232
240
field = keys [ i ] ;
233
241
value = headers [ field ] ;
234
242
235
243
if ( value instanceof Array ) {
236
244
for ( j = 0 ; j < value . length ; j ++ ) {
237
- storeHeader ( this , state , field , value [ j ] ) ;
245
+ storeHeader ( this , state , field , value [ j ] , true ) ;
238
246
}
239
247
} else {
240
- storeHeader ( this , state , field , value ) ;
248
+ storeHeader ( this , state , field , value , true ) ;
241
249
}
242
250
}
243
251
}
244
252
245
253
// Are we upgrading the connection?
246
- if ( state . sentConnectionUpgrade && state . sentUpgrade )
254
+ if ( state . connUpgrade && state . upgrade )
247
255
this . upgrading = true ;
248
256
249
257
// Date header
250
- if ( this . sendDate && ! state . sentDateHeader ) {
251
- state . messageHeader += 'Date: ' + utcDate ( ) + CRLF ;
258
+ if ( this . sendDate && ! state . date ) {
259
+ state . header += 'Date: ' + utcDate ( ) + CRLF ;
252
260
}
253
261
254
262
// Force the connection to close when the response is a 204 No Content or
@@ -274,33 +282,30 @@ function _storeHeader(firstLine, headers) {
274
282
if ( this . _removedHeader . connection ) {
275
283
this . _last = true ;
276
284
this . shouldKeepAlive = false ;
277
- } else if ( ! state . sentConnectionHeader ) {
285
+ } else if ( ! state . connection ) {
278
286
var shouldSendKeepAlive = this . shouldKeepAlive &&
279
- ( state . sentContentLengthHeader ||
280
- this . useChunkedEncodingByDefault ||
281
- this . agent ) ;
287
+ ( state . contLen || this . useChunkedEncodingByDefault || this . agent ) ;
282
288
if ( shouldSendKeepAlive ) {
283
- state . messageHeader += 'Connection: keep-alive\r\n' ;
289
+ state . header += 'Connection: keep-alive\r\n' ;
284
290
} else {
285
291
this . _last = true ;
286
- state . messageHeader += 'Connection: close\r\n' ;
292
+ state . header += 'Connection: close\r\n' ;
287
293
}
288
294
}
289
295
290
- if ( ! state . sentContentLengthHeader && ! state . sentTransferEncodingHeader ) {
296
+ if ( ! state . contLen && ! state . te ) {
291
297
if ( ! this . _hasBody ) {
292
298
// Make sure we don't end the 0\r\n\r\n at the end of the message.
293
299
this . chunkedEncoding = false ;
294
300
} else if ( ! this . useChunkedEncodingByDefault ) {
295
301
this . _last = true ;
296
302
} else {
297
- if ( ! state . sentTrailer &&
298
303
! this . _removedHeader [ 'content-length' ] &&
304
+ if ( ! state . trailer &&
299
305
typeof this . _contentLength === 'number' ) {
300
- state . messageHeader += 'Content-Length: ' + this . _contentLength +
301
- '\r\n' ;
302
306
} else if ( ! this . _removedHeader [ 'transfer-encoding' ] ) {
303
- state . messageHeader += 'Transfer-Encoding: chunked\r\n' ;
307
+ state . header += 'Content-Length: ' + this . _contentLength + CRLF ;
308
+ state . header += 'Transfer-Encoding: chunked\r\n' ;
304
309
this . chunkedEncoding = true ;
305
310
} else {
306
311
// We should only be able to get here if both Content-Length and
@@ -311,70 +316,94 @@ function _storeHeader(firstLine, headers) {
311
316
}
312
317
}
313
318
314
- this . _header = state . messageHeader + CRLF ;
319
+ this . _header = state . header + CRLF ;
315
320
this . _headerSent = false ;
316
321
317
322
// wait until the first body chunk, or close(), is sent to flush,
318
323
// UNLESS we're sending Expect: 100-continue.
319
- if ( state . sentExpect ) this . _send ( '' ) ;
324
+ if ( state . expect ) this . _send ( '' ) ;
320
325
}
321
326
322
- function storeHeader ( self , state , field , value ) {
323
- if ( ! checkIsHttpToken ( field ) ) {
324
- throw new TypeError (
325
- 'Header name must be a valid HTTP Token ["' + field + '"]' ) ;
326
- }
327
- if ( checkInvalidHeaderChar ( value ) ) {
328
- debug ( 'Header "%s" contains invalid characters' , field ) ;
329
- throw new TypeError ( 'The header content contains invalid characters' ) ;
330
- }
331
- state . messageHeader += field + ': ' + escapeHeaderValue ( value ) + CRLF ;
332
-
333
- if ( connectionExpression . test ( field ) ) {
334
- state . sentConnectionHeader = true ;
335
- if ( connCloseExpression . test ( value ) ) {
336
- self . _last = true ;
337
- } else {
338
- self . shouldKeepAlive = true ;
327
+ function storeHeader ( self , state , field , value , validate ) {
328
+ if ( validate ) {
329
+ if ( ! checkIsHttpToken ( field ) ) {
330
+ throw new TypeError (
331
+ 'Header name must be a valid HTTP Token ["' + field + '"]' ) ;
332
+ }
333
+ if ( value === undefined ) {
334
+ throw new Error ( 'Header "%s" value must not be undefined' , field ) ;
335
+ } else if ( checkInvalidHeaderChar ( value ) ) {
336
+ debug ( 'Header "%s" contains invalid characters' , field ) ;
337
+ throw new TypeError ( 'The header content contains invalid characters' ) ;
339
338
}
340
- if ( connUpgradeExpression . test ( value ) )
341
- state . sentConnectionUpgrade = true ;
342
- } else if ( transferEncodingExpression . test ( field ) ) {
343
- state . sentTransferEncodingHeader = true ;
344
- if ( trfrEncChunkExpression . test ( value ) ) self . chunkedEncoding = true ;
345
-
346
- } else if ( contentLengthExpression . test ( field ) ) {
347
- state . sentContentLengthHeader = true ;
348
- } else if ( dateExpression . test ( field ) ) {
349
- state . sentDateHeader = true ;
350
- } else if ( expectExpression . test ( field ) ) {
351
- state . sentExpect = true ;
352
- } else if ( trailerExpression . test ( field ) ) {
353
- state . sentTrailer = true ;
354
- } else if ( upgradeExpression . test ( field ) ) {
355
- state . sentUpgrade = true ;
356
339
}
340
+ state . header += field + ': ' + escapeHeaderValue ( value ) + CRLF ;
341
+ matchHeader ( self , state , field , value ) ;
357
342
}
358
343
344
+ function matchConnValue ( self , state , value ) {
345
+ var sawClose = false ;
346
+ var m = RE_CONN_VALUES . exec ( value ) ;
347
+ while ( m ) {
348
+ if ( m [ 0 ] . length === 5 )
349
+ sawClose = true ;
350
+ else
351
+ state . connUpgrade = true ;
352
+ m = RE_CONN_VALUES . exec ( value ) ;
353
+ }
354
+ if ( sawClose )
355
+ self . _last = true ;
356
+ else
357
+ self . shouldKeepAlive = true ;
358
+ }
359
359
360
- OutgoingMessage . prototype . setHeader = function setHeader ( name , value ) {
360
+ function matchHeader ( self , state , field , value ) {
361
+ var m = RE_FIELDS . exec ( field ) ;
362
+ if ( ! m )
363
+ return ;
364
+ var len = m [ 0 ] . length ;
365
+ if ( len === 10 ) {
366
+ state . connection = true ;
367
+ matchConnValue ( self , state , value ) ;
368
+ } else if ( len === 17 ) {
369
+ state . te = true ;
370
+ if ( RE_TE_CHUNKED . test ( value ) ) self . chunkedEncoding = true ;
371
+ } else if ( len === 14 ) {
372
+ state . contLen = true ;
373
+ } else if ( len === 4 ) {
374
+ state . date = true ;
375
+ } else if ( len === 6 ) {
376
+ state . expect = true ;
377
+ } else if ( len === 7 ) {
378
+ var ch = m [ 0 ] . charCodeAt ( 0 ) ;
379
+ if ( ch === 85 || ch === 117 )
380
+ state . upgrade = true ;
381
+ else
382
+ state . trailer = true ;
383
+ }
384
+ }
385
+
386
+ function validateHeader ( msg , name , value ) {
361
387
if ( ! checkIsHttpToken ( name ) )
362
388
throw new TypeError (
363
389
'Header name must be a valid HTTP Token ["' + name + '"]' ) ;
364
390
if ( value === undefined )
365
391
throw new Error ( '"value" required in setHeader("' + name + '", value)' ) ;
366
- if ( this . _header )
392
+ if ( msg . _header )
367
393
throw new Error ( 'Can\'t set headers after they are sent.' ) ;
368
394
if ( checkInvalidHeaderChar ( value ) ) {
369
395
debug ( 'Header "%s" contains invalid characters' , name ) ;
370
396
throw new TypeError ( 'The header content contains invalid characters' ) ;
371
397
}
372
- if ( this . _headers === null )
398
+ }
399
+ OutgoingMessage . prototype . setHeader = function setHeader ( name , value ) {
400
+ validateHeader ( this , name , value ) ;
401
+
402
+ if ( ! this . _headers )
373
403
this . _headers = { } ;
374
404
375
- var key = name . toLowerCase ( ) ;
376
- this . _headers [ key ] = value ;
377
- this . _headerNames [ key ] = name ;
405
+ const key = name . toLowerCase ( ) ;
406
+ this . _headers [ key ] = [ name , value ] ;
378
407
379
408
if ( automaticHeaders [ key ] )
380
409
this . _removedHeader [ key ] = false ;
@@ -388,7 +417,10 @@ OutgoingMessage.prototype.getHeader = function getHeader(name) {
388
417
389
418
if ( ! this . _headers ) return ;
390
419
391
- return this . _headers [ name . toLowerCase ( ) ] ;
420
+ var entry = this . _headers [ name . toLowerCase ( ) ] ;
421
+ if ( ! entry )
422
+ return ;
423
+ return entry [ 1 ] ;
392
424
} ;
393
425
394
426
@@ -410,30 +442,10 @@ OutgoingMessage.prototype.removeHeader = function removeHeader(name) {
410
442
411
443
if ( this . _headers ) {
412
444
delete this . _headers [ key ] ;
413
- delete this . _headerNames [ key ] ;
414
445
}
415
446
} ;
416
447
417
448
418
- OutgoingMessage . prototype . _renderHeaders = function _renderHeaders ( ) {
419
- if ( this . _header ) {
420
- throw new Error ( 'Can\'t render headers after they are sent to the client' ) ;
421
- }
422
-
423
- var headersMap = this . _headers ;
424
- if ( ! headersMap ) return { } ;
425
-
426
- var headers = { } ;
427
- var keys = Object . keys ( headersMap ) ;
428
- var headerNames = this . _headerNames ;
429
-
430
- for ( var i = 0 , l = keys . length ; i < l ; i ++ ) {
431
- var key = keys [ i ] ;
432
- headers [ headerNames [ key ] ] = headersMap [ key ] ;
433
- }
434
- return headers ;
435
- } ;
436
-
437
449
OutgoingMessage . prototype . _implicitHeader = function _implicitHeader ( ) {
438
450
throw new Error ( '_implicitHeader() method is not implemented' ) ;
439
451
} ;
@@ -492,6 +504,7 @@ OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
492
504
this . connection . cork ( ) ;
493
505
process . nextTick ( connectionCorkNT , this . connection ) ;
494
506
}
507
+
495
508
this . _send ( len . toString ( 16 ) , 'latin1' , null ) ;
496
509
this . _send ( crlf_buf , null , null ) ;
497
510
this . _send ( chunk , encoding , null ) ;
0 commit comments