@@ -25,6 +25,7 @@ const {
25
25
Array,
26
26
ArrayIsArray,
27
27
ArrayPrototypeJoin,
28
+ MathAbs,
28
29
MathFloor,
29
30
NumberPrototypeToString,
30
31
ObjectCreate,
@@ -57,6 +58,7 @@ const {
57
58
} = require ( 'internal/async_hooks' ) ;
58
59
const {
59
60
codes : {
61
+ ERR_HTTP_CONTENT_LENGTH_MISMATCH ,
60
62
ERR_HTTP_HEADERS_SENT ,
61
63
ERR_HTTP_INVALID_HEADER_VALUE ,
62
64
ERR_HTTP_TRAILER_INVALID ,
@@ -84,6 +86,8 @@ const HIGH_WATER_MARK = getDefaultHighWaterMark();
84
86
85
87
const kCorked = Symbol ( 'corked' ) ;
86
88
const kUniqueHeaders = Symbol ( 'kUniqueHeaders' ) ;
89
+ const kBytesWritten = Symbol ( 'kBytesWritten' ) ;
90
+ const kEndCalled = Symbol ( 'kEndCalled' ) ;
87
91
88
92
const nop = ( ) => { } ;
89
93
@@ -123,6 +127,9 @@ function OutgoingMessage() {
123
127
this . _removedContLen = false ;
124
128
this . _removedTE = false ;
125
129
130
+ this . strictContentLength = false ;
131
+ this [ kBytesWritten ] = 0 ;
132
+ this [ kEndCalled ] = false ;
126
133
this . _contentLength = null ;
127
134
this . _hasBody = true ;
128
135
this . _trailer = '' ;
@@ -330,7 +337,9 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) {
330
337
// This is a shameful hack to get the headers and first body chunk onto
331
338
// the same packet. Future versions of Node are going to take care of
332
339
// this at a lower level and in a more general way.
333
- if ( ! this . _headerSent ) {
340
+ if ( ! this . _headerSent && this . _header !== null ) {
341
+ // `this._header` can be null if OutgoingMessage is used without a proper Socket
342
+ // See: /test/parallel/test-http-outgoing-message-inheritance.js
334
343
if ( typeof data === 'string' &&
335
344
( encoding === 'utf8' || encoding === 'latin1' || ! encoding ) ) {
336
345
data = this . _header + data ;
@@ -349,6 +358,14 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) {
349
358
return this . _writeRaw ( data , encoding , callback ) ;
350
359
} ;
351
360
361
+ function _getMessageBodySize ( chunk , headers , encoding ) {
362
+ if ( Buffer . isBuffer ( chunk ) ) return chunk . length ;
363
+ const chunkLength = chunk ? Buffer . byteLength ( chunk , encoding ) : 0 ;
364
+ const headerLength = headers ? headers . length : 0 ;
365
+ if ( headerLength === chunkLength ) return 0 ;
366
+ if ( headerLength < chunkLength ) return MathAbs ( chunkLength - headerLength ) ;
367
+ return chunkLength ;
368
+ }
352
369
353
370
OutgoingMessage . prototype . _writeRaw = _writeRaw ;
354
371
function _writeRaw ( data , encoding , callback ) {
@@ -364,6 +381,25 @@ function _writeRaw(data, encoding, callback) {
364
381
encoding = null ;
365
382
}
366
383
384
+ // TODO(sidwebworks): flip the `strictContentLength` default to `true` in a future PR
385
+ if ( this . strictContentLength && conn && conn . writable && ! this . _removedContLen && this . _hasBody ) {
386
+ const skip = conn . _httpMessage . statusCode === 304 || ( this . hasHeader ( 'transfer-encoding' ) || this . chunkedEncoding ) ;
387
+
388
+ if ( typeof this . _contentLength === 'number' && ! skip ) {
389
+ const size = _getMessageBodySize ( data , conn . _httpMessage . _header , encoding ) ;
390
+
391
+ if ( ( size + this [ kBytesWritten ] ) > this . _contentLength ) {
392
+ throw new ERR_HTTP_CONTENT_LENGTH_MISMATCH ( size + this [ kBytesWritten ] , this . _contentLength ) ;
393
+ }
394
+
395
+ if ( this [ kEndCalled ] && ( size + this [ kBytesWritten ] ) !== this . _contentLength ) {
396
+ throw new ERR_HTTP_CONTENT_LENGTH_MISMATCH ( size + this [ kBytesWritten ] , this . _contentLength ) ;
397
+ }
398
+
399
+ this [ kBytesWritten ] += size ;
400
+ }
401
+ }
402
+
367
403
if ( conn && conn . _httpMessage === this && conn . writable ) {
368
404
// There might be pending data in the this.output buffer.
369
405
if ( this . outputData . length ) {
@@ -559,6 +595,7 @@ function matchHeader(self, state, field, value) {
559
595
break ;
560
596
case 'content-length' :
561
597
state . contLen = true ;
598
+ self . _contentLength = value ;
562
599
self . _removedContLen = false ;
563
600
break ;
564
601
case 'date' :
@@ -923,6 +960,8 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
923
960
encoding = null ;
924
961
}
925
962
963
+ this [ kEndCalled ] = true ;
964
+
926
965
if ( chunk ) {
927
966
if ( this . finished ) {
928
967
onError ( this ,
0 commit comments