Maybe incorporate an object safe Body abstraction

Open
#125 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Feature
Clarity
Needs clarification
Activity status
Stale
Tech stack
rust

Research direction

Start by reviewing the proposed BytesResponse, BytesBody, and BodyWrapper abstractions in the issue, along with the goal of an object-safe HttpClient trait. Determine whether all or part of this design belongs in http-body and define the intended behavior for consuming bodies multiple times. Done means the maintainers agree on a concrete scope and API direction.

Written by the indexing model from the issue text.

Description

I just wrote this:

/// Response with object safe body type
pub struct BytesResponse {
    /// Response status and header
    pub parts: http::response::Parts,
    /// Response body
    pub body: Box<dyn BytesBody>,
}

impl BytesResponse {
    pub(crate) async fn body(mut self) -> Result<Bytes, Box<dyn StdError + Send + Sync + 'static>> {
        self.body.into_bytes().await
    }
}

impl<B> From<Response<B>> for BytesResponse
where
    B: http_body::Body + Send + Unpin + 'static,
    B::Data: Send,
    B::Error: Into<Box<dyn StdError + Send + Sync + 'static>>,
{
    fn from(rsp: Response<B>) -> Self {
        let (parts, body) = rsp.into_parts();
        Self {
            parts,
            body: Box::new(BodyWrapper { inner: Some(body) }),
        }
    }
}

struct BodyWrapper<B> {
    inner: Option<B>,
}

#[async_trait]
impl<B> BytesBody for BodyWrapper<B>
where
    B: http_body::Body + Send + Unpin + 'static,
    B::Data: Send,
    B::Error: Into<Box<dyn StdError + Send + Sync + 'static>>,
{
    async fn into_bytes(&mut self) -> Result<Bytes, Box<dyn StdError + Send + Sync + 'static>> {
        let Some(body) = self.inner.take() else {
            return Ok(Bytes::new());
        };

        match body.collect().await {
            Ok(body) => Ok(body.to_bytes()),
            Err(e) => Err(e.into()),
        }
    }
}

/// Object safe body trait
#[async_trait]
pub trait BytesBody {
    /// Convert the body into [`Bytes`]
    ///
    /// This consumes the body. The behavior for calling this method multiple times is undefined.
    #[allow(clippy::wrong_self_convention)] // async_trait doesn't support taking `self`
    async fn into_bytes(&mut self) -> Result<Bytes, Box<dyn StdError + Send + Sync + 'static>>;
}

Reason was I wanted to have an object-safe HttpClient trait. Maybe it makes sense to incorporate all or part of this here?

Dominant language
Rust
Stars
173
Forks
71
Avg merge
1d 12h
Merged PRs (30d)
1

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from hyperium/http-body

All issues in hyperium/http-body

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.