mirror of https://github.com/rwf2/Rocket.git
Completely new raw API.
Summary of changes: * Request no longer has a lifetime parameter. * Handler type now includes a `Data` parameter. * Response is now an enum that is either `Complete` or `Forward`. * Outcome enum is now one of: Success, Failure, Forward. * Outcome::Foward for Responses must include StatusCode. * Responders are now final: they cannot forward to requests. (!!) * Responsers may only forward to catchers. (!!) * Response no longer provides wrapping methods. * Route is now cloneable. This change is fundamental to enabling streaming requests.
This commit is contained in:
parent
59623d936f
commit
8c0d11feab
|
@ -57,8 +57,8 @@ pub fn error_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
|||
let fn_arguments = error.generate_fn_arguments(ecx, err_ident, req_ident);
|
||||
|
||||
emit_item(push, quote_item!(ecx,
|
||||
fn $catch_fn_name<'_b, '_r>($err_ident: ::rocket::Error,
|
||||
$req_ident: &'_b ::rocket::Request<'_r>)
|
||||
fn $catch_fn_name<'_b>($err_ident: ::rocket::Error,
|
||||
$req_ident: &'_b ::rocket::Request)
|
||||
-> ::rocket::Response<'_b> {
|
||||
let result = $user_fn_name($fn_arguments);
|
||||
rocket::Response::with_raw_status($code, result)
|
||||
|
|
|
@ -84,7 +84,7 @@ impl RouteGenerateExt for RouteParams {
|
|||
let $name: $ty =
|
||||
match ::rocket::request::FromForm::from_form_string($form_string) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return ::rocket::Response::forward()
|
||||
Err(_) => return ::rocket::Response::forward(_data)
|
||||
};
|
||||
).expect("form statement"))
|
||||
}
|
||||
|
@ -94,7 +94,8 @@ impl RouteGenerateExt for RouteParams {
|
|||
let expr = quote_expr!(ecx,
|
||||
match ::std::str::from_utf8(_req.data.as_slice()) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return ::rocket::Response::bad_request("form isn't utf8")
|
||||
Err(_) => return ::rocket::Response::new(
|
||||
::rocket::response::Empty::bad_request("form isn't utf8"))
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -106,7 +107,7 @@ impl RouteGenerateExt for RouteParams {
|
|||
let expr = quote_expr!(ecx,
|
||||
match _req.uri().query() {
|
||||
Some(query) => query,
|
||||
None => return ::rocket::Response::forward()
|
||||
None => return ::rocket::Response::forward(_data)
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -139,7 +140,7 @@ impl RouteGenerateExt for RouteParams {
|
|||
fn_param_statements.push(quote_stmt!(ecx,
|
||||
let $ident: $ty = match $expr {
|
||||
Ok(v) => v,
|
||||
Err(_) => return ::rocket::Response::forward()
|
||||
Err(_) => return ::rocket::Response::forward(_data)
|
||||
};
|
||||
).expect("declared param parsing statement"));
|
||||
}
|
||||
|
@ -168,7 +169,7 @@ impl RouteGenerateExt for RouteParams {
|
|||
let $ident: $ty = match
|
||||
<$ty as ::rocket::request::FromRequest>::from_request(&_req) {
|
||||
Ok(v) => v,
|
||||
Err(_e) => return ::rocket::Response::forward()
|
||||
Err(_e) => return ::rocket::Response::forward(_data)
|
||||
};
|
||||
).expect("undeclared param parsing statement"));
|
||||
}
|
||||
|
@ -219,7 +220,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
|
|||
let user_fn_name = route.annotated_fn.ident();
|
||||
let route_fn_name = user_fn_name.prepend(ROUTE_FN_PREFIX);
|
||||
emit_item(push, quote_item!(ecx,
|
||||
fn $route_fn_name<'_b, '_r>(_req: &'_b ::rocket::Request<'_r>)
|
||||
fn $route_fn_name<'_b>(_req: &'_b ::rocket::Request, _data: ::rocket::Data)
|
||||
-> ::rocket::Response<'_b> {
|
||||
$form_statement
|
||||
$query_statement
|
||||
|
|
|
@ -18,10 +18,7 @@ fn err1b(_req: &Request) -> &'static str { "hi" }
|
|||
fn err2a(_err: Error, _req: &Request) -> &'static str { "hi" }
|
||||
|
||||
#[error(404)]
|
||||
fn err2b<'r>(_err: Error, _req: &Request<'r>) -> &'r str { "hi" }
|
||||
|
||||
#[error(404)]
|
||||
fn err2c<'a>(_err: Error, _req: &'a Request) -> &'a str { "hi" }
|
||||
fn err2b<'a>(_err: Error, _req: &'a Request) -> &'a str { "hi" }
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut};
|
|||
|
||||
use rocket::request::{Request, FromRequest};
|
||||
use rocket::response::{Responder, Outcome, ResponseOutcome, data};
|
||||
use rocket::http::StatusCode;
|
||||
use rocket::http::hyper::FreshHyperResponse;
|
||||
|
||||
use self::serde::{Serialize, Deserialize};
|
||||
|
@ -62,9 +63,9 @@ impl<T> JSON<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c, T: Deserialize> FromRequest<'r, 'c> for JSON<T> {
|
||||
impl<'r, T: Deserialize> FromRequest<'r> for JSON<T> {
|
||||
type Error = JSONError;
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
Ok(JSON(serde_json::from_slice(request.data.as_slice())?))
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +76,7 @@ impl<T: Serialize> Responder for JSON<T> {
|
|||
Ok(json_string) => data::JSON(json_string).respond(res),
|
||||
Err(e) => {
|
||||
error_!("JSON failed to serialize: {:?}", e);
|
||||
Outcome::FailStop
|
||||
Outcome::Forward((StatusCode::BadRequest, res))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::collections::HashMap;
|
|||
use rocket::Rocket;
|
||||
use rocket::response::{Content, Outcome, ResponseOutcome, Responder};
|
||||
use rocket::http::hyper::FreshHyperResponse;
|
||||
use rocket::http::ContentType;
|
||||
use rocket::http::{ContentType, StatusCode};
|
||||
|
||||
/// The Template type implements generic support for template rendering in
|
||||
/// Rocket.
|
||||
|
@ -155,7 +155,7 @@ impl Responder for Template {
|
|||
|
||||
match self.0 {
|
||||
Some(ref render) => Content(content_type, render.as_str()).respond(res),
|
||||
None => Outcome::Bad(res),
|
||||
None => Outcome::Forward((StatusCode::InternalServerError, res)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ impl fmt::Display for HeaderCount {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for HeaderCount {
|
||||
impl<'r> FromRequest<'r> for HeaderCount {
|
||||
type Error = ();
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
Ok(HeaderCount(request.headers().len()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,38 @@
|
|||
extern crate rocket;
|
||||
|
||||
use rocket::{Request, Response, Route};
|
||||
use rocket::{Request, Response, Route, Data};
|
||||
use rocket::request::FromParam;
|
||||
use rocket::http::Method::*;
|
||||
|
||||
fn root(req: &Request) -> Response<'static> {
|
||||
let name = req.get_param(0).unwrap_or("unnamed");
|
||||
Response::new(format!("Hello, {}!", name))
|
||||
fn forward(_req: &Request, data: Data) -> Response<'static> {
|
||||
Response::forward(data)
|
||||
}
|
||||
|
||||
fn name<'a>(req: &'a Request) -> Response<'a> {
|
||||
fn hi(_req: &Request, _: Data) -> Response<'static> {
|
||||
Response::new("Hello!")
|
||||
}
|
||||
|
||||
fn name<'a>(req: &'a Request, _: Data) -> Response<'a> {
|
||||
Response::new(req.get_param(0).unwrap_or("unnamed"))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn echo_url<'a>(req: &'a Request) -> Response<'a> {
|
||||
Response::new(req.uri().as_str().split_at(6).1)
|
||||
fn echo_url<'a>(req: &'a Request, _: Data) -> Response<'a> {
|
||||
let param = req.uri().as_str().split_at(6).1;
|
||||
Response::new(String::from_param(param))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let first = Route::new(Get, "/hello", root);
|
||||
let second = Route::new(Get, "/hello/<any>", root);
|
||||
let name = Route::new(Get, "/<name>", name);
|
||||
let always_forward = Route::ranked(1, Get, "/", forward);
|
||||
let hello = Route::ranked(2, Get, "/", hi);
|
||||
|
||||
let echo = Route::new(Get, "/", echo_url);
|
||||
let name = Route::new(Get, "/<name>", name);
|
||||
|
||||
rocket::ignite()
|
||||
.mount("/", vec![first, second, name])
|
||||
.mount("/", vec![always_forward, hello])
|
||||
.mount("/hello", vec![name.clone()])
|
||||
.mount("/hi", vec![name])
|
||||
.mount("/echo:<str>", vec![echo])
|
||||
.launch();
|
||||
}
|
||||
|
|
|
@ -14,16 +14,12 @@ pub struct Catcher {
|
|||
is_default: bool,
|
||||
}
|
||||
|
||||
// TODO: Should `Catcher` be an interface? Should there be an `ErrorHandler`
|
||||
// that takes in a `RoutingError` and returns a `Response`? What's the right
|
||||
// interface here?
|
||||
|
||||
impl Catcher {
|
||||
pub fn new(code: u16, handler: ErrorHandler) -> Catcher {
|
||||
Catcher { code: code, handler: handler, is_default: false }
|
||||
}
|
||||
|
||||
pub fn handle<'b, 'r>(&self, err: Error, request: &'b Request<'r>) -> Response<'b> {
|
||||
pub fn handle<'r>(&self, err: Error, request: &'r Request) -> Response<'r> {
|
||||
(self.handler)(err, request)
|
||||
}
|
||||
|
||||
|
@ -59,7 +55,7 @@ macro_rules! error_page_template {
|
|||
</head>
|
||||
<body align="center">
|
||||
<div align="center">
|
||||
<h1>"#, $code, " ", $name, r#"</hi>
|
||||
<h1>"#, $code, ": ", $name, r#"</h1>
|
||||
<p>"#, $description, r#"</p>
|
||||
<hr />
|
||||
<small>Rocket</small>
|
||||
|
@ -77,8 +73,7 @@ macro_rules! default_errors {
|
|||
|
||||
$(
|
||||
fn $fn_name<'r>(_: Error, _r: &'r Request) -> Response<'r> {
|
||||
Response::with_status(
|
||||
StatusCode::Unregistered($code),
|
||||
Response::with_raw_status($code,
|
||||
data::HTML(error_page_template!($code, $name, $description))
|
||||
)
|
||||
}
|
||||
|
@ -96,10 +91,8 @@ pub mod defaults {
|
|||
use std::collections::HashMap;
|
||||
|
||||
use request::Request;
|
||||
use response::Response;
|
||||
use response::data;
|
||||
use response::{Response, data};
|
||||
use error::Error;
|
||||
use http::hyper::StatusCode;
|
||||
|
||||
pub fn get() -> HashMap<u16, Catcher> {
|
||||
default_errors! {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![feature(question_mark)]
|
||||
#![feature(specialization)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
//! # Rocket - Core API Documentation
|
||||
//!
|
||||
|
@ -83,15 +84,15 @@ mod config;
|
|||
|
||||
/// Defines the types for request and error handlers.
|
||||
pub mod handler {
|
||||
use request::Request;
|
||||
use request::{Request, Data};
|
||||
use response::Response;
|
||||
use error::Error;
|
||||
|
||||
/// The type of a request handler.
|
||||
pub type Handler = for<'b, 'r: 'b> fn(&'b Request<'r>) -> Response<'b>;
|
||||
pub type Handler = for<'r> fn(&'r Request, Data) -> Response<'r>;
|
||||
|
||||
/// The type of an error handler.
|
||||
pub type ErrorHandler = for<'b, 'r: 'b> fn(error: Error, &'b Request<'r>) -> Response<'b>;
|
||||
pub type ErrorHandler = for<'r> fn(Error, &'r Request) -> Response<'r>;
|
||||
}
|
||||
|
||||
#[doc(inline)] pub use response::{Response, Responder};
|
||||
|
@ -99,7 +100,7 @@ pub mod handler {
|
|||
#[doc(inline)] pub use logger::LoggingLevel;
|
||||
pub use codegen::{StaticRouteInfo, StaticCatchInfo};
|
||||
pub use router::Route;
|
||||
pub use request::Request;
|
||||
pub use request::{Request, Data};
|
||||
pub use error::Error;
|
||||
pub use catcher::Catcher;
|
||||
pub use rocket::Rocket;
|
||||
|
|
|
@ -3,40 +3,38 @@ use std::fmt;
|
|||
use term_painter::Color::*;
|
||||
use term_painter::ToStyle;
|
||||
|
||||
use http::hyper::FreshHyperResponse;
|
||||
|
||||
#[must_use]
|
||||
pub enum Outcome<T> {
|
||||
/// Signifies a response that completed sucessfully.
|
||||
/// Signifies that all processing completed successfully.
|
||||
Success,
|
||||
/// Signifies a failing response that started responding but fail, so no
|
||||
/// further processing can occur.
|
||||
FailStop,
|
||||
/// Signifies a response that failed internally without beginning to
|
||||
/// respond but no further processing should occur.
|
||||
Bad(T),
|
||||
/// Signifies a failing response that failed internally without beginning to
|
||||
/// respond. Further processing should be attempted.
|
||||
FailForward(T),
|
||||
/// Signifies that some processing occurred that ultimately resulted in
|
||||
/// failure. As a result, no further processing can occur.
|
||||
Failure,
|
||||
/// Signifies that no processing occured and as such, processing can be
|
||||
/// forwarded to the next available target.
|
||||
Forward(T),
|
||||
}
|
||||
|
||||
pub type ResponseOutcome<'a> = Outcome<FreshHyperResponse<'a>>;
|
||||
|
||||
impl<T> Outcome<T> {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Outcome::Success => "Success",
|
||||
Outcome::FailStop => "FailStop",
|
||||
Outcome::Bad(..) => "Bad",
|
||||
Outcome::FailForward(..) => "FailForward",
|
||||
Outcome::Failure => "FailStop",
|
||||
Outcome::Forward(..) => "Forward",
|
||||
}
|
||||
}
|
||||
|
||||
fn as_int(&self) -> isize {
|
||||
match *self {
|
||||
Outcome::Success => 0,
|
||||
Outcome::Bad(..) => 1,
|
||||
Outcome::FailStop => 2,
|
||||
Outcome::FailForward(..) => 3,
|
||||
Outcome::Failure => 1,
|
||||
Outcome::Forward(..) => 2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_success(&self) {
|
||||
if *self != Outcome::Success {
|
||||
panic!("expected a successful outcome");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,9 +55,8 @@ impl<T> fmt::Display for Outcome<T> {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Outcome::Success => write!(f, "{}", Green.paint("Success")),
|
||||
Outcome::Bad(..) => write!(f, "{}", Yellow.paint("Bad Completion")),
|
||||
Outcome::FailStop => write!(f, "{}", Red.paint("Failed")),
|
||||
Outcome::FailForward(..) => write!(f, "{}", Cyan.paint("Forwarding")),
|
||||
Outcome::Failure => write!(f, "{}", Red.paint("Failure")),
|
||||
Outcome::Forward(..) => write!(f, "{}", Cyan.paint("Forwarding")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
use std::io::{BufRead, Read, Cursor, BufReader};
|
||||
use std::net::TcpStream;
|
||||
|
||||
use request::Request;
|
||||
|
||||
pub struct Data {
|
||||
stream: Cursor<Vec<u8>>,
|
||||
buffer: Vec<u8>
|
||||
}
|
||||
|
||||
impl Data {
|
||||
fn open(self) -> impl BufRead {
|
||||
Cursor::new(self.buffer).chain(BufReader::new(self.stream))
|
||||
}
|
||||
|
||||
fn peek(&self) -> &[u8] {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
pub fn new() -> Data {
|
||||
Data {
|
||||
stream: Cursor::new(vec![]),
|
||||
buffer: vec![]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,47 +3,47 @@ use std::fmt::Debug;
|
|||
use request::Request;
|
||||
use http::{ContentType, Method, Cookies};
|
||||
|
||||
pub trait FromRequest<'r, 'c>: Sized {
|
||||
pub trait FromRequest<'r>: Sized {
|
||||
type Error: Debug;
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error>;
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for &'r Request<'c> {
|
||||
impl<'r> FromRequest<'r> for &'r Request {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for Method {
|
||||
impl<'r> FromRequest<'r> for Method {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
Ok(request.method)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for &'r Cookies {
|
||||
impl<'r> FromRequest<'r> for &'r Cookies {
|
||||
type Error = ();
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
Ok(request.cookies())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for ContentType {
|
||||
impl<'r> FromRequest<'r> for ContentType {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
Ok(request.content_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c, T: FromRequest<'r, 'c>> FromRequest<'r, 'c> for Option<T> {
|
||||
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
let opt = match T::from_request(request) {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => None,
|
||||
|
@ -53,10 +53,10 @@ impl<'r, 'c, T: FromRequest<'r, 'c>> FromRequest<'r, 'c> for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r, 'c, T: FromRequest<'r, 'c>> FromRequest<'r, 'c> for Result<T, T::Error> {
|
||||
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
Ok(T::from_request(request))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
mod request;
|
||||
mod param;
|
||||
mod form;
|
||||
mod data;
|
||||
mod from_request;
|
||||
|
||||
pub use self::request::Request;
|
||||
pub use self::from_request::FromRequest;
|
||||
pub use self::param::{FromParam, FromSegments};
|
||||
pub use self::form::{FromForm, FromFormValue, FormItems};
|
||||
pub use self::data::{Data};
|
||||
|
|
|
@ -20,7 +20,7 @@ use http::{Method, ContentType, Cookies};
|
|||
/// [FromRequest](trait.FromRequest.html) implementations. It contains all of
|
||||
/// the information for a given web request. This includes the HTTP method, URI,
|
||||
/// cookies, headers, and more.
|
||||
pub struct Request<'a> {
|
||||
pub struct Request {
|
||||
/// The HTTP method associated with the request.
|
||||
pub method: Method,
|
||||
/// <div class="stability" style="margin-left: 0;">
|
||||
|
@ -37,10 +37,9 @@ pub struct Request<'a> {
|
|||
params: RefCell<Vec<&'static str>>,
|
||||
cookies: Cookies,
|
||||
headers: HyperHeaders, // This sucks.
|
||||
_phantom: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> {
|
||||
impl Request {
|
||||
/// Retrieves and parses into `T` the `n`th dynamic parameter from the
|
||||
/// request. Returns `Error::NoKey` if `n` is greater than the number of
|
||||
/// params. Returns `Error::BadParse` if the parameter type `T` can't be
|
||||
|
@ -108,7 +107,6 @@ impl<'a> Request<'a> {
|
|||
uri: URIBuf::from(uri),
|
||||
data: vec![],
|
||||
headers: HyperHeaders::new(),
|
||||
_phantom: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,7 +189,7 @@ impl<'a> Request<'a> {
|
|||
/// Create a Rocket Request from a Hyper Request.
|
||||
#[doc(hidden)]
|
||||
pub fn from_hyp<'h, 'k>(hyper_req: HyperRequest<'h, 'k>)
|
||||
-> Result<Request<'a>, String> {
|
||||
-> Result<Request, String> {
|
||||
let (_, h_method, h_headers, h_uri, _, mut h_body) = hyper_req.deconstruct();
|
||||
|
||||
let uri = match h_uri {
|
||||
|
@ -221,14 +219,13 @@ impl<'a> Request<'a> {
|
|||
uri: uri,
|
||||
data: data,
|
||||
headers: h_headers,
|
||||
_phantom: None,
|
||||
};
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> fmt::Display for Request<'r> {
|
||||
impl fmt::Display for Request {
|
||||
/// Pretty prints a Request. This is primarily used by Rocket's logging
|
||||
/// infrastructure.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
|
@ -7,9 +7,27 @@ use http::hyper::StatusCode;
|
|||
pub struct Empty(StatusCode);
|
||||
|
||||
impl Empty {
|
||||
#[inline(always)]
|
||||
pub fn new(status: StatusCode) -> Empty {
|
||||
Empty(status)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn not_found() -> Empty {
|
||||
Empty::new(StatusCode::NotFound)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn server_error(reason: &str) -> Empty {
|
||||
warn_!("internal server error: {}", reason);
|
||||
Empty::new(StatusCode::InternalServerError)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn bad_request(reason: &str) -> Empty {
|
||||
warn_!("internal server error: {}", reason);
|
||||
Empty::new(StatusCode::BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for Empty {
|
||||
|
@ -22,11 +40,3 @@ impl Responder for Empty {
|
|||
Outcome::Success
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Forward;
|
||||
|
||||
impl Responder for Forward {
|
||||
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||
Outcome::FailForward(res)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,10 +76,10 @@ impl Flash<()> {
|
|||
// be dropped needlessly. This may or may not be the intended behavior.
|
||||
// Alternatively, provide a guarantee about the order that from_request params
|
||||
// will be evaluated and recommend that Flash is last.
|
||||
impl<'r, 'c> FromRequest<'r, 'c> for Flash<()> {
|
||||
impl<'r> FromRequest<'r> for Flash<()> {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
|
||||
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
|
||||
trace_!("Flash: attemping to retrieve message.");
|
||||
request.cookies().find(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| {
|
||||
// Clear the flash message.
|
||||
|
|
|
@ -9,69 +9,60 @@ mod stream;
|
|||
pub mod data;
|
||||
|
||||
pub use self::responder::Responder;
|
||||
pub use self::empty::{Empty, Forward};
|
||||
pub use self::empty::Empty;
|
||||
pub use self::redirect::Redirect;
|
||||
pub use self::with_status::StatusResponse;
|
||||
pub use self::flash::Flash;
|
||||
pub use self::named_file::NamedFile;
|
||||
pub use self::stream::Stream;
|
||||
pub use self::data::Content;
|
||||
pub use outcome::{Outcome, ResponseOutcome};
|
||||
pub use outcome::Outcome;
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use http::hyper::StatusCode;
|
||||
use std::fmt;
|
||||
use request::Data;
|
||||
use http::hyper::{StatusCode, FreshHyperResponse};
|
||||
use term_painter::Color::*;
|
||||
use term_painter::ToStyle;
|
||||
|
||||
pub struct Response<'a>(Box<Responder + 'a>);
|
||||
pub type ResponseOutcome<'a> = Outcome<(StatusCode, FreshHyperResponse<'a>)>;
|
||||
|
||||
pub enum Response<'a> {
|
||||
Forward(Data),
|
||||
Complete(Box<Responder + 'a>)
|
||||
}
|
||||
|
||||
impl<'a> Response<'a> {
|
||||
#[inline(always)]
|
||||
pub fn new<T: Responder + 'a>(body: T) -> Response<'a> {
|
||||
Response(Box::new(body))
|
||||
Response::Complete(Box::new(body))
|
||||
}
|
||||
|
||||
pub fn with_status<T: Responder + 'a>(status: StatusCode,
|
||||
body: T)
|
||||
-> Response<'a> {
|
||||
Response(Box::new(StatusResponse::new(status, body)))
|
||||
}
|
||||
|
||||
pub fn forward() -> Response<'static> {
|
||||
Response(Box::new(Forward))
|
||||
#[inline(always)]
|
||||
pub fn forward(data: Data) -> Response<'static> {
|
||||
Response::Forward(data)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_raw_status<T: Responder + 'a>(status: u16, body: T) -> Response<'a> {
|
||||
let status_code = StatusCode::from_u16(status);
|
||||
Response(Box::new(StatusResponse::new(status_code, body)))
|
||||
Response::new(StatusResponse::new(status_code, body))
|
||||
}
|
||||
|
||||
pub fn empty() -> Response<'static> {
|
||||
Response(Box::new(Empty::new(StatusCode::Ok)))
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn responder(self) -> Option<Box<Responder + 'a>> {
|
||||
match self {
|
||||
Response::Complete(responder) => Some(responder),
|
||||
_ => None
|
||||
}
|
||||
|
||||
pub fn not_found() -> Response<'static> {
|
||||
Response(Box::new(Empty::new(StatusCode::NotFound)))
|
||||
}
|
||||
|
||||
pub fn server_error(reason: &str) -> Response<'static> {
|
||||
warn_!("internal server error: {}", reason);
|
||||
Response(Box::new(Empty::new(StatusCode::InternalServerError)))
|
||||
}
|
||||
|
||||
pub fn bad_request(reason: &str) -> Response<'static> {
|
||||
warn_!("bad request from user: {}", reason);
|
||||
Response(Box::new(Empty::new(StatusCode::BadRequest)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Response<'a> {
|
||||
type Target = Box<Responder + 'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
impl<'a> fmt::Display for Response<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Response::Complete(..) => write!(f, "{}", Green.paint("Complete")),
|
||||
Response::Forward(..) => write!(f, "{}", Yellow.paint("Forwarding")),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for Response<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Box<Responder + 'a> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,10 +37,6 @@ impl Responder for String {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: Should we set a content-type here? Safari needs text/html to render.
|
||||
// Unfortunately, the file name is gone at this point. Should fix this. There's
|
||||
// a way to retrieve a file based on its fd, strangely enough. See...
|
||||
// https://stackoverflow.com/questions/1188757/getting-filename-from-file-descriptor-in-c
|
||||
impl Responder for File {
|
||||
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||
let size = self.metadata().unwrap().len();
|
||||
|
@ -60,9 +56,8 @@ impl Responder for File {
|
|||
impl<T: Responder> Responder for Option<T> {
|
||||
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||
if self.is_none() {
|
||||
trace!("Option is none.");
|
||||
// TODO: Should this be a 404 or 500?
|
||||
return Outcome::FailForward(res);
|
||||
warn_!("response was `None`");
|
||||
return Outcome::Forward((StatusCode::NotFound, res));
|
||||
}
|
||||
|
||||
self.as_mut().unwrap().respond(res)
|
||||
|
@ -74,8 +69,7 @@ impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
|
|||
default fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
|
||||
if self.is_err() {
|
||||
error_!("{:?}", self.as_ref().err().unwrap());
|
||||
// TODO: Should this be a 404 or 500?
|
||||
return Outcome::FailForward(res);
|
||||
return Outcome::Forward((StatusCode::InternalServerError, res));
|
||||
}
|
||||
|
||||
self.as_mut().unwrap().respond(res)
|
||||
|
|
|
@ -39,20 +39,20 @@ impl<T: Read> Responder for Stream<T> {
|
|||
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||
Err(ref e) => {
|
||||
error_!("Error streaming response: {:?}", e);
|
||||
return Outcome::FailStop;
|
||||
return Outcome::Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = stream.write_all(&buffer[..read]) {
|
||||
error_!("Stream write_all() failed: {:?}", e);
|
||||
return Outcome::FailStop;
|
||||
return Outcome::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = stream.end() {
|
||||
error_!("Stream end() failed: {:?}", e);
|
||||
return Outcome::FailStop;
|
||||
return Outcome::Failure;
|
||||
}
|
||||
|
||||
Outcome::Success
|
||||
|
|
|
@ -8,13 +8,14 @@ use term_painter::ToStyle;
|
|||
|
||||
use config;
|
||||
use logger;
|
||||
use request::{Request, FormItems};
|
||||
use request::{Request, Data, FormItems};
|
||||
use response::{Response};
|
||||
use router::{Router, Route};
|
||||
use catcher::{self, Catcher};
|
||||
use outcome::Outcome;
|
||||
use error::Error;
|
||||
|
||||
use http::Method;
|
||||
use http::{Method, StatusCode};
|
||||
use http::hyper::{HyperRequest, FreshHyperResponse};
|
||||
use http::hyper::{HyperServer, HyperHandler, HyperSetCookie, header};
|
||||
|
||||
|
@ -22,6 +23,7 @@ pub struct Rocket {
|
|||
address: String,
|
||||
port: usize,
|
||||
router: Router,
|
||||
default_catchers: HashMap<u16, Catcher>,
|
||||
catchers: HashMap<u16, Catcher>,
|
||||
}
|
||||
|
||||
|
@ -50,10 +52,14 @@ impl Rocket {
|
|||
Err(ref reason) => {
|
||||
let mock_request = Request::mock(Method::Get, uri.as_str());
|
||||
debug_!("Bad request: {}", reason);
|
||||
return self.handle_internal_error(&mock_request, res);
|
||||
return self.handle_error(StatusCode::InternalServerError,
|
||||
&mock_request, res);
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieve the data from the request.
|
||||
let mut data = Data::new();
|
||||
|
||||
info!("{}:", request);
|
||||
let matches = self.router.route(&request);
|
||||
for route in matches {
|
||||
|
@ -61,27 +67,39 @@ impl Rocket {
|
|||
info_!("Matched: {}", route);
|
||||
request.set_params(route);
|
||||
|
||||
// Dispatch the request to the handler and update the cookies.
|
||||
let mut responder = (route.handler)(&request);
|
||||
// Dispatch the request to the handler.
|
||||
let response = (route.handler)(&request, data);
|
||||
|
||||
// Check if the request processing completed or if the request needs
|
||||
// to be forwarded. If it does, continue the loop to try again.
|
||||
info_!("{} {}", White.paint("Response:"), response);
|
||||
let mut responder = match response {
|
||||
Response::Complete(responder) => responder,
|
||||
Response::Forward(unused_data) => {
|
||||
data = unused_data;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// We have a responder. Update the cookies in the header.
|
||||
let cookie_delta = request.cookies().delta();
|
||||
if cookie_delta.len() > 0 {
|
||||
res.headers_mut().set(HyperSetCookie(cookie_delta));
|
||||
}
|
||||
|
||||
// Get the response.
|
||||
// Actually process the response.
|
||||
let outcome = responder.respond(res);
|
||||
info_!("{} {}", White.paint("Outcome:"), outcome);
|
||||
|
||||
// Get the result if we failed forward so we can try again.
|
||||
res = match outcome {
|
||||
Outcome::Success | Outcome::FailStop => return,
|
||||
Outcome::FailForward(r) => r,
|
||||
Outcome::Bad(r) => return self.handle_internal_error(&request, r),
|
||||
match outcome {
|
||||
Outcome::Forward((c, r)) => return self.handle_error(c, &request, r),
|
||||
Outcome::Success | Outcome::Failure => return,
|
||||
};
|
||||
}
|
||||
|
||||
error_!("No matching routes.");
|
||||
self.handle_not_found(&request, res);
|
||||
self.handle_error(StatusCode::NotFound, &request, res);
|
||||
}
|
||||
|
||||
/// Preprocess the request for Rocket-specific things. At this time, we're
|
||||
|
@ -105,22 +123,25 @@ impl Rocket {
|
|||
}
|
||||
}
|
||||
|
||||
// Call on internal server error.
|
||||
fn handle_internal_error<'r>(&self,
|
||||
request: &'r Request<'r>,
|
||||
response: FreshHyperResponse) {
|
||||
error_!("Internal server error.");
|
||||
let catcher = self.catchers.get(&500).unwrap();
|
||||
catcher.handle(Error::Internal, request).respond(response);
|
||||
}
|
||||
|
||||
// Call when no route was found.
|
||||
fn handle_not_found<'r>(&self,
|
||||
request: &'r Request<'r>,
|
||||
fn handle_error<'r>(&self,
|
||||
code: StatusCode,
|
||||
req: &'r Request,
|
||||
response: FreshHyperResponse) {
|
||||
error_!("{} dispatch failed: 404.", request);
|
||||
let catcher = self.catchers.get(&404).unwrap();
|
||||
catcher.handle(Error::NoRoute, request).respond(response);
|
||||
error_!("dispatch failed: {}.", code);
|
||||
let catcher = self.catchers.get(&code.to_u16()).unwrap();
|
||||
|
||||
if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
|
||||
if responder.respond(response) != Outcome::Success {
|
||||
error_!("catcher outcome was unsuccessul; aborting response");
|
||||
}
|
||||
} else {
|
||||
error_!("catcher returned an incomplete response");
|
||||
warn_!("using default error response");
|
||||
let catcher = self.default_catchers.get(&code.to_u16()).unwrap();
|
||||
let responder = catcher.handle(Error::Internal, req).responder();
|
||||
responder.unwrap().respond(response).expect_success()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mount(mut self, base: &'static str, routes: Vec<Route>) -> Self {
|
||||
|
@ -200,6 +221,7 @@ impl Rocket {
|
|||
address: config.active().address.clone(),
|
||||
port: config.active().port,
|
||||
router: Router::new(),
|
||||
default_catchers: catcher::defaults::get(),
|
||||
catchers: catcher::defaults::get(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ mod tests {
|
|||
use std::str::FromStr;
|
||||
|
||||
use router::Collider;
|
||||
use request::Request;
|
||||
use request::{Request, Data};
|
||||
use response::Response;
|
||||
use router::route::Route;
|
||||
use http::{Method, ContentType};
|
||||
|
@ -59,8 +59,8 @@ mod tests {
|
|||
|
||||
type SimpleRoute = (Method, &'static str);
|
||||
|
||||
fn dummy_handler(_req: &Request) -> Response<'static> {
|
||||
Response::empty()
|
||||
fn dummy_handler(_req: &Request, _: Data) -> Response<'static> {
|
||||
Response::new("hi")
|
||||
}
|
||||
|
||||
fn m_collide(a: SimpleRoute, b: SimpleRoute) -> bool {
|
||||
|
|
|
@ -72,10 +72,10 @@ mod test {
|
|||
use http::Method;
|
||||
use http::Method::*;
|
||||
use http::uri::URI;
|
||||
use {Response, Request};
|
||||
use {Response, Request, Data};
|
||||
|
||||
fn dummy_handler(_req: &Request) -> Response<'static> {
|
||||
Response::empty()
|
||||
fn dummy_handler(_req: &Request, _: Data) -> Response<'static> {
|
||||
Response::new("hi")
|
||||
}
|
||||
|
||||
fn router_with_routes(routes: &[&'static str]) -> Router {
|
||||
|
|
|
@ -79,6 +79,18 @@ impl Route {
|
|||
}
|
||||
}
|
||||
|
||||
impl Clone for Route {
|
||||
fn clone(&self) -> Route {
|
||||
Route {
|
||||
method: self.method,
|
||||
handler: self.handler,
|
||||
rank: self.rank,
|
||||
path: self.path.clone(),
|
||||
content_type: self.content_type.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Route {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.path))?;
|
||||
|
@ -122,7 +134,7 @@ impl Collider for Route {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r> Collider<Request<'r>> for Route {
|
||||
impl Collider<Request> for Route {
|
||||
fn collides_with(&self, req: &Request) -> bool {
|
||||
self.method == req.method
|
||||
&& req.uri().collides_with(&self.path.as_uri())
|
||||
|
|
Loading…
Reference in New Issue