mirror of https://github.com/rwf2/Rocket.git
Add streaming responder and example.
This commit is contained in:
parent
4e03bb6107
commit
8824d498d1
|
@ -17,4 +17,5 @@ members = [
|
||||||
"examples/hello_ranks",
|
"examples/hello_ranks",
|
||||||
"examples/testing",
|
"examples/testing",
|
||||||
"examples/from_request",
|
"examples/from_request",
|
||||||
|
"examples/stream",
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "stream"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
|
workspace = "../../"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { path = "../../lib" }
|
||||||
|
rocket_codegen = { path = "../../codegen" }
|
|
@ -0,0 +1,20 @@
|
||||||
|
#![feature(plugin)]
|
||||||
|
#![plugin(rocket_codegen)]
|
||||||
|
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
use rocket::Rocket;
|
||||||
|
use rocket::response::{Plain, Stream};
|
||||||
|
|
||||||
|
use std::io::{repeat, Repeat, Read, Take};
|
||||||
|
|
||||||
|
type LimitedRepeat = Take<Repeat>;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn root() -> Plain<Stream<LimitedRepeat>> {
|
||||||
|
Plain(Stream::from(repeat('a' as u8).take(25000)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Rocket::new("localhost", 8000).mount_and_launch("/", routes![root]);
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ mod outcome;
|
||||||
mod flash;
|
mod flash;
|
||||||
mod data_type;
|
mod data_type;
|
||||||
mod named_file;
|
mod named_file;
|
||||||
|
mod stream;
|
||||||
|
|
||||||
pub use hyper::server::Response as HyperResponse;
|
pub use hyper::server::Response as HyperResponse;
|
||||||
pub use hyper::net::Fresh as HyperFresh;
|
pub use hyper::net::Fresh as HyperFresh;
|
||||||
|
@ -21,6 +22,7 @@ pub use self::with_status::StatusResponse;
|
||||||
pub use self::outcome::Outcome;
|
pub use self::outcome::Outcome;
|
||||||
pub use self::flash::Flash;
|
pub use self::flash::Flash;
|
||||||
pub use self::named_file::NamedFile;
|
pub use self::named_file::NamedFile;
|
||||||
|
pub use self::stream::Stream;
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ use std::fs::File;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use response::mime::{Mime, TopLevel, SubLevel};
|
use response::mime::{Mime, TopLevel, SubLevel};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct NamedFile(PathBuf, File);
|
pub struct NamedFile(PathBuf, File);
|
||||||
|
|
||||||
impl NamedFile {
|
impl NamedFile {
|
||||||
|
@ -46,3 +48,17 @@ impl Responder for NamedFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for NamedFile {
|
||||||
|
type Target = File;
|
||||||
|
|
||||||
|
fn deref(&self) -> &File {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for NamedFile {
|
||||||
|
fn deref_mut(&mut self) -> &mut File {
|
||||||
|
&mut self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,49 @@
|
||||||
// const CHUNK_SIZE: u32 = 4096;
|
use response::*;
|
||||||
// pub struct Stream<T: Read>(T);
|
use std::io::{Read, Write, ErrorKind};
|
||||||
// impl<T> Responder for Stream<T> {
|
|
||||||
// fn respond<'a>(&self, mut r: HypResponse<'a, HypFresh>) {
|
|
||||||
// r.headers_mut().set(header::TransferEncoding(vec![Encoding::Chunked]));
|
|
||||||
// *(r.status_mut()) = StatusCode::Ok;
|
|
||||||
// let mut stream = r.start();
|
|
||||||
|
|
||||||
// r.write()
|
/// The size of each chunk in the streamed response.
|
||||||
// Response {
|
pub const CHUNK_SIZE: usize = 4096;
|
||||||
// status: StatusCode::Ok,
|
|
||||||
// headers: headers,
|
pub struct Stream<T: Read>(pub Box<T>);
|
||||||
// body: Body::Stream(r)
|
|
||||||
// }
|
impl<T: Read> Stream<T> {
|
||||||
// }
|
pub fn from(reader: T) -> Stream<T> {
|
||||||
// }
|
Stream(Box::new(reader))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Read> Responder for Stream<T> {
|
||||||
|
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> {
|
||||||
|
let mut stream = res.start().unwrap();
|
||||||
|
let mut buffer = [0; CHUNK_SIZE];
|
||||||
|
let mut complete = false;
|
||||||
|
while !complete {
|
||||||
|
let mut left = CHUNK_SIZE;
|
||||||
|
while left > 0 && !complete {
|
||||||
|
match self.0.read(&mut buffer[..left]) {
|
||||||
|
Ok(n) if n == 0 => complete = true,
|
||||||
|
Ok(n) if n < left => left -= n,
|
||||||
|
Ok(n) if n == left => left = CHUNK_SIZE,
|
||||||
|
Ok(n) => unreachable!("Impossible byte count {}/{}!", n, left),
|
||||||
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||||
|
Err(ref e) => {
|
||||||
|
error_!("Error streaming response: {:?}", e);
|
||||||
|
return Outcome::FailStop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = stream.write_all(&buffer) {
|
||||||
|
error_!("Stream write_all() failed: {:?}", e);
|
||||||
|
return Outcome::FailStop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = stream.end() {
|
||||||
|
error_!("Stream end() failed: {:?}", e);
|
||||||
|
return Outcome::FailStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
Outcome::Complete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue