Files
hyper/examples/web_api.rs
Sean McArthur fbc449e49c feat(body): introduce an Entity trait to represent bodies
This dedicated `Entity` trait replaces the previous `Stream<Item=impl
AsRef<[u8]>, Error=hyper::Error>`. This allows for several improvements
immediately, and prepares for HTTP2 support.

- The `Entity::is_end_stream` makes up for change away from
  `Option<Body>`, which was previously used to know if the body should be
  empty. Since `Request` and `Response` now require a body to be set,
  this method can be used to tell hyper that the body is actually empty.

  It also provides the possibility of slight optimizations when polling
  for data, by allowing to check `is_end_stream` before polling again.
  This can allow a consumer to know that a body stream has ended without
  polling for `None` afterwards.

- The `Entity::content_length` method allows a body to automatically
  declare a size, in case a user doesn't set a `Content-Length` or
  `Transfer-Encoding` header.

- It's now possible to send and receive trailers, though this will be
  for HTTP2 connections only.

By being a trait owned by hyper, new methods can be added later as new
features are wanted (with default implementations).

The `hyper::Body` type now implements `Entity` instead of `Stream`,
provides a better channel option, and is easier to use with custom
streams via `Body::wrap_stream`.

BREAKING CHANGE: All code that was assuming the body was a `Stream` must
  be adjusted to use an `Entity` instead.

  Using `hyper::Body` as a `Stream` can call `Body::into_stream`
  to get a stream wrapper.

  Passing a custom `impl Stream` will need to either implement
  `Entity`, or as an easier option, switch to `Body::wrap_stream`.

  `Body::pair` has been replaced with `Body::channel`, which returns a
  `hyper::body::Sender` instead of a `futures::sync::mpsc::Sender`.

Closes #1438
2018-03-19 11:43:47 -07:00

94 lines
3.5 KiB
Rust

#![deny(warnings)]
extern crate futures;
extern crate hyper;
extern crate pretty_env_logger;
extern crate tokio_core;
use futures::{Future, Stream};
use hyper::{Body, Chunk, Client, Method, Request, Response, StatusCode};
use hyper::server::{Http, Service};
#[allow(unused)]
use std::ascii::AsciiExt;
static NOTFOUND: &[u8] = b"Not Found";
static URL: &str = "http://127.0.0.1:1337/web_api";
static INDEX: &[u8] = b"<a href=\"test.html\">test.html</a>";
static LOWERCASE: &[u8] = b"i am a lower case string";
struct ResponseExamples(tokio_core::reactor::Handle);
impl Service for ResponseExamples {
type Request = Request<Body>;
type Response = Response<Body>;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, req: Self::Request) -> Self::Future {
match (req.method(), req.uri().path()) {
(&Method::GET, "/") | (&Method::GET, "/index.html") => {
let body = Body::from(INDEX);
Box::new(futures::future::ok(Response::new(body)))
},
(&Method::GET, "/test.html") => {
// Run a web query against the web api below
let client = Client::configure().build(&self.0);
let req = Request::builder()
.method(Method::POST)
.uri(URL)
.body(LOWERCASE.into())
.unwrap();
let web_res_future = client.request(req);
Box::new(web_res_future.map(|web_res| {
let body = Body::wrap_stream(web_res.into_body().into_stream().map(|b| {
Chunk::from(format!("before: '{:?}'<br>after: '{:?}'",
std::str::from_utf8(LOWERCASE).unwrap(),
std::str::from_utf8(&b).unwrap()))
}));
Response::new(body)
}))
},
(&Method::POST, "/web_api") => {
// A web api to run against. Simple upcasing of the body.
let body = Body::wrap_stream(req.into_body().into_stream().map(|chunk| {
let upper = chunk.iter().map(|byte| byte.to_ascii_uppercase())
.collect::<Vec<u8>>();
Chunk::from(upper)
}));
Box::new(futures::future::ok(Response::new(body)))
},
_ => {
let body = Body::from(NOTFOUND);
Box::new(futures::future::ok(Response::builder()
.status(StatusCode::NOT_FOUND)
.body(body)
.unwrap()))
}
}
}
}
fn main() {
pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
let client_handle = core.handle();
let serve = Http::new().serve_addr_handle(&addr, &handle, move || Ok(ResponseExamples(client_handle.clone()))).unwrap();
println!("Listening on http://{} with 1 thread.", serve.incoming_ref().local_addr());
let h2 = handle.clone();
handle.spawn(serve.for_each(move |conn| {
h2.spawn(conn.map(|_| ()).map_err(|err| println!("serve error: {:?}", err)));
Ok(())
}).map_err(|_| ()));
core.run(futures::future::empty::<(), ()>()).unwrap();
}