From 1f0577bfc53a8c4fb0108e6e212133c1e7271c6d Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Tue, 27 Aug 2019 16:40:23 -0700 Subject: [PATCH] Update remaining examples for async. --- examples/content_types/Cargo.toml | 1 + examples/content_types/src/main.rs | 10 ++- examples/manual_routes/Cargo.toml | 2 + examples/manual_routes/src/main.rs | 98 ++++++++++++++++------------- examples/manual_routes/src/tests.rs | 4 +- examples/pastebin/src/main.rs | 6 +- examples/raw_upload/src/main.rs | 6 +- examples/stream/Cargo.toml | 3 + examples/stream/src/main.rs | 17 +++-- 9 files changed, 85 insertions(+), 62 deletions(-) diff --git a/examples/content_types/Cargo.toml b/examples/content_types/Cargo.toml index d2d05996..385def3b 100644 --- a/examples/content_types/Cargo.toml +++ b/examples/content_types/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" publish = false [dependencies] +futures-preview = "0.3.0-alpha.18" rocket = { path = "../../core/lib" } serde = "1.0" serde_json = "1.0" diff --git a/examples/content_types/src/main.rs b/examples/content_types/src/main.rs index 83b9eda7..7c012231 100644 --- a/examples/content_types/src/main.rs +++ b/examples/content_types/src/main.rs @@ -5,10 +5,13 @@ #[cfg(test)] mod tests; -use std::io::{self, Read}; +use std::io; + +use futures::io::AsyncReadExt as _; use rocket::{Request, data::Data}; use rocket::response::{Debug, content::{Json, Html}}; +use rocket::AsyncReadExt as _; // NOTE: This example explicitly uses the `Json` type from `response::content` // for demonstration purposes. In a real application, _always_ prefer to use @@ -38,9 +41,10 @@ fn get_hello(name: String, age: u8) -> Json { // In a real application, we wouldn't use `serde_json` directly; instead, we'd // use `contrib::Json` to automatically serialize a type into JSON. #[post("/", format = "plain", data = "")] -fn post_hello(age: u8, name_data: Data) -> Result, Debug> { +async fn post_hello(age: u8, name_data: Data) -> Result, Debug> { let mut name = String::with_capacity(32); - name_data.open().take(32).read_to_string(&mut name)?; + let mut stream = name_data.open().take(32); + stream.read_to_string(&mut name).await?; let person = Person { name: name, age: age, }; // NOTE: In a real application, we'd use `rocket_contrib::json::Json`. Ok(Json(serde_json::to_string(&person).expect("valid JSON"))) diff --git a/examples/manual_routes/Cargo.toml b/examples/manual_routes/Cargo.toml index 8519ae94..093f61e6 100644 --- a/examples/manual_routes/Cargo.toml +++ b/examples/manual_routes/Cargo.toml @@ -7,3 +7,5 @@ publish = false [dependencies] rocket = { path = "../../core/lib" } +tokio = "0.2.0-alpha.2" +futures-tokio-compat = { git = "https://github.com/Nemo157/futures-tokio-compat" } diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index 9d542663..3ac19a6e 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -3,66 +3,73 @@ extern crate rocket; #[cfg(test)] mod tests; -use std::{io, env}; -use std::fs::File; +use std::env; + +use futures_tokio_compat::Compat as TokioCompat; +use tokio::fs::File; use rocket::{Request, Handler, Route, Data, Catcher, try_outcome}; use rocket::http::{Status, RawStr}; use rocket::response::{self, Responder, status::Custom}; -use rocket::handler::Outcome; +use rocket::handler::{Outcome, HandlerFuture}; use rocket::outcome::IntoOutcome; use rocket::http::Method::*; -fn forward<'r>(_req: &'r Request, data: Data) -> Outcome<'r> { - Outcome::forward(data) +fn forward<'r>(_req: &'r Request, data: Data) -> HandlerFuture<'r> { + Box::pin(async move { Outcome::forward(data) }) } -fn hi<'r>(req: &'r Request, _: Data) -> Outcome<'r> { - Outcome::from(req, "Hello!") +fn hi<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { + Box::pin(async move { Outcome::from(req, "Hello!").await }) } -fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> { - let param = req.get_param::<&'a RawStr>(0) - .and_then(|res| res.ok()) - .unwrap_or("unnamed".into()); +fn name<'a>(req: &'a Request, _: Data) -> HandlerFuture<'a> { + Box::pin(async move { + let param = req.get_param::<&'a RawStr>(0) + .and_then(|res| res.ok()) + .unwrap_or("unnamed".into()); - Outcome::from(req, param.as_str()) + Outcome::from(req, param.as_str()).await + }) } -fn echo_url<'r>(req: &'r Request, _: Data) -> Outcome<'r> { - let param_outcome = req.get_param::<&RawStr>(1) - .and_then(|res| res.ok()) - .into_outcome(Status::BadRequest); - - let param = try_outcome!(param_outcome); - Outcome::try_from(req, RawStr::from_str(param).url_decode()) +fn echo_url<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { + Box::pin(async move { + let param_outcome = req.get_param::<&RawStr>(1) + .and_then(|res| res.ok()) + .into_outcome(Status::BadRequest); + let param = try_outcome!(param_outcome); + Outcome::try_from(req, RawStr::from_str(param).url_decode()).await + }) } -fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> { - if !req.content_type().map_or(false, |ct| ct.is_plain()) { - println!(" => Content-Type of upload must be text/plain. Ignoring."); - return Outcome::failure(Status::BadRequest); - } - - let file = File::create(env::temp_dir().join("upload.txt")); - if let Ok(mut file) = file { - if let Ok(n) = io::copy(&mut data.open(), &mut file) { - return Outcome::from(req, format!("OK: {} bytes uploaded.", n)); +fn upload<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { + Box::pin(async move { + if !req.content_type().map_or(false, |ct| ct.is_plain()) { + println!(" => Content-Type of upload must be text/plain. Ignoring."); + return Outcome::failure(Status::BadRequest); } - println!(" => Failed copying."); - Outcome::failure(Status::InternalServerError) - } else { - println!(" => Couldn't open file: {:?}", file.unwrap_err()); - Outcome::failure(Status::InternalServerError) - } + let file = File::create(env::temp_dir().join("upload.txt")).await; + if let Ok(file) = file { + if let Ok(n) = data.stream_to(TokioCompat::new(file)).await { + return Outcome::from(req, format!("OK: {} bytes uploaded.", n)).await; + } + + println!(" => Failed copying."); + Outcome::failure(Status::InternalServerError) + } else { + println!(" => Couldn't open file: {:?}", file.unwrap_err()); + Outcome::failure(Status::InternalServerError) + } + }) } -fn get_upload<'r>(req: &'r Request, _: Data) -> Outcome<'r> { - Outcome::from(req, File::open(env::temp_dir().join("upload.txt")).ok()) +fn get_upload<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { + Outcome::from(req, std::fs::File::open(env::temp_dir().join("upload.txt")).ok()) } -fn not_found_handler<'r>(req: &'r Request) -> response::Result<'r> { +fn not_found_handler<'r>(req: &'r Request) -> response::ResultFuture<'r> { let res = Custom(Status::NotFound, format!("Couldn't find: {}", req.uri())); res.respond_to(req) } @@ -79,13 +86,16 @@ impl CustomHandler { } impl Handler for CustomHandler { - fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> { - let id_outcome = req.get_param::<&RawStr>(0) - .and_then(|res| res.ok()) - .or_forward(data); + fn handle<'r>(&self, req: &'r Request, data: Data) -> HandlerFuture<'r> { + let self_data = self.data; + Box::pin(async move { + let id_outcome = req.get_param::<&RawStr>(0) + .and_then(|res| res.ok()) + .or_forward(data); - let id = try_outcome!(id_outcome); - Outcome::from(req, format!("{} - {}", self.data, id)) + let id = try_outcome!(id_outcome); + Outcome::from(req, format!("{} - {}", self_data, id)).await + }) } } diff --git a/examples/manual_routes/src/tests.rs b/examples/manual_routes/src/tests.rs index 12f245fc..77af31a8 100644 --- a/examples/manual_routes/src/tests.rs +++ b/examples/manual_routes/src/tests.rs @@ -30,8 +30,8 @@ fn test_echo() { test(&uri, ContentType::Plain, Status::Ok, "echo this text".into()); } -#[test] -fn test_upload() { +#[rocket::async_test] +async fn test_upload() { let client = Client::new(rocket()).unwrap(); let expected_body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore \ diff --git a/examples/pastebin/src/main.rs b/examples/pastebin/src/main.rs index c561c000..3be264f4 100644 --- a/examples/pastebin/src/main.rs +++ b/examples/pastebin/src/main.rs @@ -7,7 +7,7 @@ mod paste_id; use std::io; use std::fs::File; -use std::path::Path; +use std::path::PathBuf; use rocket::Data; use rocket::response::{content, Debug}; @@ -18,12 +18,12 @@ const HOST: &str = "http://localhost:8000"; const ID_LENGTH: usize = 3; #[post("/", data = "")] -fn upload(paste: Data) -> Result> { +async fn upload(paste: Data) -> Result> { let id = PasteID::new(ID_LENGTH); let filename = format!("upload/{id}", id = id); let url = format!("{host}/{id}\n", host = HOST, id = id); - paste.stream_to_file(Path::new(&filename))?; + paste.stream_to_file(PathBuf::from(filename)).await?; Ok(url) } diff --git a/examples/raw_upload/src/main.rs b/examples/raw_upload/src/main.rs index 9ea67c59..22b25462 100644 --- a/examples/raw_upload/src/main.rs +++ b/examples/raw_upload/src/main.rs @@ -8,10 +8,8 @@ use std::{io, env}; use rocket::{Data, response::Debug}; #[post("/upload", format = "plain", data = "")] -fn upload(data: Data) -> Result> { - data.stream_to_file(env::temp_dir().join("upload.txt")) - .map(|n| n.to_string()) - .map_err(Debug) +async fn upload(data: Data) -> Result> { + Ok(data.stream_to_file(env::temp_dir().join("upload.txt")).await?.to_string()) } #[get("/")] diff --git a/examples/stream/Cargo.toml b/examples/stream/Cargo.toml index 96792d90..55b2b636 100644 --- a/examples/stream/Cargo.toml +++ b/examples/stream/Cargo.toml @@ -7,3 +7,6 @@ publish = false [dependencies] rocket = { path = "../../core/lib" } +futures-preview = "0.3.0-alpha.18" +tokio = "0.2.0-alpha.2" +futures-tokio-compat = { git = "https://github.com/Nemo157/futures-tokio-compat" } diff --git a/examples/stream/src/main.rs b/examples/stream/src/main.rs index 20499be4..c96acf0d 100644 --- a/examples/stream/src/main.rs +++ b/examples/stream/src/main.rs @@ -6,22 +6,27 @@ use rocket::response::{content, Stream}; -use std::io::{repeat, Repeat, Read, Take}; -use std::fs::File; +use std::io::repeat; -type LimitedRepeat = Take; +use tokio::fs::File; +use futures_tokio_compat::Compat as TokioCompat; + +use rocket::AsyncReadExt as _; + +//type LimitedRepeat = Take; +type LimitedRepeat = Box; // Generate this file using: head -c BYTES /dev/random > big_file.dat const FILENAME: &str = "big_file.dat"; #[get("/")] fn root() -> content::Plain> { - content::Plain(Stream::from(repeat('a' as u8).take(25000))) + content::Plain(Stream::from(Box::new(repeat('a' as u8).take(25000)) as Box<_>)) } #[get("/big_file")] -fn file() -> Option> { - File::open(FILENAME).map(|file| Stream::from(file)).ok() +async fn file() -> Option>> { + File::open(FILENAME).await.map(|file| Stream::from(TokioCompat::new(file))).ok() } fn rocket() -> rocket::Rocket {