如何从流中复制数据同时转发流

时间:2018-08-07 15:08:24

标签: rust hyper

我正在使用hyper 0.12构建代理服务。从上游服务器接收响应正文时,我想将其转发回客户端,并将内容保存在缓冲区中以供以后处理。

所以我需要一个函数:

  • 使用Stream(准确地说是hyper::Body
  • 返回在功能上与输入流相同的Stream
  • 还返回某种Future<Item = Vec<u8>, Error = ...>,当输出流被完全用尽时,该type BufferFuture = Box<Future<Item = Vec<u8>, Error = ()>>; pub fn copy_body(body: hyper::Body) -> (hyper::Body, BufferFuture) { let body2 = ... // ??? let buffer = body.fold(Vec::<u8>::new(), |mut buf, chunk| { buf.extend_from_slice(&chunk); // ...somehow send this chunk to body2 also? }); (body2, buffer); } 将使用输入流的缓冲内容来解析

我一生无法弄清楚该怎么做。

我猜我要寻找的功能看起来像这样:

send_data()

下面是我尝试过的方法,它可以工作到type BufferFuture = Box<Future<Item = Vec<u8>, Error = ()>>; pub fn copy_body(body: hyper::Body) -> (hyper::Body, BufferFuture) { let (mut sender, body2) = hyper::Body::channel(); let consume = body.map_err(|_| ()).fold(Vec::<u8>::new(), move |mut buf, chunk| { buf.extend_from_slice(&chunk); // What to do if this fails? if sender.send_data(chunk).is_err() {} Box::new(future::ok(buf)) }); (body2, Box::new(consume)); } 失败(很明显)为止。

Sink

但是,有些事情告诉我我走错了路。

我发现Sink.fanout()似乎是我想要的,但是我没有hyper::Body,而且我不知道如何构造它。 Stream实现Sink而不实现fetch

1 个答案:

答案 0 :(得分:2)

我最终要做的是实现一种满足我需要的新型流。这似乎是必要的,因为hyper::Body既没有实现Sink也没有实现hyper::ChunkClone是必需的),所以我不能使用现有的组合器。

首先是一个结构,其中包含我们需要的所有详细信息以及附加新块的方法,并通知缓冲区已完成。

Sink.fanout()

然后,我为此结构实现了struct BodyClone<T> { body: T, buffer: Option<Vec<u8>>, sender: Option<futures::sync::oneshot::Sender<Vec<u8>>>, } impl BodyClone<hyper::Body> { fn flush(&mut self) { if let (Some(buffer), Some(sender)) = (self.buffer.take(), self.sender.take()) { if sender.send(buffer).is_err() {} } } fn push(&mut self, chunk: &hyper::Chunk) { use hyper::body::Payload; let length = if let Some(buffer) = self.buffer.as_mut() { buffer.extend_from_slice(chunk); buffer.len() as u64 } else { 0 }; if let Some(content_length) = self.body.content_length() { if length >= content_length { self.flush(); } } } } 特性。

Stream

最后,我可以在impl Stream for BodyClone<hyper::Body> { type Item = hyper::Chunk; type Error = hyper::Error; fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> { match self.body.poll() { Ok(Async::Ready(Some(chunk))) => { self.push(&chunk); Ok(Async::Ready(Some(chunk))) } Ok(Async::Ready(None)) => { self.flush(); Ok(Async::Ready(None)) } other => other, } } } 上定义扩展方法:

hyper::Body

可以如下使用:

pub type BufferFuture = Box<Future<Item = Vec<u8>, Error = ()> + Send>;

trait CloneBody {
    fn clone_body(self) -> (hyper::Body, BufferFuture);
}

impl CloneBody for hyper::Body {
    fn clone_body(self) -> (hyper::Body, BufferFuture) {
        let (sender, receiver) = futures::sync::oneshot::channel();

        let cloning_stream = BodyClone {
            body: self,
            buffer: Some(Vec::new()),
            sender: Some(sender),
        };

        (
            hyper::Body::wrap_stream(cloning_stream),
            Box::new(receiver.map_err(|_| ())),
        )
    }
}