summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRano | Ranadeep <ranadip.bswas@gmail.com>2024-07-15 13:40:59 +0200
committerGitHub <noreply@github.com>2024-07-15 17:10:59 +0530
commit702f7ee89c882f82b2fb649212bd8dbbb4476062 (patch)
tree95dbfcaebbd13fd614730da656d1dc49524c0fcc
parent9128cc98504676a9142869743d6ab1dc319da2ce (diff)
fix(std/http2): release window capacity back to remote stream (#24576)
This PR adds logic to release window capacity after reading the chunks from the stream. Without it, large response (more than `u16::MAX`) may fill up the capacity and the whole response can't be read. Closes https://github.com/denoland/deno/issues/24552 Closes https://github.com/denoland/deno/issues/24305
-rw-r--r--ext/node/ops/http2.rs29
-rw-r--r--tests/unit_node/http2_test.ts40
2 files changed, 53 insertions, 16 deletions
diff --git a/ext/node/ops/http2.rs b/ext/node/ops/http2.rs
index d12e108e6..d877a5b14 100644
--- a/ext/node/ops/http2.rs
+++ b/ext/node/ops/http2.rs
@@ -456,24 +456,21 @@ fn poll_data_or_trailers(
cx: &mut std::task::Context,
body: &mut RecvStream,
) -> Poll<Result<DataOrTrailers, h2::Error>> {
- loop {
- if let Poll::Ready(trailers) = body.poll_trailers(cx) {
- if let Some(trailers) = trailers? {
- return Poll::Ready(Ok(DataOrTrailers::Trailers(trailers)));
- } else {
- return Poll::Ready(Ok(DataOrTrailers::Eof));
- }
- }
- if let Poll::Ready(data) = body.poll_data(cx) {
- if let Some(data) = data {
- return Poll::Ready(Ok(DataOrTrailers::Data(data?)));
- }
- // If data is None, loop one more time to check for trailers
- continue;
+ if let Poll::Ready(trailers) = body.poll_trailers(cx) {
+ if let Some(trailers) = trailers? {
+ return Poll::Ready(Ok(DataOrTrailers::Trailers(trailers)));
+ } else {
+ return Poll::Ready(Ok(DataOrTrailers::Eof));
}
- // Return pending here as poll_data will keep the waker
- return Poll::Pending;
}
+ if let Poll::Ready(Some(data)) = body.poll_data(cx) {
+ let data = data?;
+ body.flow_control().release_capacity(data.len())?;
+ return Poll::Ready(Ok(DataOrTrailers::Data(data)));
+ // If `poll_data` returns `Ready(None)`, poll one more time to check for trailers
+ }
+ // Return pending here as poll_data will keep the waker
+ Poll::Pending
}
#[op2(async)]
diff --git a/tests/unit_node/http2_test.ts b/tests/unit_node/http2_test.ts
index f2604b344..968ef15a0 100644
--- a/tests/unit_node/http2_test.ts
+++ b/tests/unit_node/http2_test.ts
@@ -203,3 +203,43 @@ Deno.test("[node/http2 client] write image buffer on request stream works", asyn
await endPromise.promise;
assertEquals(receivedData!, buffer);
});
+
+Deno.test("[node/http2 client] write 512kb buffer on request stream works", async () => {
+ const url = "https://localhost:5545";
+ const client = http2.connect(url);
+ client.on("error", (err) => console.error(err));
+
+ const filePath = join(
+ import.meta.dirname!,
+ "testdata",
+ "lorem_ipsum_512kb.txt",
+ );
+ const buffer = await readFile(filePath);
+ const req = client.request({ ":method": "POST", ":path": "/echo_server" });
+ req.write(buffer, (err) => {
+ if (err) throw err;
+ });
+
+ let receivedData: Buffer;
+ req.on("data", (chunk) => {
+ if (!receivedData) {
+ receivedData = chunk;
+ } else {
+ receivedData = Buffer.concat([receivedData, chunk]);
+ }
+ });
+ req.end();
+
+ const endPromise = Promise.withResolvers<void>();
+ setTimeout(() => {
+ try {
+ client.close();
+ } catch (_) {
+ // pass
+ }
+ endPromise.resolve();
+ }, 2000);
+
+ await endPromise.promise;
+ assertEquals(receivedData!, buffer);
+});