我正在使用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
。
答案 0 :(得分:2)
我最终要做的是实现一种满足我需要的新型流。这似乎是必要的,因为hyper::Body
既没有实现Sink
也没有实现hyper::Chunk
(Clone
是必需的),所以我不能使用现有的组合器。
首先是一个结构,其中包含我们需要的所有详细信息以及附加新块的方法,并通知缓冲区已完成。
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(|_| ())),
)
}
}