Merge pull request #1404 from jolhoeft/file_example
docs(server): Add an example of serving files
This commit is contained in:
		
							
								
								
									
										142
									
								
								examples/send_file.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								examples/send_file.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| #![deny(warnings)] | ||||
| extern crate futures; | ||||
| extern crate hyper; | ||||
| extern crate pretty_env_logger; | ||||
|  | ||||
| use futures::{Future, Sink}; | ||||
| use futures::sync::{mpsc, oneshot}; | ||||
|  | ||||
| use hyper::{Chunk, Get, StatusCode}; | ||||
| use hyper::error::Error; | ||||
| use hyper::header::ContentLength; | ||||
| use hyper::server::{Http, Service, Request, Response}; | ||||
|  | ||||
| use std::fs::File; | ||||
| use std::io::{self, copy, Read}; | ||||
| use std::thread; | ||||
|  | ||||
| static NOTFOUND: &[u8] = b"Not Found"; | ||||
| static INDEX: &str = "examples/send_file_index.html"; | ||||
|  | ||||
| fn simple_file_send(f: &str) -> Box<Future<Item = Response, Error = hyper::Error>> { | ||||
|     // 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. | ||||
|     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::new() | ||||
|                         .with_status(StatusCode::NotFound) | ||||
|                         .with_header(ContentLength(NOTFOUND.len() as u64)) | ||||
|                         .with_body(NOTFOUND)) | ||||
|                     .expect("Send error on open"); | ||||
|                 return; | ||||
|             }, | ||||
|         }; | ||||
|         let mut buf: Vec<u8> = Vec::new(); | ||||
|         match copy(&mut file, &mut buf) { | ||||
|             Ok(_) => { | ||||
|                 let res = Response::new() | ||||
|                     .with_header(ContentLength(buf.len() as u64)) | ||||
|                     .with_body(buf); | ||||
|                 tx.send(res).expect("Send error on successful file read"); | ||||
|             }, | ||||
|             Err(_) => { | ||||
|                 tx.send(Response::new().with_status(StatusCode::InternalServerError)). | ||||
|                     expect("Send error on error reading file"); | ||||
|             }, | ||||
|         }; | ||||
|     }); | ||||
|  | ||||
|     Box::new(rx.map_err(|e| Error::from(io::Error::new(io::ErrorKind::Other, e)))) | ||||
| } | ||||
|  | ||||
| struct ResponseExamples; | ||||
|  | ||||
| impl Service for ResponseExamples { | ||||
|     type Request = Request; | ||||
|     type Response = Response; | ||||
|     type Error = hyper::Error; | ||||
|     type Future = Box<Future<Item = Self::Response, Error = Self::Error>>; | ||||
|  | ||||
|     fn call(&self, req: Request) -> Self::Future { | ||||
|         match (req.method(), req.path()) { | ||||
|             (&Get, "/") | (&Get, "/index.html") => { | ||||
|                 simple_file_send(INDEX) | ||||
|             }, | ||||
|             (&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 mut file = match File::open(INDEX) { | ||||
|                         Ok(f) => f, | ||||
|                         Err(_) => { | ||||
|                             tx.send(Response::new() | ||||
|                                     .with_status(StatusCode::NotFound) | ||||
|                                     .with_header(ContentLength(NOTFOUND.len() as u64)) | ||||
|                                     .with_body(NOTFOUND)) | ||||
|                                 .expect("Send error on open"); | ||||
|                             return; | ||||
|                         }, | ||||
|                     }; | ||||
|                     let (mut tx_body, rx_body) = mpsc::channel(1); | ||||
|                     let res = Response::new().with_body(rx_body); | ||||
|                     tx.send(res).expect("Send error on successful file read"); | ||||
|                     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.to_vec().into(); | ||||
|                                     match tx_body.send(Ok(chunk)).wait() { | ||||
|                                         Ok(t) => { tx_body = t; }, | ||||
|                                         Err(_) => { break; } | ||||
|                                     }; | ||||
|                                 } | ||||
|                             }, | ||||
|                             Err(_) => { break; } | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|                  | ||||
|                 Box::new(rx.map_err(|e| Error::from(io::Error::new(io::ErrorKind::Other, e)))) | ||||
|             }, | ||||
|             (&Get, "/no_file.html") => { | ||||
|                 // Test what happens when file cannot be be found | ||||
|                 simple_file_send("this_file_should_not_exist.html") | ||||
|             }, | ||||
|             _ => { | ||||
|                 Box::new(futures::future::ok(Response::new() | ||||
|                                     .with_status(StatusCode::NotFound))) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| fn main() { | ||||
|     pretty_env_logger::init().unwrap(); | ||||
|     let addr = "127.0.0.1:1337".parse().unwrap(); | ||||
|  | ||||
|     let mut server = Http::new().bind(&addr, || Ok(ResponseExamples)).unwrap(); | ||||
|     server.no_proto(); | ||||
|     println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap()); | ||||
|     server.run().unwrap(); | ||||
| } | ||||
							
								
								
									
										11
									
								
								examples/send_file_index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								examples/send_file_index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Hyper responding example</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <h1>Hyper responding example</h1> | ||||
|     <a href="index.html">index.html</a> Top Level<br> | ||||
|     <a href="big_file.html">big_file.html</a> This page, streamed in chunks<br> | ||||
|     <a href="no_file.html">no_file.html</a> A 404 test, the requested file does not exist<br> | ||||
|   </body> | ||||
| </html> | ||||
		Reference in New Issue
	
	Block a user