mirror of https://github.com/rwf2/Rocket.git
Implement streaming requests.
This commit is contained in:
parent
07204a25dd
commit
d8db812856
|
@ -25,4 +25,5 @@ members = [
|
||||||
"examples/handlebars_templates",
|
"examples/handlebars_templates",
|
||||||
"examples/form_kitchen_sink",
|
"examples/form_kitchen_sink",
|
||||||
"examples/config",
|
"examples/config",
|
||||||
|
"examples/hello_alt_methods",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
extern crate tera;
|
extern crate tera;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use self::tera::Renderer;
|
|
||||||
|
|
||||||
use super::serde::Serialize;
|
use super::serde::Serialize;
|
||||||
use super::serde_json;
|
|
||||||
use super::{TemplateInfo, TEMPLATE_DIR};
|
use super::{TemplateInfo, TEMPLATE_DIR};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -19,17 +17,13 @@ pub const EXT: &'static str = "tera";
|
||||||
pub fn render<T>(name: &str, info: &TemplateInfo, context: &T) -> Option<String>
|
pub fn render<T>(name: &str, info: &TemplateInfo, context: &T) -> Option<String>
|
||||||
where T: Serialize
|
where T: Serialize
|
||||||
{
|
{
|
||||||
let template = match TERA.get_template(&info.path.to_string_lossy()) {
|
let template_name = &info.path.to_string_lossy();
|
||||||
Ok(template) => template,
|
if TERA.get_template(template_name).is_err() {
|
||||||
Err(_) => {
|
error_!("Tera template '{}' does not exist.", template_name);
|
||||||
error_!("Tera template '{}' does not exist.", name);
|
return None;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let value = serde_json::to_value(&context);
|
match TERA.value_render(template_name, &context) {
|
||||||
let mut renderer = Renderer::new_with_json(template, &TERA, value);
|
|
||||||
match renderer.render() {
|
|
||||||
Ok(string) => Some(string),
|
Ok(string) => Some(string),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error_!("Error rendering Tera template '{}': {}", name, e);
|
error_!("Error rendering Tera template '{}': {}", name, e);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "hello_alt_methods"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
|
workspace = "../../"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { path = "../../lib" }
|
||||||
|
rocket_codegen = { path = "../../codegen" }
|
|
@ -0,0 +1,24 @@
|
||||||
|
#![feature(plugin)]
|
||||||
|
#![plugin(rocket_codegen)]
|
||||||
|
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use rocket::response::NamedFile;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn index() -> io::Result<NamedFile> {
|
||||||
|
NamedFile::open("static/index.html")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/")]
|
||||||
|
fn put() -> &'static str {
|
||||||
|
"Hello, PUT request!"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
rocket::ignite()
|
||||||
|
.mount("/", routes![index, put])
|
||||||
|
.launch();
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<title>Hello Alt Methods</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="/" method="post" accept-charset="utf-8">
|
||||||
|
<input type="hidden" name="_method" id="_method" value="put" />
|
||||||
|
<input type="submit" name="Submit" id="Submit" value="submit" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -30,10 +30,10 @@ fn upload(req: &Request, data: Data) -> Response {
|
||||||
return Response::failed(StatusCode::BadRequest);
|
return Response::failed(StatusCode::BadRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = File::create("upload.txt");
|
let file = File::create("/tmp/upload.txt");
|
||||||
if let Ok(mut file) = file {
|
if let Ok(mut file) = file {
|
||||||
if io::copy(&mut data.open(), &mut file).is_ok() {
|
if let Ok(n) = io::copy(&mut data.open(), &mut file) {
|
||||||
return Response::complete("Upload successful.");
|
return Response::complete(format!("OK: {} bytes uploaded.", n));
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(" => Failed copying.");
|
println!(" => Failed copying.");
|
||||||
|
|
|
@ -6,10 +6,14 @@ authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
term-painter = "^0.2"
|
term-painter = "^0.2"
|
||||||
log = "^0.3"
|
log = "^0.3"
|
||||||
hyper = { version = "^0.9", default-features = false }
|
|
||||||
url = "^1"
|
url = "^1"
|
||||||
# mime = "^0.2"
|
# mime = "^0.2"
|
||||||
toml = "^0.2"
|
toml = "^0.2"
|
||||||
|
|
||||||
|
[dependencies.hyper]
|
||||||
|
git = "https://github.com/SergioBenitez/hyper"
|
||||||
|
default-features = false
|
||||||
|
branch = "0.9.x"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "*"
|
lazy_static = "*"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// TODO: Removed from Rocket in favor of a more flexible HTTP library.
|
// TODO: Remove from Rocket in favor of a more flexible HTTP library.
|
||||||
pub use hyper::server::Request as HyperRequest;
|
pub use hyper::server::Request as HyperRequest;
|
||||||
pub use hyper::server::Response as HyperResponse;
|
pub use hyper::server::Response as HyperResponse;
|
||||||
pub use hyper::server::Server as HyperServer;
|
pub use hyper::server::Server as HyperServer;
|
||||||
|
@ -13,10 +13,19 @@ pub use hyper::header::SetCookie as HyperSetCookie;
|
||||||
pub use hyper::method::Method as HyperMethod;
|
pub use hyper::method::Method as HyperMethod;
|
||||||
pub use hyper::uri::RequestUri as HyperRequestUri;
|
pub use hyper::uri::RequestUri as HyperRequestUri;
|
||||||
pub use hyper::net::Fresh as HyperFresh;
|
pub use hyper::net::Fresh as HyperFresh;
|
||||||
|
pub use hyper::net::HttpStream as HyperHttpStream;
|
||||||
|
pub use hyper::net::NetworkStream as HyperNetworkStream;
|
||||||
|
pub use hyper::http::h1::HttpReader as HyperHttpReader;
|
||||||
pub use hyper::header;
|
pub use hyper::header;
|
||||||
|
|
||||||
// This is okay.
|
// This is okay for now.
|
||||||
pub use hyper::status::StatusCode;
|
pub use hyper::status::StatusCode;
|
||||||
|
|
||||||
// TODO: Removed from Rocket in favor of a more flexible HTTP library.
|
// TODO: Remove from Rocket in favor of a more flexible HTTP library.
|
||||||
pub type FreshHyperResponse<'a> = self::HyperResponse<'a, self::HyperFresh>;
|
pub type FreshHyperResponse<'a> = self::HyperResponse<'a, self::HyperFresh>;
|
||||||
|
|
||||||
|
// TODO: Remove from Rocket in favor of a more flexible HTTP library.
|
||||||
|
use hyper::buffer::BufReader;
|
||||||
|
pub type HyperBodyReader<'a, 'b> =
|
||||||
|
HyperHttpReader<&'a mut BufReader<&'b mut HyperNetworkStream>>;
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,12 @@ impl Log for RocketLogger {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't print Hyper's messages unless Debug is enabled.
|
||||||
|
let from_hyper = record.location().module_path().starts_with("hyper::");
|
||||||
|
if from_hyper && self.0 != LoggingLevel::Debug {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// In Rocket, we abuse target with value "_" to indicate indentation.
|
// In Rocket, we abuse target with value "_" to indicate indentation.
|
||||||
if record.target() == "_" && self.0 != LoggingLevel::Critical {
|
if record.target() == "_" && self.0 != LoggingLevel::Critical {
|
||||||
print!(" {} ", White.paint("=>"));
|
print!(" {} ", White.paint("=>"));
|
||||||
|
|
|
@ -16,6 +16,15 @@ pub enum Outcome<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Outcome<T> {
|
impl<T> Outcome<T> {
|
||||||
|
pub fn of<A, B: fmt::Debug>(result: Result<A, B>) -> Outcome<T> {
|
||||||
|
if let Err(e) = result {
|
||||||
|
error_!("{:?}", e);
|
||||||
|
return Outcome::Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
Outcome::Success
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Outcome::Success => "Success",
|
Outcome::Success => "Success",
|
||||||
|
|
|
@ -1,23 +1,133 @@
|
||||||
use std::io::{BufRead, Read, Cursor, BufReader};
|
use std::io::{self, BufRead, Read, Cursor, BufReader, Chain, Take};
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::net::Shutdown;
|
||||||
|
|
||||||
|
use http::hyper::{HyperBodyReader, HyperHttpStream, HyperHttpReader};
|
||||||
|
use http::hyper::HyperNetworkStream;
|
||||||
|
use http::hyper::HyperHttpReader::*;
|
||||||
|
|
||||||
|
type StreamReader = HyperHttpReader<HyperHttpStream>;
|
||||||
|
|
||||||
|
pub struct DataStream {
|
||||||
|
stream: Chain<Take<Cursor<Vec<u8>>>, BufReader<StreamReader>>,
|
||||||
|
network: HyperHttpStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for DataStream {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.stream.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufRead for DataStream {
|
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
|
self.stream.fill_buf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
self.stream.consume(amt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_sinking<N: HyperNetworkStream>(net: &mut N) -> bool {
|
||||||
|
warn_!("Data left unread. Sinking 1k bytes.");
|
||||||
|
io::copy(&mut net.take(1024), &mut io::sink()).expect("sink");
|
||||||
|
|
||||||
|
// If there are any more bytes, kill it.
|
||||||
|
let mut buf = [0];
|
||||||
|
if let Ok(n) = net.read(&mut buf) {
|
||||||
|
if n > 0 {
|
||||||
|
warn_!("Data still remains. Force closing network stream.");
|
||||||
|
return net.close(Shutdown::Both).is_ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DataStream {
|
||||||
|
// Be a bad citizen and close the TCP stream if there's unread data.
|
||||||
|
// Unfortunately, Hyper forces us to do this.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
try_sinking(&mut self.network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
stream: Cursor<Vec<u8>>,
|
stream: StreamReader,
|
||||||
|
position: usize,
|
||||||
|
capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Data {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
try_sinking(self.stream.get_mut());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
pub fn open(self) -> impl BufRead {
|
pub fn open(mut self) -> impl BufRead {
|
||||||
Cursor::new(self.buffer).chain(BufReader::new(self.stream))
|
// Swap out the buffer and stream for empty ones so we can move.
|
||||||
|
let mut buffer = vec![];
|
||||||
|
let mut stream = EmptyReader(self.stream.get_ref().clone());
|
||||||
|
::std::mem::swap(&mut buffer, &mut self.buffer);
|
||||||
|
::std::mem::swap(&mut stream, &mut self.stream);
|
||||||
|
|
||||||
|
// Setup the underlying reader at the correct pointers.
|
||||||
|
let mut cursor = Cursor::new(buffer);
|
||||||
|
cursor.set_position(self.position as u64);
|
||||||
|
let buffered = cursor.take((self.capacity - self.position) as u64);
|
||||||
|
|
||||||
|
// Create the actual DataSteam.
|
||||||
|
DataStream {
|
||||||
|
network: stream.get_ref().clone(),
|
||||||
|
stream: buffered.chain(BufReader::new(stream)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn from_hyp(mut h_body: HyperBodyReader) -> Result<Data, &'static str> {
|
||||||
|
// FIXME: This is asolutely terrible, thanks to Hyper.
|
||||||
|
|
||||||
|
// Retrieve the underlying HTTPStream from Hyper.
|
||||||
|
let mut stream = match h_body.get_ref().get_ref()
|
||||||
|
.downcast_ref::<HyperHttpStream>() {
|
||||||
|
Some(s) => {
|
||||||
|
let owned_stream = s.clone();
|
||||||
|
let buf_len = h_body.get_ref().get_buf().len() as u64;
|
||||||
|
match h_body {
|
||||||
|
SizedReader(_, n) => SizedReader(owned_stream, n - buf_len),
|
||||||
|
EofReader(_) => EofReader(owned_stream),
|
||||||
|
EmptyReader(_) => EmptyReader(owned_stream),
|
||||||
|
ChunkedReader(_, n) =>
|
||||||
|
ChunkedReader(owned_stream, n.map(|k| k - buf_len)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => return Err("Stream is not an HTTP stream!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the read timeout to 5 seconds.
|
||||||
|
stream.get_mut().set_read_timeout(Some(Duration::from_secs(5))).unwrap();
|
||||||
|
|
||||||
|
// Create the Data object from hyper's buffer.
|
||||||
|
let (vec, pos, cap) = h_body.get_mut().take_buf();
|
||||||
|
Ok(Data::new(vec, pos, cap, stream))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek(&self) -> &[u8] {
|
pub fn peek(&self) -> &[u8] {
|
||||||
&self.buffer
|
&self.buffer[self.position..self.capacity]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Data {
|
pub fn new(buf: Vec<u8>, pos: usize, cap: usize, stream: StreamReader) -> Data {
|
||||||
|
// TODO: Make sure we always try to get some number of bytes in the
|
||||||
|
// buffer so that peek actually does something.
|
||||||
|
// const PEEK_BYTES: usize = 4096;
|
||||||
Data {
|
Data {
|
||||||
stream: Cursor::new(vec![]),
|
buffer: buf,
|
||||||
buffer: vec![]
|
stream: stream,
|
||||||
|
position: pos,
|
||||||
|
capacity: cap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ impl<'a> Responder for Redirect {
|
||||||
res.headers_mut().set(header::ContentLength(0));
|
res.headers_mut().set(header::ContentLength(0));
|
||||||
res.headers_mut().set(header::Location(self.1.clone()));
|
res.headers_mut().set(header::Location(self.1.clone()));
|
||||||
*(res.status_mut()) = self.0;
|
*(res.status_mut()) = self.0;
|
||||||
res.send(b"").unwrap();
|
Outcome::of(res.send(b""))
|
||||||
Outcome::Success
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,7 @@ impl<'a> Responder for &'a str {
|
||||||
res.headers_mut().set(header::ContentType(mime));
|
res.headers_mut().set(header::ContentType(mime));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send(self.as_bytes()).unwrap();
|
Outcome::of(res.send(self.as_bytes()))
|
||||||
Outcome::Success
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,47 +31,53 @@ impl Responder for String {
|
||||||
let mime = Mime(TopLevel::Text, SubLevel::Html, vec![]);
|
let mime = Mime(TopLevel::Text, SubLevel::Html, vec![]);
|
||||||
res.headers_mut().set(header::ContentType(mime));
|
res.headers_mut().set(header::ContentType(mime));
|
||||||
}
|
}
|
||||||
res.send(self.as_bytes()).unwrap();
|
|
||||||
Outcome::Success
|
Outcome::of(res.send(self.as_bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Responder for File {
|
impl Responder for File {
|
||||||
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||||
let size = self.metadata().unwrap().len();
|
let size = match self.metadata() {
|
||||||
|
Ok(md) => md.len(),
|
||||||
res.headers_mut().set(header::ContentLength(size));
|
Err(e) => {
|
||||||
*(res.status_mut()) = StatusCode::Ok;
|
error_!("Failed to read file metadata: {:?}", e);
|
||||||
|
return Outcome::Forward((StatusCode::InternalServerError, res));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
self.read_to_end(&mut v).unwrap();
|
if let Err(e) = self.read_to_end(&mut v) {
|
||||||
|
error_!("Failed to read file: {:?}", e);
|
||||||
|
return Outcome::Forward((StatusCode::InternalServerError, res));
|
||||||
|
}
|
||||||
|
|
||||||
let mut stream = res.start().unwrap();
|
res.headers_mut().set(header::ContentLength(size));
|
||||||
stream.write_all(&v).unwrap();
|
Outcome::of(res.start().and_then(|mut stream| stream.write_all(&v)))
|
||||||
Outcome::Success
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Responder> Responder for Option<T> {
|
impl<T: Responder> Responder for Option<T> {
|
||||||
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||||
if self.is_none() {
|
if let Some(ref mut val) = *self {
|
||||||
warn_!("response was `None`");
|
val.respond(res)
|
||||||
return Outcome::Forward((StatusCode::NotFound, res));
|
} else {
|
||||||
|
warn_!("Response was `None`.");
|
||||||
|
Outcome::Forward((StatusCode::NotFound, res))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.as_mut().unwrap().respond(res)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
|
impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
|
||||||
// prepend with `default` when using impl specialization
|
// prepend with `default` when using impl specialization
|
||||||
default fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
default fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||||
if self.is_err() {
|
match *self {
|
||||||
error_!("{:?}", self.as_ref().err().unwrap());
|
Ok(ref mut val) => val.respond(res),
|
||||||
return Outcome::Forward((StatusCode::InternalServerError, res));
|
Err(ref e) => {
|
||||||
|
error_!("{:?}", e);
|
||||||
|
Outcome::Forward((StatusCode::InternalServerError, res))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.as_mut().unwrap().respond(res)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,14 @@ impl<T: Read> Stream<T> {
|
||||||
|
|
||||||
impl<T: Read> Responder for Stream<T> {
|
impl<T: Read> Responder for Stream<T> {
|
||||||
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||||
let mut stream = res.start().unwrap();
|
let mut stream = match res.start() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
error_!("Failed opening response stream: {:?}", e);
|
||||||
|
return Outcome::Failure;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut buffer = [0; CHUNK_SIZE];
|
let mut buffer = [0; CHUNK_SIZE];
|
||||||
let mut complete = false;
|
let mut complete = false;
|
||||||
while !complete {
|
while !complete {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use term_painter::ToStyle;
|
||||||
use config;
|
use config;
|
||||||
use logger;
|
use logger;
|
||||||
use request::{Request, Data, FormItems};
|
use request::{Request, Data, FormItems};
|
||||||
use response::{Response};
|
use response::Response;
|
||||||
use router::{Router, Route};
|
use router::{Router, Route};
|
||||||
use catcher::{self, Catcher};
|
use catcher::{self, Catcher};
|
||||||
use outcome::Outcome;
|
use outcome::Outcome;
|
||||||
|
@ -45,14 +45,11 @@ impl Rocket {
|
||||||
let uri = hyp_req.uri.to_string();
|
let uri = hyp_req.uri.to_string();
|
||||||
|
|
||||||
// Get all of the information from Hyper.
|
// Get all of the information from Hyper.
|
||||||
let (_, h_method, h_headers, h_uri, _, mut _body) = hyp_req.deconstruct();
|
let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();
|
||||||
|
|
||||||
// Try to create a Rocket request from the hyper request info.
|
// Try to create a Rocket request from the hyper request info.
|
||||||
let request = match Request::new(h_method, h_headers, h_uri) {
|
let mut request = match Request::new(h_method, h_headers, h_uri) {
|
||||||
Ok(mut req) => {
|
Ok(req) => req,
|
||||||
self.preprocess_request(&mut req);
|
|
||||||
req
|
|
||||||
}
|
|
||||||
Err(ref reason) => {
|
Err(ref reason) => {
|
||||||
let mock_request = Request::mock(Method::Get, uri.as_str());
|
let mock_request = Request::mock(Method::Get, uri.as_str());
|
||||||
debug_!("Bad request: {}", reason);
|
debug_!("Bad request: {}", reason);
|
||||||
|
@ -62,9 +59,20 @@ impl Rocket {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Retrieve the data from the request.
|
// Retrieve the data from the request.
|
||||||
let mut data = Data::new();
|
let mut data = match Data::from_hyp(h_body) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(reason) => {
|
||||||
|
debug_!("Bad data in request: {}", reason);
|
||||||
|
return self.handle_error(StatusCode::InternalServerError,
|
||||||
|
&request, res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Preprocess the request.
|
||||||
|
self.preprocess_request(&mut request, &data);
|
||||||
|
|
||||||
info!("{}:", request);
|
info!("{}:", request);
|
||||||
|
info_!("Peek size: {} bytes", data.peek().len());
|
||||||
let matches = self.router.route(&request);
|
let matches = self.router.route(&request);
|
||||||
for route in matches {
|
for route in matches {
|
||||||
// Retrieve and set the requests parameters.
|
// Retrieve and set the requests parameters.
|
||||||
|
@ -108,14 +116,14 @@ impl Rocket {
|
||||||
|
|
||||||
/// Preprocess the request for Rocket-specific things. At this time, we're
|
/// Preprocess the request for Rocket-specific things. At this time, we're
|
||||||
/// only checking for _method in forms.
|
/// only checking for _method in forms.
|
||||||
fn preprocess_request(&self, req: &mut Request) {
|
fn preprocess_request(&self, req: &mut Request, data: &Data) {
|
||||||
// Check if this is a form and if the form contains the special _method
|
// Check if this is a form and if the form contains the special _method
|
||||||
// field which we use to reinterpret the request's method.
|
// field which we use to reinterpret the request's method.
|
||||||
let data_len = req.data.len();
|
let data_len = data.peek().len();
|
||||||
let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
|
let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
|
||||||
if req.content_type().is_form() && data_len >= min_len {
|
if req.content_type().is_form() && data_len >= min_len {
|
||||||
let form = unsafe {
|
let form = unsafe {
|
||||||
from_utf8_unchecked(&req.data.as_slice()[..min(data_len, max_len)])
|
from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut form_items = FormItems(form);
|
let mut form_items = FormItems(form);
|
||||||
|
@ -132,8 +140,13 @@ impl Rocket {
|
||||||
code: StatusCode,
|
code: StatusCode,
|
||||||
req: &'r Request,
|
req: &'r Request,
|
||||||
response: FreshHyperResponse) {
|
response: FreshHyperResponse) {
|
||||||
error_!("Dispatch failed: {}.", code);
|
|
||||||
let catcher = self.catchers.get(&code.to_u16()).unwrap();
|
// Find the catcher or use the one for internal server errors.
|
||||||
|
let catcher = self.catchers.get(&code.to_u16()).unwrap_or_else(|| {
|
||||||
|
error_!("No catcher found for {}.", code);
|
||||||
|
warn_!("Using internal server error catcher.");
|
||||||
|
self.catchers.get(&500).expect("500 Catcher")
|
||||||
|
});
|
||||||
|
|
||||||
if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
|
if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
|
||||||
if responder.respond(response) != Outcome::Success {
|
if responder.respond(response) != Outcome::Success {
|
||||||
|
@ -144,7 +157,8 @@ impl Rocket {
|
||||||
} else {
|
} else {
|
||||||
error_!("Catcher returned an incomplete response.");
|
error_!("Catcher returned an incomplete response.");
|
||||||
warn_!("Using default error response.");
|
warn_!("Using default error response.");
|
||||||
let catcher = self.default_catchers.get(&code.to_u16()).unwrap();
|
let catcher = self.default_catchers.get(&code.to_u16())
|
||||||
|
.unwrap_or(self.default_catchers.get(&500).expect("500 default"));
|
||||||
let responder = catcher.handle(Error::Internal, req).responder();
|
let responder = catcher.handle(Error::Internal, req).responder();
|
||||||
responder.unwrap().respond(response).expect_success()
|
responder.unwrap().respond(response).expect_success()
|
||||||
}
|
}
|
||||||
|
@ -166,8 +180,7 @@ impl Rocket {
|
||||||
pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
|
pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
|
||||||
info!("👾 {}:", Magenta.paint("Catchers"));
|
info!("👾 {}:", Magenta.paint("Catchers"));
|
||||||
for c in catchers {
|
for c in catchers {
|
||||||
if self.catchers.contains_key(&c.code) &&
|
if self.catchers.get(&c.code).map_or(false, |e| e.is_default()) {
|
||||||
!self.catchers.get(&c.code).unwrap().is_default() {
|
|
||||||
let msg = format!("warning: overrides {} catcher!", c.code);
|
let msg = format!("warning: overrides {} catcher!", c.code);
|
||||||
warn!("{} ({})", c, Yellow.paint(msg.as_str()));
|
warn!("{} ({})", c, Yellow.paint(msg.as_str()));
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue