diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig index 44eb029494..ec5cac2639 100644 --- a/src/bun.js/api/bun/h2_frame_parser.zig +++ b/src/bun.js/api/bun/h2_frame_parser.zig @@ -700,13 +700,13 @@ pub const H2FrameParser = struct { // local Window limits the download of data // current window size for the connection - windowSize: u64 = 65535, + windowSize: u64 = DEFAULT_WINDOW_SIZE, // used window size for the connection usedWindowSize: u64 = 0, // remote Window limits the upload of data // remote window size for the connection - remoteWindowSize: u64 = 0, + remoteWindowSize: u64 = DEFAULT_WINDOW_SIZE, // remote used window size for the connection remoteUsedWindowSize: u64 = 0, @@ -993,13 +993,13 @@ pub const H2FrameParser = struct { const able_to_send = frame_slice[0..max_size]; client.queuedDataSize -= able_to_send.len; written.* += able_to_send.len; - this.remoteUsedWindowSize += able_to_send.len; - client.remoteUsedWindowSize += able_to_send.len; - log("dataFrame partial flushed {} {} {} {} {} {} {}", .{ able_to_send.len, frame.end_stream, client.queuedDataSize, this.remoteUsedWindowSize, client.remoteUsedWindowSize, this.remoteWindowSize, client.remoteWindowSize }); - - const padding = this.getPadding(able_to_send.len, MAX_PAYLOAD_SIZE_WITHOUT_FRAME - 1); + const padding = this.getPadding(able_to_send.len, max_size); const payload_size = able_to_send.len + (if (padding != 0) padding + 1 else 0); + + this.remoteUsedWindowSize += payload_size; + client.remoteUsedWindowSize += payload_size; + var flags: u8 = 0; // we ignore end_stream for now because we know we have more data to send if (padding != 0) { flags |= @intFromEnum(DataFrameFlags.PADDED); @@ -1024,12 +1024,11 @@ pub const H2FrameParser = struct { // flush with some payload client.queuedDataSize -= frame_slice.len; written.* += frame_slice.len; - this.remoteUsedWindowSize += frame_slice.len; - client.remoteUsedWindowSize += frame_slice.len; - log("dataFrame flushed {} {}", .{ frame_slice.len, frame.end_stream }); - const padding = this.getPadding(frame_slice.len, MAX_PAYLOAD_SIZE_WITHOUT_FRAME - 1); + const padding = this.getPadding(frame_slice.len, max_size); const payload_size = frame_slice.len + (if (padding != 0) padding + 1 else 0); + this.remoteUsedWindowSize += payload_size; + client.remoteUsedWindowSize += payload_size; var flags: u8 = if (frame.end_stream and !this.waitForTrailers) @intFromEnum(DataFrameFlags.END_STREAM) else 0; if (padding != 0) { flags |= @intFromEnum(DataFrameFlags.PADDED); @@ -2394,7 +2393,6 @@ pub const H2FrameParser = struct { log("remoteSettings.initialWindowSize: {} {} {}", .{ remoteSettings.initialWindowSize, this.remoteUsedWindowSize, this.remoteWindowSize }); if (remoteSettings.initialWindowSize >= this.remoteWindowSize) { - this.remoteWindowSize = remoteSettings.initialWindowSize; var it = this.streams.valueIterator(); while (it.next()) |stream| { if (remoteSettings.initialWindowSize >= stream.remoteWindowSize) { @@ -2426,7 +2424,6 @@ pub const H2FrameParser = struct { this.remoteSettings = remoteSettings; log("remoteSettings.initialWindowSize: {} {} {}", .{ remoteSettings.initialWindowSize, this.remoteUsedWindowSize, this.remoteWindowSize }); if (remoteSettings.initialWindowSize >= this.remoteWindowSize) { - this.remoteWindowSize = remoteSettings.initialWindowSize; var it = this.streams.valueIterator(); while (it.next()) |stream| { if (remoteSettings.initialWindowSize >= stream.remoteWindowSize) { @@ -3262,11 +3259,7 @@ pub const H2FrameParser = struct { max_size = MAX_PAYLOAD_SIZE_WITHOUT_FRAME; } const size = @min(payload.len - offset, max_size); - defer if (!enqueued) { - log("remoteUsedWindowSize += {} {} {} {}", .{ size, stream.remoteUsedWindowSize, this.remoteUsedWindowSize, this.isServer }); - stream.remoteUsedWindowSize += size; - this.remoteUsedWindowSize += size; - }; + const slice = payload[offset..(size + offset)]; offset += size; const end_stream = offset >= payload.len and can_close; @@ -3277,8 +3270,10 @@ pub const H2FrameParser = struct { // the callback will only be called after the last frame is sended stream.queueFrame(this, slice, if (offset >= payload.len) callback else .js_undefined, offset >= payload.len and close); } else { - const padding = stream.getPadding(size, max_size - 1); + const padding = stream.getPadding(size, max_size); const payload_size = size + (if (padding != 0) padding + 1 else 0); + stream.remoteUsedWindowSize += payload_size; + this.remoteUsedWindowSize += payload_size; var flags: u8 = if (end_stream) @intFromEnum(DataFrameFlags.END_STREAM) else 0; if (padding != 0) { flags |= @intFromEnum(DataFrameFlags.PADDED); @@ -4428,8 +4423,6 @@ pub const H2FrameParser = struct { this.strong_ctx.set(globalObject, context_obj); this.hpack = lshpack.HPACK.init(this.localSettings.headerTableSize); - this.windowSize = this.localSettings.initialWindowSize; - log("windowSize: {d} isServer: {}", .{ this.windowSize, is_server }); if (is_server) { _ = this.setSettings(this.localSettings); } else { diff --git a/test/bun.lock b/test/bun.lock index 9789a4e0ee..47993d82c0 100644 --- a/test/bun.lock +++ b/test/bun.lock @@ -84,6 +84,7 @@ "typeorm": "0.3.20", "typescript": "5.0.2", "undici": "5.20.0", + "unzipper": "^0.12.3", "v8-heapsnapshot": "1.3.1", "verdaccio": "6.0.0", "vitest": "0.32.2", @@ -952,6 +953,8 @@ "bl": ["bl@5.1.0", "", { "dependencies": { "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ=="], + "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], + "blueimp-md5": ["blueimp-md5@2.19.0", "", {}, "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="], "bmp-ts": ["bmp-ts@1.0.9", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="], @@ -1188,6 +1191,8 @@ "duckdb": ["duckdb@1.3.1", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "node-addon-api": "^7.0.0", "node-gyp": "^9.3.0" } }, "sha512-wSCxu6zSkHkGHtLrI5MmHYUOpbi08s2eIY/QCg2f1YsSyohjA3MRnUMdDb88oqgLa7/h+/wHuIe1RXRu4k04Sw=="], + "duplexer2": ["duplexer2@0.1.4", "", { "dependencies": { "readable-stream": "^2.0.2" } }, "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA=="], + "duplexify": ["duplexify@4.1.3", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="], "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], @@ -1886,6 +1891,8 @@ "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.0.7", "", { "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-test": "build-test.js", "node-gyp-build-optional-packages-optional": "optional.js" } }, "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w=="], + "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], + "node-mock-http": ["node-mock-http@1.0.0", "", {}, "sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ=="], "node-releases": ["node-releases@2.0.14", "", {}, "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="], @@ -2518,6 +2525,8 @@ "unstorage": ["unstorage@1.15.0", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.3", "h3": "^1.15.0", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.6", "ofetch": "^1.4.1", "ufo": "^1.5.4" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-m40eHdGY/gA6xAPqo8eaxqXgBuzQTlAKfmB1iF7oCKXE1HfwHwzDJBywK+qQGn52dta+bPlZluPF7++yR3p/bg=="], + "unzipper": ["unzipper@0.12.3", "", { "dependencies": { "bluebird": "~3.7.2", "duplexer2": "~0.1.4", "fs-extra": "^11.2.0", "graceful-fs": "^4.2.2", "node-int64": "^0.4.0" } }, "sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA=="], + "update-browserslist-db": ["update-browserslist-db@1.0.16", "", { "dependencies": { "escalade": "^3.1.2", "picocolors": "^1.0.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], @@ -2928,6 +2937,8 @@ "duckdb/node-gyp": ["node-gyp@9.4.1", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^7.1.4", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.0.3", "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^2.0.2" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ=="], + "duplexer2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "duplexify/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "ecc-jsbn/jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="], @@ -3466,6 +3477,12 @@ "duckdb/node-gyp/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "duplexer2/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "duplexer2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "duplexer2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "engine.io-client/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], "engine.io/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], diff --git a/test/js/third_party/grpc-js/fixtures/tonic-server/Cargo.toml b/test/js/third_party/grpc-js/fixtures/tonic-server/Cargo.toml new file mode 100644 index 0000000000..7f65079bb7 --- /dev/null +++ b/test/js/third_party/grpc-js/fixtures/tonic-server/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "server" +version = "0.1.0" +edition = "2021" + +[dependencies] +tonic = { version = "0.13.1", features = ["transport", "prost"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +tokio-stream = "0.1.11" +prost = "0.13" +[build-dependencies] +tonic-build = "0.13.1" diff --git a/test/js/third_party/grpc-js/fixtures/tonic-server/build.rs b/test/js/third_party/grpc-js/fixtures/tonic-server/build.rs new file mode 100644 index 0000000000..fb515867e5 --- /dev/null +++ b/test/js/third_party/grpc-js/fixtures/tonic-server/build.rs @@ -0,0 +1,3 @@ +fn main() { + tonic_build::compile_protos("proto/helloworld.proto").unwrap(); +} diff --git a/test/js/third_party/grpc-js/fixtures/tonic-server/proto/helloworld.proto b/test/js/third_party/grpc-js/fixtures/tonic-server/proto/helloworld.proto new file mode 100644 index 0000000000..dc709267e4 --- /dev/null +++ b/test/js/third_party/grpc-js/fixtures/tonic-server/proto/helloworld.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package helloworld; + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/test/js/third_party/grpc-js/fixtures/tonic-server/src/main.rs b/test/js/third_party/grpc-js/fixtures/tonic-server/src/main.rs new file mode 100644 index 0000000000..271ca32d5b --- /dev/null +++ b/test/js/third_party/grpc-js/fixtures/tonic-server/src/main.rs @@ -0,0 +1,40 @@ +use tonic::{transport::Server, Request, Response, Status}; +use helloworld::greeter_server::{Greeter, GreeterServer}; +use helloworld::{HelloRequest, HelloReply}; +use tokio::net::TcpListener; + +pub mod helloworld { + tonic::include_proto!("helloworld"); +} + +#[derive(Default)] +pub struct MyGreeter {} + +#[tonic::async_trait] +impl Greeter for MyGreeter { + async fn say_hello( + &self, + request: Request, + ) -> Result, Status> { + let reply = HelloReply { + message: request.into_inner().name, + }; + Ok(Response::new(reply)) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + + let greeter = MyGreeter::default(); + let mut server: Server = Server::builder(); + println!("Listening on {}", addr); + server.add_service(GreeterServer::new(greeter)) + .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener)) + .await?; + + + Ok(()) +} diff --git a/test/js/third_party/grpc-js/test-tonic.test.ts b/test/js/third_party/grpc-js/test-tonic.test.ts new file mode 100644 index 0000000000..ca390576bc --- /dev/null +++ b/test/js/third_party/grpc-js/test-tonic.test.ts @@ -0,0 +1,126 @@ +import grpc from "@grpc/grpc-js"; +import protoLoader from "@grpc/proto-loader"; +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { rmSync } from "fs"; +import { chmod, cp, mkdir } from "fs/promises"; +import { tmpdirSync } from "harness"; +import path, { join } from "path"; +import unzipper from "unzipper"; + +const protoVersion = "31.0"; + +const releases = { + "win32_x86_32": `https://github.com/protocolbuffers/protobuf/releases/download/v${protoVersion}/protoc-${protoVersion}-win32.zip`, + "win32_x86_64": `https://github.com/protocolbuffers/protobuf/releases/download/v${protoVersion}/protoc-${protoVersion}-win64.zip`, + "linux_x86_32": `https://github.com/protocolbuffers/protobuf/releases/download/v${protoVersion}/protoc-${protoVersion}-linux-x86_32.zip`, + "linux_x86_64": `https://github.com/protocolbuffers/protobuf/releases/download/v${protoVersion}/protoc-${protoVersion}-linux-x86_64.zip`, + "darwin_x86_64": `https://github.com/protocolbuffers/protobuf/releases/download/v${protoVersion}/protoc-${protoVersion}-osx-x86_64.zip`, + "darwin_arm64": `https://github.com/protocolbuffers/protobuf/releases/download/v${protoVersion}/protoc-${protoVersion}-osx-aarch_64.zip`, +}; + +const platform = process.platform; +const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x86_64" : "x86_32"; +const release = platform + "_" + arch; +const binPath = join("bin", platform === "win32" ? "protoc.exe" : "protoc"); + +// Load proto +const packageDefinition = protoLoader.loadSync(join(import.meta.dir, "fixtures/tonic-server/proto/helloworld.proto"), { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, +}); + +type Server = { address: string; kill: () => Promise }; + +const cargoBin = Bun.which("cargo") as string; +async function startServer(): Promise { + const tmpDir = tmpdirSync(); + await cp(join(import.meta.dir, "fixtures/tonic-server"), tmpDir, { recursive: true }); + const protocZip = await unzipper.Open.buffer(await fetch(releases[release]).then(res => res.bytes())); + + const protocPath = join(tmpDir, "protoc"); + await mkdir(protocPath, { recursive: true }); + await protocZip.extract({ path: protocPath }); + const protocExec = join(protocPath, binPath); + await chmod(protocExec, 0o755); + + const server = Bun.spawn([cargoBin, "run", "--quiet", path.join(tmpDir, "server")], { + cwd: tmpDir, + env: { + PROTOC: protocExec, + PATH: process.env.PATH, + CARGO_HOME: process.env.CARGO_HOME, + RUSTUP_HOME: process.env.RUSTUP_HOME, + }, + stdout: "pipe", + stdin: "ignore", + stderr: "inherit", + }); + + { + const { promise, reject, resolve } = Promise.withResolvers(); + const reader = server.stdout.getReader(); + const decoder = new TextDecoder(); + async function killServer() { + try { + server.kill(); + await server.exited; + rmSync(tmpDir, { recursive: true, force: true }); + } catch {} + } + while (true) { + const { done, value } = await reader.read(); + if (done) { + break; + } + const text = decoder.decode(value); + if (text.includes("Listening on")) { + const [_, address] = text.split("Listening on "); + resolve({ + address: address?.trim(), + kill: killServer, + }); + break; + } else { + await killServer(); + reject(new Error("Server not started")); + break; + } + } + return await promise; + } +} + +describe.skipIf(!cargoBin || !releases[release])("test tonic server", () => { + let server: Server; + + beforeAll(async () => { + server = await startServer(); + }); + + afterAll(() => { + server.kill(); + }); + + test("flow control should work in both directions", async () => { + const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld; + + // Create client + const client = new hello_proto.Greeter(server.address, grpc.credentials.createInsecure()); + const payload = Buffer.alloc(1024 * 1024, "bun").toString(); + for (let i = 0; i < 20; i++) { + const { promise, reject, resolve } = Promise.withResolvers(); + // Call SayHello + client.SayHello({ name: payload }, (err, response) => { + if (err) reject(err); + else resolve(response.message); + }); + const result = await promise; + expect(result.length).toBe(payload.length); + expect(result).toBe(payload); + } + await client.close(); + }, 20_000); // debug can take some time +}); diff --git a/test/no-validate-exceptions.txt b/test/no-validate-exceptions.txt index 2b64395bf3..7927ad3a70 100644 --- a/test/no-validate-exceptions.txt +++ b/test/no-validate-exceptions.txt @@ -2051,6 +2051,7 @@ test/js/third_party/grpc-js/test-server-interceptors.test.ts test/js/third_party/grpc-js/test-server.test.ts test/js/third_party/grpc-js/test-status-builder.test.ts test/js/third_party/grpc-js/test-uri-parser.test.ts +test/js/third_party/grpc-js/test-tonic.test.ts test/js/third_party/http2-wrapper/http2-wrapper.test.ts test/js/third_party/jsonwebtoken/async_sign.test.js test/js/third_party/jsonwebtoken/buffer.test.js diff --git a/test/package.json b/test/package.json index a710c5ef02..7cc769bfd6 100644 --- a/test/package.json +++ b/test/package.json @@ -89,6 +89,7 @@ "typeorm": "0.3.20", "typescript": "5.0.2", "undici": "5.20.0", + "unzipper": "0.12.3", "v8-heapsnapshot": "1.3.1", "verdaccio": "6.0.0", "vitest": "0.32.2",