diff --git a/examples/io/tcp/websocketclient/websocketclient.js b/examples/io/tcp/websocketclient/websocketclient.js index 1d693ab2b9..cf49fb3563 100644 --- a/examples/io/tcp/websocketclient/websocketclient.js +++ b/examples/io/tcp/websocketclient/websocketclient.js @@ -541,11 +541,24 @@ class WebSocketClient { message.push("", ""); - //@@ if headers exceed count, send in pieces - message = ArrayBuffer.fromString(message.join("\r\n")); + options.request = new Uint8Array(ArrayBuffer.fromString(message.join("\r\n"))); + this.#state = "sendRequest"; + } + case "sendRequest": { + const options = this.#options; + const request = options.request; + const use = Math.min(this.#writable, request.byteLength); + if (!use) + break; + const message = request.subarray(0, use); this.#writable = this.#socket.write(message); - this.#state = "receiveStatus" + if (use < request.byteLength) { + options.request = request.subarray(use); + break; + } + delete options.request; + this.#state = "receiveStatus"; this.#line = ""; options.flags = 0; this.#socket.format = NumberFormat; diff --git a/tests/modules/network/websocket/client/request-backpressure.js b/tests/modules/network/websocket/client/request-backpressure.js new file mode 100644 index 0000000000..9b6143e4e4 --- /dev/null +++ b/tests/modules/network/websocket/client/request-backpressure.js @@ -0,0 +1,49 @@ +/*--- +description: WebSocket upgrade request respects socket write capacity +flags: [async, module] +---*/ +import Timer from "timer"; +import WebSocketClient from "embedded:network/websocket/client"; + +const capacity = 64; +let client; +class Resolver { + resolve(options) { + options.onResolved(options.host, "127.0.0.1"); + } +} +class Socket { + #options; + static writes = 0; + + constructor(options) { + this.#options = options; + Timer.set(() => options.onWritable(capacity)); + } + write(buffer) { + assert(buffer.byteLength <= capacity, "write exceeds reported capacity"); + Socket.writes++; + const bytes = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); + const length = bytes.byteLength; + if ((length >= 4) && (13 === bytes[length - 4]) && (10 === bytes[length - 3]) && + (13 === bytes[length - 2]) && (10 === bytes[length - 1])) { + Timer.set(() => $DO(() => { + assert(Socket.writes > 1, "request should be split across writes"); + client.close(); + })()); + } + else + Timer.set(() => this.#options.onWritable(capacity)); + return 0; + } + close() {} + set format(_) {} +} +$TESTMC.timeout(1_000); +client = new WebSocketClient({ + host: "example.com", + headers: [["X-Padding", "x".repeat(160)]], + dns: {io: Resolver}, + socket: {io: Socket}, + onError() { $DONE("unexpected error"); } +}); diff --git a/tools/testmc/manifest.json b/tools/testmc/manifest.json index 78be7a4265..4e57174997 100644 --- a/tools/testmc/manifest.json +++ b/tools/testmc/manifest.json @@ -8,6 +8,7 @@ "$(MODULES)/data/base64/manifest.json", "$(MODULES)/data/crc/manifest.json", "$(MODULES)/data/hex/manifest.json", + "$(MODULES)/data/logical/manifest.json", "$(MODULES)/data/qrcode/manifest.json", "$(MODULES)/data/text/decoder/manifest.json", "$(MODULES)/data/text/encoder/manifest.json", @@ -42,6 +43,7 @@ "commodetto/*": "$(MODULES)/commodetto/commodettoPocoBlit", "commodetto/cfe": "$(MODULES)/commodetto/cfeBMF", "commodetto/checksumOut": "./commodettoChecksumOut", + "embedded:network/websocket/client": "$(MODDABLE)/examples/io/tcp/websocketclient/websocketclient", "piu/Sound": "$(MODULES)/piu/MC/piuSound" }, "preload": [