Update and fix guide tests.

This commit is contained in:
Jeb Rosen 2020-07-11 09:41:53 -07:00
parent 08b34e8263
commit 2c82b3e1d5
9 changed files with 74 additions and 72 deletions

View File

@ -268,13 +268,13 @@ Here's our version (in `src/main.rs`):
# fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
# }
use std::path::Path;
use std::path::PathBuf;
use rocket::Data;
use rocket::response::Debug;
#[post("/", data = "<paste>")]
fn upload(paste: Data) -> Result<String, Debug<std::io::Error>> {
async fn upload(paste: Data) -> Result<String, Debug<std::io::Error>> {
let id = PasteId::new(3);
let filename = format!("upload/{id}", id = id);
let url = format!("{host}/{id}\n", host = "http://localhost:8000", id = id);

View File

@ -259,11 +259,14 @@ stalls, or sometimes even deadlocks can occur.
`tokio::task::spawn_blocking`:
```rust
# #[macro_use] extern crate rocket;
use rocket::tokio::task::spawn_blocking;
#[get("/blocking_task")]
async fn blocking_task() -> String {
async fn blocking_task() -> Vec<u8> {
// In a real application, we would use rocket::response::NamedFile
tokio::task::spawn_blocking(|| {
std::fs::read_file("data.txt")
}).await
spawn_blocking(|| {
std::fs::read("data.txt").expect("failed to read file")
}).await.unwrap()
}
```

View File

@ -166,8 +166,8 @@ this, a safe and secure static file server can be implemented in 4 lines:
use rocket::response::NamedFile;
#[get("/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).ok()
async fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).await.ok()
}
```
@ -1058,8 +1058,8 @@ use rocket::Data;
use rocket::response::Debug;
#[post("/upload", format = "plain", data = "<data>")]
fn upload(data: Data) -> Result<String, Debug<std::io::Error>> {
Ok(data.stream_to_file("/tmp/upload.txt").map(|n| n.to_string())?)
async fn upload(data: Data) -> Result<String, Debug<std::io::Error>> {
Ok(data.stream_to_file("/tmp/upload.txt").await.map(|n| n.to_string())?)
}
```
@ -1083,24 +1083,18 @@ returned. The handler above is complete. It really is that simple! See the
Rocket makes it easy to use `async/await` in routes.
```rust
#[get("/weather")]
async fn weather() -> String {
let response = reqwest::get("https://www.example.com").await;
response.text().await
# #[macro_use] extern crate rocket;
use rocket::tokio::time::{delay_for, Duration};
#[get("/delay/<seconds>")]
async fn delay(seconds: u64) -> String {
delay_for(Duration::from_secs(seconds)).await;
format!("Waited for {} seconds", seconds)
}
```
First, notice that the route function is an `async fn`. This enables
the use of `await` inside the handler. `reqwest` is an asynchronous
HTTP client, so we must `await` the response. Finally, we call
the `text()` function, which asynchronously downloads the full
response data from the server.
! warning: You should _always_ set limits when reading incoming data.
Just like with client input, you should usually limit the amount
of data read from external APIs. The exact method will depend
on the library you are using to make requests.
the use of `await` inside the handler. `delay_for` is an asynchronous
function, so we must `await` it.
## Error Catchers

View File

@ -185,14 +185,15 @@ use rocket::response::{self, Response, Responder};
use rocket::http::ContentType;
# struct String(std::string::String);
impl<'a> Responder<'a> for String {
fn respond_to(self, _: &Request) -> response::Result<'a> {
#[rocket::async_trait]
impl<'r> Responder<'r, 'static> for String {
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
Response::build()
.header(ContentType::Plain)
# /*
.sized_body(Cursor::new(self))
.sized_body(self.len(), Cursor::new(self))
# */
# .sized_body(Cursor::new(self.0))
# .sized_body(self.0.len(), Cursor::new(self.0))
.ok()
}
}
@ -230,8 +231,8 @@ found and a `404` when a file is not found in just 4, idiomatic lines:
use rocket::response::NamedFile;
#[get("/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).ok()
async fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).await.ok()
}
```
@ -256,9 +257,9 @@ use rocket::response::NamedFile;
use rocket::response::status::NotFound;
#[get("/<file..>")]
fn files(file: PathBuf) -> Result<NamedFile, NotFound<String>> {
async fn files(file: PathBuf) -> Result<NamedFile, NotFound<String>> {
let path = Path::new("static/").join(file);
NamedFile::open(&path).map_err(|e| NotFound(e.to_string()))
NamedFile::open(&path).await.map_err(|e| NotFound(e.to_string()))
}
```
@ -306,14 +307,17 @@ example, to stream from a local Unix stream, we might write:
# #[macro_use] extern crate rocket;
# fn main() {}
# #[cfg(unix)]
# mod test {
use std::os::unix::net::UnixStream;
use std::net::SocketAddr;
use rocket::response::{Stream, Debug};
use rocket::tokio::net::TcpStream;
#[get("/stream")]
fn stream() -> Result<Stream<UnixStream>, Debug<std::io::Error>> {
Ok(UnixStream::connect("/path/to/my/socket").map(Stream::from)?)
async fn stream() -> Result<Stream<TcpStream>, Debug<std::io::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 9999));
Ok(TcpStream::connect(addr).await.map(Stream::from)?)
}
# }
```

View File

@ -127,11 +127,12 @@ use rocket::request::{self, Request, FromRequest};
# struct T;
# struct HitCount { count: AtomicUsize }
# type ErrorType = ();
#[rocket::async_trait]
impl<'a, 'r> FromRequest<'a, 'r> for T {
type Error = ErrorType;
fn from_request(req: &'a Request<'r>) -> request::Outcome<T, Self::Error> {
let hit_count_state = try_outcome!(req.guard::<State<HitCount>>());
async fn from_request(req: &'a Request<'r>) -> request::Outcome<T, Self::Error> {
let hit_count_state = try_outcome!(req.guard::<State<HitCount>>().await);
let current_count = hit_count_state.count.load(Ordering::Relaxed);
/* ... */
# request::Outcome::Success(T)
@ -171,10 +172,11 @@ static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
struct RequestId(pub usize);
/// Returns the current request's ID, assigning one only as necessary.
#[rocket::async_trait]
impl<'a, 'r> FromRequest<'a, 'r> for &'a RequestId {
type Error = ();
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
async fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
// The closure passed to `local_cache` will be executed at most once per
// request: the first time the `RequestId` guard is used. If it is
// requested again, `local_cache` will return the same value.

View File

@ -49,8 +49,8 @@ example, the following snippet attached two fairings, `req_fairing` and
`res_fairing`, to a new Rocket instance:
```rust
# let req_fairing = rocket::fairing::AdHoc::on_request("example", |_, _| {});
# let res_fairing = rocket::fairing::AdHoc::on_response("example", |_, _| {});
# let req_fairing = rocket::fairing::AdHoc::on_request("example", |_, _| Box::pin(async {}));
# let res_fairing = rocket::fairing::AdHoc::on_response("example", |_, _| Box::pin(async {}));
# if false {
rocket::ignite()
@ -157,6 +157,7 @@ struct Counter {
post: AtomicUsize,
}
#[rocket::async_trait]
impl Fairing for Counter {
// This is a request and response fairing named "GET/POST Counter".
fn info(&self) -> Info {
@ -167,7 +168,7 @@ impl Fairing for Counter {
}
// Increment the counter for `GET` and `POST` requests.
fn on_request(&self, request: &mut Request, _: &Data) {
async fn on_request<'a>(&'a self, request: &'a mut Request<'_>, _: &'a Data) {
match request.method() {
Method::Get => self.get.fetch_add(1, Ordering::Relaxed),
Method::Post => self.post.fetch_add(1, Ordering::Relaxed),
@ -175,24 +176,22 @@ impl Fairing for Counter {
};
}
fn on_response<'a>(&'a self, request: &'a Request<'_>, response: &'a mut Response<'_>) -> BoxFuture<'a, ()> {
Box::pin(async move {
// Don't change a successful user's response, ever.
if response.status() != Status::NotFound {
return
}
async fn on_response<'a>(&'a self, request: &'a Request<'_>, response: &'a mut Response<'_>) {
// Don't change a successful user's response, ever.
if response.status() != Status::NotFound {
return
}
// Rewrite the response to return the current counts.
if request.method() == Method::Get && request.uri().path() == "/counts" {
let get_count = self.get.load(Ordering::Relaxed);
let post_count = self.post.load(Ordering::Relaxed);
let body = format!("Get: {}\nPost: {}", get_count, post_count);
// Rewrite the response to return the current counts.
if request.method() == Method::Get && request.uri().path() == "/counts" {
let get_count = self.get.load(Ordering::Relaxed);
let post_count = self.post.load(Ordering::Relaxed);
let body = format!("Get: {}\nPost: {}", get_count, post_count);
response.set_status(Status::Ok);
response.set_header(ContentType::Plain);
response.set_sized_body(Cursor::new(body));
}
})
response.set_status(Status::Ok);
response.set_header(ContentType::Plain);
response.set_sized_body(body.len(), Cursor::new(body));
}
}
}
```

View File

@ -22,7 +22,7 @@ instance. Usage is straightforward:
2. Construct a `Client` using the `Rocket` instance.
```rust
# use rocket::local::Client;
# use rocket::local::blocking::Client;
# let rocket = rocket::ignite();
let client = Client::new(rocket).expect("valid rocket instance");
# let _ = client;
@ -31,7 +31,7 @@ instance. Usage is straightforward:
3. Construct requests using the `Client` instance.
```rust
# use rocket::local::Client;
# use rocket::local::blocking::Client;
# let rocket = rocket::ignite();
# let client = Client::new(rocket).unwrap();
let req = client.get("/");
@ -41,7 +41,7 @@ instance. Usage is straightforward:
4. Dispatch the request to retrieve the response.
```rust
# use rocket::local::Client;
# use rocket::local::blocking::Client;
# let rocket = rocket::ignite();
# let client = Client::new(rocket).unwrap();
# let req = client.get("/");
@ -94,11 +94,11 @@ These methods are typically used in combination with the `assert_eq!` or
# Response::build()
# .header(ContentType::Plain)
# .header(Header::new("X-Special", ""))
# .sized_body(Cursor::new("Expected Body"))
# .sized_body("Expected Body".len(), Cursor::new("Expected Body"))
# .finalize()
# }
use rocket::local::Client;
use rocket::local::blocking::Client;
use rocket::http::{ContentType, Status};
let rocket = rocket::ignite().mount("/", routes![hello]);
@ -108,7 +108,7 @@ let mut response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.content_type(), Some(ContentType::Plain));
assert!(response.headers().get_one("X-Special").is_some());
assert_eq!(response.body_string(), Some("Expected Body".into()));
assert_eq!(response.into_string(), Some("Expected Body".into()));
```
## Testing "Hello, world!"
@ -173,7 +173,7 @@ testing: we _want_ our tests to panic when something goes wrong.
```rust
# fn rocket() -> rocket::Rocket { rocket::ignite() }
# use rocket::local::Client;
# use rocket::local::blocking::Client;
let client = Client::new(rocket()).expect("valid rocket instance");
```
@ -183,7 +183,7 @@ application's response:
```rust
# fn rocket() -> rocket::Rocket { rocket::ignite() }
# use rocket::local::Client;
# use rocket::local::blocking::Client;
# let client = Client::new(rocket()).expect("valid rocket instance");
let mut response = client.get("/").dispatch();
```
@ -203,7 +203,7 @@ We do this by checking the `Response` object directly:
# #[get("/")]
# fn hello() -> &'static str { "Hello, world!" }
# use rocket::local::Client;
# use rocket::local::blocking::Client;
use rocket::http::{ContentType, Status};
#
# let rocket = rocket::ignite().mount("/", routes![hello]);

View File

@ -201,15 +201,15 @@ use rocket::fairing::AdHoc;
struct AssetsDir(String);
#[get("/<asset..>")]
fn assets(asset: PathBuf, assets_dir: State<AssetsDir>) -> Option<NamedFile> {
NamedFile::open(Path::new(&assets_dir.0).join(asset)).ok()
async fn assets(asset: PathBuf, assets_dir: State<'_, AssetsDir>) -> Option<NamedFile> {
NamedFile::open(Path::new(&assets_dir.0).join(asset)).await.ok()
}
fn main() {
# if false {
rocket::ignite()
.mount("/", routes![assets])
.attach(AdHoc::on_attach("Assets Config", |mut rocket| {
.attach(AdHoc::on_attach("Assets Config", |mut rocket| async {
let assets_dir = rocket.config().await
.get_str("assets_dir")
.unwrap_or("assets/")

View File

@ -1,3 +1,3 @@
// #![feature(external_doc)]
#![feature(external_doc)]
// rocket::rocket_internal_guide_tests!("../guide/*.md");
rocket::rocket_internal_guide_tests!("../guide/*.md");