From 7eca445ff9934bf0c7d86b50bfb5b29a986cbfc2 Mon Sep 17 00:00:00 2001 From: Aaron Riekenberg Date: Mon, 4 Jun 2018 11:59:42 -0500 Subject: [PATCH] docs(examples): Update send_file example to use tokio-fs --- Cargo.toml | 1 + examples/README.md | 2 +- examples/send_file.rs | 112 +++++++++--------------------------------- 3 files changed, 26 insertions(+), 89 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac9e4a9f..cd4a5d82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ num_cpus = "1.0" pretty_env_logger = "0.2.0" spmc = "0.2" url = "1.0" +tokio-fs = "0.1" tokio-mockstream = "1.1.0" [features] diff --git a/examples/README.md b/examples/README.md index ecd36ae9..90a42523 100644 --- a/examples/README.md +++ b/examples/README.md @@ -14,6 +14,6 @@ Run examples with `cargo run --example example_name`. * [`params`](params.rs) - A webserver that accept a form, with a name and a number, checks the parameters are presents and validates the input. -* [`send_file`](send_file.rs) - A server that sends back content of files either simply or streaming the response. +* [`send_file`](send_file.rs) - A server that sends back content of files using tokio_fs to read the files asynchronously. * [`web_api`](web_api.rs) - A server consisting in a service that returns incoming POST request's content in the response in uppercase and a service that call that call the first service and includes the first service response in its own response. diff --git a/examples/send_file.rs b/examples/send_file.rs index 76758a8e..e2eeefd2 100644 --- a/examples/send_file.rs +++ b/examples/send_file.rs @@ -2,16 +2,15 @@ extern crate futures; extern crate hyper; extern crate pretty_env_logger; +extern crate tokio_fs; +extern crate tokio_io; use futures::{future, Future}; -use futures::sync::oneshot; use hyper::{Body, Method, Request, Response, Server, StatusCode}; use hyper::service::service_fn; -use std::fs::File; -use std::io::{self, copy/*, Read*/}; -use std::thread; +use std::io; static NOTFOUND: &[u8] = b"Not Found"; static INDEX: &str = "examples/send_file_index.html"; @@ -35,58 +34,9 @@ type ResponseFuture = Box, Error=io::Error> + Send>; fn response_examples(req: Request) -> ResponseFuture { match (req.method(), req.uri().path()) { - (&Method::GET, "/") | (&Method::GET, "/index.html") => { + (&Method::GET, "/") | (&Method::GET, "/index.html") | (&Method::GET, "/big_file.html") => { simple_file_send(INDEX) }, - (&Method::GET, "/big_file.html") => { - // Stream a large file in chunks. This requires a - // little more overhead with two channels, (one for - // the response future, and a second for the response - // body), but can handle arbitrarily large files. - // - // We use an artificially small buffer, since we have - // a small test file. - let (tx, rx) = oneshot::channel(); - thread::spawn(move || { - let _file = match File::open(INDEX) { - Ok(f) => f, - Err(_) => { - tx.send(Response::builder() - .status(StatusCode::NOT_FOUND) - .body(NOTFOUND.into()) - .unwrap()) - .expect("Send error on open"); - return; - }, - }; - let (_tx_body, rx_body) = Body::channel(); - let res = Response::new(rx_body.into()); - tx.send(res).expect("Send error on successful file read"); - /* TODO: fix once we have futures 0.2 Sink working - let mut buf = [0u8; 16]; - loop { - match file.read(&mut buf) { - Ok(n) => { - if n == 0 { - // eof - tx_body.close().expect("panic closing"); - break; - } else { - let chunk: Chunk = buf[0..n].to_vec().into(); - match tx_body.send_data(chunk).wait() { - Ok(t) => { tx_body = t; }, - Err(_) => { break; } - }; - } - }, - Err(_) => { break; } - } - } - */ - }); - - Box::new(rx.map_err(|e| io::Error::new(io::ErrorKind::Other, e))) - }, (&Method::GET, "/no_file.html") => { // Test what happens when file cannot be be found simple_file_send("this_file_should_not_exist.html") @@ -102,42 +52,28 @@ fn response_examples(req: Request) -> ResponseFuture { } fn simple_file_send(f: &str) -> ResponseFuture { - // Serve a file by reading it entirely into memory. As a result - // this is limited to serving small files, but it is somewhat - // simpler with a little less overhead. - // - // On channel errors, we panic with the expect method. The thread - // ends at that point in any case. + // Serve a file by asynchronously reading it entirely into memory. + // Uses tokio_fs to open file asynchronously, then tokio_io to read into + // memory asynchronously. let filename = f.to_string(); // we need to copy for lifetime issues - let (tx, rx) = oneshot::channel(); - thread::spawn(move || { - let mut file = match File::open(filename) { - Ok(f) => f, - Err(_) => { - tx.send(Response::builder() - .status(StatusCode::NOT_FOUND) - .body(NOTFOUND.into()) - .unwrap()) - .expect("Send error on open"); - return; - }, - }; - let mut buf: Vec = Vec::new(); - match copy(&mut file, &mut buf) { - Ok(_) => { - let res = Response::new(buf.into()); - tx.send(res).expect("Send error on successful file read"); - }, - Err(_) => { - tx.send(Response::builder() + Box::new(tokio_fs::file::File::open(filename) + .and_then(|file| { + let buf: Vec = Vec::new(); + tokio_io::io::read_to_end(file, buf) + .and_then(|item| { + Ok(Response::new(item.1.into())) + }) + .or_else(|_| { + Ok(Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(Body::empty()) .unwrap()) - .expect("Send error on error reading file"); - }, - }; - }); - - Box::new(rx.map_err(|e| io::Error::new(io::ErrorKind::Other, e))) + }) + }) + .or_else(|_| { + Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body(NOTFOUND.into()) + .unwrap()) + })) } -