Skip to content

Feature Proposal: Add try_clone method to Body trait #158

@pearzl

Description

@pearzl

Description

When building HTTP clients with tower, tower-http, http-body, we often need to reuse the body data—most commonly for:

  • Retrying requests after transient errors
  • Following 3xx redirects
  • Others...

It is awkward now when working with something like tower::retry, because any middleware is free to wrap the original body in a new type that does not implement Clone.
Once the concrete type is erased (e.g. BoxBody), it forces users to add an extra warpper (“clone adapter”) to make it compile, or to buffer the entire body data, neither of which is ideal.


Proposal

Add a default method to the Body trait:

/// Try to clone this body if the underlying implementation supports it.
///
/// Returns `Some(cloned)` if the entire body can be reproduced verbatim, `None` otherwise.
///
/// Implementations that wrap another body must delegate this call so that the capability is preserved through arbitrary middleware.
fn try_clone(&self) -> Option<Self>
where
    Self: Sized,
{
    None
}
  • No breaking change—every existing type continues to compile, and works as before.
  • Middleware that only wrap the inner body can forward the call in one line.
  • Retry/redirect policies can attempt try_clone() first and only fall back to buffering when it returns None, keeping the fast path zero-copy.

Alternatives considered

rewind() would also work, which reset the body stream to begining. it good for seekable I/O like files maybe.

/// Return value tell is if rewind success.
///
/// For example: body construct from short `String`, `Vec<u8>`, `Bytes`,... always return true, and retry goes on.
/// body construct from channel/stream, return false, so that retry can not continue.
fn rewind(&mut self) -> bool {
    false
}

However, I believe try_clone is more general and the better choice.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions