How do I read the entire body of a Tokio-based Hyper request?
我想使用Hyper的当前主分支编写服务器,该分支保存由POST请求传递的消息,并将此消息发送到每个传入的GET请求。
我有这个,主要是从超级示例目录中复制的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | extern crate futures; extern crate hyper; extern crate pretty_env_logger; use futures::future::FutureResult; use hyper::{Get, Post, StatusCode}; use hyper::header::{ContentLength}; use hyper::server::{Http, Service, Request, Response}; use futures::Stream; struct Echo { data: Vec<u8>, } impl Echo { fn new() -> Self { Echo { data:"text".into(), } } } impl Service for Echo { type Request = Request; type Response = Response; type Error = hyper::Error; type Future = FutureResult<Response, hyper::Error>; fn call(&self, req: Self::Request) -> Self::Future { let resp = match (req.method(), req.path()) { (&Get,"/") | (&Get,"/echo") => { Response::new() .with_header(ContentLength(self.data.len() as u64)) .with_body(self.data.clone()) }, (&Post,"/") => { //self.data.clear(); // argh. &self is not mutable :( // even if it was mutable... how to put the entire body into it? //req.body().fold(...) ? let mut res = Response::new(); if let Some(len) = req.headers().get::<ContentLength>() { res.headers_mut().set(ContentLength(0)); } res.with_body(req.body()) }, _ => { Response::new() .with_status(StatusCode::NotFound) } }; futures::future::ok(resp) } } fn main() { pretty_env_logger::init().unwrap(); let addr ="127.0.0.1:12346".parse().unwrap(); let server = Http::new().bind(&addr, || Ok(Echo::new())).unwrap(); println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap()); server.run().unwrap(); } |
如何将
Hyper 0.13为此提供了
1 2 3 4 5 6 7 | use hyper::body; use hyper::{Body, Response}; pub async fn read_response_body(res: Response<Body>) -> Result<String, hyper::Error> { let bytes = body::to_bytes(res.into_body()).await?; Ok(String::from_utf8(bytes.to_vec()).expect("response was not valid utf-8")) } |
我将简化问题,仅返回总字节数,而不是回显整个流。
期货0.3
超级0.13
如果只希望将所有数据作为一个大块,请参阅euclio关于
访问流可进行更细粒度的控制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | use futures::TryStreamExt; // 0.3.7 use hyper::{server::Server, service, Body, Method, Request, Response}; // 0.13.9 use std::convert::Infallible; use tokio; // 0.2.22 #[tokio::main] async fn main() { let addr ="127.0.0.1:12346".parse().expect("Unable to parse address"); let server = Server::bind(&addr).serve(service::make_service_fn(|_conn| async { Ok::<_, Infallible>(service::service_fn(echo)) })); println!("Listening on http://{}.", server.local_addr()); if let Err(e) = server.await { eprintln!("Error: {}", e); } } async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> { let (parts, body) = req.into_parts(); match (parts.method, parts.uri.path()) { (Method::POST,"/") => { let entire_body = body .try_fold(Vec::new(), |mut data, chunk| async move { data.extend_from_slice(&chunk); Ok(data) }) .await; entire_body.map(|body| { let body = Body::from(format!("Read {} bytes", body.len())); Response::new(body) }) } _ => { let body = Body::from("Can only POST to /"); Ok(Response::new(body)) } } } |
不幸的是,
期货0.1
超级0.12
自期货0.1.14起,您可以使用
1 2 3 4 | fn concat2(self) -> Concat2<Self> where Self: Sized, Self::Item: Extend<<Self::Item as IntoIterator>::Item> + IntoIterator + Default, |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | use futures::{ future::{self, Either}, Future, Stream, }; // 0.1.25 use hyper::{server::Server, service, Body, Method, Request, Response}; // 0.12.20 use tokio; // 0.1.14 fn main() { let addr ="127.0.0.1:12346".parse().expect("Unable to parse address"); let server = Server::bind(&addr).serve(|| service::service_fn(echo)); println!("Listening on http://{}.", server.local_addr()); let server = server.map_err(|e| eprintln!("Error: {}", e)); tokio::run(server); } fn echo(req: Request<Body>) -> impl Future<Item = Response<Body>, Error = hyper::Error> { let (parts, body) = req.into_parts(); match (parts.method, parts.uri.path()) { (Method::POST,"/") => { let entire_body = body.concat2(); let resp = entire_body.map(|body| { let body = Body::from(format!("Read {} bytes", body.len())); Response::new(body) }); Either::A(resp) } _ => { let body = Body::from("Can only POST to /"); let resp = future::ok(Response::new(body)); Either::B(resp) } } } |
您还可以通过
另请参见:
- 如何将字节向量(u8)转换为字符串
超级0.11
类似于
1 2 3 4 5 6 | fn fold<F, T, Fut>(self, init: T, f: F) -> Fold<Self, F, Fut, T> where F: FnMut(T, Self::Item) -> Fut, Fut: IntoFuture<Item = T>, Self::Error: From<Fut::Error>, Self: Sized, |
我们可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | extern crate futures; // 0.1.23 extern crate hyper; // 0.11.27 use futures::{Future, Stream}; use hyper::{ server::{Http, Request, Response, Service}, Post, }; fn main() { let addr ="127.0.0.1:12346".parse().unwrap(); let server = Http::new().bind(&addr, || Ok(Echo)).unwrap(); println!( "Listening on http://{} with 1 thread.", server.local_addr().unwrap() ); server.run().unwrap(); } struct Echo; impl Service for Echo { type Request = Request; type Response = Response; type Error = hyper::Error; type Future = Box<futures::Future<Item = Response, Error = Self::Error>>; fn call(&self, req: Self::Request) -> Self::Future { match (req.method(), req.path()) { (&Post,"/") => { let f = req.body() .fold(Vec::new(), |mut acc, chunk| { acc.extend_from_slice(&*chunk); futures::future::ok::<_, Self::Error>(acc) }) .map(|body| Response::new().with_body(format!("Read {} bytes", body.len()))); Box::new(f) } _ => panic!("Nope"), } } } |
您还可以将
另请参见:
- 如何将字节向量(u8)转换为字符串
输出
从命令行调用时,我们可以看到结果:
1 2 | $ curl -X POST --data hello http://127.0.0.1:12346/ Read 5 bytes |
警告
所有这些解决方案都允许恶意的最终用户发布无限大小的文件,这将导致计算机内存不足。根据预期的用途,您可能希望对读取的字节数设置某种上限,从而有可能在某个断点处写入文件系统。
另请参见:
- 如何对futures :: Stream :: concat2读取的字节数施加限制?
有关此主题的大多数答案都已过时或过于复杂。解决方案非常简单:
1 2 3 4 5 6 7 8 | /* WARNING for beginners!!! This use statement is important so we can later use .data() method!!! */ use hyper::body::HttpBody; let my_vector: Vec<u8> = request.into_body().data().await.unwrap().unwrap().to_vec(); let my_string = String::from_utf8(my_vector).unwrap(); |
您也可以使用