mirror of https://github.com/rwf2/Rocket.git
Update remaining examples for async.
This commit is contained in:
parent
560f0977d3
commit
1f0577bfc5
|
@ -6,6 +6,7 @@ edition = "2018"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
futures-preview = "0.3.0-alpha.18"
|
||||||
rocket = { path = "../../core/lib" }
|
rocket = { path = "../../core/lib" }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
|
@ -5,10 +5,13 @@
|
||||||
|
|
||||||
#[cfg(test)] mod tests;
|
#[cfg(test)] mod tests;
|
||||||
|
|
||||||
use std::io::{self, Read};
|
use std::io;
|
||||||
|
|
||||||
|
use futures::io::AsyncReadExt as _;
|
||||||
|
|
||||||
use rocket::{Request, data::Data};
|
use rocket::{Request, data::Data};
|
||||||
use rocket::response::{Debug, content::{Json, Html}};
|
use rocket::response::{Debug, content::{Json, Html}};
|
||||||
|
use rocket::AsyncReadExt as _;
|
||||||
|
|
||||||
// NOTE: This example explicitly uses the `Json` type from `response::content`
|
// NOTE: This example explicitly uses the `Json` type from `response::content`
|
||||||
// for demonstration purposes. In a real application, _always_ prefer to use
|
// for demonstration purposes. In a real application, _always_ prefer to use
|
||||||
|
@ -38,9 +41,10 @@ fn get_hello(name: String, age: u8) -> Json<String> {
|
||||||
// In a real application, we wouldn't use `serde_json` directly; instead, we'd
|
// In a real application, we wouldn't use `serde_json` directly; instead, we'd
|
||||||
// use `contrib::Json` to automatically serialize a type into JSON.
|
// use `contrib::Json` to automatically serialize a type into JSON.
|
||||||
#[post("/<age>", format = "plain", data = "<name_data>")]
|
#[post("/<age>", format = "plain", data = "<name_data>")]
|
||||||
fn post_hello(age: u8, name_data: Data) -> Result<Json<String>, Debug<io::Error>> {
|
async fn post_hello(age: u8, name_data: Data) -> Result<Json<String>, Debug<io::Error>> {
|
||||||
let mut name = String::with_capacity(32);
|
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, };
|
let person = Person { name: name, age: age, };
|
||||||
// NOTE: In a real application, we'd use `rocket_contrib::json::Json`.
|
// NOTE: In a real application, we'd use `rocket_contrib::json::Json`.
|
||||||
Ok(Json(serde_json::to_string(&person).expect("valid JSON")))
|
Ok(Json(serde_json::to_string(&person).expect("valid JSON")))
|
||||||
|
|
|
@ -7,3 +7,5 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { path = "../../core/lib" }
|
rocket = { path = "../../core/lib" }
|
||||||
|
tokio = "0.2.0-alpha.2"
|
||||||
|
futures-tokio-compat = { git = "https://github.com/Nemo157/futures-tokio-compat" }
|
||||||
|
|
|
@ -3,51 +3,57 @@ extern crate rocket;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use std::{io, env};
|
use std::env;
|
||||||
use std::fs::File;
|
|
||||||
|
use futures_tokio_compat::Compat as TokioCompat;
|
||||||
|
use tokio::fs::File;
|
||||||
|
|
||||||
use rocket::{Request, Handler, Route, Data, Catcher, try_outcome};
|
use rocket::{Request, Handler, Route, Data, Catcher, try_outcome};
|
||||||
use rocket::http::{Status, RawStr};
|
use rocket::http::{Status, RawStr};
|
||||||
use rocket::response::{self, Responder, status::Custom};
|
use rocket::response::{self, Responder, status::Custom};
|
||||||
use rocket::handler::Outcome;
|
use rocket::handler::{Outcome, HandlerFuture};
|
||||||
use rocket::outcome::IntoOutcome;
|
use rocket::outcome::IntoOutcome;
|
||||||
use rocket::http::Method::*;
|
use rocket::http::Method::*;
|
||||||
|
|
||||||
fn forward<'r>(_req: &'r Request, data: Data) -> Outcome<'r> {
|
fn forward<'r>(_req: &'r Request, data: Data) -> HandlerFuture<'r> {
|
||||||
Outcome::forward(data)
|
Box::pin(async move { Outcome::forward(data) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hi<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
|
fn hi<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> {
|
||||||
Outcome::from(req, "Hello!")
|
Box::pin(async move { Outcome::from(req, "Hello!").await })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> {
|
fn name<'a>(req: &'a Request, _: Data) -> HandlerFuture<'a> {
|
||||||
|
Box::pin(async move {
|
||||||
let param = req.get_param::<&'a RawStr>(0)
|
let param = req.get_param::<&'a RawStr>(0)
|
||||||
.and_then(|res| res.ok())
|
.and_then(|res| res.ok())
|
||||||
.unwrap_or("unnamed".into());
|
.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> {
|
fn echo_url<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> {
|
||||||
|
Box::pin(async move {
|
||||||
let param_outcome = req.get_param::<&RawStr>(1)
|
let param_outcome = req.get_param::<&RawStr>(1)
|
||||||
.and_then(|res| res.ok())
|
.and_then(|res| res.ok())
|
||||||
.into_outcome(Status::BadRequest);
|
.into_outcome(Status::BadRequest);
|
||||||
|
|
||||||
let param = try_outcome!(param_outcome);
|
let param = try_outcome!(param_outcome);
|
||||||
Outcome::try_from(req, RawStr::from_str(param).url_decode())
|
Outcome::try_from(req, RawStr::from_str(param).url_decode()).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
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()) {
|
if !req.content_type().map_or(false, |ct| ct.is_plain()) {
|
||||||
println!(" => Content-Type of upload must be text/plain. Ignoring.");
|
println!(" => Content-Type of upload must be text/plain. Ignoring.");
|
||||||
return Outcome::failure(Status::BadRequest);
|
return Outcome::failure(Status::BadRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = File::create(env::temp_dir().join("upload.txt"));
|
let file = File::create(env::temp_dir().join("upload.txt")).await;
|
||||||
if let Ok(mut file) = file {
|
if let Ok(file) = file {
|
||||||
if let Ok(n) = io::copy(&mut data.open(), &mut file) {
|
if let Ok(n) = data.stream_to(TokioCompat::new(file)).await {
|
||||||
return Outcome::from(req, format!("OK: {} bytes uploaded.", n));
|
return Outcome::from(req, format!("OK: {} bytes uploaded.", n)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(" => Failed copying.");
|
println!(" => Failed copying.");
|
||||||
|
@ -56,13 +62,14 @@ fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
||||||
println!(" => Couldn't open file: {:?}", file.unwrap_err());
|
println!(" => Couldn't open file: {:?}", file.unwrap_err());
|
||||||
Outcome::failure(Status::InternalServerError)
|
Outcome::failure(Status::InternalServerError)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_upload<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
|
fn get_upload<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> {
|
||||||
Outcome::from(req, File::open(env::temp_dir().join("upload.txt")).ok())
|
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()));
|
let res = Custom(Status::NotFound, format!("Couldn't find: {}", req.uri()));
|
||||||
res.respond_to(req)
|
res.respond_to(req)
|
||||||
}
|
}
|
||||||
|
@ -79,13 +86,16 @@ impl CustomHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler for CustomHandler {
|
impl Handler for CustomHandler {
|
||||||
fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> {
|
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)
|
let id_outcome = req.get_param::<&RawStr>(0)
|
||||||
.and_then(|res| res.ok())
|
.and_then(|res| res.ok())
|
||||||
.or_forward(data);
|
.or_forward(data);
|
||||||
|
|
||||||
let id = try_outcome!(id_outcome);
|
let id = try_outcome!(id_outcome);
|
||||||
Outcome::from(req, format!("{} - {}", self.data, id))
|
Outcome::from(req, format!("{} - {}", self_data, id)).await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,8 @@ fn test_echo() {
|
||||||
test(&uri, ContentType::Plain, Status::Ok, "echo this text".into());
|
test(&uri, ContentType::Plain, Status::Ok, "echo this text".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[rocket::async_test]
|
||||||
fn test_upload() {
|
async fn test_upload() {
|
||||||
let client = Client::new(rocket()).unwrap();
|
let client = Client::new(rocket()).unwrap();
|
||||||
let expected_body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
|
let expected_body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
|
||||||
sed do eiusmod tempor incididunt ut labore et dolore \
|
sed do eiusmod tempor incididunt ut labore et dolore \
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod paste_id;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use rocket::Data;
|
use rocket::Data;
|
||||||
use rocket::response::{content, Debug};
|
use rocket::response::{content, Debug};
|
||||||
|
@ -18,12 +18,12 @@ const HOST: &str = "http://localhost:8000";
|
||||||
const ID_LENGTH: usize = 3;
|
const ID_LENGTH: usize = 3;
|
||||||
|
|
||||||
#[post("/", data = "<paste>")]
|
#[post("/", data = "<paste>")]
|
||||||
fn upload(paste: Data) -> Result<String, Debug<io::Error>> {
|
async fn upload(paste: Data) -> Result<String, Debug<io::Error>> {
|
||||||
let id = PasteID::new(ID_LENGTH);
|
let id = PasteID::new(ID_LENGTH);
|
||||||
let filename = format!("upload/{id}", id = id);
|
let filename = format!("upload/{id}", id = id);
|
||||||
let url = format!("{host}/{id}\n", host = HOST, 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)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,8 @@ use std::{io, env};
|
||||||
use rocket::{Data, response::Debug};
|
use rocket::{Data, response::Debug};
|
||||||
|
|
||||||
#[post("/upload", format = "plain", data = "<data>")]
|
#[post("/upload", format = "plain", data = "<data>")]
|
||||||
fn upload(data: Data) -> Result<String, Debug<io::Error>> {
|
async fn upload(data: Data) -> Result<String, Debug<io::Error>> {
|
||||||
data.stream_to_file(env::temp_dir().join("upload.txt"))
|
Ok(data.stream_to_file(env::temp_dir().join("upload.txt")).await?.to_string())
|
||||||
.map(|n| n.to_string())
|
|
||||||
.map_err(Debug)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
|
|
|
@ -7,3 +7,6 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { path = "../../core/lib" }
|
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" }
|
||||||
|
|
|
@ -6,22 +6,27 @@
|
||||||
|
|
||||||
use rocket::response::{content, Stream};
|
use rocket::response::{content, Stream};
|
||||||
|
|
||||||
use std::io::{repeat, Repeat, Read, Take};
|
use std::io::repeat;
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
type LimitedRepeat = Take<Repeat>;
|
use tokio::fs::File;
|
||||||
|
use futures_tokio_compat::Compat as TokioCompat;
|
||||||
|
|
||||||
|
use rocket::AsyncReadExt as _;
|
||||||
|
|
||||||
|
//type LimitedRepeat = Take<Repeat>;
|
||||||
|
type LimitedRepeat = Box<dyn futures::io::AsyncRead + Send + Unpin>;
|
||||||
|
|
||||||
// Generate this file using: head -c BYTES /dev/random > big_file.dat
|
// Generate this file using: head -c BYTES /dev/random > big_file.dat
|
||||||
const FILENAME: &str = "big_file.dat";
|
const FILENAME: &str = "big_file.dat";
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn root() -> content::Plain<Stream<LimitedRepeat>> {
|
fn root() -> content::Plain<Stream<LimitedRepeat>> {
|
||||||
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")]
|
#[get("/big_file")]
|
||||||
fn file() -> Option<Stream<File>> {
|
async fn file() -> Option<Stream<TokioCompat<File>>> {
|
||||||
File::open(FILENAME).map(|file| Stream::from(file)).ok()
|
File::open(FILENAME).await.map(|file| Stream::from(TokioCompat::new(file))).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rocket() -> rocket::Rocket {
|
fn rocket() -> rocket::Rocket {
|
||||||
|
|
Loading…
Reference in New Issue