Skip to content

Commit 35b3147

Browse files
oddgrdseanmonstar
authored andcommitted
fix: master hello-world, revert legacy guides
1 parent 8eb1b17 commit 35b3147

File tree

3 files changed

+162
-179
lines changed

3 files changed

+162
-179
lines changed

_guides/legacy/server/echo.md

Lines changed: 106 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Echo, echo, echo
33
---
44

5-
You already have a [Hello World server](../hello-world)? Excellent! Usually,
5+
You already have a [Hello World server](hello-world.md)? Excellent! Usually,
66
servers do more than just spit out the same body for every request. To
77
exercise several more parts of hyper, this guide will go through
88
building an echo server.
@@ -17,142 +17,138 @@ routing. We want to have a route explaining instructions on how to use
1717
our server, and another for receiving data. Oh, and we should also
1818
handle the case when someone asks for a route we don't know!
1919

20-
Before we get started we need to add some new imports:
20+
We're going to be using more of the [futures][future-crate] crate, so let's add
21+
that as a dependency:
22+
23+
```toml
24+
[dependencies]
25+
hyper = { version = "0.14", features = ["full"] }
26+
tokio = { version = "1", features = ["full"] }
27+
futures = "0.3"
28+
```
29+
30+
Then, we need to add some to our imports:
2131

2232
```rust
23-
use hyper::body::Frame;
33+
# extern crate hyper;
34+
# extern crate futures;
2435
use hyper::{Method, StatusCode};
25-
use http_body_util::{combinators::BoxBody, BodyExt};
2636
# fn main() {}
2737
```
2838

29-
Next, we need to make some changes to our `Service` function, but as you can see
30-
it's still just an async function that takes a `Request` and returns a `Response`
31-
future, and you can pass it to your server just like we did for the `hello` service.
32-
33-
Unlike our `hello` service where we didn't care about the request body and
34-
we always returned a single chunk of bytes containing our greeting, we're now
35-
going to want a bit more freedom in how we shape our response `Body`. To achieve
36-
this we will change the type of the `Body` in our `Response` to a boxed trait object.
37-
We only care that the response body implements the [Body](https://docs.rs/http-body/1.0.0-rc1/http_body/trait.Body.html) trait, that its data is `Bytes` and its error is a `hyper::Error`.
39+
And make some changes to your `Service`, such as returning a `Future` of a `Response`,
40+
since we may not have one ready immediately:
3841

3942
```rust
40-
# use bytes::Bytes;
41-
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
42-
# use hyper::{Method, Request, Response, StatusCode};
43-
async fn echo(
44-
req: Request<hyper::body::Incoming>,
45-
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
43+
# extern crate futures;
44+
# extern crate hyper;
45+
# use futures::future::{self, Future};
46+
# use hyper::{Body, Method, Request, Response, StatusCode};
47+
#
48+
async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
49+
let mut response = Response::new(Body::empty());
50+
4651
match (req.method(), req.uri().path()) {
47-
(&Method::GET, "/") => Ok(Response::new(full(
48-
"Try POSTing data to /echo",
49-
))),
52+
(&Method::GET, "/") => {
53+
*response.body_mut() = Body::from("Try POSTing data to /echo");
54+
},
5055
(&Method::POST, "/echo") => {
5156
// we'll be back
5257
},
53-
54-
// Return 404 Not Found for other routes.
5558
_ => {
56-
let mut not_found = Response::new(empty());
57-
*not_found.status_mut() = StatusCode::NOT_FOUND;
58-
Ok(not_found)
59-
}
60-
}
61-
}
62-
// We create some utility functions to make Empty and Full bodies
63-
// fit our broadened Response body type.
64-
fn empty() -> BoxBody<Bytes, hyper::Error> {
65-
Empty::<Bytes>::new()
66-
.map_err(|never| match never {})
67-
.boxed()
68-
}
69-
fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
70-
Full::new(chunk.into())
71-
.map_err(|never| match never {})
72-
.boxed()
59+
*response.status_mut() = StatusCode::NOT_FOUND;
60+
},
61+
};
62+
63+
Ok(response)
7364
}
7465
# fn main() {}
7566
```
7667

77-
We built a super simple routing table just by matching on the `method` and `path`
78-
of an incoming `Request`. If someone requests `GET /`, our service will let them
79-
know they should try our echo powers out. We also check for `POST /echo`, but
80-
currently don't do anything about it.
68+
We built a super simple routing table just by matching on the `method`
69+
and `path` of an incoming `Request`. If someone requests `GET /`, our
70+
service will let them know they should try our echo powers out. We also
71+
are checking for `POST /echo`, but currently don't do anything about it.
8172

82-
Our third rule catches any other method and path combination, and changes the
83-
`StatusCode` of the `Response`. The default status of a `Response` is HTTP’s
84-
`200 OK` (`StatusCode::OK`), which is correct for the other routes. But the third
85-
case will instead send back `404 Not Found`.
73+
Our third rule catches any other method and path combination, and
74+
changes the `StatusCode` of the `Response`. The default status of a
75+
`Response` is HTTP's `200 OK` (`StatusCode::OK`), which is correct for
76+
the other routes. But the third case will instead send back `404 Not
77+
Found`.
8678

8779
## Body Streams
8880

89-
Now let's get that echo in place. An HTTP body is a stream of
90-
`Frames`, each [Frame](https://docs.rs/http-body/1.0.0-rc1/http_body/struct.Frame.html)
91-
containing parts of the `Body` data or trailers. So rather than reading the entire `Body`
92-
into a buffer before sending our response, we can stream each frame as it arrives.
93-
We'll start with the simplest solution, and then make alterations exercising more complex
94-
things you can do with the `Body` streams.
81+
Now let's get that echo in place. We'll start with the simplest solution, and
82+
then make alterations exercising more complex things you can do with the
83+
`Body` streams.
9584

9685
First up, plain echo. Both the `Request` and the `Response` have body streams,
97-
and by default, you can easily pass the `Body` of the `Request` into a `Response`.
86+
and by default, you can easily just pass the `Body` of the `Request` into the
87+
`Response`.
9888

9989
```rust
100-
# use bytes::Bytes;
101-
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
102-
# use hyper::{Method, Request, Response, StatusCode};
103-
# async fn echo(
104-
# req: Request<hyper::body::Incoming>,
105-
# ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
106-
# match (req.method(), req.uri().path()) {
107-
// Inside the match from before
108-
(&Method::POST, "/echo") => Ok(Response::new(req.into_body().boxed())),
109-
# _ => unreachable!(),
110-
# }
90+
# extern crate hyper;
91+
# use hyper::{Body, Method, Request, Response};
92+
# fn echo(req: Request<Body>) -> Response<Body> {
93+
# let mut response = Response::default();
94+
# match (req.method(), req.uri().path()) {
95+
// inside that match from before
96+
(&Method::POST, "/echo") => {
97+
*response.body_mut() = req.into_body();
98+
},
99+
# _ => unreachable!(),
100+
# }
101+
# response
111102
# }
112103
# fn main() {}
113104
```
114105

115106
Running our server now will echo any data we `POST` to `/echo`. That was easy.
116107
What if we wanted to uppercase all the text? We could use a `map` on our streams.
117108

118-
## Body mapping
109+
### Body mapping
110+
111+
We're going to need a couple of extra imports, so let's add those to the top of the file:
112+
113+
```rust
114+
# extern crate futures;
115+
# extern crate hyper;
116+
use futures::TryStreamExt as _;
117+
# fn main() {}
118+
```
119119

120-
Every data `Frame` of our body stream is a chunk of bytes, which we can conveniently
121-
represent using the `Bytes` type from hyper. It can be easily converted into other
120+
A `Body` implements the `Stream` trait from futures, producing a bunch of
121+
`Bytes`s, as data comes in. `Bytes` is just a convenient type from hyper
122+
that represents a bunch of bytes. It can be easily converted into other
122123
typical containers of bytes.
123124

124-
Next, let's add a new `/echo/uppercase` route, mapping each byte in the data `Frame`s
125-
of our request body to uppercase, and returning the stream in our `Response`:
125+
Next, let's add a new `/echo/uppercase` route mapping the body to uppercase:
126126

127127
```rust
128-
# use bytes::Bytes;
129-
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
130-
# use hyper::body::Frame;
131-
# use hyper::{Method, Request, Response, StatusCode};
132-
# async fn echo(
133-
# req: Request<hyper::body::Incoming>,
134-
# ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
135-
# match (req.method(), req.uri().path()) {
128+
# extern crate hyper;
129+
# extern crate futures;
130+
# use futures::TryStreamExt as _;
131+
# use hyper::{Body, Method, Request, Response};
132+
# fn echo(req: Request<Body>) -> Response<Body> {
133+
# let mut response = Response::default();
134+
# match (req.method(), req.uri().path()) {
136135
// Yet another route inside our match block...
137136
(&Method::POST, "/echo/uppercase") => {
138-
// Map this body's frame to a different type
139-
let frame_stream = req.into_body().map_frame(|frame| {
140-
let frame = if let Some(data) = frame.into_data() {
141-
// Convert every byte in every Data frame to uppercase
142-
data.iter()
137+
// This is actually a new `futures::Stream`...
138+
let mapping = req
139+
.into_body()
140+
.map_ok(|chunk| {
141+
chunk.iter()
143142
.map(|byte| byte.to_ascii_uppercase())
144-
.collect::<Bytes>()
145-
} else {
146-
Bytes::new()
147-
};
143+
.collect::<Vec<u8>>()
144+
});
148145

149-
Frame::data(frame)
150-
});
151-
152-
Ok(Response::new(frame_stream.boxed()))
146+
// Use `Body::wrap_stream` to convert it to a `Body`...
147+
*response.body_mut() = Body::wrap_stream(mapping);
153148
},
154149
# _ => unreachable!(),
155150
# }
151+
# response
156152
# }
157153
# fn main() {}
158154
```
@@ -168,40 +164,38 @@ back to us? We can't really stream the data as it comes in, since we need to
168164
find the end before we can respond. To do this, we can explore how to easily
169165
collect the full body.
170166

171-
We want to collect the entire request body and map the result into our `reverse`
172-
function, then return the eventual result. If we import the `http_body_util::BodyExt`
173-
extension trait, we can call the `collect` method on our body, which will drive the
174-
stream to completion, collecting all the data and trailer frames into a `Collected` type.
175-
We can easily turn the `Collected` body into a single `Bytes` by calling its `into_bytes`
176-
method.
167+
In this case, we can't really generate a `Response` immediately. Instead, we
168+
must wait for the full request body to be received.
169+
170+
We want to concatenate the request body, and map the result into our `reverse` function, and return the eventual result. We can make use of the `hyper::body::to_bytes` utility function to make this easy.
177171

178172
```rust
179-
# use bytes::Bytes;
180-
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
181-
# use hyper::{Method, Request, Response, StatusCode};
182-
# async fn echo(
183-
# req: Request<hyper::body::Incoming>,
184-
# ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
185-
# match (req.method(), req.uri().path()) {
173+
# extern crate hyper;
174+
# use hyper::{Body, Method, Request, Response};
175+
# async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
176+
# let mut response = Response::default();
177+
# match (req.method(), req.uri().path()) {
186178
// Yet another route inside our match block...
187-
(&Method::POST, "/echo/reversed") => {
188-
// Await the whole body to be collected into a single `Bytes`...
189-
let whole_body = req.collect().await?.to_bytes();
179+
(&Method::POST, "/echo/reverse") => {
180+
// Await the full body to be concatenated into a single `Bytes`...
181+
let full_body = hyper::body::to_bytes(req.into_body()).await?;
190182

191-
// Iterate the whole body in reverse order and collect into a new Vec.
192-
let reversed_body = whole_body.iter()
183+
// Iterate the full body in reverse order and collect into a new Vec.
184+
let reversed = full_body.iter()
193185
.rev()
194186
.cloned()
195187
.collect::<Vec<u8>>();
196188

197-
Ok(Response::new(full(reversed_body)))
189+
*response.body_mut() = reversed.into();
198190
},
199191
# _ => unreachable!(),
200192
# }
193+
# Ok(response)
201194
# }
202195
# fn main() {}
203196
```
204197

205198
You can see a compiling [example here][example].
206199

207200
[example]: {{ site.examples_url }}/echo.rs
201+
[future-crate]: https://github.com/rust-lang-nursery/futures-rs

0 commit comments

Comments
 (0)