Skip to content

Commit

Permalink
fix(node-http-handler): skip sending body without waiting for a timeo…
Browse files Browse the repository at this point in the history
…ut on response, if "expect" request header with "100-continue" is provided (#1459)

* fix(node-http-handler): skip sending body without waiting for a timeout on response, if "expect" request header with "100-continue" is provided

* fix(node-http-handler): skip sending body without waiting for a timeout on response, if "expect" request header with "100-continue" is provided

* add unit tests

---------

Co-authored-by: George Fu <[email protected]>
  • Loading branch information
bolt-juri-gavshin and kuhe authored Nov 25, 2024
1 parent 430021a commit f4e1a45
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/hungry-eels-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/node-http-handler": patch
---

skip sending body without waiting for a timeout on response, if "expect" request header with "100-continue" is provided
62 changes: 62 additions & 0 deletions packages/node-http-handler/src/write-request-body.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import EventEmitter from "events";
import { describe, expect, test as it, vi } from "vitest";

import { writeRequestBody } from "./write-request-body";

describe(writeRequestBody.name, () => {
it("should wait for the continue event if request has expect=100-continue", async () => {
const httpRequest = Object.assign(new EventEmitter(), {
end: vi.fn(),
}) as any;
const request = {
headers: {
expect: "100-continue",
},
body: Buffer.from("abcd"),
method: "GET",
hostname: "",
protocol: "https:",
path: "/",
};
let done: (value?: unknown) => void;
const promise = new Promise((r) => (done = r));
setTimeout(async () => {
httpRequest.emit("continue", {});
done();
}, 25);
await writeRequestBody(httpRequest, request);
expect(httpRequest.end).toHaveBeenCalled();
await promise;
});
it(
"should not send the body if the request is expect=100-continue" +
"but a response is received before the continue event",
async () => {
const httpRequest = Object.assign(new EventEmitter(), {
end: vi.fn(),
}) as any;
const request = {
headers: {
expect: "100-continue",
},
body: {
pipe: vi.fn(),
},
method: "GET",
hostname: "",
protocol: "https:",
path: "/",
};
let done: (value?: unknown) => void;
const promise = new Promise((r) => (done = r));
setTimeout(() => {
httpRequest.emit("response", {});
done();
}, 25);
await writeRequestBody(httpRequest, request);
expect(request.body.pipe).not.toHaveBeenCalled();
expect(httpRequest.end).not.toHaveBeenCalled();
await promise;
}
);
});
18 changes: 12 additions & 6 deletions packages/node-http-handler/src/write-request-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,38 @@ export async function writeRequestBody(
const expect = headers["Expect"] || headers["expect"];

let timeoutId = -1;
let hasError = false;
let sendBody = true;

if (expect === "100-continue") {
await Promise.race<void>([
sendBody = await Promise.race<boolean>([
new Promise((resolve) => {
timeoutId = Number(timing.setTimeout(resolve, Math.max(MIN_WAIT_TIME, maxContinueTimeoutMs)));
}),
new Promise((resolve) => {
httpRequest.on("continue", () => {
timing.clearTimeout(timeoutId);
resolve();
resolve(true);
});
httpRequest.on("response", () => {
// if this handler is called, then response is
// already received and there is no point in
// sending body or waiting
timing.clearTimeout(timeoutId);
resolve(false);
});
httpRequest.on("error", () => {
hasError = true;
timing.clearTimeout(timeoutId);
// this handler does not reject with the error
// because there is already an error listener
// on the request in node-http-handler
// and node-http2-handler.
resolve();
resolve(false);
});
}),
]);
}

if (!hasError) {
if (sendBody) {
writeBody(httpRequest, request.body);
}
}
Expand Down

0 comments on commit f4e1a45

Please sign in to comment.