WebSocket to TCP Proxy Connection
Relevant source files
The following files were used as context for generating this wiki page:
Mailiner uses a WebSocket to TCP proxy to overcome browser limitations that prevent direct TCP connections to IMAP and SMTP servers. The application establishes a WebSocket connection to an intermediate server (ws-tcp-proxy), which forwards the payload to the target mail server via TCP.
Sources: README.md:14-20
Architecture Overview
The connection architecture relies on three primary tiers: the WASM client in the browser, the WebSocket-to-TCP proxy, and the destination email server. The browser client wraps standard mail protocols (IMAP/SMTP) within WebSocket frames. The proxy decapsulates these frames and initiates a raw TCP connection to the mail server.
The diagram above shows the data flow from the browser through the proxy to the target mail server.
Sources: README.md:16-20, crates/mailiner-app/src/websocket_stream.rs:188-210
Proxy Configuration
Proxy behavior is governed by ProxySettings, which define the bridge between the browser and the target server. The application constructs a specific URL containing the destination host, port, and a security token.
WebSocket URL Construction
The websocket_url function builds the connection string. It percent-encodes the token and remote parameters to ensure RFC 3986 compliance. The resulting URL follows the pattern:
{base_url}?token={encoded_token}&remote={host}:{port}.
Sources: crates/mailiner-app/src/account_config.rs:163-195
Configuration Parameters
| Parameter | Type | Description |
|---|---|---|
base_url | String | The proxy endpoint (e.g., ws://localhost:9400/proxy). |
token | String | Shared secret for proxy authentication. |
remote_host | Option<String> | Overrides the target host (defaults to IMAP/SMTP host). |
remote_port | Option<u16> | Overrides the target port (defaults to IMAP/SMTP port). |
Sources: crates/mailiner-app/src/account_config.rs:149-159
Connection Implementation
The system implements AsyncRead and AsyncWrite for WebSocketStream, allowing standard Rust networking libraries to use the WebSocket as a raw byte stream.
WebSocket Lifecycle
The WebSocketStream manages the internal state of the browser's web_sys::WebSocket object.
| State | Description |
|---|---|
Connecting | The initial state when the WebSocket is opening. |
Open | The socket is ready for data transmission. |
Error | A fatal error occurred on the socket. |
Closed | The connection was terminated by either end. |
Sources: crates/mailiner-app/src/websocket_stream.rs:15-22
Stream Flow
- Initiation:
WebSocketStream::try_new(url)creates the JS WebSocket and attaches event listeners foronopen,onmessage,onerror, andonclose. - Synchronization: The
WaitUntilOpenfuture blocks until theonopenevent fires. - Data Write:
poll_writesends binary data viaweb_socket.send_with_u8_array(buf). - Data Read:
on_messagepushes received data into an internal buffer, whichpoll_readdrains for the caller.
Sources: crates/mailiner-app/src/websocket_stream.rs:105-144, crates/mailiner-app/src/websocket_stream.rs:252-290
Security and Encryption
Communication is encrypted end-to-end between the browser and the mail server. The proxy only sees the encrypted TLS/SSL traffic.
Transport Layer Security
Mailiner performs the TLS handshake inside the browser using rustls over the proxy stream.
- IMAP: Uses implicit TLS over the proxy stream.
- SMTP: Supports Implicit TLS (port 465) or STARTTLS (port 587). In STARTTLS mode, the initial greeting and
STARTTLScommand occur in the clear through the proxy before the browser initiates the TLS handshake.
Sources: README.md:28-32, crates/mailiner-smtp-connector/src/lib.rs:77-101
The sequence above illustrates the end-to-end encrypted connection through the proxy.
Sources: crates/mailiner-app/src/websocket_stream.rs:105-156, crates/mailiner-smtp-connector/src/lib.rs:88-101
Local Development Setup
To run a local instance for development, the ws-tcp-proxy must be running alongside the Mailiner application.
- Proxy Server: Run
cargo runin thews-tcp-proxyrepository. It listens onws://localhost:9400/proxyby default. - Proxy Token: Use a matching token (e.g.,
testtoken) in the onboarding form. - Prefill: Using the
dev-defaultsfeature inmailiner-appseeds the onboarding form with local proxy settings.
Sources: README.md:55-65, crates/mailiner-app/src/account_config.rs:434-460
Summary
The WebSocket to TCP proxy connection is a critical compatibility layer for Mailiner. It enables standard email protocols to function in a browser environment while maintaining security through client-side TLS termination. By wrapping TCP traffic in WebSocket frames, Mailiner avoids the need for a full-featured intermediate mail server.