Processing HTTP requests from a tokio::net::TcpStream

Hi, I am trying to write a server accepting HTTP requests and WebSocket connections. The HTTP request should come from the browser to get the HTML of a web app, and the web app will make a request for opening a web socket connection. I am looking to use tokio-tungstenite to handle web sockets, because my server is built on tokio, so it is highly asynchronous:

async fn handle_websocket(stream: tokio::net::TcpStream) -> Result<(), Error> {
    let websocket = tokio_tungstenite::accept_async(stream).await?;
    println!("WebSocket accepted");
    websocket.for_each(async move |result| {
        println!("{:#?}", result);
    }).await;
    Ok(())
}

I have been using async_h1 before to handle HTTP requests:

async fn handle_http_request(stream: async_std::net::TcpStream) -> Result<(), Error> {
    async_h1::accept(stream, |req| async move {
        Self::handle_request(req).await
    })
    .await
    .map_err(Into::into)
}

But this library is not compatible with the tokio::net::TcpStreams, because they use different libraries to provide AsyncRead and AsyncWrite traits, specifically, tokio uses its own traits (tokio::io::AsyncRead) and async_h1 uses futures_io::AsyncRead.

So I would like to drop async_h1, because I want to use the same TcpStream type, so that I can use a single TcpListener (tokio::net::TcpListener).

But I can't find a crate compatible with tokio that provides a similar functionality as the async_h1::server::accept function, i.e. taking a TcpStream, reading HTTP requests and processing them asynchronously (returning a future).

Does anyone have an idea what I could do or use?
Thanks a lot

The Tokio front page https://tokio.rs/ suggests using hyper
for HTTP which in turn suggests using warp as a more convenient HTTP server liibrary.

I notice warp supports web sockets as well so you may not need tokio-tunstenite.

Alternatively you could run your HTTP server and web socket server on different threads. Pass messages between them using mpsc channels. Although that does require that they listen on different ports.

Personally I just create two separate programs, one for HTTP using Rocket and one for web sockets using tokio-tungstenite. The outside world connects to them on the HTTPS port of my nginx server which proxy passes the connection them. That at least keeps the servers simpler, means I only have to deal with HTTPS certs in one place and would scale nicely if I ever need it to.

1 Like

You can convert between the two AsyncRead traits using tokio_util::compat.

1 Like

This worked perfectly, I was able to use async_std::net:TcpStream with tokio_tungstenite::server::accept_async like this:

use tokio_util::compat::{
    FuturesAsyncReadCompatExt,
    FuturesAsyncWriteCompatExt,
};
let websocket = tokio_tungstenite::accept_async(stream.compat()).await?;

Unfortunately it is still not working for whatever reason, I guess there is some problem with tokio_tungstenite. I am looking into warp now, which seems like a very nice framework, although I will have to tinker with it a bit to fit it into my current architecture, which is completely based on Streams of messages.

To be clear, I have used tokio-tungstenite without issues many times, so it should definitely work. I used the Tokio TcpStream, not the one from async-std though.

I am probably doing something wrong then. I have not really grasped how the WebSocket handshake procedure works yet. I will follow warp for now and see how it goes.