@@ -25,7 +25,6 @@ const {
2525 Array,
2626 ArrayIsArray,
2727 ArrayPrototypeJoin,
28- MathAbs,
2928 MathFloor,
3029 NumberPrototypeToString,
3130 ObjectDefineProperty,
@@ -86,7 +85,6 @@ const HIGH_WATER_MARK = getDefaultHighWaterMark();
8685const kCorked = Symbol ( 'corked' ) ;
8786const kUniqueHeaders = Symbol ( 'kUniqueHeaders' ) ;
8887const kBytesWritten = Symbol ( 'kBytesWritten' ) ;
89- const kEndCalled = Symbol ( 'kEndCalled' ) ;
9088const kErrored = Symbol ( 'errored' ) ;
9189
9290const nop = ( ) => { } ;
@@ -133,7 +131,6 @@ function OutgoingMessage() {
133131
134132 this . strictContentLength = false ;
135133 this [ kBytesWritten ] = 0 ;
136- this [ kEndCalled ] = false ;
137134 this . _contentLength = null ;
138135 this . _hasBody = true ;
139136 this . _trailer = '' ;
@@ -355,7 +352,7 @@ OutgoingMessage.prototype.destroy = function destroy(error) {
355352
356353
357354// This abstract either writing directly to the socket or buffering it.
358- OutgoingMessage . prototype . _send = function _send ( data , encoding , callback ) {
355+ OutgoingMessage . prototype . _send = function _send ( data , encoding , callback , byteLength ) {
359356 // This is a shameful hack to get the headers and first body chunk onto
360357 // the same packet. Future versions of Node are going to take care of
361358 // this at a lower level and in a more general way.
@@ -377,20 +374,11 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) {
377374 }
378375 this . _headerSent = true ;
379376 }
380- return this . _writeRaw ( data , encoding , callback ) ;
377+ return this . _writeRaw ( data , encoding , callback , byteLength ) ;
381378} ;
382379
383- function _getMessageBodySize ( chunk , headers , encoding ) {
384- if ( Buffer . isBuffer ( chunk ) ) return chunk . length ;
385- const chunkLength = chunk ? Buffer . byteLength ( chunk , encoding ) : 0 ;
386- const headerLength = headers ? headers . length : 0 ;
387- if ( headerLength === chunkLength ) return 0 ;
388- if ( headerLength < chunkLength ) return MathAbs ( chunkLength - headerLength ) ;
389- return chunkLength ;
390- }
391-
392380OutgoingMessage . prototype . _writeRaw = _writeRaw ;
393- function _writeRaw ( data , encoding , callback ) {
381+ function _writeRaw ( data , encoding , callback , size ) {
394382 const conn = this . socket ;
395383 if ( conn && conn . destroyed ) {
396384 // The socket was destroyed. If we're still trying to write to it,
@@ -403,25 +391,6 @@ function _writeRaw(data, encoding, callback) {
403391 encoding = null ;
404392 }
405393
406- // TODO(sidwebworks): flip the `strictContentLength` default to `true` in a future PR
407- if ( this . strictContentLength && conn && conn . writable && ! this . _removedContLen && this . _hasBody ) {
408- const skip = conn . _httpMessage . statusCode === 304 || ( this . hasHeader ( 'transfer-encoding' ) || this . chunkedEncoding ) ;
409-
410- if ( typeof this . _contentLength === 'number' && ! skip ) {
411- const size = _getMessageBodySize ( data , conn . _httpMessage . _header , encoding ) ;
412-
413- if ( ( size + this [ kBytesWritten ] ) > this . _contentLength ) {
414- throw new ERR_HTTP_CONTENT_LENGTH_MISMATCH ( size + this [ kBytesWritten ] , this . _contentLength ) ;
415- }
416-
417- if ( this [ kEndCalled ] && ( size + this [ kBytesWritten ] ) !== this . _contentLength ) {
418- throw new ERR_HTTP_CONTENT_LENGTH_MISMATCH ( size + this [ kBytesWritten ] , this . _contentLength ) ;
419- }
420-
421- this [ kBytesWritten ] += size ;
422- }
423- }
424-
425394 if ( conn && conn . _httpMessage === this && conn . writable ) {
426395 // There might be pending data in the this.output buffer.
427396 if ( this . outputData . length ) {
@@ -886,18 +855,24 @@ function emitErrorNt(msg, err, callback) {
886855 }
887856}
888857
858+ function strictContentLength ( msg ) {
859+ return (
860+ msg . strictContentLength &&
861+ msg . _contentLength != null &&
862+ msg . _hasBody &&
863+ ! msg . _removedContLen &&
864+ ! msg . chunkedEncoding &&
865+ ! msg . hasHeader ( 'transfer-encoding' )
866+ ) ;
867+ }
868+
889869function write_ ( msg , chunk , encoding , callback , fromEnd ) {
890870 if ( typeof callback !== 'function' )
891871 callback = nop ;
892872
893- let len ;
894873 if ( chunk === null ) {
895874 throw new ERR_STREAM_NULL_VALUES ( ) ;
896- } else if ( typeof chunk === 'string' ) {
897- len = Buffer . byteLength ( chunk , encoding ) ;
898- } else if ( isUint8Array ( chunk ) ) {
899- len = chunk . length ;
900- } else {
875+ } else if ( typeof chunk !== 'string' && ! isUint8Array ( chunk ) ) {
901876 throw new ERR_INVALID_ARG_TYPE (
902877 'chunk' , [ 'string' , 'Buffer' , 'Uint8Array' ] , chunk ) ;
903878 }
@@ -918,8 +893,24 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
918893 return false ;
919894 }
920895
896+ let len ;
897+
898+ if ( msg . strictContentLength ) {
899+ len ??= typeof chunk === 'string' ? Buffer . byteLength ( chunk , encoding ) : chunk . byteLength ;
900+
901+ if (
902+ strictContentLength ( msg ) &&
903+ ( fromEnd ? msg [ kBytesWritten ] + len !== msg . _contentLength : msg [ kBytesWritten ] + len > msg . _contentLength )
904+ ) {
905+ throw new ERR_HTTP_CONTENT_LENGTH_MISMATCH ( len + msg [ kBytesWritten ] , msg . _contentLength ) ;
906+ }
907+
908+ msg [ kBytesWritten ] += len ;
909+ }
910+
921911 if ( ! msg . _header ) {
922912 if ( fromEnd ) {
913+ len ??= typeof chunk === 'string' ? Buffer . byteLength ( chunk , encoding ) : chunk . byteLength ;
923914 msg . _contentLength = len ;
924915 }
925916 msg . _implicitHeader ( ) ;
@@ -939,12 +930,13 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
939930
940931 let ret ;
941932 if ( msg . chunkedEncoding && chunk . length !== 0 ) {
933+ len ??= typeof chunk === 'string' ? Buffer . byteLength ( chunk , encoding ) : chunk . byteLength ;
942934 msg . _send ( NumberPrototypeToString ( len , 16 ) , 'latin1' , null ) ;
943935 msg . _send ( crlf_buf , null , null ) ;
944- msg . _send ( chunk , encoding , null ) ;
936+ msg . _send ( chunk , encoding , null , len ) ;
945937 ret = msg . _send ( crlf_buf , null , callback ) ;
946938 } else {
947- ret = msg . _send ( chunk , encoding , callback ) ;
939+ ret = msg . _send ( chunk , encoding , callback , len ) ;
948940 }
949941
950942 debug ( 'write ret = ' + ret ) ;
@@ -1016,8 +1008,6 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
10161008 encoding = null ;
10171009 }
10181010
1019- this [ kEndCalled ] = true ;
1020-
10211011 if ( chunk ) {
10221012 if ( this . finished ) {
10231013 onError ( this ,
@@ -1052,6 +1042,10 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
10521042 if ( typeof callback === 'function' )
10531043 this . once ( 'finish' , callback ) ;
10541044
1045+ if ( strictContentLength ( this ) && this [ kBytesWritten ] !== this . _contentLength ) {
1046+ throw new ERR_HTTP_CONTENT_LENGTH_MISMATCH ( this [ kBytesWritten ] , this . _contentLength ) ;
1047+ }
1048+
10551049 const finish = onFinish . bind ( undefined , this ) ;
10561050
10571051 if ( this . _hasBody && this . chunkedEncoding ) {
0 commit comments