我试图通过使用静态响应实现我自己的hyper::Client
来测试一些使用hyper::client::Connect
的代码。我已经找到了类型,但无法找出tokio-proto
抱怨说request / response mismatch
的运行时问题。这是我的代码的简化版本,用于演示失败:
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_io;
use futures::{future, Future, Stream};
use std::str::from_utf8;
use std::io::Cursor;
struct Client<'a, C: 'a> {
client: &'a hyper::Client<C>,
url: &'a str,
}
impl<'a, C: hyper::client::Connect> Client<'a, C> {
fn get(&self) -> Box<Future<Item = String, Error = hyper::Error>> {
Box::new(self.client.get(self.url.parse().unwrap()).and_then(|res| {
let body = Vec::new();
res.body()
.fold(body, |mut acc, chunk| {
acc.extend_from_slice(chunk.as_ref());
Ok::<_, hyper::Error>(acc)
})
.and_then(move |value| Ok(String::from(from_utf8(&value).unwrap())))
}))
}
}
struct StaticConnector<'a> {
body: &'a [u8],
}
impl<'a> StaticConnector<'a> {
fn new(body: &'a [u8]) -> StaticConnector {
StaticConnector { body: body }
}
}
impl<'a> hyper::server::Service for StaticConnector<'a> {
type Request = hyper::Uri;
type Response = Cursor<Vec<u8>>;
type Error = std::io::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, _: Self::Request) -> Self::Future {
Box::new(future::ok(Cursor::new(self.body.to_vec())))
}
}
fn main() {
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
// My StaticConnector for testing
let hyper_client = hyper::Client::configure()
.connector(StaticConnector::new(
b"\
HTTP/1.1 200 OK\r\n\
Content-Length: 8\r\n\
\r\n\
Maldives\
",
))
.build(&handle);
// Real Connector
/*
let hyper_client = hyper::Client::configure().build(&handle);
*/
let client = Client {
client: &hyper_client,
url: "http://ifconfig.co/country",
};
let result = core.run(client.get()).unwrap();
println!("{}", result);
}
我猜测我使用的Cursor
Io
在某种程度上是不完整的,但我没有调试并取得进展。一种想法是,Cursor
hyper::Client
对sink
的写入可能不符合预期。也许我需要组合DEBUG_FILE = ./debug/debug.txt
INPUTS0 = syn.tcl
OUTPUTS0 = syn.out
OUTPUT_ILA = $(DEBUG_FILE)
############################################################################
#
# Dependencies / rules
#
############################################################################
ifdef DEBUG_FILE
all: ila
else
all: syn
endif
## Synthesis
$(OUTPUTS0): $(INPUTS0)
echo "Synthesis Process."
touch $(OUTPUTS0)
$(OUTPUT_ILA): $(OUTPUTS0)
echo "Debug Probe Insertion."
touch $(OUTPUT_ILA)
syn: $(OUTPUTS0)
ila: $(OUTPUT_ILA)
用于写入和静态内容用于读取?所有想法都未能在使用中取得进展!
答案 0 :(得分:1)
原始代码无法正常工作的原因是因为读者方在客户端发送请求之前提供了响应,因此tokio-proto
对request / response mismatch
产生了错误。修复原来是非常重要的,首先我们需要安排读者阻止,或者更具体地说是std::io::ErrorKind::WouldBlock
错误输出,以向事件循环表明还没有任何东西,但是不要认为它是EOF
。另外,一旦我们得到表示请求已经发送并且tokio-proto机器正在等待响应的写入,我们使用futures::task::current.notify
来取消阻止读取。这是一个按预期工作的更新实现:
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_io;
use futures::{future, Future, Stream, task, Poll};
use std::str::from_utf8;
use std::io::{self, Cursor, Read, Write};
use tokio_io::{AsyncRead, AsyncWrite};
struct Client<'a, C: 'a> {
client: &'a hyper::Client<C>,
url: &'a str,
}
impl<'a, C: hyper::client::Connect> Client<'a, C> {
fn get(&self) -> Box<Future<Item = String, Error = hyper::Error>> {
Box::new(self.client.get(self.url.parse().unwrap()).and_then(|res| {
let body = Vec::new();
res.body()
.fold(body, |mut acc, chunk| {
acc.extend_from_slice(chunk.as_ref());
Ok::<_, hyper::Error>(acc)
})
.and_then(move |value| Ok(String::from(from_utf8(&value).unwrap())))
}))
}
}
struct StaticStream {
wrote: bool,
body: Cursor<Vec<u8>>,
}
impl Read for StaticStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.wrote {
self.body.read(buf)
} else {
Err(io::ErrorKind::WouldBlock.into())
}
}
}
impl Write for StaticStream {
fn write<'a>(&mut self, buf: &'a [u8]) -> io::Result<usize> {
self.wrote = true;
task::current().notify();
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl AsyncRead for StaticStream {}
impl AsyncWrite for StaticStream {
fn shutdown(&mut self) -> Poll<(), io::Error> {
Ok(().into())
}
}
struct StaticConnector<'a> {
body: &'a [u8],
}
impl<'a> StaticConnector<'a> {
fn new(body: &'a [u8]) -> StaticConnector {
StaticConnector { body: body }
}
}
impl<'a> hyper::server::Service for StaticConnector<'a> {
type Request = hyper::Uri;
type Response = StaticStream;
type Error = std::io::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, _: Self::Request) -> Self::Future {
Box::new(future::ok(StaticStream {
wrote: false,
body: Cursor::new(self.body.to_vec()),
}))
}
}
fn main() {
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
// My StaticConnector for testing
let hyper_client = hyper::Client::configure()
.connector(StaticConnector::new(
b"\
HTTP/1.1 200 OK\r\n\
Content-Length: 8\r\n\
\r\n\
Maldives\
",
))
.build(&handle);
// Real Connector
/*
let hyper_client = hyper::Client::configure().build(&handle);
*/
let client = Client {
client: &hyper_client,
url: "http://ifconfig.co/country",
};
let result = core.run(client.get()).unwrap();
println!("{}", result);
}
注意:此实现适用于简单的情况,但我还没有测试过更复杂的场景。例如,我不确定的一件事是请求/响应有多大,因为它们可能涉及多于一次的读/写调用。