IXWebSocket: TLS chunking on Windows/mbedTLS crashes server

Hello, I’ve read through #140 and I am experiencing sort of similar issues here.

Anything below 50ms sleep in the progress callback causes my very simple ws server to crash.

The server is running Node v16 and is proxied through nginx to be secure / available via wss. The client ran versions v10.4.0, v11.0.8 and v11.3.1 with both mbedTLS v2 and mbedTLS v3.

The server code:

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080, perMessageDeflate: false });

wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    console.log('received', data.length);
  });
});

The simplified client code:

std::string generateRandomData(size_t length = 0) {
	static const std::string allowed_chars{ "123456789BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz" };

	static thread_local std::default_random_engine randomEngine(std::random_device{}());
	static thread_local std::uniform_int_distribution<int> randomDistribution(0, allowed_chars.size() - 1);

	std::string id(length ? length : 32, '\0');
	for (std::string::value_type& c : id) {
		c = allowed_chars[randomDistribution(randomEngine)];
	}

	return id;
}

void messageCallback(const ix::WebSocketMessagePtr &msg)
{
	if (msg->type == ix::WebSocketMessageType::Message) {
		std::cout << "> message: " << msg->str << std::endl;
	}

	if (msg->type == ix::WebSocketMessageType::Open) {
		std::cout << "> connected" << std::endl;
	}

	if (msg->type == ix::WebSocketMessageType::Close) {
		std::cout << "> disconnected " << msg->closeInfo.code << ", " << msg->closeInfo.reason << std::endl;
	}

	if (msg->type == ix::WebSocketMessageType::Error) {
		std::cout << "> error " << msg->errorInfo.http_status << ": " << msg->errorInfo.reason << std::endl;
	}
}

bool progressCallback(int32_t current, int32_t total) {
	printf("progress: %d/%d\n", current + 1, total);
	std::this_thread::sleep_for(std::chrono::milliseconds(25));
	return true;
}

int main(void) {
	std::string url("wss://ixws.luastoned.com");
	std::string data = generateRandomData(2 * 1000 * 1000);

	ix::initNetSystem();

	webSocket.setUrl(url);
	webSocket.disablePerMessageDeflate();
	webSocket.setOnMessageCallback(messageCallback);

	std::cout << "> connecting to " << url << " ..." << std::endl;
	webSocket.start();

	std::cout << "> sending data" << std::endl;
	webSocket.send(data, true, progressCallback);

	// Stop the connection
	webSocket.stop();
	ix::uninitNetSystem();

	return 0;
}

Upon execution I get the following:

> connecting to wss://ixws.luastoned.com ...
> connected
> sending data
progress: 1/61
progress: 2/61
progress: 3/61
> disconnected 1002,
> disconnected 1002,
> error 502: Expecting status 101 (Switching Protocol), got 502 status connecting to wss://ixws.luastoned.com, HTTP Status line: HTTP/1.1 502 Bad Gateway

and

RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear
    at Receiver.getInfo (ixws-debug/node_modules/ws/lib/receiver.js:176:14)
    at Receiver.startLoop (ixws-debug/node_modules/ws/lib/receiver.js:136:22)
    at Receiver._write (ixws-debug/node_modules/ws/lib/receiver.js:83:10)
    at writeOrBuffer (node:internal/streams/writable:389:12)
    at _write (node:internal/streams/writable:330:10)
    at Receiver.Writable.write (node:internal/streams/writable:334:10)
    at Socket.socketOnData (ixws-debug/node_modules/ws/lib/websocket.js:1116:35)
    at Socket.emit (node:events:394:28)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
Emitted 'error' event on WebSocket instance at:
    at Receiver.receiverOnError (ixws-debug/node_modules/ws/lib/websocket.js:1002:13)
    at Receiver.emit (node:events:394:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  code: 'WS_ERR_UNEXPECTED_RSV_2_3',
  [Symbol(status-code)]: 1002
}

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (2 by maintainers)

Commits related to this issue

Most upvoted comments

After some debugging I found what causes the problem: The background thread doesn’t start sending the tx-buffer because it never gets notified to do so. But this only happens under Windows because there is no implementation for “SelectInterrupt” (for other platforms there is the SelectInterruptPipe implementation).

    bool SelectInterrupt::notify(uint64_t /*value*/)
    {
        return true;
    }

The missing Windows implementation was already discussed in #131.

For Windows the WSAEventSelect, WSAWaitForMultipleEvents and WSAEnumNetworkEvents APIs could be used to combine a socket with a simple “event” that can wake up the thread and then query a SelectInterrupt implemenation that holds the SendRequest and CloseRequest.