GithubHelp home page GithubHelp logo

sammhicks / picoserve Goto Github PK

View Code? Open in Web Editor NEW
174.0 5.0 20.0 356 KB

An async no_std HTTP server suitable for bare-metal environments, heavily inspired by axum

License: MIT License

Rust 100.00%

picoserve's People

Contributors

bugadani avatar ddystopia avatar dzamlo avatar elrafoon avatar lu-zero avatar msrd0 avatar philcrump avatar sammhicks avatar wetheredge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

picoserve's Issues

Multi-TCP Port Support?

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:?}"),
        }
    }
}

Stability issues

I'm experiencing some issues when there are multiple concurrent requests.

https://github.com/flyaruu/esp32-http-api

This hello-world repo works fine on a esp32 as long as I keep the request speed reasonable, it keeps working, but if I go a bit wild (e.g. holding the reload button in a browser) it will stop accepting connections and never recover.
Things that seem to help a little bit:

  • More slots in the StackResource
  • Returning a Connection: Close header

That the performance is limited, that is reasonable, but the breaking and never recovering is a bit of an issue.
Any ideas? Do you see it on other platforms as well? Or is it a esp32 thing?

Trying to create a "catch all" instance for 'Router'.

Hello,

I'm currently developing a captive portal for a Pico W using embassy. It works when I hardcode responses for each device:

fn make_app() -> picoserve::Router<AppRouter> {
        picoserve::Router::new()
            .route(
                "/generate_204",
                get(|| picoserve::response::File::html(include_str!("connect.html"))),
            )
            .route(
                "/hotspot-detect.html",
                get(|| picoserve::response::File::html(include_str!("connect.html"))),
            )
            .route(
                "/connecttest.txt",
                get(|| picoserve::response::File::html(include_str!("connect.html"))),
            )
            .route(
                "/redirect",
                get(|| picoserve::response::File::html(include_str!("connect.html"))),
            )
    }

However, I can't figure out how to implement a catch-all route as to support any device.

Am I missing something, or is this just not yet implemented?

Thanks :)

With the `Json` response, empty objects miss the first `{`

With the following types:

#[derive(Serialize)]
struct EmptyResponse {}

#[derive(Serialize)]
struct SingleEmptyField {
    field: EmptyResponse,
}

If you try to return Json(SingleEmptyField { field: EmptyResponse {}}), you get {"field":}} as a response while the expected is {"field":{}}. If you try to return Json(EmptyResponse {}) you get } instead of {}.

Query parameters parsing is too strict

For whatever reason, my client (a WASM app that use gloo-net to send the query) add an extra & at the end of the query.

The makes the parsing of query parameters fails with a Bad Query error.

The error originate at

let (key, value) = value.split_once('=').ok_or(BadUrlEncodedForm)?;

I think we could be more forgiving and ignore empty URL segments at least at the end of the URL.

Compilation Error when enabling defmt

I am trying to compile the latest git commit (d71c934) with alloc, defmt and embassy features enabled. However, it fails to compile with the following errors:

error[E0277]: the trait bound `AcceptError: Format` is not satisfied
   --> /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/logging.rs:26:13
    |
26  |             defmt::warn!($f $(,$arg)*);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Format` is not implemented for `AcceptError`
    |
   ::: /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/lib.rs:384:13
    |
384 |             log_warn!("{}: accept error: {:?}", task_id, err);
    |             ------------------------------------------------- in this macro invocation
    |
    = help: the following other types implement trait `Format`:
              bool
              char
              isize
              i8
              i16
              i32
              i64
              i128
            and 163 others
note: required by a bound in `defmt::export::fmt`
   --> /usr/local/share/rust/registry/src/github.com-1ecc6299db9ec823/defmt-0.3.6/src/export/mod.rs:137:15
    |
137 | pub fn fmt<T: Format + ?Sized>(f: &T) {
    |               ^^^^^^ required by this bound in `fmt`
    = note: this error originates in the macro `defmt::warn` which comes from the expansion of the macro `log_warn` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `IpEndpoint: Format` is not satisfied
   --> /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/logging.rs:42:13
    |
42  |               defmt::info!($f $(,$arg)*);
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Format` is not implemented for `IpEndpoint`, which is required by `core::option::Option<IpEndpoint>: Format`
    |
   ::: /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/lib.rs:390:9
    |
390 | /         log_info!(
391 | |             "{}: Received connection from {:?}",
392 | |             task_id,
393 | |             remote_endpoint
394 | |         );
    | |_________- in this macro invocation
    |
    = help: the following other types implement trait `Format`:
              bool
              char
              isize
              i8
              i16
              i32
              i64
              i128
            and 163 others
    = note: required for `core::option::Option<IpEndpoint>` to implement `Format`
note: required by a bound in `defmt::export::fmt`
   --> /usr/local/share/rust/registry/src/github.com-1ecc6299db9ec823/defmt-0.3.6/src/export/mod.rs:137:15
    |
137 | pub fn fmt<T: Format + ?Sized>(f: &T) {
    |               ^^^^^^ required by this bound in `fmt`
    = note: this error originates in the macro `defmt::info` which comes from the expansion of the macro `log_info` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `IpEndpoint: Format` is not satisfied
   --> /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/logging.rs:42:13
    |
42  |               defmt::info!($f $(,$arg)*);
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Format` is not implemented for `IpEndpoint`, which is required by `core::option::Option<IpEndpoint>: Format`
    |
   ::: /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/lib.rs:398:17
    |
398 | /                 log_info!(
399 | |                     "{} requests handled from {:?}",
400 | |                     handled_requests_count,
401 | |                     remote_endpoint
402 | |                 );
    | |_________________- in this macro invocation
    |
    = help: the following other types implement trait `Format`:
              bool
              char
              isize
              i8
              i16
              i32
              i64
              i128
            and 163 others
    = note: required for `core::option::Option<IpEndpoint>` to implement `Format`
note: required by a bound in `defmt::export::fmt`
   --> /usr/local/share/rust/registry/src/github.com-1ecc6299db9ec823/defmt-0.3.6/src/export/mod.rs:137:15
    |
137 | pub fn fmt<T: Format + ?Sized>(f: &T) {
    |               ^^^^^^ required by this bound in `fmt`
    = note: this error originates in the macro `defmt::info` which comes from the expansion of the macro `log_info` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `<R as ErrorType>::Error: Format` is not satisfied
   --> /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/logging.rs:10:13
    |
10  |             defmt::error!($f $(,$arg)*);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Format` is not implemented for `<R as ErrorType>::Error`, which is required by `ReadExactError<<R as ErrorType>::Error>: Format`
    |
   ::: /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/extract.rs:199:17
    |
199 |                 log_error!("Failed to read body: {:?}", err);
    |                 -------------------------------------------- in this macro invocation
    |
    = note: required for `ReadExactError<<R as ErrorType>::Error>` to implement `Format`
note: required by a bound in `defmt::export::fmt`
   --> /usr/local/share/rust/registry/src/github.com-1ecc6299db9ec823/defmt-0.3.6/src/export/mod.rs:137:15
    |
137 | pub fn fmt<T: Format + ?Sized>(f: &T) {
    |               ^^^^^^ required by this bound in `fmt`
    = note: this error originates in the macro `defmt::error` which comes from the expansion of the macro `log_error` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
   --> /usr/local/share/rust/git/checkouts/picoserve-521def08cc55ec79/d71c934/src/extract.rs:182:39
    |
182 |     ) -> Result<Self, Self::Rejection> where <R as ErrorType>::Error: Format {
    |                                        +++++++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
error: could not compile `picoserve` (lib) due to 4 previous errors

Stupid Question: How do you display formatted HTML in Response Message? Thoughts?

Hello,

Awesome crate, thanks for making it.

For the life of me I can't get the proper formatted response in the browser (Chrome, Mozilla, Safari) when constructing the response in no_std

The code works but the response I get is the Raw output not the bold Hello Rust!

async fn get_site() -> Response<BodyHeaders, ContentBody<&'static str>> {
    Response::ok("
        <html>\
            <body>\
                <h1>Hello Rust!</h1>\
            </body>\
        </html>\r\n\
    ")
}


//.... other generic code

loop{
//.... other generic code

let app = Router::new().route("/", get(get_site)); 
  
//.... other generic code
}

Websocket on embassy

Is websocket expected to work on embassy? I tried to set them up working from https://github.com/sammhicks/picoserve/blob/main/examples/web_sockets/src/main.rs, and the requests just fail on the browser side. I'm not sure why that would be. Other requests for / and others work fine, but when I try to create a new WebSocket() in the js, the GET request never even shows up on the microcontroller.

The very reduced route for /ws just to see if the GET request is even coming in.

.route("/ws", get(|| async move {
                println!("requested websocet");
                "stuff"
            }));

The WebSocket constructor in js. This is run from the index.html|js that are hosted on the microcontroller.

let ws = new WebSocket("ws://" + window.location.host + "/ws", ["messages"]);

Chrome console error message
index.js:66 WebSocket connection to 'ws://test-hostname.lan:8000/ws' failed:

Firefox console error message
Firefox canโ€™t establish a connection to the server at ws://test-hostname:8000/ws

Running picoserve on esp32

Hi, I've spent some effort on getting picoserve to work on an esp32, nostd + embassy.

I've got it to work, but I needed to patch the network crate (esp-wifi) as it relies on the embassy crates published to crates.io, and picoserve relies on a git dependency. Embassy does not seem to release their crates to crates.io a lot.

As far as you know, Is there a specific feature picoserve needs from the more recent embassy dependencies?

Version 0.10.0 missing on crates.io

I saw you bumped the version of picoserve to 0.10.0 yesterday and marked a release date in the changelog. However, I cannot find the new version on crates.io. Is that intentional or an oversight?

Browser loads forever

Now this may not be a problem with this library itself but is anyone experiencing infinite loading times in browsers (both Firefox and Chromium based) when it is deployed on the Pi Pico W?

Given the following code:

#[embassy_executor::task]
async fn web_task(
    stack: &'static embassy_net::Stack<cyw43::NetDriver<'static>>,
    app: &'static picoserve::Router<AppRouter>,
    config: &'static picoserve::Config<Duration>,
) -> ! {
    let mut rx_buffer = [0; 1024];
    let mut tx_buffer = [0; 1024];

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

        info!("Listening on TCP:80...");
        if let Err(e) = socket.accept(80).await {
            warn!("accept error: {:?}", e);
            continue;
        }

        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) => {
                info!(
                    "{} requests handled from {:?}",
                    handled_requests_count,
                    socket.remote_endpoint()
                );
            }
            Err(_) => error!("an error occurred"),
        }
    }
}

async fn get_root() -> impl IntoResponse {
    Response::new(OK, "hello world!").with_headers(("Connection", "close"))
}

fn make_app() -> picoserve::Router<AppRouter> {
    picoserve::Router::new()
        .route("/", get(get_root))
 }

The log message shows:
10.986010 INFO Received connection from Some(192.168.1.6:50968)
โ””โ”€ wifi_tcp_server::__web_task_task::{async_fn#0} @ src/bin/wifi_tcp_server.rs:86
15.986416 INFO 0 requests handled from Some(192.168.1.6:50968)
โ””โ”€ wifi_tcp_server::__web_task_task::{async_fn#0} @ src/bin/wifi_tcp_server.rs:92
15.986596 INFO Listening on TCP:80...
โ””โ”€ wifi_tcp_server::__web_task_task::{async_fn#0} @ src/bin/wifi_tcp_server.rs:80

If I make the same request with any other HTTP client (e.g., curl) it successfully returns the result perfectly fine and almost immediately. This makes me think that the browser doesn't know when to stop reading the HTTP response or something.

The same server running on the computer works in the browser.

Debugging the problem with Wireshark
Expected (using curl):
[SYN] Seq=0 Win=32120 Len=0 MSS=1460 SACK_PERM=1 TSval=647729354 TSecr=0 WS=128
[SYN, ACK] Seq=0 Ack=1 Win=1024 Len=0 MSS=1460 WS=1 SACK_PERM=1
[ACK] Seq=1 Ack=1 Win=32128 Len=0
GET / HTTP/1.1
HTTP/1.1 200
[ACK] Seq=77 Ack=77 Win=32128 Len=0
[FIN, ACK] Seq=77 Ack=77 Win=32128 Len=0
[ACK] Seq=77 Ack=78 Win=1024 Len=0

Found (using Firefox):
[SYN] Seq=0 Win=32120 Len=0 MSS=1460 SACK_PERM=1 TSval=646394589 TSecr=0 WS=128
[SYN] Seq=0 Win=32120 Len=0 MSS=1460 SACK_PERM=1 TSval=646394840 TSecr=0 WS=128
[RST, ACK] Seq=1 Ack=1 Win=0 Len=0
[SYN] Seq=0 Win=32120 Len=0 MSS=1460 SACK_PERM=1 TSval=646394856 TSecr=0 WS=128
[RST, ACK] Seq=1 Ack=1 Win=0 Len=0
[SYN, ACK] Seq=0 Ack=1 Win=1024 Len=0 MSS=1460 WS=1 SACK_PERM=1
[ACK] Seq=1 Ack=1 Win=32128 Len=0

Custom router to handle paths dynamically

Hello,

I'd like to run a tile server on my esp32, serving tiles off of an SD card. This involves many thousands of files, and I cannot load all the file names into RAM. Instead, whenever I get a request, I'd like to check the SD card for the requested file and send it.

I believe this is doable using a custom type implementing PathRouter, and wrapping it in a Router. Unfortunately, I can't actually find a way to do this. It doesn't seem to be possible to instantiate a Router<T>, where T is my own type.

Anything I'm overlooking here? Otherwise I'd be happy to submit a PR to allow this.

Thanks!

Stable Toolchain and embassy / usage without `type_alias_impl_trait`

This is a very cool project. I'd like to use it on my embassy project, but I'm restricted to the stable toolchain.
Looking at the docu and the examples, it looks like this is not possible, as this crate relies on the #![feature(type_alias_impl_trait)] feature (which doesn't look like it will be stabilized soon).

How to add response headers to response?

Hi, I'd like to return a response with a response header aside from just a body.
Before I just returned a &str, that worked fine, now I'm attempting this:

async fn get_root()-> impl IntoResponse {
    Response::new(OK, "hello world!")
        .with_headers(("Connection","close"))
    
}

But that errors:

type `picoserve::response::BodyHeaders` is private

Is this the correct way to do this?

Add a JSON extractor

It can be useful to accept JSON in request.

Here is a minimal implementation using serde_json_core

use serde_json_core::de::Error as JsonError;

pub struct JsonRejection(JsonError);

impl IntoResponse for JsonRejection {
    async fn write_to<W: picoserve::response::ResponseWriter>(
        self,
        response_writer: W,
    ) -> Result<picoserve::ResponseSent, W::Error> {
        (
            picoserve::response::status::BAD_REQUEST,
            format_args!("Request Body is not valid JSON: {}", self.0),
        )
            .write_to(response_writer)
            .await
    }
}

pub struct Json<T: serde::de::DeserializeOwned>(pub T);

impl<State, T: serde::de::DeserializeOwned> FromRequest<State> for Json<T> {
    type Rejection = JsonRejection;

    async fn from_request(
        _state: &State,
        request: &Request<'_>,
    ) -> Result<Json<T>, JsonRejection> {
        serde_json_core::from_slice(request.body)
            .map(|(v, _)| Self(v))
            .map_err(JsonRejection)
    }
}

Compiler Bug: "thread 'rustc' panicked at 'assertion failed: `(left == right)`"

hey could use some advice on why this is happening.

struct EmbassyTimer;

impl picoserve::Timer for EmbassyTimer {
    type Duration = embassy_time::Duration;
    type TimeoutError = embassy_time::TimeoutError;

    async fn run_with_timeout<F: core::future::Future>(
        &mut self,
        duration: Self::Duration,
        future: F,
    ) -> Result<F::Output, Self::TimeoutError> {
        embassy_time::with_timeout(duration, future).await
    }
}

async fn get_root()-> impl IntoResponse {
    "Hello World"
}

#[embassy_executor::task]
pub async fn web_task(
    stack: &'static Stack<WifiDevice<'static, WifiStaDevice>>,
    config: &'static picoserve::Config<Duration>,
    //sender: Sender<'static, NoopRawMutex, MoveCommand,QUEUE_SIZE>
) -> ! {
    let mut rx_buffer = [0; 1024];
    let mut tx_buffer = [0; 1024];

    loop {
        if stack.is_link_up() {
            break;
        }
        Timer::after(Duration::from_millis(500)).await;
    }

    println!("Waiting to get IP address...");
    loop {
        if let Some(config) = stack.config_v4() {
            println!("Got IP: {}", config.address);
            break;
        }
        Timer::after(Duration::from_millis(500)).await;
    }

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

        log::info!("Listening on TCP:80...");
        if let Err(e) = socket.accept(80).await {
            log::warn!("accept error: {:?}", e);
            continue;
        }

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

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

        let app = Router::new()
            .route("/", get(get_root))
        ;

        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:?}"),
        }
    }
}

#[embassy_executor::task]
pub async fn connection(mut controller: WifiController<'static>) {
    println!("start connection task");
    loop {
        match esp_wifi::wifi::get_wifi_state() {
            WifiState::StaConnected => {
                // wait until we're no longer connected
                controller.wait_for_event(WifiEvent::StaDisconnected).await;
                Timer::after(Duration::from_millis(5000)).await
            }
            _ => {}
        }
        
        if !matches!(controller.is_ap_enabled(), Ok(true)) {
            let client_config = Configuration::Client(ClientConfiguration {
                ssid: SSID.into(),
                password: PASSWORD.into(),
                ..Default::default()
            });
            controller.set_configuration(&client_config).unwrap();
            println!("Starting wifi");
            controller.start().await.unwrap();
            println!("Wifi started!");
        }
        println!("About to connect...");

        match controller.connect().await {
            Ok(_) => println!("Wifi connected!"),
            Err(e) => {
                println!("Failed to connect to wifi: {e:?}");
                Timer::after(Duration::from_millis(5000)).await
            }
        }
    }
}

#[embassy_executor::task]
pub async fn net_task(stack: &'static Stack<WifiDevice<'static,WifiStaDevice >>) {
    stack.run().await
}
left: `*mut [async fn body@<&str as picoserve::response::IntoResponse>::write_to<picoserve::response::ResponseStream<&mut &mut embassy_net::tcp::TcpReader<'_>, &mut embassy_net::tcp::TcpWriter<'_>>>::{closure#0}]`,
 right: `*mut [async fn body@<&str as picoserve::response::IntoResponse>::write_to<picoserve::response::ResponseStream<&mut &mut embassy_net::tcp::TcpReader<'_>, &mut embassy_net::tcp::TcpWriter<'_>>>::{closure#0}]`: unexpected initial operand type', /home/rust/rust/compiler/rustc_codegen_ssa/src/mir/locals.rs:46:21
stack backtrace:
   0:     0x7faa9acc782d - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h42c9bd351a3ba23a
   1:     0x7faa9ad6adff - core::fmt::write::he924c8a6086ba81f
   2:     0x7faa9acf2af7 - std::io::Write::write_fmt::he436599ec2c6627c
   3:     0x7faa9acc75e5 - std::sys_common::backtrace::print::h5304f99ffa5b6692
   4:     0x7faa9ad0c7d3 - std::panicking::default_hook::{{closure}}::h8bda132361c184fa
   5:     0x7faa9ad0c4e2 - std::panicking::default_hook::h6601e926d2a538e4
   6:     0x7faa9bae38bb - rustc_driver_impl[c8b89ddd6b0e3357]::install_ice_hook::{closure#0}
   7:     0x7faa9ad0d057 - std::panicking::rust_panic_with_hook::h2f963e18465786ef
   8:     0x7faa9acc8117 - std::panicking::begin_panic_handler::{{closure}}::hafe668c1547a5d7b
   9:     0x7faa9acc7926 - std::sys_common::backtrace::__rust_end_short_backtrace::hb084cc80883bb58b
  10:     0x7faa9ad0cbf2 - rust_begin_unwind
  11:     0x7faa9aca7c53 - core::panicking::panic_fmt::h7b7ab3fb5fa6c34f
  12:     0x7faa9aca8131 - core::panicking::assert_failed_inner::h3a31df9dd62de533
  13:     0x7faa9b7ea23b - core[ba37dea25f4d3bf]::panicking::assert_failed::<rustc_middle[390b614d8a4d42db]::ty::Ty, rustc_middle[390b614d8a4d42db]::ty::Ty>
  14:     0x7faa9be53e6a - rustc_codegen_ssa[17da4602a24c95d7]::mir::codegen_mir::<rustc_codegen_llvm[aae5e29ef0a87e02]::builder::Builder>
  15:     0x7faa9be6c6f2 - rustc_codegen_ssa[17da4602a24c95d7]::base::codegen_instance::<rustc_codegen_llvm[aae5e29ef0a87e02]::builder::Builder>
  16:     0x7faa9be4ce05 - rustc_codegen_llvm[aae5e29ef0a87e02]::base::compile_codegen_unit::module_codegen
  17:     0x7faa9bd9da56 - <rustc_middle[390b614d8a4d42db]::dep_graph::dep_node::DepKind as rustc_query_system[870567952b1039d0]::dep_graph::DepKind>::with_deps::<<rustc_query_system[870567952b1039d0]::dep_graph::graph::DepGraphData<rustc_middle[390b614d8a4d42db]::dep_graph::dep_node::DepKind>>::with_task<rustc_middle[390b614d8a4d42db]::ty::context::TyCtxt, rustc_span[927b0836cec2b3bf]::symbol::Symbol, rustc_codegen_ssa[17da4602a24c95d7]::ModuleCodegen<rustc_codegen_llvm[aae5e29ef0a87e02]::ModuleLlvm>>::{closure#0}::{closure#0}, rustc_codegen_ssa[17da4602a24c95d7]::ModuleCodegen<rustc_codegen_llvm[aae5e29ef0a87e02]::ModuleLlvm>>
  18:     0x7faa9be4c769 - rustc_codegen_llvm[aae5e29ef0a87e02]::base::compile_codegen_unit
  19:     0x7faa9be6bd9d - rustc_codegen_ssa[17da4602a24c95d7]::base::codegen_crate::<rustc_codegen_llvm[aae5e29ef0a87e02]::LlvmCodegenBackend>
  20:     0x7faa9bd6096f - <rustc_codegen_llvm[aae5e29ef0a87e02]::LlvmCodegenBackend as rustc_codegen_ssa[17da4602a24c95d7]::traits::backend::CodegenBackend>::codegen_crate
  21:     0x7faa9bc618a3 - <rustc_session[3020f9ab2aa3381d]::session::Session>::time::<alloc[6c5b139dcb2c8bbf]::boxed::Box<dyn core[ba37dea25f4d3bf]::any::Any>, rustc_interface[c2fe7fc1c7f3d840]::passes::start_codegen::{closure#0}>
  22:     0x7faa9bcda22d - rustc_interface[c2fe7fc1c7f3d840]::passes::start_codegen
  23:     0x7faa9bc86a41 - <rustc_middle[390b614d8a4d42db]::ty::context::GlobalCtxt>::enter::<<rustc_interface[c2fe7fc1c7f3d840]::queries::Queries>::ongoing_codegen::{closure#0}, core[ba37dea25f4d3bf]::result::Result<alloc[6c5b139dcb2c8bbf]::boxed::Box<dyn core[ba37dea25f4d3bf]::any::Any>, rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>>
  24:     0x7faa9bc603a6 - <rustc_interface[c2fe7fc1c7f3d840]::queries::Queries>::ongoing_codegen
  25:     0x7faa9baea458 - <rustc_interface[c2fe7fc1c7f3d840]::interface::Compiler>::enter::<rustc_driver_impl[c8b89ddd6b0e3357]::run_compiler::{closure#1}::{closure#2}, core[ba37dea25f4d3bf]::result::Result<core[ba37dea25f4d3bf]::option::Option<rustc_interface[c2fe7fc1c7f3d840]::queries::Linker>, rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>>
  26:     0x7faa9bb19b01 - rustc_span[927b0836cec2b3bf]::set_source_map::<core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>, rustc_interface[c2fe7fc1c7f3d840]::interface::run_compiler<core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>, rustc_driver_impl[c8b89ddd6b0e3357]::run_compiler::{closure#1}>::{closure#0}::{closure#0}>
  27:     0x7faa9bb206c3 - <scoped_tls[9d8b08ed86df779e]::ScopedKey<rustc_span[927b0836cec2b3bf]::SessionGlobals>>::set::<rustc_interface[c2fe7fc1c7f3d840]::interface::run_compiler<core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>, rustc_driver_impl[c8b89ddd6b0e3357]::run_compiler::{closure#1}>::{closure#0}, core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>>
  28:     0x7faa9bb02229 - std[912838f7fde4888d]::sys_common::backtrace::__rust_begin_short_backtrace::<rustc_interface[c2fe7fc1c7f3d840]::util::run_in_thread_pool_with_globals<rustc_interface[c2fe7fc1c7f3d840]::interface::run_compiler<core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>, rustc_driver_impl[c8b89ddd6b0e3357]::run_compiler::{closure#1}>::{closure#0}, core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>>
  29:     0x7faa9bb02f01 - <<std[912838f7fde4888d]::thread::Builder>::spawn_unchecked_<rustc_interface[c2fe7fc1c7f3d840]::util::run_in_thread_pool_with_globals<rustc_interface[c2fe7fc1c7f3d840]::interface::run_compiler<core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>, rustc_driver_impl[c8b89ddd6b0e3357]::run_compiler::{closure#1}>::{closure#0}, core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[ba37dea25f4d3bf]::result::Result<(), rustc_span[927b0836cec2b3bf]::ErrorGuaranteed>>::{closure#1} as core[ba37dea25f4d3bf]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
  30:     0x7faa9acde065 - std::sys::unix::thread::Thread::new::thread_start::hc275d8893483110f
  31:     0x7faa9aa8f6ba - start_thread
                               at ./nptl/pthread_create.c:444:8
  32:     0x7faa9ab1e0d0 - __GI___clone3
                               at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
  33:                0x0 - <unknown>

error: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: rustc 1.72.0-nightly (1fd11f948 2023-08-23) (1.72.0.0) running on x86_64-unknown-linux-gnu

note: compiler flags: --crate-type bin -C embed-bitcode=no -C debuginfo=2 -C incremental=[REDACTED] -Z unstable-options -C link-arg=-Tlinkall.x -C link-arg=-nostartfiles -C link-arg=-Trom_functions.x

note: some of the compiler flags provided by cargo are hidden

query stack during panic:
end of query stack

How to parse a request body?

I seem unable to parse a request body for a post.

I have created a type modeling my request, and created a custom FromRequest implementation, but can't figure out what kind of signature my handler function should have.

Can you perhaps add an example for this?

Error in `Content`

Hello, currently signature of Content::write_content suggests, that no other error could be return other than from writer, but I would like to return other errors. In my use case I'm reading from filesystem and it can give an error.

Request body as `io::Read`

Hello,

we use picoserve for updating firmware and configurations on embedded devices. Our updates are large, and storing the entire body in RAM is resource-intensive.

We suggest enhancing request.body() to implement io::Read instead of holding the entire payload in memory. Our devices support async writes, and streaming the body directly would greatly reduce RAM usage, benefiting not only us but anyone with similar embedded device constraints.

This change would streamline operations for large payloads, making picoserve more efficient for embedded systems.

Got links issue when build embassy/examples/nrf52840/src/bin/wifi_esp_hosted.rs

cargo build
    Blocking waiting for file lock on package cache
    Updating `mirror` index
    Updating git repository `https://github.com/embassy-rs/embassy.git`
error: failed to select a version for `embassy-time-driver`.
    ... required by package `embassy-time v0.3.0`
    ... which satisfies dependency `embassy-time = "^0.3.0"` of package `picoserve v0.10.2`
    ... which satisfies dependency `picoserve = "^0.10.2"` of package `lot-http v0.1.0 (/Users/shuanghuiyan/Workspace/my-projects/lot-http)`
versions that meet the requirements `^0.1.0` are: 0.1.0

the package `embassy-time-driver` links to the native library `embassy-time`, but it conflicts with a previous package which links to `embassy-time` as well:
package `embassy-time-driver v0.1.0 (https://github.com/embassy-rs/embassy.git#4d4cbc0d)`
    ... which satisfies git dependency `embassy-time-driver` of package `lot-http v0.1.0 (/Users/shuanghuiyan/Workspace/my-projects/lot-http)`
Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = "embassy-time"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.

failed to select a version for `embassy-time-driver` which could resolve this conflict

Support Websocket Concurrency

Hey,

Getting this weird error where I'm connecting to port 80 and have my routes setup for my app properly, but I can only access either the http or the websocket connection not both at the same time on the same port.

Thoughts?

Thanks

panic if percent sign present in a header field

The header parsing code panics with an overflow error if a header value contains a percent sign followed by anything other than the characters A to F.
e.g., X-Foobar: abc%20def

The issue appears to be this code:

picoserve/src/request.rs

Lines 670 to 675 in ebc1002

let first_nibble = s
.get(first_index)
.copied()
.filter(u8::is_ascii_hexdigit)?
.to_ascii_uppercase()
- b'A';

Subtracting b'A' causes an overflow if the value is anything other than A through F. Presumably the code should subtract b'0' if the character is 0 through 9.

However, it's not clear to me that it is correct for this code to try and attempt percent decoding in the first place. HTTP header fields are not percent encoded according to RFC 7230. It specifies percent encoding for URI values only. With regards to header field parsing, section 3.2.4 states:

Historically, HTTP has allowed field content with text in the ISO-8859-1 charset [ISO-8859-1], supporting other charsets only through use of [RFC2047] encoding. In practice, most HTTP header field values use only a subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD limit their field values to US-ASCII octets. A recipient SHOULD treat other octets in field content (obs-text) as opaque data.

RFC 8187 does specify a more complex encoding scheme to support non-ASCII characters in headers that use "header: token; name=value" parameter syntax, and it uses percent encoding as part of this. However, this doesn't appear to apply generally to all headers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.