sammhicks / picoserve

An async no_std HTTP server suitable for bare-metal environments, heavily inspired by axum
MIT License
204 stars 26 forks source link

Multi-TCP Port Support? #10

Closed Token-Thinker closed 10 months ago

Token-Thinker commented 10 months ago

Hey its me again haha.

I'm trying to create a function for setting up multiple ports with various routes using a single function to conserve memory on my device, however i'm running into a type error when setting up the Router (which is understandable) between IntoResponse and WebSocketUpgrade and a little lost on what to do. Any advice?

Also note - I did split it into two separate (http and ws) however i still can't get two ws ports to spawn for some reason (i believe resource issue) just kind of at my wits end.

mismatched types
expected fn item `fn() -> impl Future<Output = impl picoserve::response::IntoResponse> {get_site}`
   found closure `{closure@src/network/server.rs:94:44: 94:71}`
#[embassy_executor::task]
pub async fn setup_serve(
    stack: &'static Stack<WifiDevice<'static, WifiStaDevice>>,
    config: &'static picoserve::Config<Duration>,
    port: u16,

)->!{
    let mut rx_buffer = [0; 1024];
    let mut tx_buffer = [0; 1024];

    let app: Router<_, _, _> = match port {
        80 => {
            // HTTP router configuration
            Router::new().route("/", get(get_site))
        }
        81 | 82 => {
            // WebSocket router configuration
            Router::new().route("/ws", get(|upgrade: WebSocketUpgrade| {
                upgrade.on_upgrade(crate::network::websockets::WebsocketHandler {})
            }))
        }
        _ => unimplemented!(), // Handle other cases if needed
    };

    println!("Starting Websocket Servers");

    loop {
        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);

        log::info!("Listening on TCP:{}...", port);
        println!("Listening on TCP:{}...", port);

        if let Err(e) = socket.accept(port).await {
            log::warn!("accept error: {:?}", e);
            continue;
        }

        log::info!(
            "Received connection from {:?}",
            socket.remote_endpoint()
        );

        let (socket_rx, socket_tx) = socket.split();

        match picoserve::serve(
            &app,
            EmbassyTimer,
            config,
            &mut [0; 2048],
            socket_rx,
            socket_tx,
        )
        .await
        {
            Ok(handled_requests_count) => {
                log::info!(
                    "{handled_requests_count} requests handled from {:?}",
                    socket.remote_endpoint()
                );
            }
            Err(err) => log::error!("{err:?}"),
        }
    }
}
sammhicks commented 10 months ago

Hey its me again haha.

No worries, ask away :) !

Unfortunately, due to the lack of type erasing (such as Box<dyn MyTrait>) in bare metal, the routes in a Router are heavily baked into the generated type, and thus Router::new().route("/", get(get_site)) and Router::new().route("/ws", get(|upgrade: WebSocketUpgrade| { upgrade.on_upgrade(crate::network::websockets::WebsocketHandler {}) })) are different types.

This might be able to be resolved if we get dyn safe async traits, but not in the current version of Rust.

As for the resource error, one possible solution would be to shrink the TCP buffers. This would potentially slow the connection down, as more packets would be dropped before being transferred into the HTTP buffer, but it shouldn't be too bad.

Token-Thinker commented 10 months ago

okay, that makes sense. Thank you!