perf(ext/websocket): integrate fastwebsockets PR #133 tokio_fast path
For plain `TcpStream` server transports, drive sends via `try_write` /
`try_write_vectored` instead of the `fastwebsockets::WebSocketWrite`
async state machine. Reads still go through `WebSocketRead`, so
auto-pong / auto-close / pong-to-JS / fragmentation reassembly /
max-message-size semantics are byte-for-byte identical to the
existing path.
Other transports (TLS, Unix sockets, vsock, tunnel, H2) keep the
existing `fastwebsockets::WebSocket` path. The choice is per-resource
at `ws_create_server_stream` time; `DENO_WS_DISABLE_FAST_TCP=1` opts
out for regression hunting.
Mirrors the strategy from fastwebsockets PR #133
(https://github.com/denoland/fastwebsockets/pull/133) head `a14f4ba`,
specifically the `echo_server_tokio_fast.rs` adapter:
- Avoid the per-frame Future state machine that `WebSocketWrite`
builds. `try_write` on a `TcpStream` is a direct `send()` syscall
with no Future-state allocation when the kernel accepts the bytes
immediately, which is the steady state on loopback.
- Use `try_write_vectored` to ship the 2-10 byte header + payload in
one syscall.
- Only enter `writable().await` when the kernel send buffer is full.
The read side keeps `fastwebsockets::WebSocketRead`. The PR #133
example's per-frame allocation savings come from running the engine
synchronously on the read buffer, which doesn't compose with Deno's
JS-callback round trip — every text/binary frame still needs a JS
op return. Replacing only the write side is the part of the
optimization that maps cleanly onto Deno's resource model.
Closes denoland/orchid#169
Co-Authored-By: Divy Srivastava <me@littledivy.com>