redline
← arena

The limit that is the load

SeniorJavaScriptNode streamsMined
MINED FROM REAL HISTORYaxios/axiosMIT

The code below is the file exactly as it stood in axios/axios. The defect was introduced on 2016-07-09, reviewed, merged, and fixed on 2021-05-04 in commit 0ece97c7 — after 1760 days in the tree.

The commit was written by a human contributor to axios/axios, not by an agent. The ticket and the description on the next two panels are ours — a reconstruction of the case the original change made, so you meet it the way its reviewer did. The code, the defect and the dates are untouched.

HTTP-311the ticket

Enforce maxContentLength while the response is streaming

A misbehaving upstream can send a response far larger than we are willing to hold in memory, and today we only find out once the whole body has been buffered.

Enforce `maxContentLength` as the body arrives: as soon as the accumulated response exceeds the configured limit, destroy the stream and reject. Do not rely on the `content-length` header, which is optional and can lie.

the case the change madereconstructed by us

The check now runs inside the `data` handler rather than at `end`, so an oversized response is cut off mid-flight instead of being buffered to completion first.

Deliberately not trusting `content-length`: the size is measured from the bytes actually received, which is the only number an upstream cannot misreport. `Buffer.concat` gives the exact accumulated byte count including multi-byte characters, so there is no encoding-dependent drift. `maxContentLength > -1` preserves the existing opt-out.

The `stream` response type is unaffected — no buffering happens there, so there is nothing to measure. Tests cover a 2 KB limit with a 1 KB and a 3 KB body.

Our reconstruction of the argument the real change made, not a quotation of it. Fluent, specific, and not evidence of anything — which is the point.

lib/adapters/http.js43 lines
1 var response = {
2 status: res.statusCode,
3 statusText: res.statusMessage,
4 headers: res.headers,
5 config: config,
6 request: lastRequest
7 };
8
9 if (config.responseType === 'stream') {
10 response.data = stream;
11 settle(resolve, reject, response);
12 } else {
13 var responseBuffer = [];
14
15 stream.on('data', function handleStreamData(chunk) {
16 responseBuffer.push(chunk);
17
18 // make sure the content length is not over the maxContentLength if specified
19 if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
20 stream.destroy();
21 reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
22 config, null, lastRequest));
23 }
24 });
25
26 stream.on('error', function handleStreamError(err) {
27 if (req.aborted) return;
28 reject(enhanceError(err, config, null, lastRequest));
29 });
30
31 stream.on('end', function handleStreamEnd() {
32 var responseData = Buffer.concat(responseBuffer);
33 if (config.responseType !== 'arraybuffer') {
34 responseData = responseData.toString(config.responseEncoding);
35 if (!config.responseEncoding || config.responseEncoding === 'utf8') {
36 responseData = utils.stripBOM(responseData);
37 }
38 }
39
40 response.data = responseData;
41 settle(resolve, reject, response);
42 });
43 }