|
1 |
| -#![deny(warnings)] |
2 |
| -extern crate futures; |
| 1 | +#![feature(async_await)] |
| 2 | +// #![deny(warnings)] // FIXME: https://github.com/rust-lang/rust/issues/62411 |
3 | 3 | extern crate hyper;
|
4 | 4 | extern crate pretty_env_logger;
|
5 | 5 | extern crate url;
|
6 | 6 |
|
7 |
| -use futures::{future, Future, Stream}; |
8 |
| - |
9 | 7 | use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
10 |
| -use hyper::service::service_fn; |
| 8 | +use hyper::service::{service_fn, make_service_fn}; |
11 | 9 |
|
12 | 10 | use std::collections::HashMap;
|
13 | 11 | use url::form_urlencoded;
|
| 12 | +use futures_util::TryStreamExt; |
14 | 13 |
|
15 | 14 | static INDEX: &[u8] = b"<html><body><form action=\"post\" method=\"post\">Name: <input type=\"text\" name=\"name\"><br>Number: <input type=\"text\" name=\"number\"><br><input type=\"submit\"></body></html>";
|
16 | 15 | static MISSING: &[u8] = b"Missing field";
|
17 | 16 | static NOTNUMERIC: &[u8] = b"Number field is not numeric";
|
18 | 17 |
|
19 | 18 | // Using service_fn, we can turn this function into a `Service`.
|
20 |
| -fn param_example(req: Request<Body>) -> Box<dyn Future<Item=Response<Body>, Error=hyper::Error> + Send> { |
| 19 | +async fn param_example(req: Request<Body>) -> Result<Response<Body>, hyper::Error> { |
21 | 20 | match (req.method(), req.uri().path()) {
|
22 | 21 | (&Method::GET, "/") | (&Method::GET, "/post") => {
|
23 |
| - Box::new(future::ok(Response::new(INDEX.into()))) |
| 22 | + Ok(Response::new(INDEX.into())) |
24 | 23 | },
|
25 | 24 | (&Method::POST, "/post") => {
|
26 |
| - Box::new(req.into_body().concat2().map(|b| { |
27 |
| - // Parse the request body. form_urlencoded::parse |
28 |
| - // always succeeds, but in general parsing may |
29 |
| - // fail (for example, an invalid post of json), so |
30 |
| - // returning early with BadRequest may be |
31 |
| - // necessary. |
32 |
| - // |
33 |
| - // Warning: this is a simplified use case. In |
34 |
| - // principle names can appear multiple times in a |
35 |
| - // form, and the values should be rolled up into a |
36 |
| - // HashMap<String, Vec<String>>. However in this |
37 |
| - // example the simpler approach is sufficient. |
38 |
| - let params = form_urlencoded::parse(b.as_ref()).into_owned().collect::<HashMap<String, String>>(); |
| 25 | + let b = req.into_body().try_concat().await?; |
| 26 | + // Parse the request body. form_urlencoded::parse |
| 27 | + // always succeeds, but in general parsing may |
| 28 | + // fail (for example, an invalid post of json), so |
| 29 | + // returning early with BadRequest may be |
| 30 | + // necessary. |
| 31 | + // |
| 32 | + // Warning: this is a simplified use case. In |
| 33 | + // principle names can appear multiple times in a |
| 34 | + // form, and the values should be rolled up into a |
| 35 | + // HashMap<String, Vec<String>>. However in this |
| 36 | + // example the simpler approach is sufficient. |
| 37 | + let params = form_urlencoded::parse(b.as_ref()).into_owned().collect::<HashMap<String, String>>(); |
39 | 38 |
|
40 |
| - // Validate the request parameters, returning |
41 |
| - // early if an invalid input is detected. |
42 |
| - let name = if let Some(n) = params.get("name") { |
43 |
| - n |
44 |
| - } else { |
45 |
| - return Response::builder() |
46 |
| - .status(StatusCode::UNPROCESSABLE_ENTITY) |
47 |
| - .body(MISSING.into()) |
48 |
| - .unwrap(); |
49 |
| - }; |
50 |
| - let number = if let Some(n) = params.get("number") { |
51 |
| - if let Ok(v) = n.parse::<f64>() { |
52 |
| - v |
53 |
| - } else { |
54 |
| - return Response::builder() |
55 |
| - .status(StatusCode::UNPROCESSABLE_ENTITY) |
56 |
| - .body(NOTNUMERIC.into()) |
57 |
| - .unwrap(); |
58 |
| - } |
| 39 | + // Validate the request parameters, returning |
| 40 | + // early if an invalid input is detected. |
| 41 | + let name = if let Some(n) = params.get("name") { |
| 42 | + n |
| 43 | + } else { |
| 44 | + return Ok(Response::builder() |
| 45 | + .status(StatusCode::UNPROCESSABLE_ENTITY) |
| 46 | + .body(MISSING.into()) |
| 47 | + .unwrap()); |
| 48 | + }; |
| 49 | + let number = if let Some(n) = params.get("number") { |
| 50 | + if let Ok(v) = n.parse::<f64>() { |
| 51 | + v |
59 | 52 | } else {
|
60 |
| - return Response::builder() |
| 53 | + return Ok(Response::builder() |
61 | 54 | .status(StatusCode::UNPROCESSABLE_ENTITY)
|
62 |
| - .body(MISSING.into()) |
63 |
| - .unwrap(); |
64 |
| - }; |
| 55 | + .body(NOTNUMERIC.into()) |
| 56 | + .unwrap()); |
| 57 | + } |
| 58 | + } else { |
| 59 | + return Ok(Response::builder() |
| 60 | + .status(StatusCode::UNPROCESSABLE_ENTITY) |
| 61 | + .body(MISSING.into()) |
| 62 | + .unwrap()); |
| 63 | + }; |
65 | 64 |
|
66 |
| - // Render the response. This will often involve |
67 |
| - // calls to a database or web service, which will |
68 |
| - // require creating a new stream for the response |
69 |
| - // body. Since those may fail, other error |
70 |
| - // responses such as InternalServiceError may be |
71 |
| - // needed here, too. |
72 |
| - let body = format!("Hello {}, your number is {}", name, number); |
73 |
| - Response::new(body.into()) |
74 |
| - })) |
| 65 | + // Render the response. This will often involve |
| 66 | + // calls to a database or web service, which will |
| 67 | + // require creating a new stream for the response |
| 68 | + // body. Since those may fail, other error |
| 69 | + // responses such as InternalServiceError may be |
| 70 | + // needed here, too. |
| 71 | + let body = format!("Hello {}, your number is {}", name, number); |
| 72 | + Ok(Response::new(body.into())) |
75 | 73 | },
|
76 | 74 | _ => {
|
77 |
| - Box::new(future::ok(Response::builder() |
78 |
| - .status(StatusCode::NOT_FOUND) |
79 |
| - .body(Body::empty()) |
80 |
| - .unwrap())) |
| 75 | + Ok(Response::builder() |
| 76 | + .status(StatusCode::NOT_FOUND) |
| 77 | + .body(Body::empty()) |
| 78 | + .unwrap()) |
81 | 79 | }
|
82 | 80 | }
|
83 |
| - |
84 | 81 | }
|
85 | 82 |
|
86 |
| -fn main() { |
| 83 | +#[hyper::rt::main] |
| 84 | +async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { |
87 | 85 | pretty_env_logger::init();
|
88 | 86 |
|
89 | 87 | let addr = ([127, 0, 0, 1], 1337).into();
|
90 | 88 |
|
91 | 89 | let server = Server::bind(&addr)
|
92 |
| - .serve(|| service_fn(param_example)) |
93 |
| - .map_err(|e| eprintln!("server error: {}", e)); |
| 90 | + .serve(make_service_fn(|_| { |
| 91 | + async { |
| 92 | + Ok::<_, hyper::Error>(service_fn(param_example)) |
| 93 | + } |
| 94 | + })); |
| 95 | + |
| 96 | + println!("Listening on http://{}", addr); |
| 97 | + |
| 98 | + server.await?; |
94 | 99 |
|
95 |
| - hyper::rt::run(server); |
| 100 | + Ok(()) |
96 | 101 | }
|
0 commit comments