27 releases (16 breaking)

0.17.0 Dec 18, 2024
0.15.0 Sep 15, 2024
0.14.0 Jun 3, 2024
0.13.0 Dec 11, 2023
0.3.0 Mar 2, 2021

#18 in WebSocket

Download history 5091/week @ 2024-10-30 4981/week @ 2024-11-06 4214/week @ 2024-11-13 4967/week @ 2024-11-20 3997/week @ 2024-11-27 6271/week @ 2024-12-04 6732/week @ 2024-12-11 3671/week @ 2024-12-18 1973/week @ 2024-12-25 3531/week @ 2025-01-01 5312/week @ 2025-01-08 6274/week @ 2025-01-15 4568/week @ 2025-01-22 4524/week @ 2025-01-29 5069/week @ 2025-02-05 5298/week @ 2025-02-12

20,463 downloads per month
Used in 39 crates (26 directly)

BSD-2-Clause

18KB
102 lines

Docs.rs CI

hyper-tungstenite

This crate allows hyper servers to accept websocket connections, backed by tungstenite.

The upgrade function allows you to upgrade a HTTP connection to a websocket connection. It returns a HTTP response to send to the client, and a future that resolves to a WebSocketStream. The response must be sent to the client for the future to be resolved. In practise this means that you must spawn the future in a different task.

Note that the upgrade function itself does not check if the request is actually an upgrade request. For simple cases, you can check this using the is_upgrade_request function before calling upgrade. For more complicated cases where the server should support multiple upgrade protocols, you can manually inspect the Connection and Upgrade headers.

Example

use futures::sink::SinkExt;
use futures::stream::StreamExt;
use http_body_util::Full;
use hyper::body::{Bytes, Incoming};
use hyper::{Request, Response};
use hyper_tungstenite::{tungstenite, HyperWebsocket};
use hyper_util::rt::TokioIo;
use tungstenite::Message;

type Error = Box<dyn std::error::Error + Send + Sync + 'static>;

/// Handle a HTTP or WebSocket request.
async fn handle_request(mut request: Request<Incoming>) -> Result<Response<Full<Bytes>>, Error> {
    // Check if the request is a websocket upgrade request.
    if hyper_tungstenite::is_upgrade_request(&request) {
        let (response, websocket) = hyper_tungstenite::upgrade(&mut request, None)?;

        // Spawn a task to handle the websocket connection.
        tokio::spawn(async move {
            if let Err(e) = serve_websocket(websocket).await {
                eprintln!("Error in websocket connection: {e}");
            }
        });

        // Return the response so the spawned future can continue.
        Ok(response)
    } else {
        // Handle regular HTTP requests here.
        Ok(Response::new(Full::<Bytes>::from("Hello HTTP!")))
    }
}

/// Handle a websocket connection.
async fn serve_websocket(websocket: HyperWebsocket) -> Result<(), Error> {
    let mut websocket = websocket.await?;
    while let Some(message) = websocket.next().await {
        match message? {
            Message::Text(msg) => {
                println!("Received text message: {msg}");
                websocket.send(Message::text("Thank you, come again.")).await?;
            },
            Message::Binary(msg) => {
                println!("Received binary message: {msg:02X?}");
                websocket.send(Message::binary(b"Thank you, come again.".to_vec())).await?;
            },
            Message::Ping(msg) => {
                // No need to send a reply: tungstenite takes care of this for you.
                println!("Received ping message: {msg:02X?}");
            },
            Message::Pong(msg) => {
                println!("Received pong message: {msg:02X?}");
            }
            Message::Close(msg) => {
                // No need to send a reply: tungstenite takes care of this for you.
                if let Some(msg) = &msg {
                    println!("Received close message with code {} and message: {}", msg.code, msg.reason);
                } else {
                    println!("Received close message");
                }
            },
            Message::Frame(_msg) => {
                unreachable!();
            }
        }
    }

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    let addr: std::net::SocketAddr = "[::1]:3000".parse()?;
    let listener = tokio::net::TcpListener::bind(&addr).await?;
    println!("Listening on http://{addr}");

    let mut http = hyper::server::conn::http1::Builder::new();
    http.keep_alive(true);

    loop {
        let (stream, _) = listener.accept().await?;
        let connection = http
            .serve_connection(TokioIo::new(stream), hyper::service::service_fn(handle_request))
            .with_upgrades();
        tokio::spawn(async move {
            if let Err(err) = connection.await {
                println!("Error serving HTTP connection: {err:?}");
            }
        });
    }
}

Dependencies

~6–15MB
~178K SLoC

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy