diff --git a/src/rules/requests/request-step-impls.ts b/src/rules/requests/request-step-impls.ts index 89e04b634..6d32523fe 100644 --- a/src/rules/requests/request-step-impls.ts +++ b/src/rules/requests/request-step-impls.ts @@ -678,7 +678,23 @@ export class PassThroughStepImpl extends PassThroughStep { reqBodyOverride = await buildOverriddenBody(modifiedReq, headers); - if (reqBodyOverride || modifiedReq?.headers) { + // If the body was larger than maxBodySize, the buffered copy is empty, but the + // original body is still streamed upstream unchanged, so it can't be measured here. + const originalContentLength = getHeaderValue(clientHeaders, 'content-length'); + const bodyWasTruncated = !reqBodyOverride && + originalContentLength !== undefined && + completedRequest.body.buffer.byteLength !== parseInt(originalContentLength, 10); + + if (bodyWasTruncated) { + // Keep the original framing, unless the new headers set their own: + if ( + !isH2Downstream && + getHeaderValue(headers, 'content-length') === undefined && + !getHeaderValue(headers, 'transfer-encoding')?.includes('chunked') + ) { + headers['content-length'] = originalContentLength; + } + } else if (reqBodyOverride || modifiedReq?.headers) { // Automatically match the content-length to the body: const updatedCLHeader = getRequestContentLengthAfterModification( reqBodyOverride || completedRequest.body.buffer, diff --git a/test/integration/proxying/http-proxying.spec.ts b/test/integration/proxying/http-proxying.spec.ts index d189b32eb..db4592671 100644 --- a/test/integration/proxying/http-proxying.spec.ts +++ b/test/integration/proxying/http-proxying.spec.ts @@ -1329,6 +1329,32 @@ nodeOnly(() => { expect(await proxiedRequestData.body.getText()).to.equal(''); }); + it("should still proxy larger request bodies if beforeRequest changes only the headers", async () => { + const message = "A large request body"; + + const remoteEndpoint = await remoteServer.forAnyRequest().thenReply(200); + await server.forPost(remoteServer.url).thenPassThrough({ + beforeRequest: (req) => ({ + headers: { ...req.headers, 'x-added-header': 'yes' } + }) + }); + + let response = await request.post({ + url: remoteServer.url, + body: message, + resolveWithFullResponse: true + }); + + expect(response.statusCode).to.equal(200); + + // The body was too large to buffer, so its length can't be measured here - but + // the original body is still streamed upstream, with its original framing: + const resultingRequest = (await remoteEndpoint.getSeenRequests())[0]; + expect(resultingRequest.headers['x-added-header']).to.equal('yes'); + expect(resultingRequest.headers['content-length']).to.equal(message.length.toString()); + expect(await resultingRequest.body.getText()).to.equal(message); + }); + it("should still proxy larger response bodies", async () => { await remoteServer.forAnyRequest().thenReply(200, "A large response body"); const proxyEndpoint = await server.forGet(remoteServer.url).thenPassThrough();