Skip to content

Commit b1e44ab

Browse files
committed
http: throw error on content-length mismatch
1 parent 53ec358 commit b1e44ab

3 files changed

Lines changed: 36 additions & 2 deletions

File tree

lib/_http_incoming.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,5 +442,6 @@ function onError(self, error, cb) {
442442
module.exports = {
443443
IncomingMessage,
444444
readStart,
445-
readStop
445+
readStop,
446+
kHeaders
446447
};

lib/_http_outgoing.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const {
3737
SafeSet,
3838
StringPrototypeToLowerCase,
3939
Symbol,
40+
NumberPOSITIVE_INFINITY,
41+
NumberParseInt
4042
} = primordials;
4143

4244
const { getDefaultHighWaterMark } = require('internal/streams/state');
@@ -69,12 +71,14 @@ const {
6971
ERR_STREAM_ALREADY_FINISHED,
7072
ERR_STREAM_WRITE_AFTER_END,
7173
ERR_STREAM_NULL_VALUES,
72-
ERR_STREAM_DESTROYED
74+
ERR_STREAM_DESTROYED,
75+
ERR_HTTP_CONTENT_LENGTH_MISMATCH
7376
},
7477
hideStackFrames
7578
} = require('internal/errors');
7679
const { validateString } = require('internal/validators');
7780
const { isUint8Array } = require('internal/util/types');
81+
const { kHeaders } = require('_http_incoming');
7882

7983
let debug = require('internal/util/debuglog').debuglog('http', (fn) => {
8084
debug = fn;
@@ -84,6 +88,8 @@ const HIGH_WATER_MARK = getDefaultHighWaterMark();
8488

8589
const kCorked = Symbol('corked');
8690
const kUniqueHeaders = Symbol('kUniqueHeaders');
91+
const kBytesWritten = Symbol('kBytesWritten');
92+
const kSentContentLength = Symbol('kSentContentLength');
8793

8894
const nop = () => {};
8995

@@ -123,6 +129,8 @@ function OutgoingMessage() {
123129
this._removedContLen = false;
124130
this._removedTE = false;
125131

132+
this[kBytesWritten] = 0;
133+
this[kSentContentLength] = null;
126134
this._contentLength = null;
127135
this._hasBody = true;
128136
this._trailer = '';
@@ -345,10 +353,20 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) {
345353
this._onPendingData(header.length);
346354
}
347355
this._headerSent = true;
356+
this[kSentContentLength] = _getContentLength.call(this)
348357
}
349358
return this._writeRaw(data, encoding, callback);
350359
};
351360

361+
function _getContentLength() {
362+
const outgoing = this.getHeader('content-length')
363+
const incoming = this.req
364+
if (!!outgoing) return NumberParseInt(outgoing)
365+
if (incoming && incoming[kHeaders] && !!incoming[kHeaders]['content-length']) {
366+
return NumberParseInt(incoming[kHeaders]['content-length']);
367+
}
368+
return NumberPOSITIVE_INFINITY
369+
}
352370

353371
OutgoingMessage.prototype._writeRaw = _writeRaw;
354372
function _writeRaw(data, encoding, callback) {
@@ -534,6 +552,9 @@ function processHeader(self, state, key, value, validate) {
534552
function storeHeader(self, state, key, value, validate) {
535553
if (validate)
536554
validateHeaderValue(key, value);
555+
if (StringPrototypeToLowerCase(key) === 'content-length') {
556+
self[kSentContentLength] = value
557+
}
537558
state.header += key + ': ' + value + '\r\n';
538559
matchHeader(self, state, key, value);
539560
}
@@ -814,6 +835,16 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
814835
err = new ERR_STREAM_DESTROYED('write');
815836
}
816837

838+
if (msg && !msg.destroyed) {
839+
const byteLength = Buffer.byteLength(chunk, encoding)
840+
if (msg[kSentContentLength] > 0 && msg[kSentContentLength] !== NumberPOSITIVE_INFINITY) {
841+
if (byteLength + msg[kBytesWritten] > msg[kSentContentLength]) {
842+
throw new ERR_HTTP_CONTENT_LENGTH_MISMATCH(byteLength + msg[kBytesWritten], msg[kSentContentLength]);
843+
}
844+
}
845+
msg[kBytesWritten] += byteLength;
846+
}
847+
817848
if (err) {
818849
if (!msg.destroyed) {
819850
onError(msg, err, callback);

lib/internal/errors.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,8 @@ E('ERR_HTTP2_TRAILERS_NOT_READY',
11441144
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.', Error);
11451145
E('ERR_HTTP_HEADERS_SENT',
11461146
'Cannot %s headers after they are sent to the client', Error);
1147+
E('ERR_HTTP_CONTENT_LENGTH_MISMATCH',
1148+
'Response body\'s content-length of %s byte(s) does not match the content-length of %s byte(s) set in header', Error);
11471149
E('ERR_HTTP_INVALID_HEADER_VALUE',
11481150
'Invalid value "%s" for header "%s"', TypeError);
11491151
E('ERR_HTTP_INVALID_STATUS_CODE', 'Invalid status code: %s', RangeError);

0 commit comments

Comments
 (0)