Documentation Index
Fetch the complete documentation index at: https://mintlify.com/MercuryWorkshop/epoxy-tls/llms.txt
Use this file to discover all available pages before exploring further.
ClientMux<W> is a type alias for Multiplexor<ClientImpl, W> that represents the client side of a Wisp connection. Once created it manages all stream lifecycle events—opening, flow-control, and closing—over a single WebSocket transport. You interact with individual connections through MuxStream handles that implement both Stream and Sink<Payload>, and can be split into independent read and write halves.
Creating a ClientMux step by step
Connect the WebSocket
Use whichever backend is enabled in your
Cargo.toml. With the tokio-websockets feature the transport is a TokioWebsocketsTransport wrapper:Split the transport into rx / tx
ClientMux::new takes separate read and write halves. Call split_fast from the TransportExt trait to obtain them:TransportRead (a Stream<Item = Result<Payload, WispError>>) and TransportWrite (a Sink<Payload, Error = WispError>) is accepted. You can also implement these traits yourself for custom transports.Call ClientMux::new
Pass
None for a plain Wisp v1 connection, or a WispV2Handshake to attempt v2:ClientMux::new performs the handshake (sends / receives the INFO packet and the initial CONTINUE packet) before returning.Resolve required extensions
new returns a MuxResult rather than the multiplexor directly. This lets you assert that certain extensions are available before proceeding:Spawn the actor future
The second element returned by the resolution step is a
MultiplexorActorFuture—a pinned, boxed future that drives all internal event processing. You must either await it or spawn it concurrently. If it stops, all streams are severed.Open a stream
Call For UDP streams pass
new_stream with the stream type, target host, and port. This sends a CONNECT packet and returns a MuxStream once the server acknowledges:StreamType::Udp. The call returns WispError::ExtensionsNotSupported if the UDP extension was not negotiated during the handshake.Use the stream's read and write halves
MuxStream itself implements Stream<Item = Result<Payload, WispError>> and Sink<Payload>, so you can use it directly with futures combinators. For independent read/write tasks, call into_split:into_async_rw() wraps the stream into a MuxStreamAsyncRW that implements futures::AsyncRead + AsyncWrite + AsyncBufRead, for use with any code that accepts the futures async I/O traits.Complete example — TCP throughput test
The code below is adapted fromsimple-wisp-client, the reference client that ships with the Epoxy TLS project. It opens multiple concurrent TCP streams and feeds zero-filled packets as fast as the server’s flow-control window allows:
Wisp v2 setup
Construct aWispV2Handshake with the extension builders you want to advertise. The server picks which ones it supports; after the handshake, call get_extensions() or get_extension_ids() to inspect what was actually negotiated.
ClientMux::new.
Not every extension you advertise is guaranteed to be supported by the server. Always inspect
mux.get_extensions() or use with_required_extensions if your application requires a specific extension.Checking version downgrade
When the server does not support Wisp v2, the client automatically falls back to v1. You can detect this after the handshake:Closing the connection
close() signals the actor to shut down all streams and terminate the actor future:
close_with_reason:
Error handling
The most commonWispError variants you will encounter when using ClientMux:
| Variant | When it occurs |
|---|---|
WsImplSocketClosed | The underlying WebSocket was closed before the multiplexor shut down. |
WsImplError(e) | A transport-level I/O error wrapped from the WebSocket backend. |
MuxTaskEnded | new_stream was called after the actor future had already completed. |
MuxMessageFailedToSend / MuxMessageFailedToRecv | Internal channel between the caller and the actor broke — usually because the actor panicked or was dropped. |
MaxStreamCountReached | More than 2³²-1 concurrent streams were requested on one connection. |
StreamAlreadyClosed | A write or close was attempted on a stream that already received a CLOSE packet. |
ExtensionsNotSupported(ids) | with_required_extensions found that the listed extension IDs were absent on the server. |
IncompatibleProtocolVersion(found, needed) | The server sent an INFO packet with a version the client cannot use. |
PasswordExtensionCredsInvalid | The server rejected the supplied username/password during v2 handshake. |
CertAuthExtensionSigInvalid | Certificate authentication failed — the server rejected the Ed25519 signature. |