Is it possible to accept a WebSocket request by using only the request and not the stream?

60 Views Asked by At

I am trying to make an HTTP and WebSocket server in Rust using hyper and tungstenite. I need to accept WebSocket upgrades in a function where I handle HTTP requests. This function only has the request as an argument. I could send the stream as an argument to my function using a closure but I get another one of those pesky "use of moved value" errors. This is my code:

use http_body_util::Full;
use hyper::{
    body::{Bytes, Incoming},
    header,
    server::conn::http1,
    service::service_fn,
    Request, Response, StatusCode,
};
use hyper_util::rt::TokioIo;
use std::{convert::Infallible, error::Error, net::SocketAddr, path::Path};
use tokio::net::TcpListener;
use tokio_tungstenite::accept_async;

async fn handle_request(request: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
    let mut file_path: &str = "/404.html";
    let mut status_code = StatusCode::NOT_FOUND;
    match request.uri().path() {
        "/" => {
            if request.headers().contains_key(header::UPGRADE) {
                println!("websocket connection");
                //let mut socket = accept_async(request.body());
            } else {
                file_path = "/index.html";
                status_code = StatusCode::OK;
            }
        }
        other => {
            let path = String::from("public") + other;
            if let Ok(_) = tokio::fs::read(path).await {
                file_path = other;
                status_code = StatusCode::OK;
            };
        }
    }
    let response = Response::builder()
        .status(status_code)
        .header(header::CONTENT_TYPE, get_content_type(file_path))
        .body(Full::new(Bytes::from(
            tokio::fs::read(format!("public{file_path}")).await.unwrap(),
        )))
        .unwrap();
    return Ok(response);
}

fn get_content_type(path: &str) -> &str {
    match Path::new(path)
        .extension()
        .and_then(|e| e.to_str())
        .unwrap()
    {
        "html" => "text/html",
        "css" => "text/css",
        "js" => "application/javascript",
        "ico" => "image/x-icon",
        _ => "text/plain",
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    let listener = TcpListener::bind(addr).await?;

    loop {
        let (stream, _) = listener.accept().await?;

        let io = TokioIo::new(stream);

        tokio::task::spawn(async move {
            if let Err(err) = http1::Builder::new()
                .serve_connection(io, service_fn(handle_request))
                .await
            {
                println!("Error serving connection: {:?}", err);
            }
        });
    }
}

I tried to use the stream as an argument for my handle_request function. It resulted in a "use of borrowed value" error.

0

There are 0 best solutions below