rwf2 / multer

An async parser for multipart/form-data content-type in Rust
MIT License
156 stars 35 forks source link

Support for async-std #14

Closed dbrgn closed 3 years ago

dbrgn commented 3 years ago

I tried to use multer in an async-std application.

# Cargo.toml
[package]
name = "multer-async-std"
version = "0.1.0"
authors = ["Danilo <mail@dbrgn.ch>"]
edition = "2018"

[dependencies]
anyhow = "1"
async-h1 = "2"
async-std = { version = "1", features = ["attributes"] }
http-types = "2"
multer = "1.2"
// src/main.rs

use async_std::{
    net::{TcpListener, TcpStream},
    prelude::*,
    task,
};
use http_types::{headers, Response, Result, StatusCode};

#[async_std::main]
async fn main() -> anyhow::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8080").await?;

    let mut incoming = listener.incoming();
    while let Some(stream) = incoming.next().await {
        let stream = stream?;
        task::spawn(async {
            if let Err(e) = accept(stream).await {
                eprintln!("Error: {}", e);
            }
        });
    }

    Ok(())
}

async fn accept(stream: TcpStream) -> Result<()> {
    async_h1::accept(stream.clone(), |mut req| {
        async move {
            let content_type = req
                .header(headers::CONTENT_TYPE)
                .map(|ct| ct.last().as_str())
                .unwrap();
            let boundary = multer::parse_boundary(content_type).unwrap();
            let body = req.take_body();
            let multipart = multer::Multipart::new(body.bytes(), boundary);
            Ok(Response::new(StatusCode::Ok))
        }
    })
    .await?;
    Ok(())
}

This fails to compile because Multipart::new wants a stream of Bytes, not a stream of u8.

error[E0277]: the trait bound `bytes::bytes::Bytes: std::convert::From<u8>` is not satisfied
  --> src/main.rs:34:29
   |
34 |             let multipart = multer::Multipart::new(body.bytes(), boundary);
   |                             ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<u8>` is not implemented for `bytes::bytes::Bytes`
   |
  ::: /home/danilo/.cargo/registry/src/github.com-1ecc6299db9ec823/multer-1.2.2/src/multipart.rs:59:12
   |
59 |         O: Into<Bytes> + 'static,
   |            ----------- required by this bound in `multer::multipart::Multipart::new`
   |
   = help: the following implementations were found:
             <bytes::bytes::Bytes as std::convert::From<&'static [u8]>>
             <bytes::bytes::Bytes as std::convert::From<&'static str>>
             <bytes::bytes::Bytes as std::convert::From<bytes::bytes_mut::BytesMut>>
             <bytes::bytes::Bytes as std::convert::From<http::byte_str::ByteStr>>
           and 4 others
   = note: required because of the requirements on the impl of `std::convert::Into<bytes::bytes::Bytes>` for `u8`

I cannot use from_reader either since that's a Tokio thing.

What would be the appropriate way to use multer with async-std? (If I can get it to work, I can also contribute an example.)

rousan commented 3 years ago

Hey @dbrgn , there is nothing to do with async-std or tokio for the core features provided by multer. You have provided a wrong argument to the Multipart ::new() method as described in the compiler error message.

The Multipart::new() accepts a stream of Bytes as described here.

First convert the body.bytes() to a stream of Bytes as below:

use bytes::Bytes;

let data: &[u8] = body.bytes();

// Convert your data to a stream of Bytes.
let stream = futures::stream::once(async move { Result::<Bytes, Infallible>::Ok(Bytes::from(data)) });

let multipart = multer::Multipart::new(stream, boundary);
dbrgn commented 3 years ago

Ah, perfect, thanks @rousan!