Open DaneSlattery opened 1 month ago
Feature request for multi part forms support.
Sorry for the late reply!
If you would like to work on a no_std
multiparts form support, you are welcome :) We can also include it in the edge-net
suite once we have an initial implementation.
I personally don't have spare cycles for this, and I also don't need it right now, as most HTTP-related code on Embedded seems to get away with other POST payloads (most often than not application/json
) rather than multipart/form-data
.
This crate comes the closest out of the many that I have tried, but requires stream support to be implemented for a connection/request.
I think it would rather require Stream
support NOT for the "connection" / "request" but for the request body.
The request body in edge-http
(both client and server) is just something which happens to implement... embedded_io_async::Read
(on the server) and embedded_io_async::Write
(on the client, I guess less interesting to you, as you are probably more interested in utility code for parsing multipart stuff rather than building it).
Saying that ^^^ because your task is then to map embedded_io_async::Read
to a futures(_lite)::Stream
which is relatively easy.
I hacked it together with
sans_io
, but I cannot get thebytes::Bytes
to be cast from the buffer type&[u8]
Couldn't find much info about "sans-io" by a quick googling, but regardless, mapping an embedded_io_async::Read
to futures::Stream<Output = std::io::Result<Bytes>>
might be as simple as (excuse not having this type-checked):
fn as_stream<R: embedded_io_async::Read>(read: R) -> impl futures::Stream<Output = std::io::Result<Bytes>> {
futures::stream::poll_fn(move |ctx| {
let mut buf = [0; 64];
let read = core::pin::pin!(read.read(&mut buf));
match read.poll(ctx) {
Ok(0) => Poll::Ready(None), // Reading 0 bytes from the input stream <=> eof
Ok(n) => Poll::Ready(Some(alloc::boxed::Box::new(&buf[..n]),
Err(e) => Poll::Ready(Some(convert_to_io_err(e)),
}
})
}
With that said, using Bytes
on embedded is suboptimal, as the Bytes
contract is not life-timed (owns the data) and as such requires allocations (as my Box
thing from above).
I have also tried:
- multipart but this only supports
std::io::Read
and is no longer in development.
Yes, if it only wants blocking reads, this is a no-go. If it also supports AsyncRead
, you can map embedded_io_async::Read
to AsyncRead
and the other way around. Look inside the embedded_io_async
crate.
- axum multipart, but this requires
futures_util::stream::StreamExt
.
If you have something which implements futures(_util)::stream::Stream
, then it also implements StreamExt
, as StreamExt
is a decorator-only trait of Stream
, just like FutureExt
is a decorator-only trait of Future
.
- actix_multipart but this requires `futures_core::stream::Stream
futures
, futures-core
, futures-lite
are in the end - roughly speaking - all the same thing:
futures
is re-exporting futures-core
plus offers more
futures-lite
is re-exporting futures-core
plus offers more
Basically, the futures-core
is the place where traits like Stream
live, which are not yet stable in regular upstream Rust (unlike Future
, which is stable in upstream Rust since several years, and hence the futures(-core)
's crate futures::Future
is a type-alias for core::future::Future
now).
Are there any plans for an embedded-io-async approach for this?
As per above, somebody needs to drive this. :)
UPDATE: Unfortunately the above code might not work as embedded_io_async::Read
- unlike AsyncRead
does not have poll_read
:(
This is the general problem of wrapping traits containing async
functions to something which looks like a Future
or a Stream
(i.e. stuff which has poll
on the object itself, i.e. it is itself a Future or a Stream...)
Also see this.
Feature request for multi part forms support. This crate comes the closest out of the many that I have tried, but requires stream support to be implemented for a connection/request. I hacked it together with
sans_io
, but I cannot get thebytes::Bytes
to be cast from the buffer type&[u8]
I have also tried:
std::io::Read
and is no longer in development.futures_util::stream::StreamExt
.Are there any plans for an embedded-io-async approach for this?