|
| 1 | +#![feature(async_await)] |
1 | 2 | #![deny(warnings)]
|
2 |
| -extern crate futures; |
| 3 | + |
3 | 4 | extern crate hyper;
|
4 | 5 |
|
5 |
| -use futures::future; |
6 |
| -use hyper::rt::{Future, Stream}; |
7 |
| -use hyper::service::service_fn; |
8 | 6 | use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
9 |
| - |
10 |
| -/// We need to return different futures depending on the route matched, |
11 |
| -/// and we can do that with an enum, such as `futures::Either`, or with |
12 |
| -/// trait objects. |
13 |
| -/// |
14 |
| -/// A boxed Future (trait object) is used as it is easier to understand |
15 |
| -/// and extend with more types. Advanced users could switch to `Either`. |
16 |
| -type BoxFut = Box<dyn Future<Item = Response<Body>, Error = hyper::Error> + Send>; |
| 7 | +use hyper::service::{make_service_fn, service_fn}; |
| 8 | +use futures_util::TryStreamExt; |
17 | 9 |
|
18 | 10 | /// This is our service handler. It receives a Request, routes on its
|
19 | 11 | /// path, and returns a Future of a Response.
|
20 |
| -fn echo(req: Request<Body>) -> BoxFut { |
21 |
| - let mut response = Response::new(Body::empty()); |
| 12 | +async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> { |
22 | 13 |
|
23 | 14 | match (req.method(), req.uri().path()) {
|
24 | 15 | // Serve some instructions at /
|
25 | 16 | (&Method::GET, "/") => {
|
26 |
| - *response.body_mut() = Body::from("Try POSTing data to /echo"); |
| 17 | + Ok(Response::new(Body::from("Try POSTing data to /echo such as: `curl localhost:3000/echo -XPOST -d 'hello world'`"))) |
27 | 18 | }
|
28 | 19 |
|
29 | 20 | // Simply echo the body back to the client.
|
30 | 21 | (&Method::POST, "/echo") => {
|
31 |
| - *response.body_mut() = req.into_body(); |
| 22 | + Ok(Response::new(req.into_body())) |
32 | 23 | }
|
33 | 24 |
|
34 |
| - // Convert to uppercase before sending back to client. |
| 25 | + // Convert to uppercase before sending back to client using a stream. |
35 | 26 | (&Method::POST, "/echo/uppercase") => {
|
36 |
| - let mapping = req.into_body().map(|chunk| { |
| 27 | + let mapping = req.into_body().map_ok(|chunk| { |
37 | 28 | chunk
|
38 | 29 | .iter()
|
39 | 30 | .map(|byte| byte.to_ascii_uppercase())
|
40 | 31 | .collect::<Vec<u8>>()
|
41 | 32 | });
|
42 |
| - |
43 |
| - *response.body_mut() = Body::wrap_stream(mapping); |
| 33 | + Ok(Response::new(Body::wrap_stream(mapping))) |
44 | 34 | }
|
45 | 35 |
|
46 | 36 | // Reverse the entire body before sending back to the client.
|
47 | 37 | //
|
48 | 38 | // Since we don't know the end yet, we can't simply stream
|
49 |
| - // the chunks as they arrive. So, this returns a different |
50 |
| - // future, waiting on concatenating the full body, so that |
51 |
| - // it can be reversed. Only then can we return a `Response`. |
| 39 | + // the chunks as they arrive as we did with the above uppercase endpoint. |
| 40 | + // So here we do `.await` on the future, waiting on concatenating the full body, |
| 41 | + // then afterwards the content can be reversed. Only then can we return a `Response`. |
52 | 42 | (&Method::POST, "/echo/reversed") => {
|
53 |
| - let reversed = req.into_body().concat2().map(move |chunk| { |
54 |
| - let body = chunk.iter().rev().cloned().collect::<Vec<u8>>(); |
55 |
| - *response.body_mut() = Body::from(body); |
56 |
| - response |
57 |
| - }); |
| 43 | + let chunks = req.into_body().try_concat().await; |
| 44 | + |
| 45 | + let reversed_body = chunks.map(move |chunk| { |
| 46 | + chunk.iter().rev().cloned().collect::<Vec<u8>>() |
58 | 47 |
|
59 |
| - return Box::new(reversed); |
| 48 | + })?; |
| 49 | + Ok(Response::new(Body::from(reversed_body))) |
60 | 50 | }
|
61 | 51 |
|
62 |
| - // The 404 Not Found route... |
| 52 | + // Return The 404 Not Found for other routes. |
63 | 53 | _ => {
|
64 |
| - *response.status_mut() = StatusCode::NOT_FOUND; |
| 54 | + let mut not_found = Response::new(Body::empty()); |
| 55 | + *not_found.status_mut() = StatusCode::NOT_FOUND; |
| 56 | + Ok(not_found) |
65 | 57 | }
|
66 |
| - }; |
67 |
| - |
68 |
| - Box::new(future::ok(response)) |
| 58 | + } |
69 | 59 | }
|
70 | 60 |
|
71 |
| -fn main() { |
| 61 | + |
| 62 | +#[tokio::main] |
| 63 | +async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { |
72 | 64 | let addr = ([127, 0, 0, 1], 3000).into();
|
73 | 65 |
|
| 66 | + let service = make_service_fn(|_| { |
| 67 | + async { |
| 68 | + Ok::<_, hyper::Error>(service_fn(echo)) |
| 69 | + } |
| 70 | + }); |
| 71 | + |
74 | 72 | let server = Server::bind(&addr)
|
75 |
| - .serve(|| service_fn(echo)) |
76 |
| - .map_err(|e| eprintln!("server error: {}", e)); |
| 73 | + .serve(service); |
77 | 74 |
|
78 | 75 | println!("Listening on http://{}", addr);
|
79 |
| - hyper::rt::run(server); |
| 76 | + |
| 77 | + server.await?; |
| 78 | + |
| 79 | + Ok(()) |
80 | 80 | }
|
0 commit comments