tungstenite

repository·master·Indexed 25 days ago

https://github.com/snapview/tungstenite-rs

A lightweight, stream-based WebSocket implementation for Rust that follows RFC6455. Designed as a barebones, high-control library, it supports both synchronous and asynchronous usage and can be integrated into custom event loops like MIO. It provides tools for performing handshakes, managing WebSocket configurations, and handling protocol, capacity, and TLS errors. Version 0.30.0.

Tokens
8.2K
Snippets
18
Records
52
Agent score
81%

What's inside tungstenite

  1. Understand Tungstenite's design and use cases

    master

    Tungstenite is a lightweight, stream-based WebSocket implementation of [RFC6455].

    Key Characteristics:

    • Synchronous/Asynchronous: It supports both synchronous usage (similar to TcpStream) and asynchronous usage. It is designed to be easily integrated into third-party event loops like [MIO].
    • Low-level Control: The API abstracts the WebSocket protocol internals while still providing access for users who require full control over the network.
    • When to use something else: Tungstenite is a 'barebone' library. If you require a production-ready, 'batteries included' library for non-blocking sockets and full-duplex communication, use tokio-tungstenite instead.
  2. Configure TLS features for Tungstenite

    master

    By default, no TLS feature is activated. To communicate with TLS endpoints (wss://), you must enable one of the following features in your Cargo.toml. Choose the one that best fits your platform and security requirements:

    • native-tls
    • native-tls-vendored
    • rustls-tls-native-roots
    • rustls-tls-webpki-roots
  3. Run Tungstenite benchmarks

    master

    Benchmarks are located in the ./benches/ directory. You can run them using cargo bench with specific flags:

    To run all benchmarks:

    cargo bench --bench * -- --quick --noplot

    To run a specific benchmark set (e.g., "e2e"):

    cargo bench --bench e2e -- --quick --noplot
  4. Understand WebSocket roles and connection states

    master

    Tungstenite distinguishes between the two sides of a WebSocket connection using the Role enum:

    • Role::Client: The side that initiates the connection. Clients must mask frames sent to the server according to RFC 6455. Tungstenite handles this masking automatically.
    • Role::Server: The side that accepts connections. Servers should generally not receive masked frames (unless accept_unmasked_frames is enabled in WebSocketConfig).

    Connection Lifecycle

    • Active: The connection is open and can both read and write.
    • Closing: A close handshake has been initiated (either by us or the peer). In this state, can_read() may still be true as the peer might send final data before acknowledging the close.
    • Terminated: The connection is closed and cannot be used further. Attempting to use a terminated connection will result in Error::AlreadyClosed or Error::ConnectionClosed.
  5. How `IntoClientRequest` types work together

    master

    The IntoClientRequest trait acts as the bridge between user-provided connection identifiers (like a simple URL string) and the complex HTTP handshake request required by the WebSocket protocol.

    When you pass a &str or Uri to connect(), the library uses this trait to:

    1. Parse the string into a URI.
    2. Extract the host and port.
    3. Generate a unique Sec-WebSocket-Key.
    4. Construct a GET request with the necessary Upgrade: websocket and Connection: Upgrade headers.

    This abstraction allows the connect family of functions to be highly flexible, accepting everything from a simple string to a fully customized ClientRequestBuilder.

  6. Implement a custom WebSocket handshake callback

    master

    You can intercept the WebSocket handshake process by implementing the Callback trait. This allows you to inspect the incoming Request, add custom headers to the Response, or reject the connection by returning an error.

    Common use cases include:

    • Adding authentication headers to the response.
    • Rejecting connections based on specific headers (e.g., Origin).
    • Returning custom HTTP error responses.

    You can use a closure as a callback directly, or implement the Callback trait for a custom struct.

  7. Implement a WebSocket echo server with Tungstenite

    master

    You can build a synchronous WebSocket server by using tungstenite::accept to upgrade a standard TcpStream. In the example below, the server listens on 127.0.0.1:9001, accepts incoming connections, and echoes back any received binary or text messages while ignoring control frames like ping/pong.

    use std::net::TcpListener;
    use std::thread::spawn;
    use tungstenite::accept;
    
    /// A WebSocket echo server
    fn main () {
        let server = TcpListener::bind("127.0.0.1:9001").unwrap();
        for stream in server.incoming() {
            spawn (move || {
                let mut websocket = accept(stream.unwrap()).unwrap();
                loop {
                    let msg = websocket.read().unwrap();
    
                    // We do not want to send back ping/pong messages.
                    if msg.is_binary() || msg.is_text() {
                        websocket.send(msg).unwrap();
                    }
                }
            });
        }
    }
  8. Configure WebSocket connection settings with WebSocketConfig

    master

    Use WebSocketConfig to tune the performance and security of your WebSocket connection. You can adjust buffer sizes, message limits, and protocol compliance settings using a builder-like pattern.

    Key Configuration Options

    • read_buffer_size: The capacity of the eagerly allocated read buffer. Use larger values (e.g., 128 KiB) for high read loads, or smaller values (e.g., 4 KiB) to save memory in high-concurrency scenarios. Default is 128 KiB.
    • write_buffer_size: The target minimum size of the write buffer before data is flushed to the underlying stream. Setting this to 0 causes eager writes. Default is 128 KiB.
    • max_write_buffer_size: The maximum size the write buffer can reach. This provides backpressure if writes to the underlying stream are failing. Must be greater than write_buffer_size. Default is usize::MAX.
    • max_message_size: The maximum size of an incoming message. Set to None for no limit. Default is 64 MiB.
    • max_frame_size: The maximum size of a single incoming frame payload. Set to None for no limit. Default is 16 MiB.
    • accept_unmasked_frames: If true, the server will accept unmasked frames from clients (violating RFC 6455). Default is false.
    # use tungstenite::protocol::WebSocketConfig;
    let conf = WebSocketConfig::default()
        .read_buffer_size(256 * 1024)
        .write_buffer_size(256 * 1024);
  9. Generate WebSocket handshake responses

    master

    If you are manually managing the handshake state machine, you can use these utility functions to generate or write responses:

    • create_response(request: &Request) -> Result<Response>: Generates a standard 101 Switching Protocols response based on a valid WebSocket request.
    • create_response_with_body<T1, T2>(request: &HttpRequest<T1>, generate_body: impl FnOnce() -> T2) -> Result<HttpResponse<T2>>: Generates a response with a custom body.
    • write_response<T>(mut w: impl io::Write, response: &HttpResponse<T>) -> Result<()>: Writes an HTTP response directly to a writer (like a TCP stream).
  10. Handle Tungstenite library errors using the Error enum

    master

    The Error enum is the central error type for all Tungstenite library calls. When performing WebSocket operations, you should handle this enum to distinguish between normal connection closures, protocol violations, and IO issues.

    Key variants to watch for:

    • ConnectionClosed: The WebSocket connection closed normally. This is not a failure; it means the close handshake finished. It is safe to drop the underlying connection.
    • AlreadyClosed: You attempted to read or write to a connection that has already received a ConnectionClosed signal. This usually indicates a logic error in your application.
    • Io: Underlying input/output errors. Generally considered fatal unless they are WouldBlock errors.
    • Protocol: A violation of the WebSocket protocol.
    • Capacity: The message size exceeds the configured limit or buffer capacity is exhausted.
    • Utf8: Errors related to UTF-8 encoding/decoding.
    • Url: Errors related to the connection URL.
    • Tls: Errors occurring during the TLS handshake or connection.
  11. Initialize a WebSocket from a raw socket

    master

    If you are integrating Tungstenite into an existing web framework or already have an established stream, use WebSocket::from_raw_socket to wrap the stream without performing a new handshake.

    You must specify the Role (Role::Client or Role::Server) and can optionally provide a WebSocketConfig.

    Note: This function will panic if the provided configuration is invalid (e.g., if max_write_buffer_size <= write_buffer_size).

  12. Connect to a WebSocket with custom configuration using `connect_with_config`

    master

    If you need to specify a WebSocketConfig or control the number of allowed redirects, use connect_with_config.

    Arguments:

    • request: An object implementing IntoClientRequest (e.g., &str, Uri, or ClientRequestBuilder).
    • config: An Option<WebSocketConfig>. Passing None uses default settings.
    • max_redirects: The maximum number of HTTP redirects to follow.

    Returns a Result containing the WebSocket instance and the handshake Response.

    // Example signature
    pub fn connect_with_config<Req: IntoClientRequest>(
        request: Req,
        config: Option<WebSocketConfig>,
        max_redirects: u8,
    ) -> Result<(WebSocket<MaybeTlsStream<TcpStream>>, Response)>