mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-27 17:52:56 +01:00
refactor HttpRequest mutability
This commit is contained in:
parent
445ea043dd
commit
fec6047ddc
@ -7,12 +7,13 @@ use http::{Method, StatusCode};
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::Middleware;
|
||||
use param::Params;
|
||||
use pipeline::{HandlerType, Pipeline, PipelineHandler};
|
||||
use pred::Predicate;
|
||||
use resource::ResourceHandler;
|
||||
use router::{Resource, Router};
|
||||
use router::{Resource, RouteInfo, Router};
|
||||
use scope::Scope;
|
||||
use server::{HttpHandler, HttpHandlerTask, IntoHttpHandler, ServerSettings};
|
||||
use server::{HttpHandler, HttpHandlerTask, IntoHttpHandler, Request, ServerSettings};
|
||||
|
||||
/// Application
|
||||
pub struct HttpApplication<S = ()> {
|
||||
@ -46,36 +47,34 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
|
||||
}
|
||||
|
||||
fn handle(
|
||||
&self, req: HttpRequest<S>, htype: HandlerType,
|
||||
&self, req: &HttpRequest<S>, htype: HandlerType,
|
||||
) -> AsyncResult<HttpResponse> {
|
||||
match htype {
|
||||
HandlerType::Normal(idx) => match self.resources[idx].handle(req) {
|
||||
Ok(result) => result,
|
||||
Err(req) => match self.default.handle(req) {
|
||||
Ok(result) => result,
|
||||
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
|
||||
},
|
||||
},
|
||||
HandlerType::Normal(idx) => {
|
||||
if let Some(id) = self.resources[idx].get_route_id(req) {
|
||||
return self.resources[idx].handle(id, req);
|
||||
}
|
||||
}
|
||||
HandlerType::Handler(idx) => match self.handlers[idx] {
|
||||
PrefixHandlerType::Handler(_, ref hnd) => hnd.handle(req),
|
||||
PrefixHandlerType::Scope(_, ref hnd, _) => hnd.handle(req),
|
||||
},
|
||||
HandlerType::Default => match self.default.handle(req) {
|
||||
Ok(result) => result,
|
||||
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
|
||||
PrefixHandlerType::Handler(_, ref hnd) => return hnd.handle(req),
|
||||
PrefixHandlerType::Scope(_, ref hnd, _) => return hnd.handle(req),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
if let Some(id) = self.default.get_route_id(req) {
|
||||
self.default.handle(id, req)
|
||||
} else {
|
||||
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> HttpApplication<S> {
|
||||
#[inline]
|
||||
fn get_handler(&self, req: &mut HttpRequest<S>) -> HandlerType {
|
||||
if let Some(idx) = self.router.recognize(req) {
|
||||
HandlerType::Normal(idx)
|
||||
fn get_handler(&self, req: &Request) -> (RouteInfo, HandlerType) {
|
||||
if let Some((idx, info)) = self.router.recognize(req) {
|
||||
(info, HandlerType::Normal(idx))
|
||||
} else {
|
||||
req.match_info_mut().set_tail(0);
|
||||
|
||||
'outer: for idx in 0..self.inner.handlers.len() {
|
||||
match self.inner.handlers[idx] {
|
||||
PrefixHandlerType::Handler(ref prefix, _) => {
|
||||
@ -90,77 +89,68 @@ impl<S: 'static> HttpApplication<S> {
|
||||
|
||||
if m {
|
||||
let prefix_len = (self.inner.prefix + prefix.len()) as u16;
|
||||
let url = req.url().clone();
|
||||
req.set_prefix_len(prefix_len);
|
||||
req.match_info_mut().set_url(url);
|
||||
req.match_info_mut().set_tail(prefix_len);
|
||||
return HandlerType::Handler(idx);
|
||||
let info = self.router.route_info(req, prefix_len);
|
||||
return (info, HandlerType::Handler(idx));
|
||||
}
|
||||
}
|
||||
PrefixHandlerType::Scope(ref pattern, _, ref filters) => {
|
||||
if let Some(prefix_len) =
|
||||
if let Some(params) =
|
||||
pattern.match_prefix_with_params(req, self.inner.prefix)
|
||||
{
|
||||
for filter in filters {
|
||||
if !filter.check(req) {
|
||||
if !filter.check(req, &self.state) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
let prefix_len = (self.inner.prefix + prefix_len) as u16;
|
||||
let url = req.url().clone();
|
||||
req.set_prefix_len(prefix_len);
|
||||
let params = req.match_info_mut();
|
||||
params.set_tail(prefix_len);
|
||||
params.set_url(url);
|
||||
return HandlerType::Handler(idx);
|
||||
let info = self
|
||||
.router
|
||||
.route_info_params(params, self.inner.prefix as u16);
|
||||
return (info, HandlerType::Handler(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
HandlerType::Default
|
||||
(
|
||||
self.router.default_route_info(self.inner.prefix as u16),
|
||||
HandlerType::Default,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn run(&self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
let tp = self.get_handler(&mut req);
|
||||
self.inner.handle(req, tp)
|
||||
}
|
||||
pub(crate) fn run(&self, mut req: Request) -> AsyncResult<HttpResponse> {
|
||||
let (info, tp) = self.get_handler(&req);
|
||||
let req = HttpRequest::new(req, Rc::clone(&self.state), info);
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest<S> {
|
||||
req.with_state(Rc::clone(&self.state), self.router.clone())
|
||||
self.inner.handle(&req, tp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> HttpHandler for HttpApplication<S> {
|
||||
type Task = Pipeline<S, Inner<S>>;
|
||||
|
||||
fn handle(&self, req: HttpRequest) -> Result<Pipeline<S, Inner<S>>, HttpRequest> {
|
||||
fn handle(&self, mut msg: Request) -> Result<Pipeline<S, Inner<S>>, Request> {
|
||||
let m = {
|
||||
let path = req.path();
|
||||
let path = msg.path();
|
||||
path.starts_with(&self.prefix)
|
||||
&& (path.len() == self.prefix_len
|
||||
|| path.split_at(self.prefix_len).1.starts_with('/'))
|
||||
};
|
||||
if m {
|
||||
let mut req2 =
|
||||
req.clone_with_state(Rc::clone(&self.state), self.router.clone());
|
||||
|
||||
if let Some(ref filters) = self.filters {
|
||||
for filter in filters {
|
||||
if !filter.check(&mut req2) {
|
||||
return Err(req);
|
||||
if !filter.check(&msg, &self.state) {
|
||||
return Err(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tp = self.get_handler(&mut req2);
|
||||
let (info, tp) = self.get_handler(&msg);
|
||||
let inner = Rc::clone(&self.inner);
|
||||
Ok(Pipeline::new(req2, Rc::clone(&self.middlewares), inner, tp))
|
||||
let req = HttpRequest::new(msg, Rc::clone(&self.state), info);
|
||||
Ok(Pipeline::new(req, Rc::clone(&self.middlewares), inner, tp))
|
||||
} else {
|
||||
Err(req)
|
||||
Err(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +158,6 @@ impl<S: 'static> HttpHandler for HttpApplication<S> {
|
||||
struct ApplicationParts<S> {
|
||||
state: S,
|
||||
prefix: String,
|
||||
settings: ServerSettings,
|
||||
default: Rc<ResourceHandler<S>>,
|
||||
resources: Vec<(Resource, Option<ResourceHandler<S>>)>,
|
||||
handlers: Vec<PrefixHandlerType<S>>,
|
||||
@ -219,7 +208,6 @@ where
|
||||
parts: Some(ApplicationParts {
|
||||
state,
|
||||
prefix: "/".to_owned(),
|
||||
settings: ServerSettings::default(),
|
||||
default: Rc::new(ResourceHandler::default_not_found()),
|
||||
resources: Vec::new(),
|
||||
handlers: Vec::new(),
|
||||
@ -498,7 +486,7 @@ where
|
||||
/// # extern crate actix_web;
|
||||
/// use actix_web::{App, HttpRequest, HttpResponse, Result};
|
||||
///
|
||||
/// fn index(mut req: HttpRequest) -> Result<HttpResponse> {
|
||||
/// fn index(req: &HttpRequest) -> Result<HttpResponse> {
|
||||
/// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?;
|
||||
/// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
|
||||
/// Ok(HttpResponse::Ok().into())
|
||||
@ -544,7 +532,7 @@ where
|
||||
/// use actix_web::{http, App, HttpRequest, HttpResponse};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new().handler("/app", |req: HttpRequest| match *req.method() {
|
||||
/// let app = App::new().handler("/app", |req: &HttpRequest| match *req.method() {
|
||||
/// http::Method::GET => HttpResponse::Ok(),
|
||||
/// http::Method::POST => HttpResponse::MethodNotAllowed(),
|
||||
/// _ => HttpResponse::NotFound(),
|
||||
@ -636,8 +624,7 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
let (router, resources) =
|
||||
Router::new(&prefix, parts.settings.clone(), resources);
|
||||
let (router, resources) = Router::new(&prefix, resources);
|
||||
|
||||
let inner = Rc::new(Inner {
|
||||
prefix: prefix_len,
|
||||
@ -708,7 +695,7 @@ struct BoxedApplication<S> {
|
||||
impl<S: 'static> HttpHandler for BoxedApplication<S> {
|
||||
type Task = Box<HttpHandlerTask>;
|
||||
|
||||
fn handle(&self, req: HttpRequest) -> Result<Self::Task, HttpRequest> {
|
||||
fn handle(&self, req: Request) -> Result<Self::Task, Request> {
|
||||
self.app.handle(req).map(|t| {
|
||||
let task: Self::Task = Box::new(t);
|
||||
task
|
||||
@ -719,11 +706,7 @@ impl<S: 'static> HttpHandler for BoxedApplication<S> {
|
||||
impl<S: 'static> IntoHttpHandler for App<S> {
|
||||
type Handler = HttpApplication<S>;
|
||||
|
||||
fn into_handler(mut self, settings: ServerSettings) -> HttpApplication<S> {
|
||||
{
|
||||
let parts = self.parts.as_mut().expect("Use after finish");
|
||||
parts.settings = settings;
|
||||
}
|
||||
fn into_handler(mut self) -> HttpApplication<S> {
|
||||
self.finish()
|
||||
}
|
||||
}
|
||||
@ -731,11 +714,7 @@ impl<S: 'static> IntoHttpHandler for App<S> {
|
||||
impl<'a, S: 'static> IntoHttpHandler for &'a mut App<S> {
|
||||
type Handler = HttpApplication<S>;
|
||||
|
||||
fn into_handler(self, settings: ServerSettings) -> HttpApplication<S> {
|
||||
{
|
||||
let parts = self.parts.as_mut().expect("Use after finish");
|
||||
parts.settings = settings;
|
||||
}
|
||||
fn into_handler(self) -> HttpApplication<S> {
|
||||
self.finish()
|
||||
}
|
||||
}
|
||||
@ -769,18 +748,18 @@ mod tests {
|
||||
.resource("/test", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/test").finish();
|
||||
let req = TestRequest::with_uri("/test").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/blah").finish();
|
||||
let req = TestRequest::with_uri("/blah").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let app = App::new()
|
||||
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
|
||||
.finish();
|
||||
let req = TestRequest::with_uri("/blah").finish();
|
||||
let req = TestRequest::with_uri("/blah").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
@ -791,7 +770,8 @@ mod tests {
|
||||
.prefix("/test")
|
||||
.resource("/test", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.finish();
|
||||
assert!(app.handle(HttpRequest::default()).is_err());
|
||||
let ctx = TestRequest::default().request();
|
||||
assert!(app.handle(ctx).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -799,8 +779,7 @@ mod tests {
|
||||
let app = App::with_state(10)
|
||||
.resource("/", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.finish();
|
||||
let req =
|
||||
HttpRequest::default().with_state(Rc::clone(&app.state), app.router.clone());
|
||||
let req = TestRequest::with_state(10).request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
@ -811,69 +790,73 @@ mod tests {
|
||||
.prefix("/test")
|
||||
.resource("/blah", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.finish();
|
||||
let req = TestRequest::with_uri("/test").finish();
|
||||
let req = TestRequest::with_uri("/test").request();
|
||||
let resp = app.handle(req);
|
||||
assert!(resp.is_ok());
|
||||
|
||||
let req = TestRequest::with_uri("/test/").finish();
|
||||
let req = TestRequest::with_uri("/test/").request();
|
||||
let resp = app.handle(req);
|
||||
assert!(resp.is_ok());
|
||||
|
||||
let req = TestRequest::with_uri("/test/blah").finish();
|
||||
let req = TestRequest::with_uri("/test/blah").request();
|
||||
let resp = app.handle(req);
|
||||
assert!(resp.is_ok());
|
||||
|
||||
let req = TestRequest::with_uri("/testing").finish();
|
||||
let req = TestRequest::with_uri("/testing").request();
|
||||
let resp = app.handle(req);
|
||||
assert!(resp.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_handler() {
|
||||
let app = App::new().handler("/test", |_| HttpResponse::Ok()).finish();
|
||||
let app = App::new()
|
||||
.handler("/test", |_: &_| HttpResponse::Ok())
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/test").finish();
|
||||
let req = TestRequest::with_uri("/test").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/test/").finish();
|
||||
let req = TestRequest::with_uri("/test/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/test/app").finish();
|
||||
let req = TestRequest::with_uri("/test/app").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/testapp").finish();
|
||||
let req = TestRequest::with_uri("/testapp").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/blah").finish();
|
||||
let req = TestRequest::with_uri("/blah").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_handler2() {
|
||||
let app = App::new().handler("test", |_| HttpResponse::Ok()).finish();
|
||||
let app = App::new()
|
||||
.handler("test", |_: &_| HttpResponse::Ok())
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/test").finish();
|
||||
let req = TestRequest::with_uri("/test").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/test/").finish();
|
||||
let req = TestRequest::with_uri("/test/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/test/app").finish();
|
||||
let req = TestRequest::with_uri("/test/app").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/testapp").finish();
|
||||
let req = TestRequest::with_uri("/testapp").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/blah").finish();
|
||||
let req = TestRequest::with_uri("/blah").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -882,26 +865,26 @@ mod tests {
|
||||
fn test_handler_with_prefix() {
|
||||
let app = App::new()
|
||||
.prefix("prefix")
|
||||
.handler("/test", |_| HttpResponse::Ok())
|
||||
.handler("/test", |_: &_| HttpResponse::Ok())
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/prefix/test").finish();
|
||||
let req = TestRequest::with_uri("/prefix/test").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/prefix/test/").finish();
|
||||
let req = TestRequest::with_uri("/prefix/test/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/prefix/test/app").finish();
|
||||
let req = TestRequest::with_uri("/prefix/test/app").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/prefix/testapp").finish();
|
||||
let req = TestRequest::with_uri("/prefix/testapp").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/prefix/blah").finish();
|
||||
let req = TestRequest::with_uri("/prefix/blah").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -915,15 +898,19 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/test").method(Method::GET).finish();
|
||||
let req = TestRequest::with_uri("/test").method(Method::GET).request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/test").method(Method::POST).finish();
|
||||
let req = TestRequest::with_uri("/test")
|
||||
.method(Method::POST)
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
|
||||
let req = TestRequest::with_uri("/test").method(Method::HEAD).finish();
|
||||
let req = TestRequest::with_uri("/test")
|
||||
.method(Method::HEAD)
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -932,31 +919,30 @@ mod tests {
|
||||
fn test_handler_prefix() {
|
||||
let app = App::new()
|
||||
.prefix("/app")
|
||||
.handler("/test", |_| HttpResponse::Ok())
|
||||
.handler("/test", |_: &_| HttpResponse::Ok())
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/test").finish();
|
||||
let req = TestRequest::with_uri("/test").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/test").finish();
|
||||
let resp = app.run(req.clone());
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
assert_eq!(req.prefix_len(), 9);
|
||||
|
||||
let req = TestRequest::with_uri("/app/test/").finish();
|
||||
let req = TestRequest::with_uri("/app/test").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/test/app").finish();
|
||||
let req = TestRequest::with_uri("/app/test/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/testapp").finish();
|
||||
let req = TestRequest::with_uri("/app/test/app").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/testapp").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/blah").finish();
|
||||
let req = TestRequest::with_uri("/app/blah").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -966,7 +952,7 @@ mod tests {
|
||||
let mut srv = TestServer::with_factory(|| {
|
||||
App::new()
|
||||
.filter(pred::Get())
|
||||
.handler("/test", |_| HttpResponse::Ok())
|
||||
.handler("/test", |_: &_| HttpResponse::Ok())
|
||||
});
|
||||
|
||||
let request = srv.get().uri(srv.url("/test")).finish().unwrap();
|
||||
@ -985,11 +971,11 @@ mod tests {
|
||||
.resource("/some", |r| r.f(|_| Some("some")))
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/none").finish();
|
||||
let req = TestRequest::with_uri("/none").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/some").finish();
|
||||
let req = TestRequest::with_uri("/some").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
assert_eq!(resp.as_msg().body(), &Body::Binary(Binary::Slice(b"some")));
|
||||
|
@ -35,6 +35,7 @@ pub use self::connector::{
|
||||
Pause, Resume,
|
||||
};
|
||||
pub(crate) use self::parser::{HttpResponseParser, HttpResponseParserError};
|
||||
pub(crate) use self::pipeline::Pipeline;
|
||||
pub use self::pipeline::{SendRequest, SendRequestError};
|
||||
pub use self::request::{ClientRequest, ClientRequestBuilder};
|
||||
pub use self::response::ClientResponse;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::sync::oneshot;
|
||||
use futures::{Async, Future, Poll};
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use http::header::CONTENT_ENCODING;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{io, mem};
|
||||
@ -230,7 +230,7 @@ impl Future for SendRequest {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Pipeline {
|
||||
pub struct Pipeline {
|
||||
body: IoBody,
|
||||
body_completed: bool,
|
||||
conn: Option<Connection>,
|
||||
@ -315,7 +315,7 @@ impl Pipeline {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> {
|
||||
pub(crate) fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> {
|
||||
if self.conn.is_none() {
|
||||
return Ok(Async::Ready(None));
|
||||
}
|
||||
@ -522,3 +522,13 @@ impl Drop for Pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Future that resolves to a complete request body.
|
||||
impl Stream for Box<Pipeline> {
|
||||
type Item = Bytes;
|
||||
type Error = PayloadError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
Pipeline::poll(self)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::cell::RefCell;
|
||||
use std::{fmt, str};
|
||||
|
||||
use bytes::Bytes;
|
||||
@ -30,23 +31,33 @@ impl Default for ClientMessage {
|
||||
}
|
||||
|
||||
/// An HTTP Client response
|
||||
pub struct ClientResponse(ClientMessage, Option<Box<Pipeline>>);
|
||||
pub struct ClientResponse(ClientMessage, RefCell<Option<Box<Pipeline>>>);
|
||||
|
||||
impl HttpMessage for ClientResponse {
|
||||
type Stream = Box<Pipeline>;
|
||||
|
||||
/// Get the headers from the response.
|
||||
#[inline]
|
||||
fn headers(&self) -> &HeaderMap {
|
||||
&self.0.headers
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn payload(&self) -> Box<Pipeline> {
|
||||
self.1
|
||||
.borrow_mut()
|
||||
.take()
|
||||
.expect("Payload is already consumed.")
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientResponse {
|
||||
pub(crate) fn new(msg: ClientMessage) -> ClientResponse {
|
||||
ClientResponse(msg, None)
|
||||
ClientResponse(msg, RefCell::new(None))
|
||||
}
|
||||
|
||||
pub(crate) fn set_pipeline(&mut self, pl: Box<Pipeline>) {
|
||||
self.1 = Some(pl);
|
||||
*self.1.borrow_mut() = Some(pl);
|
||||
}
|
||||
|
||||
/// Get the HTTP version of this response.
|
||||
@ -95,20 +106,6 @@ impl fmt::Debug for ClientResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Future that resolves to a complete request body.
|
||||
impl Stream for ClientResponse {
|
||||
type Item = Bytes;
|
||||
type Error = PayloadError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if let Some(ref mut pl) = self.1 {
|
||||
pl.poll()
|
||||
} else {
|
||||
Ok(Async::Ready(None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -612,9 +612,6 @@ pub enum UrlGenerationError {
|
||||
/// Not all path pattern covered
|
||||
#[fail(display = "Not all path pattern covered")]
|
||||
NotEnoughElements,
|
||||
/// Router is not available
|
||||
#[fail(display = "Router is not available")]
|
||||
RouterNotAvailable,
|
||||
/// URL parse error
|
||||
#[fail(display = "{}", _0)]
|
||||
ParseError(#[cause] UrlParseError),
|
||||
|
@ -267,12 +267,12 @@ where
|
||||
|
||||
#[inline]
|
||||
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
|
||||
let req = req.clone();
|
||||
let req2 = req.clone();
|
||||
let err = Rc::clone(&cfg.ehandler);
|
||||
Box::new(
|
||||
UrlEncoded::new(req.clone())
|
||||
UrlEncoded::new(req)
|
||||
.limit(cfg.limit)
|
||||
.map_err(move |e| (*err)(e, req))
|
||||
.map_err(move |e| (*err)(e, &req2))
|
||||
.map(Form),
|
||||
)
|
||||
}
|
||||
@ -321,7 +321,7 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
|
||||
/// ```
|
||||
pub struct FormConfig<S> {
|
||||
limit: usize,
|
||||
ehandler: Rc<Fn(UrlencodedError, HttpRequest<S>) -> Error>,
|
||||
ehandler: Rc<Fn(UrlencodedError, &HttpRequest<S>) -> Error>,
|
||||
}
|
||||
|
||||
impl<S> FormConfig<S> {
|
||||
@ -334,7 +334,7 @@ impl<S> FormConfig<S> {
|
||||
/// Set custom error handler
|
||||
pub fn error_handler<F>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(UrlencodedError, HttpRequest<S>) -> Error + 'static,
|
||||
F: Fn(UrlencodedError, &HttpRequest<S>) -> Error + 'static,
|
||||
{
|
||||
self.ehandler = Rc::new(f);
|
||||
self
|
||||
@ -383,9 +383,7 @@ impl<S: 'static> FromRequest<S> for Bytes {
|
||||
// check content-type
|
||||
cfg.check_mimetype(req)?;
|
||||
|
||||
Ok(Box::new(
|
||||
MessageBody::new(req.clone()).limit(cfg.limit).from_err(),
|
||||
))
|
||||
Ok(Box::new(MessageBody::new(req).limit(cfg.limit).from_err()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,7 +427,7 @@ impl<S: 'static> FromRequest<S> for String {
|
||||
let encoding = req.encoding()?;
|
||||
|
||||
Ok(Box::new(
|
||||
MessageBody::new(req.clone())
|
||||
MessageBody::new(req)
|
||||
.limit(cfg.limit)
|
||||
.from_err()
|
||||
.and_then(move |body| {
|
||||
@ -617,7 +615,6 @@ mod tests {
|
||||
use mime;
|
||||
use resource::ResourceHandler;
|
||||
use router::{Resource, Router};
|
||||
use server::ServerSettings;
|
||||
use test::TestRequest;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
@ -628,9 +625,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_bytes() {
|
||||
let cfg = PayloadConfig::default();
|
||||
let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish();
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"hello=world"));
|
||||
let req = TestRequest::with_header(header::CONTENT_LENGTH, "11")
|
||||
.set_payload(Bytes::from_static(b"hello=world"))
|
||||
.finish();
|
||||
|
||||
match Bytes::from_request(&req, &cfg).unwrap().poll().unwrap() {
|
||||
Async::Ready(s) => {
|
||||
@ -643,9 +640,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let cfg = PayloadConfig::default();
|
||||
let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish();
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"hello=world"));
|
||||
let req = TestRequest::with_header(header::CONTENT_LENGTH, "11")
|
||||
.set_payload(Bytes::from_static(b"hello=world"))
|
||||
.finish();
|
||||
|
||||
match String::from_request(&req, &cfg).unwrap().poll().unwrap() {
|
||||
Async::Ready(s) => {
|
||||
@ -657,13 +654,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_form() {
|
||||
let mut req = TestRequest::with_header(
|
||||
let req = TestRequest::with_header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded",
|
||||
).header(header::CONTENT_LENGTH, "11")
|
||||
.set_payload(Bytes::from_static(b"hello=world"))
|
||||
.finish();
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"hello=world"));
|
||||
|
||||
let mut cfg = FormConfig::default();
|
||||
cfg.limit(4096);
|
||||
@ -677,7 +673,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_payload_config() {
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
let mut cfg = PayloadConfig::default();
|
||||
cfg.mimetype(mime::APPLICATION_JSON);
|
||||
assert!(cfg.check_mimetype(&req).is_err());
|
||||
@ -712,14 +708,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_request_extract() {
|
||||
let mut req = TestRequest::with_uri("/name/user1/?id=test").finish();
|
||||
let req = TestRequest::with_uri("/name/user1/?id=test").finish();
|
||||
|
||||
let mut resource = ResourceHandler::<()>::default();
|
||||
resource.name("index");
|
||||
let mut routes = Vec::new();
|
||||
routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource)));
|
||||
let (router, _) = Router::new("", ServerSettings::default(), routes);
|
||||
assert!(router.recognize(&mut req).is_some());
|
||||
let (router, _) = Router::new("", routes);
|
||||
let info = router.recognize(&req).unwrap().1;
|
||||
let req = req.with_route_info(info);
|
||||
|
||||
let s = Path::<MyStruct>::from_request(&req, &()).unwrap();
|
||||
assert_eq!(s.key, "name");
|
||||
@ -732,8 +729,9 @@ mod tests {
|
||||
let s = Query::<Id>::from_request(&req, &()).unwrap();
|
||||
assert_eq!(s.id, "test");
|
||||
|
||||
let mut req = TestRequest::with_uri("/name/32/").finish();
|
||||
assert!(router.recognize(&mut req).is_some());
|
||||
let req = TestRequest::with_uri("/name/32/").finish_with_router(router.clone());
|
||||
let info = router.recognize(&req).unwrap().1;
|
||||
let req = req.with_route_info(info);
|
||||
|
||||
let s = Path::<Test2>::from_request(&req, &()).unwrap();
|
||||
assert_eq!(s.as_ref().key, "name");
|
||||
@ -754,24 +752,26 @@ mod tests {
|
||||
resource.name("index");
|
||||
let mut routes = Vec::new();
|
||||
routes.push((Resource::new("index", "/{value}/"), Some(resource)));
|
||||
let (router, _) = Router::new("", ServerSettings::default(), routes);
|
||||
let (router, _) = Router::new("", routes);
|
||||
|
||||
let mut req = TestRequest::with_uri("/32/").finish();
|
||||
assert!(router.recognize(&mut req).is_some());
|
||||
|
||||
assert_eq!(*Path::<i8>::from_request(&mut req, &()).unwrap(), 32);
|
||||
let req = TestRequest::with_uri("/32/").finish_with_router(router.clone());
|
||||
let info = router.recognize(&req).unwrap().1;
|
||||
let req = req.with_route_info(info);
|
||||
assert_eq!(*Path::<i8>::from_request(&req, &()).unwrap(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_extract() {
|
||||
let mut req = TestRequest::with_uri("/name/user1/?id=test").finish();
|
||||
|
||||
let mut resource = ResourceHandler::<()>::default();
|
||||
resource.name("index");
|
||||
let mut routes = Vec::new();
|
||||
routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource)));
|
||||
let (router, _) = Router::new("", ServerSettings::default(), routes);
|
||||
assert!(router.recognize(&mut req).is_some());
|
||||
let (router, _) = Router::new("", routes);
|
||||
|
||||
let mut req = TestRequest::with_uri("/name/user1/?id=test")
|
||||
.finish_with_router(router.clone());
|
||||
let info = router.recognize(&req).unwrap().1;
|
||||
let req = req.with_route_info(info);
|
||||
|
||||
let res = match <(Path<(String, String)>,)>::extract(&req).poll() {
|
||||
Ok(Async::Ready(res)) => res,
|
||||
|
63
src/fs.rs
63
src/fs.rs
@ -2,6 +2,7 @@
|
||||
use std::fmt::Write;
|
||||
use std::fs::{DirEntry, File, Metadata};
|
||||
use std::io::{Read, Seek};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
@ -19,7 +20,9 @@ use mime_guess::{get_mime_type, guess_mime_type};
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
|
||||
use error::Error;
|
||||
use handler::{AsyncResult, Handler, Responder, RouteHandler, WrapHandler};
|
||||
use handler::{
|
||||
AsyncResult, AsyncResultItem, Handler, Responder, RouteHandler, WrapHandler,
|
||||
};
|
||||
use header;
|
||||
use http::{ContentEncoding, Method, StatusCode};
|
||||
use httpmessage::HttpMessage;
|
||||
@ -610,7 +613,7 @@ impl<S: 'static> StaticFiles<S> {
|
||||
index: None,
|
||||
show_index: false,
|
||||
cpu_pool: pool,
|
||||
default: Box::new(WrapHandler::new(|_| {
|
||||
default: Box::new(WrapHandler::new(|_: &_| {
|
||||
HttpResponse::new(StatusCode::NOT_FOUND)
|
||||
})),
|
||||
renderer: Box::new(directory_listing),
|
||||
@ -657,7 +660,7 @@ impl<S: 'static> StaticFiles<S> {
|
||||
impl<S: 'static> Handler<S> for StaticFiles<S> {
|
||||
type Result = Result<AsyncResult<HttpResponse>, Error>;
|
||||
|
||||
fn handle(&self, req: HttpRequest<S>) -> Self::Result {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
|
||||
if !self.accessible {
|
||||
Ok(self.default.handle(req))
|
||||
} else {
|
||||
@ -667,7 +670,9 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
|
||||
.map(|tail| PathBuf::from_param(tail.trim_left_matches('/')))
|
||||
{
|
||||
Some(Ok(path)) => path,
|
||||
_ => return Ok(self.default.handle(req)),
|
||||
_ => {
|
||||
return Ok(self.default.handle(req));
|
||||
}
|
||||
};
|
||||
|
||||
// full filepath
|
||||
@ -833,7 +838,8 @@ mod tests {
|
||||
let _f: &mut File = &mut file;
|
||||
}
|
||||
|
||||
let resp = file.respond_to(&HttpRequest::default()).unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/x-toml"
|
||||
@ -858,7 +864,8 @@ mod tests {
|
||||
let _f: &mut File = &mut file;
|
||||
}
|
||||
|
||||
let resp = file.respond_to(&HttpRequest::default()).unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/xml"
|
||||
@ -882,7 +889,8 @@ mod tests {
|
||||
let _f: &mut File = &mut file;
|
||||
}
|
||||
|
||||
let resp = file.respond_to(&HttpRequest::default()).unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"image/png"
|
||||
@ -916,7 +924,8 @@ mod tests {
|
||||
let _f: &mut File = &mut file;
|
||||
}
|
||||
|
||||
let resp = file.respond_to(&HttpRequest::default()).unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"image/png"
|
||||
@ -940,7 +949,8 @@ mod tests {
|
||||
let _f: &mut File = &mut file;
|
||||
}
|
||||
|
||||
let resp = file.respond_to(&HttpRequest::default()).unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"application/octet-stream"
|
||||
@ -965,7 +975,8 @@ mod tests {
|
||||
let _f: &mut File = &mut file;
|
||||
}
|
||||
|
||||
let resp = file.respond_to(&HttpRequest::default()).unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/x-toml"
|
||||
@ -1136,7 +1147,6 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(te, "chunked");
|
||||
}
|
||||
|
||||
let bytes = srv.execute(response.body()).unwrap();
|
||||
let data = Bytes::from(fs::read("tests/test.binary").unwrap());
|
||||
assert_eq!(bytes, data);
|
||||
@ -1178,27 +1188,22 @@ mod tests {
|
||||
fn test_static_files() {
|
||||
let mut st = StaticFiles::new(".").show_files_listing();
|
||||
st.accessible = false;
|
||||
let resp = st
|
||||
.handle(HttpRequest::default())
|
||||
.respond_to(&HttpRequest::default())
|
||||
.unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = st.handle(&req).respond_to(&req).unwrap();
|
||||
let resp = resp.as_msg();
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
st.accessible = true;
|
||||
st.show_index = false;
|
||||
let resp = st
|
||||
.handle(HttpRequest::default())
|
||||
.respond_to(&HttpRequest::default())
|
||||
.unwrap();
|
||||
let req = TestRequest::default().finish();
|
||||
let resp = st.handle(&req).respond_to(&req).unwrap();
|
||||
let resp = resp.as_msg();
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.match_info_mut().add_static("tail", "");
|
||||
let req = TestRequest::default().param("tail", "").finish();
|
||||
|
||||
st.show_index = true;
|
||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||
let resp = st.handle(&req).respond_to(&req).unwrap();
|
||||
let resp = resp.as_msg();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
@ -1213,7 +1218,7 @@ mod tests {
|
||||
let st = StaticFiles::new(".").index_file("index.html");
|
||||
let req = TestRequest::default().uri("/tests").finish();
|
||||
|
||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||
let resp = st.handle(&req).respond_to(&req).unwrap();
|
||||
let resp = resp.as_msg();
|
||||
assert_eq!(resp.status(), StatusCode::FOUND);
|
||||
assert_eq!(
|
||||
@ -1222,8 +1227,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let req = TestRequest::default().uri("/tests/").finish();
|
||||
|
||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||
let resp = st.handle(&req).respond_to(&req).unwrap();
|
||||
let resp = resp.as_msg();
|
||||
assert_eq!(resp.status(), StatusCode::FOUND);
|
||||
assert_eq!(
|
||||
@ -1234,15 +1238,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_redirect_to_index_nested() {
|
||||
let st = StaticFiles::new(".").index_file("Cargo.toml");
|
||||
let req = TestRequest::default().uri("/tools/wsload").finish();
|
||||
|
||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||
let st = StaticFiles::new(".").index_file("mod.rs");
|
||||
let req = TestRequest::default().uri("/src/client").finish();
|
||||
let resp = st.handle(&req).respond_to(&req).unwrap();
|
||||
let resp = resp.as_msg();
|
||||
assert_eq!(resp.status(), StatusCode::FOUND);
|
||||
assert_eq!(
|
||||
resp.headers().get(header::LOCATION).unwrap(),
|
||||
"/tools/wsload/Cargo.toml"
|
||||
"/src/client/mod.rs"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use http::StatusCode;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use resource::ResourceHandler;
|
||||
use server::Request;
|
||||
|
||||
/// Trait defines object that could be registered as route handler
|
||||
#[allow(unused_variables)]
|
||||
@ -18,7 +19,7 @@ pub trait Handler<S>: 'static {
|
||||
type Result: Responder;
|
||||
|
||||
/// Handle request
|
||||
fn handle(&self, req: HttpRequest<S>) -> Self::Result;
|
||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result;
|
||||
}
|
||||
|
||||
/// Trait implemented by types that generate responses for clients.
|
||||
@ -203,12 +204,12 @@ where
|
||||
/// Handler<S> for Fn()
|
||||
impl<F, R, S> Handler<S> for F
|
||||
where
|
||||
F: Fn(HttpRequest<S>) -> R + 'static,
|
||||
F: Fn(&HttpRequest<S>) -> R + 'static,
|
||||
R: Responder + 'static,
|
||||
{
|
||||
type Result = R;
|
||||
|
||||
fn handle(&self, req: HttpRequest<S>) -> R {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> R {
|
||||
(self)(req)
|
||||
}
|
||||
}
|
||||
@ -402,9 +403,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// /// Trait defines object that could be registered as resource route
|
||||
pub(crate) trait RouteHandler<S>: 'static {
|
||||
fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse>;
|
||||
fn handle(&self, &HttpRequest<S>) -> AsyncResult<HttpResponse>;
|
||||
|
||||
fn has_default_resource(&self) -> bool {
|
||||
false
|
||||
@ -443,8 +443,8 @@ where
|
||||
R: Responder + 'static,
|
||||
S: 'static,
|
||||
{
|
||||
fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
match self.h.handle(req.clone()).respond_to(&req) {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
match self.h.handle(req).respond_to(req) {
|
||||
Ok(reply) => reply.into(),
|
||||
Err(err) => AsyncResult::err(err.into()),
|
||||
}
|
||||
@ -454,7 +454,7 @@ where
|
||||
/// Async route handler
|
||||
pub(crate) struct AsyncHandler<S, H, F, R, E>
|
||||
where
|
||||
H: Fn(HttpRequest<S>) -> F + 'static,
|
||||
H: Fn(&HttpRequest<S>) -> F + 'static,
|
||||
F: Future<Item = R, Error = E> + 'static,
|
||||
R: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
@ -466,7 +466,7 @@ where
|
||||
|
||||
impl<S, H, F, R, E> AsyncHandler<S, H, F, R, E>
|
||||
where
|
||||
H: Fn(HttpRequest<S>) -> F + 'static,
|
||||
H: Fn(&HttpRequest<S>) -> F + 'static,
|
||||
F: Future<Item = R, Error = E> + 'static,
|
||||
R: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
@ -482,14 +482,15 @@ where
|
||||
|
||||
impl<S, H, F, R, E> RouteHandler<S> for AsyncHandler<S, H, F, R, E>
|
||||
where
|
||||
H: Fn(HttpRequest<S>) -> F + 'static,
|
||||
H: Fn(&HttpRequest<S>) -> F + 'static,
|
||||
F: Future<Item = R, Error = E> + 'static,
|
||||
R: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
S: 'static,
|
||||
{
|
||||
fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
let fut = (self.h)(req.clone()).map_err(|e| e.into()).then(move |r| {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
let req = req.clone();
|
||||
let fut = (self.h)(&req).map_err(|e| e.into()).then(move |r| {
|
||||
match r.respond_to(&req) {
|
||||
Ok(reply) => match reply.into().into() {
|
||||
AsyncResultItem::Ok(resp) => Either::A(ok(resp)),
|
||||
|
124
src/helpers.rs
124
src/helpers.rs
@ -36,7 +36,7 @@ use httpresponse::HttpResponse;
|
||||
/// # use actix_web::*;
|
||||
/// use actix_web::http::NormalizePath;
|
||||
///
|
||||
/// # fn index(req: HttpRequest) -> HttpResponse {
|
||||
/// # fn index(req: &HttpRequest) -> HttpResponse {
|
||||
/// # HttpResponse::Ok().into()
|
||||
/// # }
|
||||
/// fn main() {
|
||||
@ -86,57 +86,41 @@ impl NormalizePath {
|
||||
impl<S> Handler<S> for NormalizePath {
|
||||
type Result = HttpResponse;
|
||||
|
||||
fn handle(&self, req: HttpRequest<S>) -> Self::Result {
|
||||
if let Some(router) = req.router() {
|
||||
let query = req.query_string();
|
||||
if self.merge {
|
||||
// merge slashes
|
||||
let p = self.re_merge.replace_all(req.path(), "/");
|
||||
if p.len() != req.path().len() {
|
||||
if router.has_route(p.as_ref()) {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
|
||||
let query = req.query_string();
|
||||
if self.merge {
|
||||
// merge slashes
|
||||
let p = self.re_merge.replace_all(req.path(), "/");
|
||||
if p.len() != req.path().len() {
|
||||
if req.route().has_route(p.as_ref()) {
|
||||
let p = if !query.is_empty() {
|
||||
p + "?" + query
|
||||
} else {
|
||||
p
|
||||
};
|
||||
return HttpResponse::build(self.redirect)
|
||||
.header(header::LOCATION, p.as_ref())
|
||||
.finish();
|
||||
}
|
||||
// merge slashes and append trailing slash
|
||||
if self.append && !p.ends_with('/') {
|
||||
let p = p.as_ref().to_owned() + "/";
|
||||
if req.route().has_route(&p) {
|
||||
let p = if !query.is_empty() {
|
||||
p + "?" + query
|
||||
} else {
|
||||
p
|
||||
};
|
||||
return HttpResponse::build(self.redirect)
|
||||
.header(header::LOCATION, p.as_ref())
|
||||
.header(header::LOCATION, p.as_str())
|
||||
.finish();
|
||||
}
|
||||
// merge slashes and append trailing slash
|
||||
if self.append && !p.ends_with('/') {
|
||||
let p = p.as_ref().to_owned() + "/";
|
||||
if router.has_route(&p) {
|
||||
let p = if !query.is_empty() {
|
||||
p + "?" + query
|
||||
} else {
|
||||
p
|
||||
};
|
||||
return HttpResponse::build(self.redirect)
|
||||
.header(header::LOCATION, p.as_str())
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to remove trailing slash
|
||||
if p.ends_with('/') {
|
||||
let p = p.as_ref().trim_right_matches('/');
|
||||
if router.has_route(p) {
|
||||
let mut req = HttpResponse::build(self.redirect);
|
||||
return if !query.is_empty() {
|
||||
req.header(
|
||||
header::LOCATION,
|
||||
(p.to_owned() + "?" + query).as_str(),
|
||||
)
|
||||
} else {
|
||||
req.header(header::LOCATION, p)
|
||||
}.finish();
|
||||
}
|
||||
}
|
||||
} else if p.ends_with('/') {
|
||||
// try to remove trailing slash
|
||||
// try to remove trailing slash
|
||||
if p.ends_with('/') {
|
||||
let p = p.as_ref().trim_right_matches('/');
|
||||
if router.has_route(p) {
|
||||
if req.route().has_route(p) {
|
||||
let mut req = HttpResponse::build(self.redirect);
|
||||
return if !query.is_empty() {
|
||||
req.header(
|
||||
@ -148,22 +132,36 @@ impl<S> Handler<S> for NormalizePath {
|
||||
}.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
// append trailing slash
|
||||
if self.append && !req.path().ends_with('/') {
|
||||
let p = req.path().to_owned() + "/";
|
||||
if router.has_route(&p) {
|
||||
let p = if !query.is_empty() {
|
||||
p + "?" + query
|
||||
} else if p.ends_with('/') {
|
||||
// try to remove trailing slash
|
||||
let p = p.as_ref().trim_right_matches('/');
|
||||
if req.route().has_route(p) {
|
||||
let mut req = HttpResponse::build(self.redirect);
|
||||
return if !query.is_empty() {
|
||||
req.header(
|
||||
header::LOCATION,
|
||||
(p.to_owned() + "?" + query).as_str(),
|
||||
)
|
||||
} else {
|
||||
p
|
||||
};
|
||||
return HttpResponse::build(self.redirect)
|
||||
.header(header::LOCATION, p.as_str())
|
||||
.finish();
|
||||
req.header(header::LOCATION, p)
|
||||
}.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
// append trailing slash
|
||||
if self.append && !req.path().ends_with('/') {
|
||||
let p = req.path().to_owned() + "/";
|
||||
if req.route().has_route(&p) {
|
||||
let p = if !query.is_empty() {
|
||||
p + "?" + query
|
||||
} else {
|
||||
p
|
||||
};
|
||||
return HttpResponse::build(self.redirect)
|
||||
.header(header::LOCATION, p.as_str())
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
HttpResponse::new(self.not_found)
|
||||
}
|
||||
}
|
||||
@ -175,7 +173,7 @@ mod tests {
|
||||
use http::{header, Method};
|
||||
use test::TestRequest;
|
||||
|
||||
fn index(_req: HttpRequest) -> HttpResponse {
|
||||
fn index(_req: &HttpRequest) -> HttpResponse {
|
||||
HttpResponse::new(StatusCode::OK)
|
||||
}
|
||||
|
||||
@ -207,9 +205,9 @@ mod tests {
|
||||
("/resource2/?p1=1&p2=2", "", StatusCode::OK),
|
||||
];
|
||||
for (path, target, code) in params {
|
||||
let req = app.prepare_request(TestRequest::with_uri(path).finish());
|
||||
let req = TestRequest::with_uri(path).request();
|
||||
let resp = app.run(req);
|
||||
let r = resp.as_msg();
|
||||
let r = &resp.as_msg();
|
||||
assert_eq!(r.status(), code);
|
||||
if !target.is_empty() {
|
||||
assert_eq!(
|
||||
@ -246,9 +244,9 @@ mod tests {
|
||||
("/resource2/?p1=1&p2=2", StatusCode::OK),
|
||||
];
|
||||
for (path, code) in params {
|
||||
let req = app.prepare_request(TestRequest::with_uri(path).finish());
|
||||
let req = TestRequest::with_uri(path).request();
|
||||
let resp = app.run(req);
|
||||
let r = resp.as_msg();
|
||||
let r = &resp.as_msg();
|
||||
assert_eq!(r.status(), code);
|
||||
}
|
||||
}
|
||||
@ -329,9 +327,9 @@ mod tests {
|
||||
),
|
||||
];
|
||||
for (path, target, code) in params {
|
||||
let req = app.prepare_request(TestRequest::with_uri(path).finish());
|
||||
let req = TestRequest::with_uri(path).request();
|
||||
let resp = app.run(req);
|
||||
let r = resp.as_msg();
|
||||
let r = &resp.as_msg();
|
||||
assert_eq!(r.status(), code);
|
||||
if !target.is_empty() {
|
||||
assert_eq!(
|
||||
@ -509,9 +507,9 @@ mod tests {
|
||||
),
|
||||
];
|
||||
for (path, target, code) in params {
|
||||
let req = app.prepare_request(TestRequest::with_uri(path).finish());
|
||||
let req = TestRequest::with_uri(path).request();
|
||||
let resp = app.run(req);
|
||||
let r = resp.as_msg();
|
||||
let r = &resp.as_msg();
|
||||
assert_eq!(r.status(), code);
|
||||
if !target.is_empty() {
|
||||
assert_eq!(
|
||||
|
@ -16,12 +16,19 @@ use error::{
|
||||
use header::Header;
|
||||
use json::JsonBody;
|
||||
use multipart::Multipart;
|
||||
use payload::Payload;
|
||||
|
||||
/// Trait that implements general purpose operations on http messages
|
||||
pub trait HttpMessage {
|
||||
pub trait HttpMessage: Sized {
|
||||
/// Type of message payload stream
|
||||
type Stream: Stream<Item = Bytes, Error = PayloadError> + Sized;
|
||||
|
||||
/// Read the message headers.
|
||||
fn headers(&self) -> &HeaderMap;
|
||||
|
||||
/// Message payload stream
|
||||
fn payload(&self) -> Self::Stream;
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Get a header
|
||||
fn get_header<H: Header>(&self) -> Option<H>
|
||||
@ -123,10 +130,7 @@ pub trait HttpMessage {
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
fn body(self) -> MessageBody<Self>
|
||||
where
|
||||
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
|
||||
{
|
||||
fn body(&self) -> MessageBody<Self> {
|
||||
MessageBody::new(self)
|
||||
}
|
||||
|
||||
@ -160,10 +164,7 @@ pub trait HttpMessage {
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
fn urlencoded<T: DeserializeOwned>(self) -> UrlEncoded<Self, T>
|
||||
where
|
||||
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
|
||||
{
|
||||
fn urlencoded<T: DeserializeOwned>(&self) -> UrlEncoded<Self, T> {
|
||||
UrlEncoded::new(self)
|
||||
}
|
||||
|
||||
@ -199,10 +200,7 @@ pub trait HttpMessage {
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
fn json<T: DeserializeOwned>(self) -> JsonBody<Self, T>
|
||||
where
|
||||
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
|
||||
{
|
||||
fn json<T: DeserializeOwned>(&self) -> JsonBody<Self, T> {
|
||||
JsonBody::new(self)
|
||||
}
|
||||
|
||||
@ -241,45 +239,42 @@ pub trait HttpMessage {
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
fn multipart(self) -> Multipart<Self>
|
||||
where
|
||||
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
|
||||
{
|
||||
fn multipart(&self) -> Multipart<Self::Stream> {
|
||||
let boundary = Multipart::boundary(self.headers());
|
||||
Multipart::new(boundary, self)
|
||||
Multipart::new(boundary, self.payload())
|
||||
}
|
||||
|
||||
/// Return stream of lines.
|
||||
fn readlines(self) -> Readlines<Self>
|
||||
where
|
||||
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
|
||||
{
|
||||
fn readlines(&self) -> Readlines<Self> {
|
||||
Readlines::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream to read request line by line.
|
||||
pub struct Readlines<T>
|
||||
where
|
||||
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
{
|
||||
req: T,
|
||||
pub struct Readlines<T: HttpMessage> {
|
||||
stream: T::Stream,
|
||||
buff: BytesMut,
|
||||
limit: usize,
|
||||
checked_buff: bool,
|
||||
encoding: EncodingRef,
|
||||
err: Option<ReadlinesError>,
|
||||
}
|
||||
|
||||
impl<T> Readlines<T>
|
||||
where
|
||||
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
{
|
||||
impl<T: HttpMessage> Readlines<T> {
|
||||
/// Create a new stream to read request line by line.
|
||||
fn new(req: T) -> Self {
|
||||
fn new(req: &T) -> Self {
|
||||
let encoding = match req.encoding() {
|
||||
Ok(enc) => enc,
|
||||
Err(err) => return Self::err(req, err.into()),
|
||||
};
|
||||
|
||||
Readlines {
|
||||
req,
|
||||
stream: req.payload(),
|
||||
buff: BytesMut::with_capacity(262_144),
|
||||
limit: 262_144,
|
||||
checked_buff: true,
|
||||
err: None,
|
||||
encoding,
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,17 +283,28 @@ where
|
||||
self.limit = limit;
|
||||
self
|
||||
}
|
||||
|
||||
fn err(req: &T, err: ReadlinesError) -> Self {
|
||||
Readlines {
|
||||
stream: req.payload(),
|
||||
buff: BytesMut::with_capacity(262_144),
|
||||
limit: 262_144,
|
||||
checked_buff: true,
|
||||
encoding: UTF_8,
|
||||
err: Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Stream for Readlines<T>
|
||||
where
|
||||
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
{
|
||||
impl<T: HttpMessage + 'static> Stream for Readlines<T> {
|
||||
type Item = String;
|
||||
type Error = ReadlinesError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
let encoding = self.req.encoding()?;
|
||||
if let Some(err) = self.err.take() {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// check if there is a newline in the buffer
|
||||
if !self.checked_buff {
|
||||
let mut found: Option<usize> = None;
|
||||
@ -313,13 +319,13 @@ where
|
||||
if ind + 1 > self.limit {
|
||||
return Err(ReadlinesError::LimitOverflow);
|
||||
}
|
||||
let enc: *const Encoding = encoding as *const Encoding;
|
||||
let enc: *const Encoding = self.encoding as *const Encoding;
|
||||
let line = if enc == UTF_8 {
|
||||
str::from_utf8(&self.buff.split_to(ind + 1))
|
||||
.map_err(|_| ReadlinesError::EncodingError)?
|
||||
.to_owned()
|
||||
} else {
|
||||
encoding
|
||||
self.encoding
|
||||
.decode(&self.buff.split_to(ind + 1), DecoderTrap::Strict)
|
||||
.map_err(|_| ReadlinesError::EncodingError)?
|
||||
};
|
||||
@ -328,7 +334,7 @@ where
|
||||
self.checked_buff = true;
|
||||
}
|
||||
// poll req for more bytes
|
||||
match self.req.poll() {
|
||||
match self.stream.poll() {
|
||||
Ok(Async::Ready(Some(mut bytes))) => {
|
||||
// check if there is a newline in bytes
|
||||
let mut found: Option<usize> = None;
|
||||
@ -343,13 +349,13 @@ where
|
||||
if ind + 1 > self.limit {
|
||||
return Err(ReadlinesError::LimitOverflow);
|
||||
}
|
||||
let enc: *const Encoding = encoding as *const Encoding;
|
||||
let enc: *const Encoding = self.encoding as *const Encoding;
|
||||
let line = if enc == UTF_8 {
|
||||
str::from_utf8(&bytes.split_to(ind + 1))
|
||||
.map_err(|_| ReadlinesError::EncodingError)?
|
||||
.to_owned()
|
||||
} else {
|
||||
encoding
|
||||
self.encoding
|
||||
.decode(&bytes.split_to(ind + 1), DecoderTrap::Strict)
|
||||
.map_err(|_| ReadlinesError::EncodingError)?
|
||||
};
|
||||
@ -369,13 +375,13 @@ where
|
||||
if self.buff.len() > self.limit {
|
||||
return Err(ReadlinesError::LimitOverflow);
|
||||
}
|
||||
let enc: *const Encoding = encoding as *const Encoding;
|
||||
let enc: *const Encoding = self.encoding as *const Encoding;
|
||||
let line = if enc == UTF_8 {
|
||||
str::from_utf8(&self.buff)
|
||||
.map_err(|_| ReadlinesError::EncodingError)?
|
||||
.to_owned()
|
||||
} else {
|
||||
encoding
|
||||
self.encoding
|
||||
.decode(&self.buff, DecoderTrap::Strict)
|
||||
.map_err(|_| ReadlinesError::EncodingError)?
|
||||
};
|
||||
@ -388,19 +394,36 @@ where
|
||||
}
|
||||
|
||||
/// Future that resolves to a complete http message body.
|
||||
pub struct MessageBody<T> {
|
||||
pub struct MessageBody<T: HttpMessage> {
|
||||
limit: usize,
|
||||
req: Option<T>,
|
||||
length: Option<usize>,
|
||||
stream: Option<T::Stream>,
|
||||
err: Option<PayloadError>,
|
||||
fut: Option<Box<Future<Item = Bytes, Error = PayloadError>>>,
|
||||
}
|
||||
|
||||
impl<T> MessageBody<T> {
|
||||
/// Create `RequestBody` for request.
|
||||
pub fn new(req: T) -> MessageBody<T> {
|
||||
impl<T: HttpMessage> MessageBody<T> {
|
||||
/// Create `MessageBody` for request.
|
||||
pub fn new(req: &T) -> MessageBody<T> {
|
||||
let mut len = None;
|
||||
if let Some(l) = req.headers().get(header::CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
} else {
|
||||
return Self::err(PayloadError::UnknownLength);
|
||||
}
|
||||
} else {
|
||||
return Self::err(PayloadError::UnknownLength);
|
||||
}
|
||||
}
|
||||
|
||||
MessageBody {
|
||||
limit: 262_144,
|
||||
req: Some(req),
|
||||
length: len,
|
||||
stream: Some(req.payload()),
|
||||
fut: None,
|
||||
err: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,68 +432,114 @@ impl<T> MessageBody<T> {
|
||||
self.limit = limit;
|
||||
self
|
||||
}
|
||||
|
||||
fn err(e: PayloadError) -> Self {
|
||||
MessageBody {
|
||||
stream: None,
|
||||
limit: 262_144,
|
||||
fut: None,
|
||||
err: Some(e),
|
||||
length: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for MessageBody<T>
|
||||
where
|
||||
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
T: HttpMessage + 'static,
|
||||
{
|
||||
type Item = Bytes;
|
||||
type Error = PayloadError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
if let Some(req) = self.req.take() {
|
||||
if let Some(len) = req.headers().get(header::CONTENT_LENGTH) {
|
||||
if let Ok(s) = len.to_str() {
|
||||
if let Ok(len) = s.parse::<usize>() {
|
||||
if len > self.limit {
|
||||
return Err(PayloadError::Overflow);
|
||||
}
|
||||
} else {
|
||||
return Err(PayloadError::UnknownLength);
|
||||
}
|
||||
} else {
|
||||
return Err(PayloadError::UnknownLength);
|
||||
}
|
||||
}
|
||||
|
||||
// future
|
||||
let limit = self.limit;
|
||||
self.fut = Some(Box::new(
|
||||
req.from_err()
|
||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
if (body.len() + chunk.len()) > limit {
|
||||
Err(PayloadError::Overflow)
|
||||
} else {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok(body)
|
||||
}
|
||||
})
|
||||
.map(|body| body.freeze()),
|
||||
));
|
||||
if let Some(ref mut fut) = self.fut {
|
||||
return fut.poll();
|
||||
}
|
||||
|
||||
self.fut
|
||||
.as_mut()
|
||||
.expect("UrlEncoded could not be used second time")
|
||||
.poll()
|
||||
if let Some(err) = self.err.take() {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
if let Some(len) = self.length.take() {
|
||||
if len > self.limit {
|
||||
return Err(PayloadError::Overflow);
|
||||
}
|
||||
}
|
||||
|
||||
// future
|
||||
let limit = self.limit;
|
||||
self.fut = Some(Box::new(
|
||||
self.stream
|
||||
.take()
|
||||
.expect("Can not be used second time")
|
||||
.from_err()
|
||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
if (body.len() + chunk.len()) > limit {
|
||||
Err(PayloadError::Overflow)
|
||||
} else {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok(body)
|
||||
}
|
||||
})
|
||||
.map(|body| body.freeze()),
|
||||
));
|
||||
self.poll()
|
||||
}
|
||||
}
|
||||
|
||||
/// Future that resolves to a parsed urlencoded values.
|
||||
pub struct UrlEncoded<T, U> {
|
||||
req: Option<T>,
|
||||
pub struct UrlEncoded<T: HttpMessage, U> {
|
||||
stream: Option<T::Stream>,
|
||||
limit: usize,
|
||||
length: Option<usize>,
|
||||
encoding: EncodingRef,
|
||||
err: Option<UrlencodedError>,
|
||||
fut: Option<Box<Future<Item = U, Error = UrlencodedError>>>,
|
||||
}
|
||||
|
||||
impl<T, U> UrlEncoded<T, U> {
|
||||
impl<T: HttpMessage, U> UrlEncoded<T, U> {
|
||||
/// Create a new future to URL encode a request
|
||||
pub fn new(req: T) -> UrlEncoded<T, U> {
|
||||
pub fn new(req: &T) -> UrlEncoded<T, U> {
|
||||
// check content type
|
||||
if req.content_type().to_lowercase() != "application/x-www-form-urlencoded" {
|
||||
return Self::err(UrlencodedError::ContentType);
|
||||
}
|
||||
let encoding = match req.encoding() {
|
||||
Ok(enc) => enc,
|
||||
Err(_) => return Self::err(UrlencodedError::ContentType),
|
||||
};
|
||||
|
||||
let mut len = None;
|
||||
if let Some(l) = req.headers().get(header::CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
} else {
|
||||
return Self::err(UrlencodedError::UnknownLength);
|
||||
}
|
||||
} else {
|
||||
return Self::err(UrlencodedError::UnknownLength);
|
||||
}
|
||||
};
|
||||
|
||||
UrlEncoded {
|
||||
req: Some(req),
|
||||
encoding,
|
||||
stream: Some(req.payload()),
|
||||
limit: 262_144,
|
||||
length: len,
|
||||
fut: None,
|
||||
err: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn err(e: UrlencodedError) -> Self {
|
||||
UrlEncoded {
|
||||
stream: None,
|
||||
limit: 262_144,
|
||||
fut: None,
|
||||
err: Some(e),
|
||||
length: None,
|
||||
encoding: UTF_8,
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,66 +552,58 @@ impl<T, U> UrlEncoded<T, U> {
|
||||
|
||||
impl<T, U> Future for UrlEncoded<T, U>
|
||||
where
|
||||
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
T: HttpMessage + 'static,
|
||||
U: DeserializeOwned + 'static,
|
||||
{
|
||||
type Item = U;
|
||||
type Error = UrlencodedError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
if let Some(req) = self.req.take() {
|
||||
if let Some(len) = req.headers().get(header::CONTENT_LENGTH) {
|
||||
if let Ok(s) = len.to_str() {
|
||||
if let Ok(len) = s.parse::<u64>() {
|
||||
if len > 262_144 {
|
||||
return Err(UrlencodedError::Overflow);
|
||||
}
|
||||
} else {
|
||||
return Err(UrlencodedError::UnknownLength);
|
||||
}
|
||||
} else {
|
||||
return Err(UrlencodedError::UnknownLength);
|
||||
}
|
||||
}
|
||||
|
||||
// check content type
|
||||
if req.content_type().to_lowercase() != "application/x-www-form-urlencoded" {
|
||||
return Err(UrlencodedError::ContentType);
|
||||
}
|
||||
let encoding = req.encoding().map_err(|_| UrlencodedError::ContentType)?;
|
||||
|
||||
// future
|
||||
let limit = self.limit;
|
||||
let fut = req
|
||||
.from_err()
|
||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
if (body.len() + chunk.len()) > limit {
|
||||
Err(UrlencodedError::Overflow)
|
||||
} else {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok(body)
|
||||
}
|
||||
})
|
||||
.and_then(move |body| {
|
||||
let enc: *const Encoding = encoding as *const Encoding;
|
||||
if enc == UTF_8 {
|
||||
serde_urlencoded::from_bytes::<U>(&body)
|
||||
.map_err(|_| UrlencodedError::Parse)
|
||||
} else {
|
||||
let body = encoding
|
||||
.decode(&body, DecoderTrap::Strict)
|
||||
.map_err(|_| UrlencodedError::Parse)?;
|
||||
serde_urlencoded::from_str::<U>(&body)
|
||||
.map_err(|_| UrlencodedError::Parse)
|
||||
}
|
||||
});
|
||||
self.fut = Some(Box::new(fut));
|
||||
if let Some(ref mut fut) = self.fut {
|
||||
return fut.poll();
|
||||
}
|
||||
|
||||
self.fut
|
||||
.as_mut()
|
||||
if let Some(err) = self.err.take() {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// payload size
|
||||
let limit = self.limit;
|
||||
if let Some(len) = self.length.take() {
|
||||
if len > limit {
|
||||
return Err(UrlencodedError::Overflow);
|
||||
}
|
||||
}
|
||||
|
||||
// future
|
||||
let encoding = self.encoding;
|
||||
let fut = self
|
||||
.stream
|
||||
.take()
|
||||
.expect("UrlEncoded could not be used second time")
|
||||
.poll()
|
||||
.from_err()
|
||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
if (body.len() + chunk.len()) > limit {
|
||||
Err(UrlencodedError::Overflow)
|
||||
} else {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok(body)
|
||||
}
|
||||
})
|
||||
.and_then(move |body| {
|
||||
if (encoding as *const Encoding) == UTF_8 {
|
||||
serde_urlencoded::from_bytes::<U>(&body)
|
||||
.map_err(|_| UrlencodedError::Parse)
|
||||
} else {
|
||||
let body = encoding
|
||||
.decode(&body, DecoderTrap::Strict)
|
||||
.map_err(|_| UrlencodedError::Parse)?;
|
||||
serde_urlencoded::from_str::<U>(&body)
|
||||
.map_err(|_| UrlencodedError::Parse)
|
||||
}
|
||||
});
|
||||
self.fut = Some(Box::new(fut));
|
||||
self.poll()
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,7 +627,7 @@ mod tests {
|
||||
TestRequest::with_header("content-type", "application/json; charset=utf=8")
|
||||
.finish();
|
||||
assert_eq!(req.content_type(), "application/json");
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
assert_eq!(req.content_type(), "");
|
||||
}
|
||||
|
||||
@ -574,7 +635,7 @@ mod tests {
|
||||
fn test_mime_type() {
|
||||
let req = TestRequest::with_header("content-type", "application/json").finish();
|
||||
assert_eq!(req.mime_type().unwrap(), Some(mime::APPLICATION_JSON));
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
assert_eq!(req.mime_type().unwrap(), None);
|
||||
let req =
|
||||
TestRequest::with_header("content-type", "application/json; charset=utf-8")
|
||||
@ -596,7 +657,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_encoding() {
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
assert_eq!(UTF_8.name(), req.encoding().unwrap().name());
|
||||
|
||||
let req = TestRequest::with_header("content-type", "application/json").finish();
|
||||
@ -626,27 +687,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_chunked() {
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
assert!(!req.chunked().unwrap());
|
||||
|
||||
let req =
|
||||
TestRequest::with_header(header::TRANSFER_ENCODING, "chunked").finish();
|
||||
assert!(req.chunked().unwrap());
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
let hdr = Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue");
|
||||
|
||||
headers.insert(
|
||||
header::TRANSFER_ENCODING,
|
||||
header::HeaderValue::from_shared(hdr).unwrap(),
|
||||
);
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::TRANSFER_ENCODING,
|
||||
Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue"),
|
||||
)
|
||||
.finish();
|
||||
assert!(req.chunked().is_err());
|
||||
}
|
||||
|
||||
@ -716,9 +769,8 @@ mod tests {
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded",
|
||||
).header(header::CONTENT_LENGTH, "11")
|
||||
.set_payload(Bytes::from_static(b"hello=world"))
|
||||
.finish();
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"hello=world"));
|
||||
|
||||
let result = req.urlencoded::<Info>().poll().ok().unwrap();
|
||||
assert_eq!(
|
||||
@ -732,9 +784,8 @@ mod tests {
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded; charset=utf-8",
|
||||
).header(header::CONTENT_LENGTH, "11")
|
||||
.set_payload(Bytes::from_static(b"hello=world"))
|
||||
.finish();
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"hello=world"));
|
||||
|
||||
let result = req.urlencoded().poll().ok().unwrap();
|
||||
assert_eq!(
|
||||
@ -759,16 +810,17 @@ mod tests {
|
||||
_ => unreachable!("error"),
|
||||
}
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.payload_mut().unread_data(Bytes::from_static(b"test"));
|
||||
let req = TestRequest::default()
|
||||
.set_payload(Bytes::from_static(b"test"))
|
||||
.finish();
|
||||
match req.body().poll().ok().unwrap() {
|
||||
Async::Ready(bytes) => assert_eq!(bytes, Bytes::from_static(b"test")),
|
||||
_ => unreachable!("error"),
|
||||
}
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"11111111111111"));
|
||||
let mut req = TestRequest::default()
|
||||
.set_payload(Bytes::from_static(b"11111111111111"))
|
||||
.finish();
|
||||
match req.body().limit(5).poll().err().unwrap() {
|
||||
PayloadError::Overflow => (),
|
||||
_ => unreachable!("error"),
|
||||
@ -777,13 +829,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_readlines() {
|
||||
let mut req = HttpRequest::default();
|
||||
req.payload_mut().unread_data(Bytes::from_static(
|
||||
b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\
|
||||
industry. Lorem Ipsum has been the industry's standard dummy\n\
|
||||
Contrary to popular belief, Lorem Ipsum is not simply random text.",
|
||||
));
|
||||
let mut r = Readlines::new(req);
|
||||
let req = TestRequest::default()
|
||||
.set_payload(Bytes::from_static(
|
||||
b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\
|
||||
industry. Lorem Ipsum has been the industry's standard dummy\n\
|
||||
Contrary to popular belief, Lorem Ipsum is not simply random text.",
|
||||
))
|
||||
.finish();
|
||||
let mut r = Readlines::new(&req);
|
||||
match r.poll().ok().unwrap() {
|
||||
Async::Ready(Some(s)) => assert_eq!(
|
||||
s,
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! HTTP Request message related code.
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::{cmp, fmt, io, str};
|
||||
|
||||
@ -22,261 +24,158 @@ use httpresponse::{HttpResponse, HttpResponseBuilder};
|
||||
use info::ConnectionInfo;
|
||||
use param::Params;
|
||||
use payload::Payload;
|
||||
use router::{Resource, Router};
|
||||
use server::helpers::SharedHttpInnerMessage;
|
||||
use router::{Resource, RouteInfo, Router};
|
||||
use server::message::{MessageFlags, Request};
|
||||
use uri::Url as InnerUrl;
|
||||
|
||||
bitflags! {
|
||||
pub(crate) struct MessageFlags: u8 {
|
||||
const KEEPALIVE = 0b0000_0010;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HttpInnerMessage {
|
||||
pub version: Version,
|
||||
pub method: Method,
|
||||
pub(crate) url: InnerUrl,
|
||||
pub(crate) flags: MessageFlags,
|
||||
pub headers: HeaderMap,
|
||||
pub extensions: Extensions,
|
||||
pub params: Params,
|
||||
pub addr: Option<SocketAddr>,
|
||||
pub payload: Option<Payload>,
|
||||
pub prefix: u16,
|
||||
resource: RouterResource,
|
||||
}
|
||||
|
||||
struct Query(HashMap<String, String>);
|
||||
struct Cookies(Vec<Cookie<'static>>);
|
||||
struct Info(ConnectionInfo);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
enum RouterResource {
|
||||
pub(crate) enum RouterResource {
|
||||
Notset,
|
||||
Normal(u16),
|
||||
}
|
||||
|
||||
impl Default for HttpInnerMessage {
|
||||
fn default() -> HttpInnerMessage {
|
||||
HttpInnerMessage {
|
||||
method: Method::GET,
|
||||
url: InnerUrl::default(),
|
||||
version: Version::HTTP_11,
|
||||
headers: HeaderMap::with_capacity(16),
|
||||
flags: MessageFlags::empty(),
|
||||
params: Params::new(),
|
||||
addr: None,
|
||||
payload: None,
|
||||
extensions: Extensions::new(),
|
||||
prefix: 0,
|
||||
resource: RouterResource::Notset,
|
||||
/// An HTTP Request
|
||||
pub struct HttpRequest<S = ()> {
|
||||
req: Rc<Request>,
|
||||
state: Rc<S>,
|
||||
route: RouteInfo,
|
||||
}
|
||||
|
||||
impl<S> HttpMessage for HttpRequest<S> {
|
||||
type Stream = Payload;
|
||||
|
||||
#[inline]
|
||||
fn headers(&self) -> &HeaderMap {
|
||||
self.req.headers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn payload(&self) -> Payload {
|
||||
if let Some(payload) = self.req.inner.payload.borrow_mut().take() {
|
||||
payload
|
||||
} else {
|
||||
Payload::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpInnerMessage {
|
||||
/// Checks if a connection should be kept alive.
|
||||
#[inline]
|
||||
pub fn keep_alive(&self) -> bool {
|
||||
self.flags.contains(MessageFlags::KEEPALIVE)
|
||||
}
|
||||
impl<S> Deref for HttpRequest<S> {
|
||||
type Target = Request;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.headers.clear();
|
||||
self.extensions.clear();
|
||||
self.params.clear();
|
||||
self.addr = None;
|
||||
self.flags = MessageFlags::empty();
|
||||
self.payload = None;
|
||||
self.prefix = 0;
|
||||
self.resource = RouterResource::Notset;
|
||||
}
|
||||
}
|
||||
|
||||
/// An HTTP Request
|
||||
pub struct HttpRequest<S = ()>(SharedHttpInnerMessage, Option<Rc<S>>, Option<Router>);
|
||||
|
||||
impl HttpRequest<()> {
|
||||
/// Construct a new Request.
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
method: Method, uri: Uri, version: Version, headers: HeaderMap,
|
||||
payload: Option<Payload>,
|
||||
) -> HttpRequest {
|
||||
let url = InnerUrl::new(uri);
|
||||
HttpRequest(
|
||||
SharedHttpInnerMessage::from_message(HttpInnerMessage {
|
||||
method,
|
||||
url,
|
||||
version,
|
||||
headers,
|
||||
payload,
|
||||
params: Params::new(),
|
||||
extensions: Extensions::new(),
|
||||
addr: None,
|
||||
prefix: 0,
|
||||
flags: MessageFlags::empty(),
|
||||
resource: RouterResource::Notset,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
|
||||
pub(crate) fn from_message(msg: SharedHttpInnerMessage) -> HttpRequest {
|
||||
HttpRequest(msg, None, None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Construct new http request with state.
|
||||
pub fn with_state<S>(self, state: Rc<S>, router: Router) -> HttpRequest<S> {
|
||||
HttpRequest(self.0, Some(state), Some(router))
|
||||
}
|
||||
|
||||
pub(crate) fn clone_with_state<S>(
|
||||
&self, state: Rc<S>, router: Router,
|
||||
) -> HttpRequest<S> {
|
||||
HttpRequest(self.0.clone(), Some(state), Some(router))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> HttpMessage for HttpRequest<S> {
|
||||
#[inline]
|
||||
fn headers(&self) -> &HeaderMap {
|
||||
&self.as_ref().headers
|
||||
fn deref(&self) -> &Request {
|
||||
self.req.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> HttpRequest<S> {
|
||||
#[inline]
|
||||
pub(crate) fn new(req: Request, state: Rc<S>, route: RouteInfo) -> HttpRequest<S> {
|
||||
HttpRequest {
|
||||
state,
|
||||
route,
|
||||
req: Rc::new(req),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Construct new http request with state.
|
||||
pub fn change_state<NS>(&self, state: Rc<NS>) -> HttpRequest<NS> {
|
||||
HttpRequest(self.0.clone(), Some(state), self.2.clone())
|
||||
pub(crate) fn with_state<NS>(&self, state: Rc<NS>) -> HttpRequest<NS> {
|
||||
HttpRequest {
|
||||
state,
|
||||
req: self.req.clone(),
|
||||
route: self.route.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Construct new http request without state.
|
||||
pub fn drop_state(&self) -> HttpRequest {
|
||||
HttpRequest(self.0.clone(), None, self.2.clone())
|
||||
}
|
||||
|
||||
/// get mutable reference for inner message
|
||||
/// mutable reference should not be returned as result for request's method
|
||||
#[inline]
|
||||
pub(crate) fn as_mut(&mut self) -> &mut HttpInnerMessage {
|
||||
self.0.get_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &HttpInnerMessage {
|
||||
self.0.get_ref()
|
||||
/// Construct new http request with new RouteInfo.
|
||||
pub(crate) fn with_route_info(&self, route: RouteInfo) -> HttpRequest<S> {
|
||||
HttpRequest {
|
||||
route,
|
||||
req: self.req.clone(),
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared application state
|
||||
#[inline]
|
||||
pub fn state(&self) -> &S {
|
||||
self.1.as_ref().unwrap()
|
||||
&self.state
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Server request
|
||||
pub fn request(&self) -> &Request {
|
||||
&self.req
|
||||
}
|
||||
|
||||
/// Request extensions
|
||||
#[inline]
|
||||
pub fn extensions(&self) -> &Extensions {
|
||||
&self.as_ref().extensions
|
||||
pub fn extensions(&self) -> Ref<Extensions> {
|
||||
self.req.extensions()
|
||||
}
|
||||
|
||||
/// Mutable reference to a the request's extensions
|
||||
#[inline]
|
||||
pub fn extensions_mut(&mut self) -> &mut Extensions {
|
||||
&mut self.as_mut().extensions
|
||||
pub fn extensions_mut(&self) -> RefMut<Extensions> {
|
||||
self.req.extensions_mut()
|
||||
}
|
||||
|
||||
/// Default `CpuPool`
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub fn cpu_pool(&self) -> &CpuPool {
|
||||
self.router()
|
||||
.expect("HttpRequest has to have Router instance")
|
||||
.server_settings()
|
||||
.cpu_pool()
|
||||
self.req.server_settings().cpu_pool()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Create http response
|
||||
pub fn response(&self, status: StatusCode, body: Body) -> HttpResponse {
|
||||
if let Some(router) = self.router() {
|
||||
router.server_settings().get_response(status, body)
|
||||
} else {
|
||||
HttpResponse::with_body(status, body)
|
||||
}
|
||||
self.req.server_settings().get_response(status, body)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Create http response builder
|
||||
pub fn build_response(&self, status: StatusCode) -> HttpResponseBuilder {
|
||||
if let Some(router) = self.router() {
|
||||
router.server_settings().get_response_builder(status)
|
||||
} else {
|
||||
HttpResponse::build(status)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn prefix_len(&self) -> u16 {
|
||||
self.as_ref().prefix as u16
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn set_prefix_len(&mut self, len: u16) {
|
||||
self.as_mut().prefix = len;
|
||||
self.req.server_settings().get_response_builder(status)
|
||||
}
|
||||
|
||||
/// Read the Request Uri.
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &Uri {
|
||||
self.as_ref().url.uri()
|
||||
self.request().inner.url.uri()
|
||||
}
|
||||
|
||||
/// Read the Request method.
|
||||
#[inline]
|
||||
pub fn method(&self) -> &Method {
|
||||
&self.as_ref().method
|
||||
&self.request().inner.method
|
||||
}
|
||||
|
||||
/// Read the Request Version.
|
||||
#[inline]
|
||||
pub fn version(&self) -> Version {
|
||||
self.as_ref().version
|
||||
}
|
||||
|
||||
///Returns mutable Request's headers.
|
||||
///
|
||||
///This is intended to be used by middleware.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||
&mut self.as_mut().headers
|
||||
self.request().inner.version
|
||||
}
|
||||
|
||||
/// The target path of this Request.
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
self.as_ref().url.path()
|
||||
self.request().inner.url.path()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn url(&self) -> &InnerUrl {
|
||||
&self.as_ref().url
|
||||
&self.request().inner.url
|
||||
}
|
||||
|
||||
/// Get *ConnectionInfo* for correct request.
|
||||
pub fn connection_info(&self) -> &ConnectionInfo {
|
||||
if self.extensions().get::<Info>().is_none() {
|
||||
let mut req = self.clone();
|
||||
req.as_mut()
|
||||
.extensions
|
||||
.insert(Info(ConnectionInfo::new(self)));
|
||||
}
|
||||
&self.extensions().get::<Info>().unwrap().0
|
||||
/// Get *ConnectionInfo* for the correct request.
|
||||
#[inline]
|
||||
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
|
||||
self.request().connection_info()
|
||||
}
|
||||
|
||||
/// Generate url for named resource
|
||||
@ -306,22 +205,7 @@ impl<S> HttpRequest<S> {
|
||||
U: IntoIterator<Item = I>,
|
||||
I: AsRef<str>,
|
||||
{
|
||||
if self.router().is_none() {
|
||||
Err(UrlGenerationError::RouterNotAvailable)
|
||||
} else {
|
||||
let path = self.router().unwrap().resource_path(name, elements)?;
|
||||
if path.starts_with('/') {
|
||||
let conn = self.connection_info();
|
||||
Ok(Url::parse(&format!(
|
||||
"{}://{}{}",
|
||||
conn.scheme(),
|
||||
conn.host(),
|
||||
path
|
||||
))?)
|
||||
} else {
|
||||
Ok(Url::parse(&path)?)
|
||||
}
|
||||
}
|
||||
self.route.url_for(&self, name, elements)
|
||||
}
|
||||
|
||||
/// Generate url for named resource
|
||||
@ -333,25 +217,16 @@ impl<S> HttpRequest<S> {
|
||||
self.url_for(name, &NO_PARAMS)
|
||||
}
|
||||
|
||||
/// This method returns reference to current `Router` object.
|
||||
/// This method returns reference to current `RouteInfo` object.
|
||||
#[inline]
|
||||
pub fn router(&self) -> Option<&Router> {
|
||||
self.2.as_ref()
|
||||
pub fn route(&self) -> &RouteInfo {
|
||||
&self.route
|
||||
}
|
||||
|
||||
/// This method returns reference to matched `Resource` object.
|
||||
#[inline]
|
||||
pub fn resource(&self) -> Option<&Resource> {
|
||||
if let Some(ref router) = self.2 {
|
||||
if let RouterResource::Normal(idx) = self.as_ref().resource {
|
||||
return Some(router.get_resource(idx as usize));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn set_resource(&mut self, res: usize) {
|
||||
self.as_mut().resource = RouterResource::Normal(res as u16);
|
||||
self.route.resource()
|
||||
}
|
||||
|
||||
/// Peer socket address
|
||||
@ -363,25 +238,20 @@ impl<S> HttpRequest<S> {
|
||||
/// be used.
|
||||
#[inline]
|
||||
pub fn peer_addr(&self) -> Option<SocketAddr> {
|
||||
self.as_ref().addr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_peer_addr(&mut self, addr: Option<SocketAddr>) {
|
||||
self.as_mut().addr = addr;
|
||||
self.request().inner.addr
|
||||
}
|
||||
|
||||
/// url query parameters.
|
||||
pub fn query(&self) -> &HashMap<String, String> {
|
||||
pub fn query(&self) -> Ref<HashMap<String, String>> {
|
||||
if self.extensions().get::<Query>().is_none() {
|
||||
let mut query = HashMap::new();
|
||||
for (key, val) in form_urlencoded::parse(self.query_string().as_ref()) {
|
||||
query.insert(key.as_ref().to_string(), val.to_string());
|
||||
}
|
||||
let mut req = self.clone();
|
||||
req.as_mut().extensions.insert(Query(query));
|
||||
self.extensions_mut().insert(Query(query));
|
||||
}
|
||||
&self.extensions().get::<Query>().unwrap().0
|
||||
Ref::map(self.extensions(), |ext| &ext.get::<Query>().unwrap().0)
|
||||
}
|
||||
|
||||
/// The query string in the URL.
|
||||
@ -397,12 +267,12 @@ impl<S> HttpRequest<S> {
|
||||
}
|
||||
|
||||
/// Load request cookies.
|
||||
pub fn cookies(&self) -> Result<&Vec<Cookie<'static>>, CookieParseError> {
|
||||
if self.extensions().get::<Query>().is_none() {
|
||||
#[inline]
|
||||
pub fn cookies(&self) -> Result<Ref<Vec<Cookie<'static>>>, CookieParseError> {
|
||||
if self.extensions().get::<Cookies>().is_none() {
|
||||
let mut req = self.clone();
|
||||
let msg = req.as_mut();
|
||||
let mut cookies = Vec::new();
|
||||
for hdr in msg.headers.get_all(header::COOKIE) {
|
||||
for hdr in self.request().inner.headers.get_all(header::COOKIE) {
|
||||
let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
|
||||
for cookie_str in s.split(';').map(|s| s.trim()) {
|
||||
if !cookie_str.is_empty() {
|
||||
@ -410,17 +280,20 @@ impl<S> HttpRequest<S> {
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.extensions.insert(Cookies(cookies));
|
||||
self.extensions_mut().insert(Cookies(cookies));
|
||||
}
|
||||
Ok(&self.extensions().get::<Cookies>().unwrap().0)
|
||||
Ok(Ref::map(self.extensions(), |ext| {
|
||||
&ext.get::<Cookies>().unwrap().0
|
||||
}))
|
||||
}
|
||||
|
||||
/// Return request cookie.
|
||||
pub fn cookie(&self, name: &str) -> Option<&Cookie> {
|
||||
#[inline]
|
||||
pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
|
||||
if let Ok(cookies) = self.cookies() {
|
||||
for cookie in cookies {
|
||||
for cookie in cookies.iter() {
|
||||
if cookie.name() == name {
|
||||
return Some(cookie);
|
||||
return Some(cookie.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -441,68 +314,31 @@ impl<S> HttpRequest<S> {
|
||||
/// access the matched value for that segment.
|
||||
#[inline]
|
||||
pub fn match_info(&self) -> &Params {
|
||||
&self.as_ref().params
|
||||
}
|
||||
|
||||
/// Get mutable reference to request's Params.
|
||||
#[inline]
|
||||
pub(crate) fn match_info_mut(&mut self) -> &mut Params {
|
||||
&mut self.as_mut().params
|
||||
}
|
||||
|
||||
/// Checks if a connection should be kept alive.
|
||||
pub fn keep_alive(&self) -> bool {
|
||||
self.as_ref().flags.contains(MessageFlags::KEEPALIVE)
|
||||
&self.route.match_info()
|
||||
}
|
||||
|
||||
/// Check if request requires connection upgrade
|
||||
pub(crate) fn upgrade(&self) -> bool {
|
||||
if let Some(conn) = self.as_ref().headers.get(header::CONNECTION) {
|
||||
if let Ok(s) = conn.to_str() {
|
||||
return s.to_lowercase().contains("upgrade");
|
||||
}
|
||||
}
|
||||
self.as_ref().method == Method::CONNECT
|
||||
self.request().upgrade()
|
||||
}
|
||||
|
||||
/// Set read buffer capacity
|
||||
///
|
||||
/// Default buffer capacity is 32Kb.
|
||||
pub fn set_read_buffer_capacity(&mut self, cap: usize) {
|
||||
if let Some(ref mut payload) = self.as_mut().payload {
|
||||
if let Some(payload) = self.request().inner.payload.borrow_mut().as_mut() {
|
||||
payload.set_read_buffer_capacity(cap)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn payload(&mut self) -> &Payload {
|
||||
let msg = self.as_mut();
|
||||
if msg.payload.is_none() {
|
||||
msg.payload = Some(Payload::empty());
|
||||
}
|
||||
msg.payload.as_ref().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn payload_mut(&mut self) -> &mut Payload {
|
||||
let msg = self.as_mut();
|
||||
if msg.payload.is_none() {
|
||||
msg.payload = Some(Payload::empty());
|
||||
}
|
||||
msg.payload.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HttpRequest<()> {
|
||||
/// Construct default request
|
||||
fn default() -> HttpRequest {
|
||||
HttpRequest(SharedHttpInnerMessage::default(), None, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Clone for HttpRequest<S> {
|
||||
fn clone(&self) -> HttpRequest<S> {
|
||||
HttpRequest(self.0.clone(), self.1.clone(), self.2.clone())
|
||||
HttpRequest {
|
||||
req: self.req.clone(),
|
||||
state: self.state.clone(),
|
||||
route: self.route.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,76 +352,23 @@ impl<S> FromRequest<S> for HttpRequest<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Stream for HttpRequest<S> {
|
||||
type Item = Bytes;
|
||||
type Error = PayloadError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> {
|
||||
let msg = self.as_mut();
|
||||
if msg.payload.is_none() {
|
||||
Ok(Async::Ready(None))
|
||||
} else {
|
||||
msg.payload.as_mut().unwrap().poll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> io::Read for HttpRequest<S> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if self.as_mut().payload.is_some() {
|
||||
match self.as_mut().payload.as_mut().unwrap().poll() {
|
||||
Ok(Async::Ready(Some(mut b))) => {
|
||||
let i = cmp::min(b.len(), buf.len());
|
||||
buf.copy_from_slice(&b.split_to(i)[..i]);
|
||||
|
||||
if !b.is_empty() {
|
||||
self.as_mut().payload.as_mut().unwrap().unread_data(b);
|
||||
}
|
||||
|
||||
if i < buf.len() {
|
||||
match self.read(&mut buf[i..]) {
|
||||
Ok(n) => Ok(i + n),
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(i),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
} else {
|
||||
Ok(i)
|
||||
}
|
||||
}
|
||||
Ok(Async::Ready(None)) => Ok(0),
|
||||
Ok(Async::NotReady) => {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "Not ready"))
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
failure::Error::from(e).compat(),
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> AsyncRead for HttpRequest<S> {}
|
||||
|
||||
impl<S> fmt::Debug for HttpRequest<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let res = writeln!(
|
||||
f,
|
||||
"\nHttpRequest {:?} {}:{}",
|
||||
self.as_ref().version,
|
||||
self.as_ref().method,
|
||||
self.version(),
|
||||
self.method(),
|
||||
self.path()
|
||||
);
|
||||
if !self.query_string().is_empty() {
|
||||
let _ = writeln!(f, " query: ?{:?}", self.query_string());
|
||||
}
|
||||
if !self.match_info().is_empty() {
|
||||
let _ = writeln!(f, " params: {:?}", self.as_ref().params);
|
||||
let _ = writeln!(f, " params: {:?}", self.match_info());
|
||||
}
|
||||
let _ = writeln!(f, " headers:");
|
||||
for (key, val) in self.as_ref().headers.iter() {
|
||||
for (key, val) in self.headers().iter() {
|
||||
let _ = writeln!(f, " {:?}: {:?}", key, val);
|
||||
}
|
||||
res
|
||||
@ -597,7 +380,6 @@ mod tests {
|
||||
use super::*;
|
||||
use resource::ResourceHandler;
|
||||
use router::Resource;
|
||||
use server::ServerSettings;
|
||||
use test::TestRequest;
|
||||
|
||||
#[test]
|
||||
@ -609,7 +391,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_no_request_cookies() {
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
assert!(req.cookies().unwrap().is_empty());
|
||||
}
|
||||
|
||||
@ -648,33 +430,27 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_request_match_info() {
|
||||
let mut req = TestRequest::with_uri("/value/?id=test").finish();
|
||||
|
||||
let mut resource = ResourceHandler::<()>::default();
|
||||
resource.name("index");
|
||||
let mut routes = Vec::new();
|
||||
routes.push((Resource::new("index", "/{key}/"), Some(resource)));
|
||||
let (router, _) = Router::new("", ServerSettings::default(), routes);
|
||||
assert!(router.recognize(&mut req).is_some());
|
||||
let (router, _) = Router::new("", routes);
|
||||
|
||||
assert_eq!(req.match_info().get("key"), Some("value"));
|
||||
let req = TestRequest::with_uri("/value/?id=test").finish();
|
||||
let info = router.recognize(&req).unwrap().1;
|
||||
assert_eq!(info.match_info().get("key"), Some("value"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_url_for() {
|
||||
let req2 = HttpRequest::default();
|
||||
assert_eq!(
|
||||
req2.url_for("unknown", &["test"]),
|
||||
Err(UrlGenerationError::RouterNotAvailable)
|
||||
);
|
||||
|
||||
let mut resource = ResourceHandler::<()>::default();
|
||||
resource.name("index");
|
||||
let routes =
|
||||
vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))];
|
||||
let (router, _) = Router::new("/", ServerSettings::default(), routes);
|
||||
assert!(router.has_route("/user/test.html"));
|
||||
assert!(!router.has_route("/test/unknown"));
|
||||
let (router, _) = Router::new("/", routes);
|
||||
let info = router.default_route_info(0);
|
||||
assert!(info.has_route("/user/test.html"));
|
||||
assert!(!info.has_route("/test/unknown"));
|
||||
|
||||
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
|
||||
.finish_with_router(router);
|
||||
@ -696,16 +472,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_url_for_with_prefix() {
|
||||
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish();
|
||||
|
||||
let mut resource = ResourceHandler::<()>::default();
|
||||
resource.name("index");
|
||||
let routes = vec![(Resource::new("index", "/user/{name}.html"), Some(resource))];
|
||||
let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes);
|
||||
assert!(router.has_route("/user/test.html"));
|
||||
assert!(!router.has_route("/prefix/user/test.html"));
|
||||
let (router, _) = Router::new("/prefix/", routes);
|
||||
let info = router.default_route_info(0);
|
||||
assert!(info.has_route("/user/test.html"));
|
||||
assert!(!info.has_route("/prefix/user/test.html"));
|
||||
|
||||
let req = req.with_state(Rc::new(()), router);
|
||||
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
|
||||
.finish_with_router(router);
|
||||
let url = req.url_for("index", &["test"]);
|
||||
assert_eq!(
|
||||
url.ok().unwrap().as_str(),
|
||||
@ -715,16 +491,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_url_for_static() {
|
||||
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish();
|
||||
|
||||
let mut resource = ResourceHandler::<()>::default();
|
||||
resource.name("index");
|
||||
let routes = vec![(Resource::new("index", "/index.html"), Some(resource))];
|
||||
let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes);
|
||||
assert!(router.has_route("/index.html"));
|
||||
assert!(!router.has_route("/prefix/index.html"));
|
||||
let (router, _) = Router::new("/prefix/", routes);
|
||||
let info = router.default_route_info(0);
|
||||
assert!(info.has_route("/index.html"));
|
||||
assert!(!info.has_route("/prefix/index.html"));
|
||||
|
||||
let req = req.with_state(Rc::new(()), router);
|
||||
let req = TestRequest::default()
|
||||
.header(header::HOST, "www.rust-lang.org")
|
||||
.finish_with_router(router);
|
||||
let url = req.url_for_static("index");
|
||||
assert_eq!(
|
||||
url.ok().unwrap().as_str(),
|
||||
@ -734,18 +511,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_url_for_external() {
|
||||
let req = HttpRequest::default();
|
||||
|
||||
let mut resource = ResourceHandler::<()>::default();
|
||||
resource.name("index");
|
||||
let routes = vec![(
|
||||
Resource::external("youtube", "https://youtube.com/watch/{video_id}"),
|
||||
None,
|
||||
)];
|
||||
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes);
|
||||
assert!(!router.has_route("https://youtube.com/watch/unknown"));
|
||||
let router = Router::new::<()>("", routes).0;
|
||||
let info = router.default_route_info(0);
|
||||
assert!(!info.has_route("https://youtube.com/watch/unknown"));
|
||||
|
||||
let req = req.with_state(Rc::new(()), router);
|
||||
let req = TestRequest::default().finish_with_router(router);
|
||||
let url = req.url_for("youtube", &["oHg5SJYRHA0"]);
|
||||
assert_eq!(
|
||||
url.ok().unwrap().as_str(),
|
||||
|
@ -553,10 +553,10 @@ impl HttpResponseBuilder {
|
||||
/// # extern crate actix_web;
|
||||
/// use actix_web::{http, HttpRequest, HttpResponse, Result};
|
||||
///
|
||||
/// fn index(req: HttpRequest) -> HttpResponse {
|
||||
/// fn index(req: &HttpRequest) -> HttpResponse {
|
||||
/// let mut builder = HttpResponse::Ok();
|
||||
///
|
||||
/// if let Some(cookie) = req.cookie("name") {
|
||||
/// if let Some(ref cookie) = req.cookie("name") {
|
||||
/// builder.del_cookie(cookie);
|
||||
/// }
|
||||
///
|
||||
@ -860,13 +860,9 @@ impl<'a> From<&'a ClientResponse> for HttpResponseBuilder {
|
||||
|
||||
impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder {
|
||||
fn from(req: &'a HttpRequest<S>) -> HttpResponseBuilder {
|
||||
if let Some(router) = req.router() {
|
||||
router
|
||||
.server_settings()
|
||||
.get_response_builder(StatusCode::OK)
|
||||
} else {
|
||||
HttpResponse::Ok()
|
||||
}
|
||||
req.request()
|
||||
.server_settings()
|
||||
.get_response_builder(StatusCode::OK)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1050,6 +1046,8 @@ mod tests {
|
||||
use std::str::FromStr;
|
||||
use time::Duration;
|
||||
|
||||
use test::TestRequest;
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let resp = HttpResponse::Ok()
|
||||
@ -1062,17 +1060,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_response_cookies() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(COOKIE, HeaderValue::from_static("cookie1=value1"));
|
||||
headers.insert(COOKIE, HeaderValue::from_static("cookie2=value2"));
|
||||
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(COOKIE, "cookie1=value1")
|
||||
.header(COOKIE, "cookie2=value2")
|
||||
.finish();
|
||||
let cookies = req.cookies().unwrap();
|
||||
|
||||
let resp = HttpResponse::Ok()
|
||||
@ -1094,7 +1085,7 @@ mod tests {
|
||||
.map(|v| v.to_str().unwrap().to_owned())
|
||||
.collect();
|
||||
val.sort();
|
||||
assert!(val[0].starts_with("cookie2=; Max-Age=0;"));
|
||||
assert!(val[0].starts_with("cookie1=; Max-Age=0;"));
|
||||
assert_eq!(
|
||||
val[1],
|
||||
"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400"
|
||||
@ -1208,7 +1199,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_into_response() {
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
|
||||
let resp: HttpResponse = "test".into();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
103
src/info.rs
103
src/info.rs
@ -1,14 +1,17 @@
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use http::header::{self, HeaderName};
|
||||
use httpmessage::HttpMessage;
|
||||
use httprequest::HttpRequest;
|
||||
use server::Request;
|
||||
|
||||
const X_FORWARDED_FOR: &str = "X-FORWARDED-FOR";
|
||||
const X_FORWARDED_HOST: &str = "X-FORWARDED-HOST";
|
||||
const X_FORWARDED_PROTO: &str = "X-FORWARDED-PROTO";
|
||||
const X_FORWARDED_FOR: &[u8] = b"x-forwarded-for";
|
||||
const X_FORWARDED_HOST: &[u8] = b"x-forwarded-host";
|
||||
const X_FORWARDED_PROTO: &[u8] = b"x-forwarded-proto";
|
||||
|
||||
/// `HttpRequest` connection information
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ConnectionInfo {
|
||||
scheme: String,
|
||||
host: String,
|
||||
@ -19,7 +22,7 @@ pub struct ConnectionInfo {
|
||||
impl ConnectionInfo {
|
||||
/// Create *ConnectionInfo* instance for a request.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
|
||||
pub fn new<S>(req: &HttpRequest<S>) -> ConnectionInfo {
|
||||
pub fn update(&mut self, req: &Request) {
|
||||
let mut host = None;
|
||||
let mut scheme = None;
|
||||
let mut remote = None;
|
||||
@ -56,7 +59,7 @@ impl ConnectionInfo {
|
||||
if scheme.is_none() {
|
||||
if let Some(h) = req
|
||||
.headers()
|
||||
.get(HeaderName::from_str(X_FORWARDED_PROTO).unwrap())
|
||||
.get(HeaderName::from_lowercase(X_FORWARDED_PROTO).unwrap())
|
||||
{
|
||||
if let Ok(h) = h.to_str() {
|
||||
scheme = h.split(',').next().map(|v| v.trim());
|
||||
@ -64,12 +67,8 @@ impl ConnectionInfo {
|
||||
}
|
||||
if scheme.is_none() {
|
||||
scheme = req.uri().scheme_part().map(|a| a.as_str());
|
||||
if scheme.is_none() {
|
||||
if let Some(router) = req.router() {
|
||||
if router.server_settings().secure() {
|
||||
scheme = Some("https")
|
||||
}
|
||||
}
|
||||
if scheme.is_none() && req.server_settings().secure() {
|
||||
scheme = Some("https")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,7 +77,7 @@ impl ConnectionInfo {
|
||||
if host.is_none() {
|
||||
if let Some(h) = req
|
||||
.headers()
|
||||
.get(HeaderName::from_str(X_FORWARDED_HOST).unwrap())
|
||||
.get(HeaderName::from_lowercase(X_FORWARDED_HOST).unwrap())
|
||||
{
|
||||
if let Ok(h) = h.to_str() {
|
||||
host = h.split(',').next().map(|v| v.trim());
|
||||
@ -91,9 +90,7 @@ impl ConnectionInfo {
|
||||
if host.is_none() {
|
||||
host = req.uri().authority_part().map(|a| a.as_str());
|
||||
if host.is_none() {
|
||||
if let Some(router) = req.router() {
|
||||
host = Some(router.server_settings().host());
|
||||
}
|
||||
host = Some(req.server_settings().host());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,7 +100,7 @@ impl ConnectionInfo {
|
||||
if remote.is_none() {
|
||||
if let Some(h) = req
|
||||
.headers()
|
||||
.get(HeaderName::from_str(X_FORWARDED_FOR).unwrap())
|
||||
.get(HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap())
|
||||
{
|
||||
if let Ok(h) = h.to_str() {
|
||||
remote = h.split(',').next().map(|v| v.trim());
|
||||
@ -115,12 +112,10 @@ impl ConnectionInfo {
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionInfo {
|
||||
scheme: scheme.unwrap_or("http").to_owned(),
|
||||
host: host.unwrap_or("localhost").to_owned(),
|
||||
remote: remote.map(|s| s.to_owned()),
|
||||
peer,
|
||||
}
|
||||
self.scheme = scheme.unwrap_or("http").to_owned();
|
||||
self.host = host.unwrap_or("localhost").to_owned();
|
||||
self.remote = remote.map(|s| s.to_owned());
|
||||
self.peer = peer;
|
||||
}
|
||||
|
||||
/// Scheme of the request.
|
||||
@ -171,59 +166,59 @@ impl ConnectionInfo {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use http::header::HeaderValue;
|
||||
use test::TestRequest;
|
||||
|
||||
#[test]
|
||||
fn test_forwarded() {
|
||||
let req = HttpRequest::default();
|
||||
let info = ConnectionInfo::new(&req);
|
||||
let req = TestRequest::default().request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
assert_eq!(info.scheme(), "http");
|
||||
assert_eq!(info.host(), "localhost");
|
||||
assert_eq!(info.host(), "localhost:8080");
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
header::FORWARDED,
|
||||
HeaderValue::from_static(
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::FORWARDED,
|
||||
"for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org",
|
||||
),
|
||||
);
|
||||
)
|
||||
.request();
|
||||
|
||||
let info = ConnectionInfo::new(&req);
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
assert_eq!(info.scheme(), "https");
|
||||
assert_eq!(info.host(), "rust-lang.org");
|
||||
assert_eq!(info.remote(), Some("192.0.2.60"));
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut()
|
||||
.insert(header::HOST, HeaderValue::from_static("rust-lang.org"));
|
||||
let req = TestRequest::default()
|
||||
.header(header::HOST, "rust-lang.org")
|
||||
.request();
|
||||
|
||||
let info = ConnectionInfo::new(&req);
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
assert_eq!(info.scheme(), "http");
|
||||
assert_eq!(info.host(), "rust-lang.org");
|
||||
assert_eq!(info.remote(), None);
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
HeaderName::from_str(X_FORWARDED_FOR).unwrap(),
|
||||
HeaderValue::from_static("192.0.2.60"),
|
||||
);
|
||||
let info = ConnectionInfo::new(&req);
|
||||
let req = TestRequest::default()
|
||||
.header(X_FORWARDED_FOR, "192.0.2.60")
|
||||
.request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
assert_eq!(info.remote(), Some("192.0.2.60"));
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
HeaderName::from_str(X_FORWARDED_HOST).unwrap(),
|
||||
HeaderValue::from_static("192.0.2.60"),
|
||||
);
|
||||
let info = ConnectionInfo::new(&req);
|
||||
let req = TestRequest::default()
|
||||
.header(X_FORWARDED_HOST, "192.0.2.60")
|
||||
.request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
assert_eq!(info.host(), "192.0.2.60");
|
||||
assert_eq!(info.remote(), None);
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
HeaderName::from_str(X_FORWARDED_PROTO).unwrap(),
|
||||
HeaderValue::from_static("https"),
|
||||
);
|
||||
let info = ConnectionInfo::new(&req);
|
||||
let mut req = TestRequest::default()
|
||||
.header(X_FORWARDED_PROTO, "https")
|
||||
.request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
assert_eq!(info.scheme(), "https");
|
||||
}
|
||||
}
|
||||
|
197
src/json.rs
197
src/json.rs
@ -140,12 +140,12 @@ where
|
||||
|
||||
#[inline]
|
||||
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
|
||||
let req = req.clone();
|
||||
let req2 = req.clone();
|
||||
let err = Rc::clone(&cfg.ehandler);
|
||||
Box::new(
|
||||
JsonBody::new(req.clone())
|
||||
JsonBody::new(req)
|
||||
.limit(cfg.limit)
|
||||
.map_err(move |e| (*err)(e, req))
|
||||
.map_err(move |e| (*err)(e, &req2))
|
||||
.map(Json),
|
||||
)
|
||||
}
|
||||
@ -183,7 +183,7 @@ where
|
||||
/// ```
|
||||
pub struct JsonConfig<S> {
|
||||
limit: usize,
|
||||
ehandler: Rc<Fn(JsonPayloadError, HttpRequest<S>) -> Error>,
|
||||
ehandler: Rc<Fn(JsonPayloadError, &HttpRequest<S>) -> Error>,
|
||||
}
|
||||
|
||||
impl<S> JsonConfig<S> {
|
||||
@ -196,7 +196,7 @@ impl<S> JsonConfig<S> {
|
||||
/// Set custom error handler
|
||||
pub fn error_handler<F>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: Fn(JsonPayloadError, HttpRequest<S>) -> Error + 'static,
|
||||
F: Fn(JsonPayloadError, &HttpRequest<S>) -> Error + 'static,
|
||||
{
|
||||
self.ehandler = Rc::new(f);
|
||||
self
|
||||
@ -243,19 +243,48 @@ impl<S> Default for JsonConfig<S> {
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub struct JsonBody<T, U: DeserializeOwned> {
|
||||
pub struct JsonBody<T: HttpMessage, U: DeserializeOwned> {
|
||||
limit: usize,
|
||||
req: Option<T>,
|
||||
length: Option<usize>,
|
||||
stream: Option<T::Stream>,
|
||||
err: Option<JsonPayloadError>,
|
||||
fut: Option<Box<Future<Item = U, Error = JsonPayloadError>>>,
|
||||
}
|
||||
|
||||
impl<T, U: DeserializeOwned> JsonBody<T, U> {
|
||||
impl<T: HttpMessage, U: DeserializeOwned> JsonBody<T, U> {
|
||||
/// Create `JsonBody` for request.
|
||||
pub fn new(req: T) -> Self {
|
||||
pub fn new(req: &T) -> Self {
|
||||
// check content-type
|
||||
let json = if let Ok(Some(mime)) = req.mime_type() {
|
||||
mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !json {
|
||||
return JsonBody {
|
||||
limit: 262_144,
|
||||
length: None,
|
||||
stream: None,
|
||||
fut: None,
|
||||
err: Some(JsonPayloadError::ContentType),
|
||||
};
|
||||
}
|
||||
|
||||
let mut len = None;
|
||||
if let Some(l) = req.headers().get(CONTENT_LENGTH) {
|
||||
if let Ok(s) = l.to_str() {
|
||||
if let Ok(l) = s.parse::<usize>() {
|
||||
len = Some(l)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsonBody {
|
||||
limit: 262_144,
|
||||
req: Some(req),
|
||||
length: len,
|
||||
stream: Some(req.payload()),
|
||||
fut: None,
|
||||
err: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,56 +295,42 @@ impl<T, U: DeserializeOwned> JsonBody<T, U> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: DeserializeOwned + 'static> Future for JsonBody<T, U>
|
||||
where
|
||||
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
|
||||
{
|
||||
impl<T: HttpMessage + 'static, U: DeserializeOwned + 'static> Future for JsonBody<T, U> {
|
||||
type Item = U;
|
||||
type Error = JsonPayloadError;
|
||||
|
||||
fn poll(&mut self) -> Poll<U, JsonPayloadError> {
|
||||
if let Some(req) = self.req.take() {
|
||||
if let Some(len) = req.headers().get(CONTENT_LENGTH) {
|
||||
if let Ok(s) = len.to_str() {
|
||||
if let Ok(len) = s.parse::<usize>() {
|
||||
if len > self.limit {
|
||||
return Err(JsonPayloadError::Overflow);
|
||||
}
|
||||
} else {
|
||||
return Err(JsonPayloadError::Overflow);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check content-type
|
||||
|
||||
let json = if let Ok(Some(mime)) = req.mime_type() {
|
||||
mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !json {
|
||||
return Err(JsonPayloadError::ContentType);
|
||||
}
|
||||
|
||||
let limit = self.limit;
|
||||
let fut = req
|
||||
.from_err()
|
||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
if (body.len() + chunk.len()) > limit {
|
||||
Err(JsonPayloadError::Overflow)
|
||||
} else {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok(body)
|
||||
}
|
||||
})
|
||||
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?));
|
||||
self.fut = Some(Box::new(fut));
|
||||
if let Some(ref mut fut) = self.fut {
|
||||
return fut.poll();
|
||||
}
|
||||
|
||||
self.fut
|
||||
.as_mut()
|
||||
if let Some(err) = self.err.take() {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let limit = self.limit;
|
||||
if let Some(len) = self.length.take() {
|
||||
if len > limit {
|
||||
return Err(JsonPayloadError::Overflow);
|
||||
}
|
||||
}
|
||||
|
||||
let fut = self
|
||||
.stream
|
||||
.take()
|
||||
.expect("JsonBody could not be used second time")
|
||||
.poll()
|
||||
.from_err()
|
||||
.fold(BytesMut::new(), move |mut body, chunk| {
|
||||
if (body.len() + chunk.len()) > limit {
|
||||
Err(JsonPayloadError::Overflow)
|
||||
} else {
|
||||
body.extend_from_slice(&chunk);
|
||||
Ok(body)
|
||||
}
|
||||
})
|
||||
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?));
|
||||
self.fut = Some(Box::new(fut));
|
||||
self.poll()
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,6 +342,7 @@ mod tests {
|
||||
use http::header;
|
||||
|
||||
use handler::Handler;
|
||||
use test::TestRequest;
|
||||
use with::With;
|
||||
|
||||
impl PartialEq for JsonPayloadError {
|
||||
@ -355,7 +371,7 @@ mod tests {
|
||||
let json = Json(MyObject {
|
||||
name: "test".to_owned(),
|
||||
});
|
||||
let resp = json.respond_to(&HttpRequest::default()).unwrap();
|
||||
let resp = json.respond_to(&TestRequest::default().finish()).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"application/json"
|
||||
@ -364,41 +380,44 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_json_body() {
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
let mut json = req.json::<MyObject>();
|
||||
assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType);
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/text"),
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/text"),
|
||||
)
|
||||
.finish();
|
||||
let mut json = req.json::<MyObject>();
|
||||
assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType);
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
);
|
||||
req.headers_mut().insert(
|
||||
header::CONTENT_LENGTH,
|
||||
header::HeaderValue::from_static("10000"),
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
)
|
||||
.header(
|
||||
header::CONTENT_LENGTH,
|
||||
header::HeaderValue::from_static("10000"),
|
||||
)
|
||||
.finish();
|
||||
let mut json = req.json::<MyObject>().limit(100);
|
||||
assert_eq!(json.poll().err().unwrap(), JsonPayloadError::Overflow);
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
);
|
||||
req.headers_mut().insert(
|
||||
header::CONTENT_LENGTH,
|
||||
header::HeaderValue::from_static("16"),
|
||||
);
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"{\"name\": \"test\"}"));
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
)
|
||||
.header(
|
||||
header::CONTENT_LENGTH,
|
||||
header::HeaderValue::from_static("16"),
|
||||
)
|
||||
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
|
||||
.finish();
|
||||
|
||||
let mut json = req.json::<MyObject>();
|
||||
assert_eq!(
|
||||
json.poll().ok().unwrap(),
|
||||
@ -414,20 +433,18 @@ mod tests {
|
||||
cfg.limit(4096);
|
||||
let handler = With::new(|data: Json<MyObject>| data, cfg);
|
||||
|
||||
let req = HttpRequest::default();
|
||||
assert!(handler.handle(req).as_err().is_some());
|
||||
let req = TestRequest::default().finish();
|
||||
assert!(handler.handle(&req).as_err().is_some());
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
req.headers_mut().insert(
|
||||
let req = TestRequest::with_header(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
);
|
||||
req.headers_mut().insert(
|
||||
).header(
|
||||
header::CONTENT_LENGTH,
|
||||
header::HeaderValue::from_static("16"),
|
||||
);
|
||||
req.payload_mut()
|
||||
.unread_data(Bytes::from_static(b"{\"name\": \"test\"}"));
|
||||
assert!(handler.handle(req).as_err().is_none())
|
||||
)
|
||||
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
|
||||
.finish();
|
||||
assert!(handler.handle(&req).as_err().is_none())
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@
|
||||
allow(decimal_literal_representation, suspicious_arithmetic_impl)
|
||||
)]
|
||||
#![warn(missing_docs)]
|
||||
#![allow(unused_mut, unused_imports, unused_variables, dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
@ -199,6 +200,7 @@ pub use httprequest::HttpRequest;
|
||||
pub use httpresponse::HttpResponse;
|
||||
pub use json::Json;
|
||||
pub use scope::Scope;
|
||||
pub use server::Request;
|
||||
|
||||
pub mod actix {
|
||||
//! Re-exports [actix's](https://docs.rs/actix/) prelude
|
||||
|
@ -60,6 +60,7 @@ use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::{Middleware, Response, Started};
|
||||
use resource::ResourceHandler;
|
||||
use server::Request;
|
||||
|
||||
/// A set of errors that can occur during processing CORS
|
||||
#[derive(Debug, Fail)]
|
||||
@ -279,11 +280,13 @@ impl Cors {
|
||||
/// `ResourceHandler::middleware()` method, but in that case *Cors*
|
||||
/// middleware wont be able to handle *OPTIONS* requests.
|
||||
pub fn register<S: 'static>(self, resource: &mut ResourceHandler<S>) {
|
||||
resource.method(Method::OPTIONS).h(|_| HttpResponse::Ok());
|
||||
resource
|
||||
.method(Method::OPTIONS)
|
||||
.h(|_: &_| HttpResponse::Ok());
|
||||
resource.middleware(self);
|
||||
}
|
||||
|
||||
fn validate_origin<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CorsError> {
|
||||
fn validate_origin(&self, req: &Request) -> Result<(), CorsError> {
|
||||
if let Some(hdr) = req.headers().get(header::ORIGIN) {
|
||||
if let Ok(origin) = hdr.to_str() {
|
||||
return match self.inner.origins {
|
||||
@ -303,9 +306,7 @@ impl Cors {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_allowed_method<S>(
|
||||
&self, req: &mut HttpRequest<S>,
|
||||
) -> Result<(), CorsError> {
|
||||
fn validate_allowed_method(&self, req: &Request) -> Result<(), CorsError> {
|
||||
if let Some(hdr) = req.headers().get(header::ACCESS_CONTROL_REQUEST_METHOD) {
|
||||
if let Ok(meth) = hdr.to_str() {
|
||||
if let Ok(method) = Method::try_from(meth) {
|
||||
@ -323,9 +324,7 @@ impl Cors {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_allowed_headers<S>(
|
||||
&self, req: &mut HttpRequest<S>,
|
||||
) -> Result<(), CorsError> {
|
||||
fn validate_allowed_headers(&self, req: &Request) -> Result<(), CorsError> {
|
||||
match self.inner.headers {
|
||||
AllOrSome::All => Ok(()),
|
||||
AllOrSome::Some(ref allowed_headers) => {
|
||||
@ -356,11 +355,11 @@ impl Cors {
|
||||
}
|
||||
|
||||
impl<S> Middleware<S> for Cors {
|
||||
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
||||
if self.inner.preflight && Method::OPTIONS == *req.method() {
|
||||
self.validate_origin(req)?;
|
||||
self.validate_allowed_method(req)?;
|
||||
self.validate_allowed_headers(req)?;
|
||||
self.validate_allowed_method(&req)?;
|
||||
self.validate_allowed_headers(&req)?;
|
||||
|
||||
// allowed headers
|
||||
let headers = if let Some(headers) = self.inner.headers.as_ref() {
|
||||
@ -434,7 +433,7 @@ impl<S> Middleware<S> for Cors {
|
||||
}
|
||||
|
||||
fn response(
|
||||
&self, req: &mut HttpRequest<S>, mut resp: HttpResponse,
|
||||
&self, req: &HttpRequest<S>, mut resp: HttpResponse,
|
||||
) -> Result<Response> {
|
||||
match self.inner.origins {
|
||||
AllOrSome::All => {
|
||||
@ -945,10 +944,9 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_origin_allows_all_origins() {
|
||||
let cors = Cors::default();
|
||||
let mut req =
|
||||
TestRequest::with_header("Origin", "https://www.example.com").finish();
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com").finish();
|
||||
|
||||
assert!(cors.start(&mut req).ok().unwrap().is_done())
|
||||
assert!(cors.start(&req).ok().unwrap().is_done())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -961,20 +959,20 @@ mod tests {
|
||||
.allowed_header(header::CONTENT_TYPE)
|
||||
.finish();
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::OPTIONS)
|
||||
.finish();
|
||||
|
||||
assert!(cors.start(&mut req).is_err());
|
||||
assert!(cors.start(&req).is_err());
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "put")
|
||||
.method(Method::OPTIONS)
|
||||
.finish();
|
||||
|
||||
assert!(cors.start(&mut req).is_err());
|
||||
assert!(cors.start(&req).is_err());
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
|
||||
.header(
|
||||
header::ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
@ -983,7 +981,7 @@ mod tests {
|
||||
.method(Method::OPTIONS)
|
||||
.finish();
|
||||
|
||||
let resp = cors.start(&mut req).unwrap().response();
|
||||
let resp = cors.start(&req).unwrap().response();
|
||||
assert_eq!(
|
||||
&b"*"[..],
|
||||
resp.headers()
|
||||
@ -1007,7 +1005,7 @@ mod tests {
|
||||
// as_bytes());
|
||||
|
||||
Rc::get_mut(&mut cors.inner).unwrap().preflight = false;
|
||||
assert!(cors.start(&mut req).unwrap().is_done());
|
||||
assert!(cors.start(&req).unwrap().is_done());
|
||||
}
|
||||
|
||||
// #[test]
|
||||
@ -1017,7 +1015,7 @@ mod tests {
|
||||
// .allowed_origin("https://www.example.com")
|
||||
// .finish();
|
||||
// let mut req = HttpRequest::default();
|
||||
// cors.start(&mut req).unwrap();
|
||||
// cors.start(&req).unwrap();
|
||||
// }
|
||||
|
||||
#[test]
|
||||
@ -1027,10 +1025,10 @@ mod tests {
|
||||
.allowed_origin("https://www.example.com")
|
||||
.finish();
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.unknown.com")
|
||||
let req = TestRequest::with_header("Origin", "https://www.unknown.com")
|
||||
.method(Method::GET)
|
||||
.finish();
|
||||
cors.start(&mut req).unwrap();
|
||||
cors.start(&req).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1039,30 +1037,30 @@ mod tests {
|
||||
.allowed_origin("https://www.example.com")
|
||||
.finish();
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::GET)
|
||||
.finish();
|
||||
|
||||
assert!(cors.start(&mut req).unwrap().is_done());
|
||||
assert!(cors.start(&req).unwrap().is_done());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_origin_response() {
|
||||
let cors = Cors::build().finish();
|
||||
|
||||
let mut req = TestRequest::default().method(Method::GET).finish();
|
||||
let req = TestRequest::default().method(Method::GET).finish();
|
||||
let resp: HttpResponse = HttpResponse::Ok().into();
|
||||
let resp = cors.response(&mut req, resp).unwrap().response();
|
||||
let resp = cors.response(&req, resp).unwrap().response();
|
||||
assert!(
|
||||
resp.headers()
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.is_none()
|
||||
);
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::OPTIONS)
|
||||
.finish();
|
||||
let resp = cors.response(&mut req, resp).unwrap().response();
|
||||
let resp = cors.response(&req, resp).unwrap().response();
|
||||
assert_eq!(
|
||||
&b"https://www.example.com"[..],
|
||||
resp.headers()
|
||||
@ -1083,12 +1081,12 @@ mod tests {
|
||||
.allowed_header(header::CONTENT_TYPE)
|
||||
.finish();
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::OPTIONS)
|
||||
.finish();
|
||||
|
||||
let resp: HttpResponse = HttpResponse::Ok().into();
|
||||
let resp = cors.response(&mut req, resp).unwrap().response();
|
||||
let resp = cors.response(&req, resp).unwrap().response();
|
||||
assert_eq!(
|
||||
&b"*"[..],
|
||||
resp.headers()
|
||||
@ -1103,7 +1101,7 @@ mod tests {
|
||||
|
||||
let resp: HttpResponse =
|
||||
HttpResponse::Ok().header(header::VARY, "Accept").finish();
|
||||
let resp = cors.response(&mut req, resp).unwrap().response();
|
||||
let resp = cors.response(&req, resp).unwrap().response();
|
||||
assert_eq!(
|
||||
&b"Accept, Origin"[..],
|
||||
resp.headers().get(header::VARY).unwrap().as_bytes()
|
||||
@ -1114,7 +1112,7 @@ mod tests {
|
||||
.allowed_origin("https://www.example.com")
|
||||
.finish();
|
||||
let resp: HttpResponse = HttpResponse::Ok().into();
|
||||
let resp = cors.response(&mut req, resp).unwrap().response();
|
||||
let resp = cors.response(&req, resp).unwrap().response();
|
||||
assert_eq!(
|
||||
&b"https://www.example.com"[..],
|
||||
resp.headers()
|
||||
|
@ -25,7 +25,7 @@
|
||||
//! use actix_web::middleware::csrf;
|
||||
//! use actix_web::{http, App, HttpRequest, HttpResponse};
|
||||
//!
|
||||
//! fn handle_post(_: HttpRequest) -> &'static str {
|
||||
//! fn handle_post(_: &HttpRequest) -> &'static str {
|
||||
//! "This action should only be triggered with requests from the same site"
|
||||
//! }
|
||||
//!
|
||||
@ -54,6 +54,7 @@ use httpmessage::HttpMessage;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::{Middleware, Started};
|
||||
use server::Request;
|
||||
|
||||
/// Potential cross-site request forgery detected.
|
||||
#[derive(Debug, Fail)]
|
||||
@ -187,7 +188,7 @@ impl CsrfFilter {
|
||||
self
|
||||
}
|
||||
|
||||
fn validate<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CsrfError> {
|
||||
fn validate(&self, req: &Request) -> Result<(), CsrfError> {
|
||||
let is_upgrade = req.headers().contains_key(header::UPGRADE);
|
||||
let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade);
|
||||
|
||||
@ -209,7 +210,7 @@ impl CsrfFilter {
|
||||
}
|
||||
|
||||
impl<S> Middleware<S> for CsrfFilter {
|
||||
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
||||
self.validate(req)?;
|
||||
Ok(Started::Done)
|
||||
}
|
||||
@ -225,35 +226,35 @@ mod tests {
|
||||
fn test_safe() {
|
||||
let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
|
||||
let req = TestRequest::with_header("Origin", "https://www.w3.org")
|
||||
.method(Method::HEAD)
|
||||
.finish();
|
||||
|
||||
assert!(csrf.start(&mut req).is_ok());
|
||||
assert!(csrf.start(&req).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_csrf() {
|
||||
let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
|
||||
let req = TestRequest::with_header("Origin", "https://www.w3.org")
|
||||
.method(Method::POST)
|
||||
.finish();
|
||||
|
||||
assert!(csrf.start(&mut req).is_err());
|
||||
assert!(csrf.start(&req).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_referer() {
|
||||
let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
|
||||
|
||||
let mut req = TestRequest::with_header(
|
||||
let req = TestRequest::with_header(
|
||||
"Referer",
|
||||
"https://www.example.com/some/path?query=param",
|
||||
).method(Method::POST)
|
||||
.finish();
|
||||
|
||||
assert!(csrf.start(&mut req).is_ok());
|
||||
assert!(csrf.start(&req).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -264,13 +265,13 @@ mod tests {
|
||||
.allowed_origin("https://www.example.com")
|
||||
.allow_upgrade();
|
||||
|
||||
let mut req = TestRequest::with_header("Origin", "https://cswsh.com")
|
||||
let req = TestRequest::with_header("Origin", "https://cswsh.com")
|
||||
.header("Connection", "Upgrade")
|
||||
.header("Upgrade", "websocket")
|
||||
.method(Method::GET)
|
||||
.finish();
|
||||
|
||||
assert!(strict_csrf.start(&mut req).is_err());
|
||||
assert!(lax_csrf.start(&mut req).is_ok());
|
||||
assert!(strict_csrf.start(&req).is_err());
|
||||
assert!(lax_csrf.start(&req).is_ok());
|
||||
}
|
||||
}
|
||||
|
@ -74,9 +74,7 @@ impl DefaultHeaders {
|
||||
}
|
||||
|
||||
impl<S> Middleware<S> for DefaultHeaders {
|
||||
fn response(
|
||||
&self, _: &mut HttpRequest<S>, mut resp: HttpResponse,
|
||||
) -> Result<Response> {
|
||||
fn response(&self, _: &HttpRequest<S>, mut resp: HttpResponse) -> Result<Response> {
|
||||
for (key, value) in self.headers.iter() {
|
||||
if !resp.headers().contains_key(key) {
|
||||
resp.headers_mut().insert(key, value.clone());
|
||||
@ -97,22 +95,23 @@ impl<S> Middleware<S> for DefaultHeaders {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use http::header::CONTENT_TYPE;
|
||||
use test::TestRequest;
|
||||
|
||||
#[test]
|
||||
fn test_default_headers() {
|
||||
let mw = DefaultHeaders::new().header(CONTENT_TYPE, "0001");
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
|
||||
let resp = HttpResponse::Ok().finish();
|
||||
let resp = match mw.response(&mut req, resp) {
|
||||
let resp = match mw.response(&req, resp) {
|
||||
Ok(Response::Done(resp)) => resp,
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
||||
|
||||
let resp = HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish();
|
||||
let resp = match mw.response(&mut req, resp) {
|
||||
let resp = match mw.response(&req, resp) {
|
||||
Ok(Response::Done(resp)) => resp,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::{Middleware, Response};
|
||||
|
||||
type ErrorHandler<S> = Fn(&mut HttpRequest<S>, HttpResponse) -> Result<Response>;
|
||||
type ErrorHandler<S> = Fn(&HttpRequest<S>, HttpResponse) -> Result<Response>;
|
||||
|
||||
/// `Middleware` for allowing custom handlers for responses.
|
||||
///
|
||||
@ -21,7 +21,7 @@ type ErrorHandler<S> = Fn(&mut HttpRequest<S>, HttpResponse) -> Result<Response>
|
||||
/// use actix_web::middleware::{ErrorHandlers, Response};
|
||||
/// use actix_web::{http, App, HttpRequest, HttpResponse, Result};
|
||||
///
|
||||
/// fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
/// fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
/// let mut builder = resp.into_builder();
|
||||
/// builder.header(http::header::CONTENT_TYPE, "application/json");
|
||||
/// Ok(Response::Done(builder.into()))
|
||||
@ -62,7 +62,7 @@ impl<S> ErrorHandlers<S> {
|
||||
/// Register error handler for specified status code
|
||||
pub fn handler<F>(mut self, status: StatusCode, handler: F) -> Self
|
||||
where
|
||||
F: Fn(&mut HttpRequest<S>, HttpResponse) -> Result<Response> + 'static,
|
||||
F: Fn(&HttpRequest<S>, HttpResponse) -> Result<Response> + 'static,
|
||||
{
|
||||
self.handlers.insert(status, Box::new(handler));
|
||||
self
|
||||
@ -70,9 +70,7 @@ impl<S> ErrorHandlers<S> {
|
||||
}
|
||||
|
||||
impl<S: 'static> Middleware<S> for ErrorHandlers<S> {
|
||||
fn response(
|
||||
&self, req: &mut HttpRequest<S>, resp: HttpResponse,
|
||||
) -> Result<Response> {
|
||||
fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
if let Some(handler) = self.handlers.get(&resp.status()) {
|
||||
handler(req, resp)
|
||||
} else {
|
||||
@ -91,7 +89,10 @@ mod tests {
|
||||
use middleware::Started;
|
||||
use test;
|
||||
|
||||
fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
use server::Request;
|
||||
use test::TestRequest;
|
||||
|
||||
fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
let mut builder = resp.into_builder();
|
||||
builder.header(CONTENT_TYPE, "0001");
|
||||
Ok(Response::Done(builder.into()))
|
||||
@ -102,7 +103,7 @@ mod tests {
|
||||
let mw =
|
||||
ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);
|
||||
|
||||
let mut req = HttpRequest::default();
|
||||
let mut req = TestRequest::default().finish();
|
||||
let resp = HttpResponse::InternalServerError().finish();
|
||||
let resp = match mw.response(&mut req, resp) {
|
||||
Ok(Response::Done(resp)) => resp,
|
||||
@ -121,7 +122,7 @@ mod tests {
|
||||
struct MiddlewareOne;
|
||||
|
||||
impl<S> Middleware<S> for MiddlewareOne {
|
||||
fn start(&self, _req: &mut HttpRequest<S>) -> Result<Started, Error> {
|
||||
fn start(&self, _: &HttpRequest<S>) -> Result<Started, Error> {
|
||||
Err(ErrorInternalServerError("middleware error"))
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@
|
||||
//! ));
|
||||
//! }
|
||||
//! ```
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use cookie::{Cookie, CookieJar, Key};
|
||||
@ -58,6 +59,7 @@ use http::header::{self, HeaderValue};
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::{Middleware, Response, Started};
|
||||
use server::Request;
|
||||
|
||||
/// The helper trait to obtain your identity from a request.
|
||||
///
|
||||
@ -88,32 +90,32 @@ use middleware::{Middleware, Response, Started};
|
||||
pub trait RequestIdentity {
|
||||
/// Return the claimed identity of the user associated request or
|
||||
/// ``None`` if no identity can be found associated with the request.
|
||||
fn identity(&self) -> Option<&str>;
|
||||
fn identity(&self) -> Option<String>;
|
||||
|
||||
/// Remember identity.
|
||||
fn remember(&mut self, identity: String);
|
||||
fn remember(&self, identity: String);
|
||||
|
||||
/// This method is used to 'forget' the current identity on subsequent
|
||||
/// requests.
|
||||
fn forget(&mut self);
|
||||
fn forget(&self);
|
||||
}
|
||||
|
||||
impl<S> RequestIdentity for HttpRequest<S> {
|
||||
fn identity(&self) -> Option<&str> {
|
||||
fn identity(&self) -> Option<String> {
|
||||
if let Some(id) = self.extensions().get::<IdentityBox>() {
|
||||
return id.0.identity();
|
||||
return id.0.identity().map(|s| s.to_owned());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn remember(&mut self, identity: String) {
|
||||
if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
|
||||
return id.0.remember(identity);
|
||||
fn remember(&self, identity: String) {
|
||||
if let Some(mut id) = self.extensions_mut().get_mut::<IdentityBox>() {
|
||||
return id.0.as_mut().remember(identity);
|
||||
}
|
||||
}
|
||||
|
||||
fn forget(&mut self) {
|
||||
if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() {
|
||||
fn forget(&self) {
|
||||
if let Some(mut id) = self.extensions_mut().get_mut::<IdentityBox>() {
|
||||
return id.0.forget();
|
||||
}
|
||||
}
|
||||
@ -145,7 +147,7 @@ pub trait IdentityPolicy<S>: Sized + 'static {
|
||||
type Future: Future<Item = Self::Identity, Error = Error>;
|
||||
|
||||
/// Parse the session from request and load data from a service identity.
|
||||
fn from_request(&self, request: &mut HttpRequest<S>) -> Self::Future;
|
||||
fn from_request(&self, request: &HttpRequest<S>) -> Self::Future;
|
||||
}
|
||||
|
||||
/// Request identity middleware
|
||||
@ -178,27 +180,21 @@ impl<T> IdentityService<T> {
|
||||
struct IdentityBox(Box<Identity>);
|
||||
|
||||
impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
|
||||
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
||||
let mut req = req.clone();
|
||||
|
||||
let fut = self
|
||||
.backend
|
||||
.from_request(&mut req)
|
||||
.then(move |res| match res {
|
||||
Ok(id) => {
|
||||
req.extensions_mut().insert(IdentityBox(Box::new(id)));
|
||||
FutOk(None)
|
||||
}
|
||||
Err(err) => FutErr(err),
|
||||
});
|
||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
||||
let req = req.clone();
|
||||
let fut = self.backend.from_request(&req).then(move |res| match res {
|
||||
Ok(id) => {
|
||||
req.extensions_mut().insert(IdentityBox(Box::new(id)));
|
||||
FutOk(None)
|
||||
}
|
||||
Err(err) => FutErr(err),
|
||||
});
|
||||
Ok(Started::Future(Box::new(fut)))
|
||||
}
|
||||
|
||||
fn response(
|
||||
&self, req: &mut HttpRequest<S>, resp: HttpResponse,
|
||||
) -> Result<Response> {
|
||||
if let Some(mut id) = req.extensions_mut().remove::<IdentityBox>() {
|
||||
id.0.write(resp)
|
||||
fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
if let Some(ref mut id) = req.extensions_mut().get_mut::<IdentityBox>() {
|
||||
id.0.as_mut().write(resp)
|
||||
} else {
|
||||
Ok(Response::Done(resp))
|
||||
}
|
||||
@ -291,9 +287,9 @@ impl CookieIdentityInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load<S>(&self, req: &mut HttpRequest<S>) -> Option<String> {
|
||||
fn load<S>(&self, req: &HttpRequest<S>) -> Option<String> {
|
||||
if let Ok(cookies) = req.cookies() {
|
||||
for cookie in cookies {
|
||||
for cookie in cookies.iter() {
|
||||
if cookie.name() == self.name {
|
||||
let mut jar = CookieJar::new();
|
||||
jar.add_original(cookie.clone());
|
||||
@ -382,7 +378,7 @@ impl<S> IdentityPolicy<S> for CookieIdentityPolicy {
|
||||
type Identity = CookieIdentity;
|
||||
type Future = FutureResult<CookieIdentity, Error>;
|
||||
|
||||
fn from_request(&self, req: &mut HttpRequest<S>) -> Self::Future {
|
||||
fn from_request(&self, req: &HttpRequest<S>) -> Self::Future {
|
||||
let identity = self.0.load(req);
|
||||
FutOk(CookieIdentity {
|
||||
identity,
|
||||
|
@ -11,6 +11,7 @@ use httpmessage::HttpMessage;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::{Finished, Middleware, Started};
|
||||
use server::Request;
|
||||
|
||||
/// `Middleware` for logging request and response info to the terminal.
|
||||
///
|
||||
@ -107,7 +108,7 @@ impl Default for Logger {
|
||||
struct StartTime(time::Tm);
|
||||
|
||||
impl Logger {
|
||||
fn log<S>(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) {
|
||||
fn log<S>(&self, req: &HttpRequest<S>, resp: &HttpResponse) {
|
||||
if let Some(entry_time) = req.extensions().get::<StartTime>() {
|
||||
let render = |fmt: &mut Formatter| {
|
||||
for unit in &self.format.0 {
|
||||
@ -121,14 +122,14 @@ impl Logger {
|
||||
}
|
||||
|
||||
impl<S> Middleware<S> for Logger {
|
||||
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
||||
if !self.exclude.contains(req.path()) {
|
||||
req.extensions_mut().insert(StartTime(time::now()));
|
||||
}
|
||||
Ok(Started::Done)
|
||||
}
|
||||
|
||||
fn finish(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) -> Finished {
|
||||
fn finish(&self, req: &HttpRequest<S>, resp: &HttpResponse) -> Finished {
|
||||
self.log(req, resp);
|
||||
Finished::Done
|
||||
}
|
||||
@ -312,34 +313,27 @@ mod tests {
|
||||
use http::header::{self, HeaderMap};
|
||||
use http::{Method, StatusCode, Uri, Version};
|
||||
use std::str::FromStr;
|
||||
use test::TestRequest;
|
||||
use time;
|
||||
|
||||
#[test]
|
||||
fn test_logger() {
|
||||
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
let req = TestRequest::with_header(
|
||||
header::USER_AGENT,
|
||||
header::HeaderValue::from_static("ACTIX-WEB"),
|
||||
);
|
||||
let mut req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
).finish();
|
||||
let resp = HttpResponse::build(StatusCode::OK)
|
||||
.header("X-Test", "ttt")
|
||||
.force_close()
|
||||
.finish();
|
||||
|
||||
match logger.start(&mut req) {
|
||||
match logger.start(&req) {
|
||||
Ok(Started::Done) => (),
|
||||
_ => panic!(),
|
||||
};
|
||||
match logger.finish(&mut req, &resp) {
|
||||
match logger.finish(&req, &resp) {
|
||||
Finished::Done => (),
|
||||
_ => panic!(),
|
||||
}
|
||||
@ -358,18 +352,10 @@ mod tests {
|
||||
fn test_default_format() {
|
||||
let format = Format::default();
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
let req = TestRequest::with_header(
|
||||
header::USER_AGENT,
|
||||
header::HeaderValue::from_static("ACTIX-WEB"),
|
||||
);
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
).finish();
|
||||
let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
|
||||
let entry_time = time::now();
|
||||
|
||||
@ -384,13 +370,7 @@ mod tests {
|
||||
assert!(s.contains("200 0"));
|
||||
assert!(s.contains("ACTIX-WEB"));
|
||||
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/?test").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::with_uri("/?test").finish();
|
||||
let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
|
||||
let entry_time = time::now();
|
||||
|
||||
|
@ -4,6 +4,7 @@ use futures::Future;
|
||||
use error::{Error, Result};
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use server::Request;
|
||||
|
||||
mod logger;
|
||||
|
||||
@ -51,20 +52,18 @@ pub enum Finished {
|
||||
pub trait Middleware<S>: 'static {
|
||||
/// Method is called when request is ready. It may return
|
||||
/// future, which should resolve before next middleware get called.
|
||||
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
||||
Ok(Started::Done)
|
||||
}
|
||||
|
||||
/// Method is called when handler returns response,
|
||||
/// but before sending http message to peer.
|
||||
fn response(
|
||||
&self, req: &mut HttpRequest<S>, resp: HttpResponse,
|
||||
) -> Result<Response> {
|
||||
fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
Ok(Response::Done(resp))
|
||||
}
|
||||
|
||||
/// Method is called after body stream get sent to peer.
|
||||
fn finish(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) -> Finished {
|
||||
fn finish(&self, req: &HttpRequest<S>, resp: &HttpResponse) -> Finished {
|
||||
Finished::Done
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ use handler::FromRequest;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::{Middleware, Response, Started};
|
||||
use server::Request;
|
||||
|
||||
/// The helper trait to obtain your session data from a request.
|
||||
///
|
||||
@ -246,7 +247,7 @@ impl<S, T: SessionBackend<S>> SessionStorage<T, S> {
|
||||
}
|
||||
|
||||
impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> {
|
||||
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
||||
fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
|
||||
let mut req = req.clone();
|
||||
|
||||
let fut = self.0.from_request(&mut req).then(move |res| match res {
|
||||
@ -260,10 +261,8 @@ impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> {
|
||||
Ok(Started::Future(Box::new(fut)))
|
||||
}
|
||||
|
||||
fn response(
|
||||
&self, req: &mut HttpRequest<S>, resp: HttpResponse,
|
||||
) -> Result<Response> {
|
||||
if let Some(s_box) = req.extensions_mut().remove::<Arc<SessionImplCell>>() {
|
||||
fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
|
||||
if let Some(s_box) = req.extensions().get::<Arc<SessionImplCell>>() {
|
||||
s_box.0.borrow_mut().write(resp)
|
||||
} else {
|
||||
Ok(Response::Done(resp))
|
||||
@ -421,7 +420,7 @@ impl CookieSessionInner {
|
||||
|
||||
fn load<S>(&self, req: &mut HttpRequest<S>) -> HashMap<String, String> {
|
||||
if let Ok(cookies) = req.cookies() {
|
||||
for cookie in cookies {
|
||||
for cookie in cookies.iter() {
|
||||
if cookie.name() == self.name {
|
||||
let mut jar = CookieJar::new();
|
||||
jar.add_original(cookie.clone());
|
||||
|
23
src/param.rs
23
src/param.rs
@ -1,7 +1,7 @@
|
||||
use std;
|
||||
use std::rc::Rc;
|
||||
use std::ops::Index;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use http::StatusCode;
|
||||
@ -29,11 +29,11 @@ pub(crate) enum ParamItem {
|
||||
/// Route match information
|
||||
///
|
||||
/// If resource path contains variable patterns, `Params` stores this variables.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Params {
|
||||
url: Url,
|
||||
pub(crate) tail: u16,
|
||||
segments: SmallVec<[(Rc<String>, ParamItem); 3]>,
|
||||
pub(crate) segments: SmallVec<[(Rc<String>, ParamItem); 3]>,
|
||||
}
|
||||
|
||||
impl Params {
|
||||
@ -45,6 +45,14 @@ impl Params {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_url(url: &Url) -> Params {
|
||||
Params {
|
||||
url: url.clone(),
|
||||
tail: 0,
|
||||
segments: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.segments.clear();
|
||||
}
|
||||
@ -62,7 +70,8 @@ impl Params {
|
||||
}
|
||||
|
||||
pub(crate) fn add_static(&mut self, name: &str, value: &'static str) {
|
||||
self.segments.push((Rc::new(name.to_string()), ParamItem::Static(value)));
|
||||
self.segments
|
||||
.push((Rc::new(name.to_string()), ParamItem::Static(value)));
|
||||
}
|
||||
|
||||
/// Check if there are any matched patterns
|
||||
@ -151,16 +160,16 @@ impl<'a> Iterator for ParamsIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Index<&'b str> for &'a Params {
|
||||
impl<'a> Index<&'a str> for Params {
|
||||
type Output = str;
|
||||
|
||||
fn index(&self, name: &'b str) -> &str {
|
||||
fn index(&self, name: &'a str) -> &str {
|
||||
self.get(name)
|
||||
.expect("Value for parameter is not available")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<usize> for &'a Params {
|
||||
impl Index<usize> for Params {
|
||||
type Output = str;
|
||||
|
||||
fn index(&self, idx: usize) -> &str {
|
||||
|
@ -59,20 +59,14 @@ impl Payload {
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates EOF of payload
|
||||
#[inline]
|
||||
pub fn eof(&self) -> bool {
|
||||
self.inner.borrow().eof()
|
||||
}
|
||||
|
||||
/// Length of the data in this payload
|
||||
#[inline]
|
||||
#[cfg(test)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.borrow().len()
|
||||
}
|
||||
|
||||
/// Is payload empty
|
||||
#[inline]
|
||||
#[cfg(test)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.borrow().len() == 0
|
||||
}
|
||||
@ -225,12 +219,7 @@ impl Inner {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn eof(&self) -> bool {
|
||||
self.items.is_empty() && self.eof
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(test)]
|
||||
fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use header::ContentEncoding;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::{Finished, Middleware, Response, Started};
|
||||
use server::{HttpHandlerTask, Writer, WriterState};
|
||||
use server::{HttpHandlerTask, Request, Writer, WriterState};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -29,13 +29,11 @@ pub enum HandlerType {
|
||||
pub trait PipelineHandler<S> {
|
||||
fn encoding(&self) -> ContentEncoding;
|
||||
|
||||
fn handle(
|
||||
&self, req: HttpRequest<S>, htype: HandlerType,
|
||||
) -> AsyncResult<HttpResponse>;
|
||||
fn handle(&self, &HttpRequest<S>, HandlerType) -> AsyncResult<HttpResponse>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct Pipeline<S, H>(
|
||||
pub struct Pipeline<S: 'static, H>(
|
||||
PipelineInfo<S>,
|
||||
PipelineState<S, H>,
|
||||
Rc<Vec<Box<Middleware<S>>>>,
|
||||
@ -76,7 +74,7 @@ impl<S: 'static, H: PipelineHandler<S>> PipelineState<S, H> {
|
||||
}
|
||||
}
|
||||
|
||||
struct PipelineInfo<S> {
|
||||
struct PipelineInfo<S: 'static> {
|
||||
req: HttpRequest<S>,
|
||||
count: u16,
|
||||
context: Option<Box<ActorHttpContext>>,
|
||||
@ -85,7 +83,7 @@ struct PipelineInfo<S> {
|
||||
encoding: ContentEncoding,
|
||||
}
|
||||
|
||||
impl<S> PipelineInfo<S> {
|
||||
impl<S: 'static> PipelineInfo<S> {
|
||||
fn new(req: HttpRequest<S>) -> PipelineInfo<S> {
|
||||
PipelineInfo {
|
||||
req,
|
||||
@ -129,16 +127,6 @@ impl<S: 'static, H: PipelineHandler<S>> Pipeline<S, H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipeline<(), Inner<()>> {
|
||||
pub fn error<R: Into<HttpResponse>>(err: R) -> Box<HttpHandlerTask> {
|
||||
Box::new(Pipeline::<(), Inner<()>>(
|
||||
PipelineInfo::new(HttpRequest::default()),
|
||||
ProcessResponse::init(err.into()),
|
||||
Rc::new(Vec::new()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static, H> Pipeline<S, H> {
|
||||
#[inline]
|
||||
fn is_done(&self) -> bool {
|
||||
@ -241,16 +229,16 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
||||
// execute middlewares, we need this stage because middlewares could be
|
||||
// non-async and we can move to next state immediately
|
||||
let len = mws.len() as u16;
|
||||
|
||||
loop {
|
||||
if info.count == len {
|
||||
let reply = hnd.handle(info.req.clone(), htype);
|
||||
let reply = hnd.handle(&info.req, htype);
|
||||
return WaitingResponse::init(info, mws, reply);
|
||||
} else {
|
||||
let state = mws[info.count as usize].start(&mut info.req);
|
||||
match state {
|
||||
match mws[info.count as usize].start(&info.req) {
|
||||
Ok(Started::Done) => info.count += 1,
|
||||
Ok(Started::Response(resp)) => {
|
||||
return RunMiddlewares::init(info, mws, resp)
|
||||
return RunMiddlewares::init(info, mws, resp);
|
||||
}
|
||||
Ok(Started::Future(fut)) => {
|
||||
return PipelineState::Starting(StartMiddlewares {
|
||||
@ -260,7 +248,9 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
||||
_s: PhantomData,
|
||||
})
|
||||
}
|
||||
Err(err) => return RunMiddlewares::init(info, mws, err.into()),
|
||||
Err(err) => {
|
||||
return RunMiddlewares::init(info, mws, err.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,9 +260,12 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
||||
&mut self, info: &mut PipelineInfo<S>, mws: &[Box<Middleware<S>>],
|
||||
) -> Option<PipelineState<S, H>> {
|
||||
let len = mws.len() as u16;
|
||||
|
||||
'outer: loop {
|
||||
match self.fut.as_mut().unwrap().poll() {
|
||||
Ok(Async::NotReady) => return None,
|
||||
Ok(Async::NotReady) => {
|
||||
return None;
|
||||
}
|
||||
Ok(Async::Ready(resp)) => {
|
||||
info.count += 1;
|
||||
if let Some(resp) = resp {
|
||||
@ -280,11 +273,11 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
||||
}
|
||||
loop {
|
||||
if info.count == len {
|
||||
let reply = self.hnd.handle(info.req.clone(), self.htype);
|
||||
let reply = self.hnd.handle(&info.req, self.htype);
|
||||
return Some(WaitingResponse::init(info, mws, reply));
|
||||
} else {
|
||||
let state = mws[info.count as usize].start(&mut info.req);
|
||||
match state {
|
||||
let res = mws[info.count as usize].start(&info.req);
|
||||
match res {
|
||||
Ok(Started::Done) => info.count += 1,
|
||||
Ok(Started::Response(resp)) => {
|
||||
return Some(RunMiddlewares::init(info, mws, resp));
|
||||
@ -298,13 +291,15 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
||||
info,
|
||||
mws,
|
||||
err.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => return Some(RunMiddlewares::init(info, mws, err.into())),
|
||||
Err(err) => {
|
||||
return Some(RunMiddlewares::init(info, mws, err.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -324,8 +319,8 @@ impl<S: 'static, H> WaitingResponse<S, H> {
|
||||
reply: AsyncResult<HttpResponse>,
|
||||
) -> PipelineState<S, H> {
|
||||
match reply.into() {
|
||||
AsyncResultItem::Err(err) => RunMiddlewares::init(info, mws, err.into()),
|
||||
AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, mws, resp),
|
||||
AsyncResultItem::Err(err) => RunMiddlewares::init(info, mws, err.into()),
|
||||
AsyncResultItem::Future(fut) => PipelineState::Handler(WaitingResponse {
|
||||
fut,
|
||||
_s: PhantomData,
|
||||
@ -339,9 +334,7 @@ impl<S: 'static, H> WaitingResponse<S, H> {
|
||||
) -> Option<PipelineState<S, H>> {
|
||||
match self.fut.poll() {
|
||||
Ok(Async::NotReady) => None,
|
||||
Ok(Async::Ready(response)) => {
|
||||
Some(RunMiddlewares::init(info, mws, response))
|
||||
}
|
||||
Ok(Async::Ready(resp)) => Some(RunMiddlewares::init(info, mws, resp)),
|
||||
Err(err) => Some(RunMiddlewares::init(info, mws, err.into())),
|
||||
}
|
||||
}
|
||||
@ -367,7 +360,7 @@ impl<S: 'static, H> RunMiddlewares<S, H> {
|
||||
let len = mws.len();
|
||||
|
||||
loop {
|
||||
let state = mws[curr].response(&mut info.req, resp);
|
||||
let state = mws[curr].response(&info.req, resp);
|
||||
resp = match state {
|
||||
Err(err) => {
|
||||
info.count = (curr + 1) as u16;
|
||||
@ -387,7 +380,7 @@ impl<S: 'static, H> RunMiddlewares<S, H> {
|
||||
fut: Some(fut),
|
||||
_s: PhantomData,
|
||||
_h: PhantomData,
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -413,7 +406,7 @@ impl<S: 'static, H> RunMiddlewares<S, H> {
|
||||
if self.curr == len {
|
||||
return Some(ProcessResponse::init(resp));
|
||||
} else {
|
||||
let state = mws[self.curr].response(&mut info.req, resp);
|
||||
let state = mws[self.curr].response(&info.req, resp);
|
||||
match state {
|
||||
Err(err) => return Some(ProcessResponse::init(err.into())),
|
||||
Ok(Response::Done(r)) => {
|
||||
@ -495,19 +488,16 @@ impl<S: 'static, H> ProcessResponse<S, H> {
|
||||
let encoding =
|
||||
self.resp.content_encoding().unwrap_or(info.encoding);
|
||||
|
||||
let result = match io.start(
|
||||
info.req.as_mut(),
|
||||
&mut self.resp,
|
||||
encoding,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
info.error = Some(err.into());
|
||||
return Ok(FinishingMiddlewares::init(
|
||||
info, mws, self.resp,
|
||||
));
|
||||
}
|
||||
};
|
||||
let result =
|
||||
match io.start(&info.req, &mut self.resp, encoding) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
info.error = Some(err.into());
|
||||
return Ok(FinishingMiddlewares::init(
|
||||
info, mws, self.resp,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(err) = self.resp.error() {
|
||||
if self.resp.status().is_server_error() {
|
||||
@ -747,8 +737,8 @@ impl<S: 'static, H> FinishingMiddlewares<S, H> {
|
||||
}
|
||||
|
||||
info.count -= 1;
|
||||
let state = mws[info.count as usize]
|
||||
.finish(&mut info.req, self.resp.as_ref().unwrap());
|
||||
let state =
|
||||
mws[info.count as usize].finish(&info.req, self.resp.as_ref().unwrap());
|
||||
match state {
|
||||
Finished::Done => {
|
||||
if info.count == 0 {
|
||||
@ -797,9 +787,10 @@ mod tests {
|
||||
use actix::*;
|
||||
use context::HttpContext;
|
||||
use futures::future::{lazy, result};
|
||||
use http::StatusCode;
|
||||
use tokio::runtime::current_thread::Runtime;
|
||||
|
||||
use test::TestRequest;
|
||||
|
||||
impl<S, H> PipelineState<S, H> {
|
||||
fn is_none(&self) -> Option<bool> {
|
||||
if let PipelineState::None = *self {
|
||||
@ -827,12 +818,13 @@ mod tests {
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(lazy(|| {
|
||||
let mut info = PipelineInfo::new(HttpRequest::default());
|
||||
let req = TestRequest::default().finish();
|
||||
let mut info = PipelineInfo::new(req);
|
||||
Completed::<(), Inner<()>>::init(&mut info)
|
||||
.is_none()
|
||||
.unwrap();
|
||||
|
||||
let req = HttpRequest::default();
|
||||
let req = TestRequest::default().finish();
|
||||
let ctx = HttpContext::new(req.clone(), MyActor);
|
||||
let addr = ctx.address();
|
||||
let mut info = PipelineInfo::new(req);
|
||||
|
196
src/pred.rs
196
src/pred.rs
@ -1,10 +1,12 @@
|
||||
//! Route match predicates
|
||||
#![allow(non_snake_case)]
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use http;
|
||||
use http::{header, HttpTryFrom};
|
||||
use httpmessage::HttpMessage;
|
||||
use httprequest::HttpRequest;
|
||||
use std::marker::PhantomData;
|
||||
use server::message::Request;
|
||||
|
||||
/// Trait defines resource route predicate.
|
||||
/// Predicate can modify request object. It is also possible to
|
||||
@ -12,7 +14,7 @@ use std::marker::PhantomData;
|
||||
/// Extensions container available via `HttpRequest::extensions()` method.
|
||||
pub trait Predicate<S> {
|
||||
/// Check if request matches predicate
|
||||
fn check(&self, &mut HttpRequest<S>) -> bool;
|
||||
fn check(&self, &Request, &S) -> bool;
|
||||
}
|
||||
|
||||
/// Return predicate that matches if any of supplied predicate matches.
|
||||
@ -45,9 +47,9 @@ impl<S> AnyPredicate<S> {
|
||||
}
|
||||
|
||||
impl<S: 'static> Predicate<S> for AnyPredicate<S> {
|
||||
fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||
fn check(&self, req: &Request, state: &S) -> bool {
|
||||
for p in &self.0 {
|
||||
if p.check(req) {
|
||||
if p.check(req, state) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -88,9 +90,9 @@ impl<S> AllPredicate<S> {
|
||||
}
|
||||
|
||||
impl<S: 'static> Predicate<S> for AllPredicate<S> {
|
||||
fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||
fn check(&self, req: &Request, state: &S) -> bool {
|
||||
for p in &self.0 {
|
||||
if !p.check(req) {
|
||||
if !p.check(req, state) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -107,8 +109,8 @@ pub fn Not<S: 'static, P: Predicate<S> + 'static>(pred: P) -> NotPredicate<S> {
|
||||
pub struct NotPredicate<S>(Box<Predicate<S>>);
|
||||
|
||||
impl<S: 'static> Predicate<S> for NotPredicate<S> {
|
||||
fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||
!self.0.check(req)
|
||||
fn check(&self, req: &Request, state: &S) -> bool {
|
||||
!self.0.check(req, state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +119,7 @@ impl<S: 'static> Predicate<S> for NotPredicate<S> {
|
||||
pub struct MethodPredicate<S>(http::Method, PhantomData<S>);
|
||||
|
||||
impl<S: 'static> Predicate<S> for MethodPredicate<S> {
|
||||
fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||
fn check(&self, req: &Request, _: &S) -> bool {
|
||||
*req.method() == self.0
|
||||
}
|
||||
}
|
||||
@ -188,7 +190,7 @@ pub fn Header<S: 'static>(
|
||||
pub struct HeaderPredicate<S>(header::HeaderName, header::HeaderValue, PhantomData<S>);
|
||||
|
||||
impl<S: 'static> Predicate<S> for HeaderPredicate<S> {
|
||||
fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||
fn check(&self, req: &Request, _: &S) -> bool {
|
||||
if let Some(val) = req.headers().get(&self.0) {
|
||||
return val == self.1;
|
||||
}
|
||||
@ -225,7 +227,7 @@ impl<S> HostPredicate<S> {
|
||||
}
|
||||
|
||||
impl<S: 'static> Predicate<S> for HostPredicate<S> {
|
||||
fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||
fn check(&self, req: &Request, _: &S) -> bool {
|
||||
let info = req.connection_info();
|
||||
if let Some(ref scheme) = self.1 {
|
||||
self.0 == info.host() && scheme == info.scheme()
|
||||
@ -237,168 +239,96 @@ impl<S: 'static> Predicate<S> for HostPredicate<S> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::*;
|
||||
use http::header::{self, HeaderMap};
|
||||
use http::{Method, Uri, Version};
|
||||
use std::str::FromStr;
|
||||
use test::TestRequest;
|
||||
|
||||
#[test]
|
||||
fn test_header() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
let req = TestRequest::with_header(
|
||||
header::TRANSFER_ENCODING,
|
||||
header::HeaderValue::from_static("chunked"),
|
||||
);
|
||||
let mut req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
).finish();
|
||||
|
||||
let pred = Header("transfer-encoding", "chunked");
|
||||
assert!(pred.check(&mut req));
|
||||
assert!(pred.check(&req, req.state()));
|
||||
|
||||
let pred = Header("transfer-encoding", "other");
|
||||
assert!(!pred.check(&mut req));
|
||||
assert!(!pred.check(&req, req.state()));
|
||||
|
||||
let pred = Header("content-type", "other");
|
||||
assert!(!pred.check(&mut req));
|
||||
assert!(!pred.check(&req, req.state()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_host() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
header::HOST,
|
||||
header::HeaderValue::from_static("www.rust-lang.org"),
|
||||
);
|
||||
let mut req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::HOST,
|
||||
header::HeaderValue::from_static("www.rust-lang.org"),
|
||||
)
|
||||
.finish();
|
||||
|
||||
let pred = Host("www.rust-lang.org");
|
||||
assert!(pred.check(&mut req));
|
||||
assert!(pred.check(&req, req.state()));
|
||||
|
||||
let pred = Host("localhost");
|
||||
assert!(!pred.check(&mut req));
|
||||
assert!(!pred.check(&req, req.state()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_methods() {
|
||||
let mut req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
let mut req2 = HttpRequest::new(
|
||||
Method::POST,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default().finish();
|
||||
let req2 = TestRequest::default().method(Method::POST).finish();
|
||||
|
||||
assert!(Get().check(&mut req));
|
||||
assert!(!Get().check(&mut req2));
|
||||
assert!(Post().check(&mut req2));
|
||||
assert!(!Post().check(&mut req));
|
||||
assert!(Get().check(&req, req.state()));
|
||||
assert!(!Get().check(&req2, req2.state()));
|
||||
assert!(Post().check(&req2, req2.state()));
|
||||
assert!(!Post().check(&req, req.state()));
|
||||
|
||||
let mut r = HttpRequest::new(
|
||||
Method::PUT,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert!(Put().check(&mut r));
|
||||
assert!(!Put().check(&mut req));
|
||||
let r = TestRequest::default().method(Method::PUT).finish();
|
||||
assert!(Put().check(&r, r.state()));
|
||||
assert!(!Put().check(&req, req.state()));
|
||||
|
||||
let mut r = HttpRequest::new(
|
||||
Method::DELETE,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert!(Delete().check(&mut r));
|
||||
assert!(!Delete().check(&mut req));
|
||||
let r = TestRequest::default().method(Method::DELETE).finish();
|
||||
assert!(Delete().check(&r, r.state()));
|
||||
assert!(!Delete().check(&req, req.state()));
|
||||
|
||||
let mut r = HttpRequest::new(
|
||||
Method::HEAD,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert!(Head().check(&mut r));
|
||||
assert!(!Head().check(&mut req));
|
||||
let r = TestRequest::default().method(Method::HEAD).finish();
|
||||
assert!(Head().check(&r, r.state()));
|
||||
assert!(!Head().check(&req, req.state()));
|
||||
|
||||
let mut r = HttpRequest::new(
|
||||
Method::OPTIONS,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert!(Options().check(&mut r));
|
||||
assert!(!Options().check(&mut req));
|
||||
let r = TestRequest::default().method(Method::OPTIONS).finish();
|
||||
assert!(Options().check(&r, r.state()));
|
||||
assert!(!Options().check(&req, req.state()));
|
||||
|
||||
let mut r = HttpRequest::new(
|
||||
Method::CONNECT,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert!(Connect().check(&mut r));
|
||||
assert!(!Connect().check(&mut req));
|
||||
let r = TestRequest::default().method(Method::CONNECT).finish();
|
||||
assert!(Connect().check(&r, r.state()));
|
||||
assert!(!Connect().check(&req, req.state()));
|
||||
|
||||
let mut r = HttpRequest::new(
|
||||
Method::PATCH,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert!(Patch().check(&mut r));
|
||||
assert!(!Patch().check(&mut req));
|
||||
let r = TestRequest::default().method(Method::PATCH).finish();
|
||||
assert!(Patch().check(&r, r.state()));
|
||||
assert!(!Patch().check(&req, req.state()));
|
||||
|
||||
let mut r = HttpRequest::new(
|
||||
Method::TRACE,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert!(Trace().check(&mut r));
|
||||
assert!(!Trace().check(&mut req));
|
||||
let r = TestRequest::default().method(Method::TRACE).finish();
|
||||
assert!(Trace().check(&r, r.state()));
|
||||
assert!(!Trace().check(&req, req.state()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_preds() {
|
||||
let mut r = HttpRequest::new(
|
||||
Method::TRACE,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
let r = TestRequest::default().method(Method::TRACE).finish();
|
||||
|
||||
assert!(Not(Get()).check(&mut r));
|
||||
assert!(!Not(Trace()).check(&mut r));
|
||||
assert!(Not(Get()).check(&r, r.state()));
|
||||
assert!(!Not(Trace()).check(&r, r.state()));
|
||||
|
||||
assert!(All(Trace()).and(Trace()).check(&mut r));
|
||||
assert!(!All(Get()).and(Trace()).check(&mut r));
|
||||
assert!(All(Trace()).and(Trace()).check(&r, r.state()));
|
||||
assert!(!All(Get()).and(Trace()).check(&r, r.state()));
|
||||
|
||||
assert!(Any(Get()).or(Trace()).check(&mut r));
|
||||
assert!(!Any(Get()).or(Get()).check(&mut r));
|
||||
assert!(Any(Get()).or(Trace()).check(&r, r.state()));
|
||||
assert!(!Any(Get()).or(Get()).check(&r, r.state()));
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,10 @@ use httpresponse::HttpResponse;
|
||||
use middleware::Middleware;
|
||||
use pred;
|
||||
use route::Route;
|
||||
use server::Request;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct RouteId(usize);
|
||||
|
||||
/// *Resource* is an entry in route table which corresponds to requested URL.
|
||||
///
|
||||
@ -131,7 +135,7 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// use actix_web::*;
|
||||
/// fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
///
|
||||
/// App::new().resource("/", |r| r.method(http::Method::GET).f(index));
|
||||
/// ```
|
||||
@ -141,7 +145,7 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// # use actix_web::*;
|
||||
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// # fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// App::new().resource("/", |r| r.route().filter(pred::Get()).f(index));
|
||||
/// ```
|
||||
pub fn method(&mut self, method: Method) -> &mut Route<S> {
|
||||
@ -154,7 +158,7 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// use actix_web::*;
|
||||
/// fn handler(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// fn handler(req: &HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
///
|
||||
/// App::new().resource("/", |r| r.h(handler));
|
||||
/// ```
|
||||
@ -164,7 +168,7 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// # use actix_web::*;
|
||||
/// # fn handler(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// # fn handler(req: &HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// App::new().resource("/", |r| r.route().h(handler));
|
||||
/// ```
|
||||
pub fn h<H: Handler<S>>(&mut self, handler: H) {
|
||||
@ -177,7 +181,7 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// use actix_web::*;
|
||||
/// fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
///
|
||||
/// App::new().resource("/", |r| r.f(index));
|
||||
/// ```
|
||||
@ -187,12 +191,12 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// # use actix_web::*;
|
||||
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// # fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
|
||||
/// App::new().resource("/", |r| r.route().f(index));
|
||||
/// ```
|
||||
pub fn f<F, R>(&mut self, handler: F)
|
||||
where
|
||||
F: Fn(HttpRequest<S>) -> R + 'static,
|
||||
F: Fn(&HttpRequest<S>) -> R + 'static,
|
||||
R: Responder + 'static,
|
||||
{
|
||||
self.routes.push(Route::default());
|
||||
@ -279,19 +283,24 @@ impl<S: 'static> ResourceHandler<S> {
|
||||
.push(Box::new(mw));
|
||||
}
|
||||
|
||||
pub(crate) fn handle(
|
||||
&self, mut req: HttpRequest<S>,
|
||||
) -> Result<AsyncResult<HttpResponse>, HttpRequest<S>> {
|
||||
for route in &self.routes {
|
||||
if route.check(&mut req) {
|
||||
return if self.middlewares.is_empty() {
|
||||
Ok(route.handle(req))
|
||||
} else {
|
||||
Ok(route.compose(req, Rc::clone(&self.middlewares)))
|
||||
};
|
||||
#[inline]
|
||||
pub(crate) fn get_route_id(&self, req: &HttpRequest<S>) -> Option<RouteId> {
|
||||
for idx in 0..self.routes.len() {
|
||||
if (&self.routes[idx]).check(req) {
|
||||
return Some(RouteId(idx));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
Err(req)
|
||||
#[inline]
|
||||
pub(crate) fn handle(
|
||||
&self, id: RouteId, req: &HttpRequest<S>,
|
||||
) -> AsyncResult<HttpResponse> {
|
||||
if self.middlewares.is_empty() {
|
||||
(&self.routes[id.0]).handle(req)
|
||||
} else {
|
||||
(&self.routes[id.0]).compose(req.clone(), Rc::clone(&self.middlewares))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
66
src/route.rs
66
src/route.rs
@ -16,6 +16,7 @@ use middleware::{
|
||||
Started as MiddlewareStarted,
|
||||
};
|
||||
use pred::Predicate;
|
||||
use server::Request;
|
||||
use with::{With, WithAsync};
|
||||
|
||||
/// Resource route definition
|
||||
@ -31,16 +32,17 @@ impl<S: 'static> Default for Route<S> {
|
||||
fn default() -> Route<S> {
|
||||
Route {
|
||||
preds: Vec::new(),
|
||||
handler: InnerHandler::new(|_| HttpResponse::new(StatusCode::NOT_FOUND)),
|
||||
handler: InnerHandler::new(|_: &_| HttpResponse::new(StatusCode::NOT_FOUND)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> Route<S> {
|
||||
#[inline]
|
||||
pub(crate) fn check(&self, req: &mut HttpRequest<S>) -> bool {
|
||||
pub(crate) fn check(&self, req: &HttpRequest<S>) -> bool {
|
||||
let state = req.state();
|
||||
for pred in &self.preds {
|
||||
if !pred.check(req) {
|
||||
if !pred.check(req, state) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -48,7 +50,7 @@ impl<S: 'static> Route<S> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
pub(crate) fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
self.handler.handle(req)
|
||||
}
|
||||
|
||||
@ -89,7 +91,7 @@ impl<S: 'static> Route<S> {
|
||||
/// during route configuration, so it does not return reference to self.
|
||||
pub fn f<F, R>(&mut self, handler: F)
|
||||
where
|
||||
F: Fn(HttpRequest<S>) -> R + 'static,
|
||||
F: Fn(&HttpRequest<S>) -> R + 'static,
|
||||
R: Responder + 'static,
|
||||
{
|
||||
self.handler = InnerHandler::new(handler);
|
||||
@ -98,7 +100,7 @@ impl<S: 'static> Route<S> {
|
||||
/// Set async handler function.
|
||||
pub fn a<H, R, F, E>(&mut self, handler: H)
|
||||
where
|
||||
H: Fn(HttpRequest<S>) -> F + 'static,
|
||||
H: Fn(&HttpRequest<S>) -> F + 'static,
|
||||
F: Future<Item = R, Error = E> + 'static,
|
||||
R: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
@ -307,7 +309,7 @@ impl<S: 'static> InnerHandler<S> {
|
||||
#[inline]
|
||||
fn async<H, R, F, E>(h: H) -> Self
|
||||
where
|
||||
H: Fn(HttpRequest<S>) -> F + 'static,
|
||||
H: Fn(&HttpRequest<S>) -> F + 'static,
|
||||
F: Future<Item = R, Error = E> + 'static,
|
||||
R: Responder + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
@ -316,7 +318,7 @@ impl<S: 'static> InnerHandler<S> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
pub fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
self.0.handle(req)
|
||||
}
|
||||
}
|
||||
@ -407,24 +409,27 @@ type Fut = Box<Future<Item = Option<HttpResponse>, Error = Error>>;
|
||||
impl<S: 'static> StartMiddlewares<S> {
|
||||
fn init(info: &mut ComposeInfo<S>) -> ComposeState<S> {
|
||||
let len = info.mws.len();
|
||||
|
||||
loop {
|
||||
if info.count == len {
|
||||
let reply = info.handler.handle(info.req.clone());
|
||||
let reply = info.handler.handle(&info.req);
|
||||
return WaitingResponse::init(info, reply);
|
||||
} else {
|
||||
let state = info.mws[info.count].start(&mut info.req);
|
||||
match state {
|
||||
let result = info.mws[info.count].start(&info.req);
|
||||
match result {
|
||||
Ok(MiddlewareStarted::Done) => info.count += 1,
|
||||
Ok(MiddlewareStarted::Response(resp)) => {
|
||||
return RunMiddlewares::init(info, resp)
|
||||
return RunMiddlewares::init(info, resp);
|
||||
}
|
||||
Ok(MiddlewareStarted::Future(fut)) => {
|
||||
return ComposeState::Starting(StartMiddlewares {
|
||||
fut: Some(fut),
|
||||
_s: PhantomData,
|
||||
})
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
return RunMiddlewares::init(info, err.into());
|
||||
}
|
||||
Err(err) => return RunMiddlewares::init(info, err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -432,9 +437,12 @@ impl<S: 'static> StartMiddlewares<S> {
|
||||
|
||||
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
|
||||
let len = info.mws.len();
|
||||
|
||||
'outer: loop {
|
||||
match self.fut.as_mut().unwrap().poll() {
|
||||
Ok(Async::NotReady) => return None,
|
||||
Ok(Async::NotReady) => {
|
||||
return None;
|
||||
}
|
||||
Ok(Async::Ready(resp)) => {
|
||||
info.count += 1;
|
||||
if let Some(resp) = resp {
|
||||
@ -442,11 +450,11 @@ impl<S: 'static> StartMiddlewares<S> {
|
||||
}
|
||||
loop {
|
||||
if info.count == len {
|
||||
let reply = info.handler.handle(info.req.clone());
|
||||
let reply = info.handler.handle(&info.req);
|
||||
return Some(WaitingResponse::init(info, reply));
|
||||
} else {
|
||||
let state = info.mws[info.count].start(&mut info.req);
|
||||
match state {
|
||||
let result = info.mws[info.count].start(&info.req);
|
||||
match result {
|
||||
Ok(MiddlewareStarted::Done) => info.count += 1,
|
||||
Ok(MiddlewareStarted::Response(resp)) => {
|
||||
return Some(RunMiddlewares::init(info, resp));
|
||||
@ -456,21 +464,25 @@ impl<S: 'static> StartMiddlewares<S> {
|
||||
continue 'outer;
|
||||
}
|
||||
Err(err) => {
|
||||
return Some(RunMiddlewares::init(info, err.into()))
|
||||
return Some(RunMiddlewares::init(info, err.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => return Some(RunMiddlewares::init(info, err.into())),
|
||||
Err(err) => {
|
||||
return Some(RunMiddlewares::init(info, err.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type HandlerFuture = Future<Item = HttpResponse, Error = Error>;
|
||||
|
||||
// waiting for response
|
||||
struct WaitingResponse<S> {
|
||||
fut: Box<Future<Item = HttpResponse, Error = Error>>,
|
||||
fut: Box<HandlerFuture>,
|
||||
_s: PhantomData<S>,
|
||||
}
|
||||
|
||||
@ -480,8 +492,8 @@ impl<S: 'static> WaitingResponse<S> {
|
||||
info: &mut ComposeInfo<S>, reply: AsyncResult<HttpResponse>,
|
||||
) -> ComposeState<S> {
|
||||
match reply.into() {
|
||||
AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()),
|
||||
AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, resp),
|
||||
AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()),
|
||||
AsyncResultItem::Future(fut) => ComposeState::Handler(WaitingResponse {
|
||||
fut,
|
||||
_s: PhantomData,
|
||||
@ -492,7 +504,7 @@ impl<S: 'static> WaitingResponse<S> {
|
||||
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
|
||||
match self.fut.poll() {
|
||||
Ok(Async::NotReady) => None,
|
||||
Ok(Async::Ready(response)) => Some(RunMiddlewares::init(info, response)),
|
||||
Ok(Async::Ready(resp)) => Some(RunMiddlewares::init(info, resp)),
|
||||
Err(err) => Some(RunMiddlewares::init(info, err.into())),
|
||||
}
|
||||
}
|
||||
@ -511,7 +523,7 @@ impl<S: 'static> RunMiddlewares<S> {
|
||||
let len = info.mws.len();
|
||||
|
||||
loop {
|
||||
let state = info.mws[curr].response(&mut info.req, resp);
|
||||
let state = info.mws[curr].response(&info.req, resp);
|
||||
resp = match state {
|
||||
Err(err) => {
|
||||
info.count = curr + 1;
|
||||
@ -530,7 +542,7 @@ impl<S: 'static> RunMiddlewares<S> {
|
||||
curr,
|
||||
fut: Some(fut),
|
||||
_s: PhantomData,
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -554,7 +566,7 @@ impl<S: 'static> RunMiddlewares<S> {
|
||||
if self.curr == len {
|
||||
return Some(FinishingMiddlewares::init(info, resp));
|
||||
} else {
|
||||
let state = info.mws[self.curr].response(&mut info.req, resp);
|
||||
let state = info.mws[self.curr].response(&info.req, resp);
|
||||
match state {
|
||||
Err(err) => {
|
||||
return Some(FinishingMiddlewares::init(info, err.into()))
|
||||
@ -625,7 +637,7 @@ impl<S: 'static> FinishingMiddlewares<S> {
|
||||
info.count -= 1;
|
||||
|
||||
let state = info.mws[info.count as usize]
|
||||
.finish(&mut info.req, self.resp.as_ref().unwrap());
|
||||
.finish(&info.req, self.resp.as_ref().unwrap());
|
||||
match state {
|
||||
MiddlewareFinished::Done => {
|
||||
if info.count == 0 {
|
||||
|
567
src/router.rs
567
src/router.rs
@ -4,29 +4,133 @@ use std::rc::Rc;
|
||||
|
||||
use regex::{escape, Regex};
|
||||
use smallvec::SmallVec;
|
||||
use url::Url;
|
||||
|
||||
use error::UrlGenerationError;
|
||||
use httprequest::HttpRequest;
|
||||
use param::ParamItem;
|
||||
use param::{ParamItem, Params};
|
||||
use resource::ResourceHandler;
|
||||
use server::ServerSettings;
|
||||
use server::Request;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub(crate) enum RouterResource {
|
||||
Notset,
|
||||
Normal(u16),
|
||||
}
|
||||
|
||||
/// Interface for application router.
|
||||
pub struct Router(Rc<Inner>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RouteInfo {
|
||||
router: Rc<Inner>,
|
||||
resource: RouterResource,
|
||||
prefix: u16,
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl RouteInfo {
|
||||
/// This method returns reference to matched `Resource` object.
|
||||
#[inline]
|
||||
pub fn resource(&self) -> Option<&Resource> {
|
||||
if let RouterResource::Normal(idx) = self.resource {
|
||||
Some(&self.router.patterns[idx as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the Params object.
|
||||
///
|
||||
/// Params is a container for url parameters.
|
||||
/// A variable segment is specified in the form `{identifier}`,
|
||||
/// where the identifier can be used later in a request handler to
|
||||
/// access the matched value for that segment.
|
||||
#[inline]
|
||||
pub fn match_info(&self) -> &Params {
|
||||
&self.params
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn prefix_len(&self) -> u16 {
|
||||
self.prefix
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn merge(&self, mut params: Params) -> RouteInfo {
|
||||
let mut p = self.params.clone();
|
||||
p.set_tail(params.tail);
|
||||
for item in ¶ms.segments {
|
||||
p.add(item.0.clone(), item.1.clone());
|
||||
}
|
||||
|
||||
RouteInfo {
|
||||
params: p,
|
||||
router: self.router.clone(),
|
||||
resource: self.resource,
|
||||
prefix: self.prefix,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate url for named resource
|
||||
///
|
||||
/// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method.
|
||||
/// url_for) for detailed information.
|
||||
pub fn url_for<U, I>(
|
||||
&self, req: &Request, name: &str, elements: U,
|
||||
) -> Result<Url, UrlGenerationError>
|
||||
where
|
||||
U: IntoIterator<Item = I>,
|
||||
I: AsRef<str>,
|
||||
{
|
||||
if let Some(pattern) = self.router.named.get(name) {
|
||||
let path = pattern.0.resource_path(elements, &self.router.prefix)?;
|
||||
if path.starts_with('/') {
|
||||
let conn = req.connection_info();
|
||||
Ok(Url::parse(&format!(
|
||||
"{}://{}{}",
|
||||
conn.scheme(),
|
||||
conn.host(),
|
||||
path
|
||||
))?)
|
||||
} else {
|
||||
Ok(Url::parse(&path)?)
|
||||
}
|
||||
} else {
|
||||
Err(UrlGenerationError::ResourceNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if application contains matching route.
|
||||
///
|
||||
/// This method does not take `prefix` into account.
|
||||
/// For example if prefix is `/test` and router contains route `/name`,
|
||||
/// following path would be recognizable `/test/name` but `has_route()` call
|
||||
/// would return `false`.
|
||||
pub fn has_route(&self, path: &str) -> bool {
|
||||
let path = if path.is_empty() { "/" } else { path };
|
||||
|
||||
for pattern in &self.router.patterns {
|
||||
if pattern.is_match(path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
prefix: String,
|
||||
prefix_len: usize,
|
||||
named: HashMap<String, (Resource, bool)>,
|
||||
patterns: Vec<Resource>,
|
||||
srv: ServerSettings,
|
||||
}
|
||||
|
||||
impl Router {
|
||||
/// Create new router
|
||||
pub fn new<S>(
|
||||
prefix: &str, settings: ServerSettings,
|
||||
map: Vec<(Resource, Option<ResourceHandler<S>>)>,
|
||||
prefix: &str, map: Vec<(Resource, Option<ResourceHandler<S>>)>,
|
||||
) -> (Router, Vec<ResourceHandler<S>>) {
|
||||
let prefix = prefix.trim().trim_right_matches('/').to_owned();
|
||||
let mut named = HashMap::new();
|
||||
@ -52,7 +156,6 @@ impl Router {
|
||||
prefix_len,
|
||||
named,
|
||||
patterns,
|
||||
srv: settings,
|
||||
})),
|
||||
resources,
|
||||
)
|
||||
@ -64,67 +167,61 @@ impl Router {
|
||||
&self.0.prefix
|
||||
}
|
||||
|
||||
/// Server settings
|
||||
#[inline]
|
||||
pub fn server_settings(&self) -> &ServerSettings {
|
||||
&self.0.srv
|
||||
}
|
||||
|
||||
pub(crate) fn get_resource(&self, idx: usize) -> &Resource {
|
||||
&self.0.patterns[idx]
|
||||
}
|
||||
|
||||
pub(crate) fn route_info(&self, req: &Request, prefix: u16) -> RouteInfo {
|
||||
let mut params = Params::with_url(req.url());
|
||||
params.set_tail(prefix);
|
||||
|
||||
RouteInfo {
|
||||
params,
|
||||
router: self.0.clone(),
|
||||
resource: RouterResource::Notset,
|
||||
prefix: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn route_info_params(&self, params: Params, prefix: u16) -> RouteInfo {
|
||||
RouteInfo {
|
||||
params,
|
||||
prefix,
|
||||
router: self.0.clone(),
|
||||
resource: RouterResource::Notset,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn default_route_info(&self, prefix: u16) -> RouteInfo {
|
||||
RouteInfo {
|
||||
prefix,
|
||||
router: self.0.clone(),
|
||||
resource: RouterResource::Notset,
|
||||
params: Params::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Query for matched resource
|
||||
pub fn recognize<S>(&self, req: &mut HttpRequest<S>) -> Option<usize> {
|
||||
pub fn recognize(&self, req: &Request) -> Option<(usize, RouteInfo)> {
|
||||
if self.0.prefix_len > req.path().len() {
|
||||
return None;
|
||||
}
|
||||
for (idx, pattern) in self.0.patterns.iter().enumerate() {
|
||||
if pattern.match_with_params(req, self.0.prefix_len, true) {
|
||||
let url = req.url().clone();
|
||||
req.match_info_mut().set_url(url);
|
||||
req.set_resource(idx);
|
||||
req.set_prefix_len(self.0.prefix_len as u16);
|
||||
return Some(idx);
|
||||
if let Some(params) = pattern.match_with_params(req, self.0.prefix_len, true)
|
||||
{
|
||||
return Some((
|
||||
idx,
|
||||
RouteInfo {
|
||||
params,
|
||||
router: self.0.clone(),
|
||||
resource: RouterResource::Normal(idx as u16),
|
||||
prefix: self.0.prefix_len as u16,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if application contains matching route.
|
||||
///
|
||||
/// This method does not take `prefix` into account.
|
||||
/// For example if prefix is `/test` and router contains route `/name`,
|
||||
/// following path would be recognizable `/test/name` but `has_route()` call
|
||||
/// would return `false`.
|
||||
pub fn has_route(&self, path: &str) -> bool {
|
||||
let path = if path.is_empty() { "/" } else { path };
|
||||
|
||||
for pattern in &self.0.patterns {
|
||||
if pattern.is_match(path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Build named resource path.
|
||||
///
|
||||
/// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method.
|
||||
/// url_for) for detailed information.
|
||||
pub fn resource_path<U, I>(
|
||||
&self, name: &str, elements: U,
|
||||
) -> Result<String, UrlGenerationError>
|
||||
where
|
||||
U: IntoIterator<Item = I>,
|
||||
I: AsRef<str>,
|
||||
{
|
||||
if let Some(pattern) = self.0.named.get(name) {
|
||||
pattern.0.resource_path(self, elements)
|
||||
} else {
|
||||
Err(UrlGenerationError::ResourceNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Router {
|
||||
@ -160,7 +257,7 @@ pub enum ResourceType {
|
||||
}
|
||||
|
||||
/// Resource type describes an entry in resources table
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Resource {
|
||||
tp: PatternType,
|
||||
rtp: ResourceType,
|
||||
@ -208,9 +305,7 @@ impl Resource {
|
||||
// actix creates one router per thread
|
||||
let names = re
|
||||
.capture_names()
|
||||
.filter_map(|name| {
|
||||
name.map(|name| Rc::new(name.to_owned()))
|
||||
})
|
||||
.filter_map(|name| name.map(|name| Rc::new(name.to_owned())))
|
||||
.collect();
|
||||
PatternType::Dynamic(re, names, len)
|
||||
} else if for_prefix {
|
||||
@ -253,127 +348,128 @@ impl Resource {
|
||||
}
|
||||
|
||||
/// Are the given path and parameters a match against this resource?
|
||||
pub fn match_with_params<S>(
|
||||
&self, req: &mut HttpRequest<S>, plen: usize, insert: bool,
|
||||
) -> bool {
|
||||
let mut segments: SmallVec<[ParamItem; 5]> = SmallVec::new();
|
||||
|
||||
let names = {
|
||||
let path = &req.path()[plen..];
|
||||
if insert {
|
||||
if path.is_empty() {
|
||||
"/"
|
||||
} else {
|
||||
path
|
||||
}
|
||||
pub fn match_with_params(
|
||||
&self, req: &Request, plen: usize, insert: bool,
|
||||
) -> Option<Params> {
|
||||
let path = &req.path()[plen..];
|
||||
if insert {
|
||||
if path.is_empty() {
|
||||
"/"
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
match self.tp {
|
||||
PatternType::Static(ref s) => return s == path,
|
||||
PatternType::Dynamic(ref re, ref names, _) => {
|
||||
if let Some(captures) = re.captures(path) {
|
||||
let mut passed = false;
|
||||
for capture in captures.iter() {
|
||||
if let Some(ref m) = capture {
|
||||
if !passed {
|
||||
passed = true;
|
||||
continue;
|
||||
}
|
||||
segments.push(ParamItem::UrlSegment(
|
||||
(plen + m.start()) as u16,
|
||||
(plen + m.end()) as u16,
|
||||
));
|
||||
}
|
||||
}
|
||||
names
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PatternType::Prefix(ref s) => return path.starts_with(s),
|
||||
}
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
let len = req.path().len();
|
||||
let params = req.match_info_mut();
|
||||
params.set_tail(len as u16);
|
||||
for (idx, segment) in segments.into_iter().enumerate() {
|
||||
params.add(names[idx].clone(), segment);
|
||||
match self.tp {
|
||||
PatternType::Static(ref s) => if s != path {
|
||||
None
|
||||
} else {
|
||||
Some(Params::with_url(req.url()))
|
||||
},
|
||||
PatternType::Dynamic(ref re, ref names, _) => {
|
||||
if let Some(captures) = re.captures(path) {
|
||||
let mut params = Params::with_url(req.url());
|
||||
let mut idx = 0;
|
||||
let mut passed = false;
|
||||
for capture in captures.iter() {
|
||||
if let Some(ref m) = capture {
|
||||
if !passed {
|
||||
passed = true;
|
||||
continue;
|
||||
}
|
||||
params.add(
|
||||
names[idx].clone(),
|
||||
ParamItem::UrlSegment(
|
||||
(plen + m.start()) as u16,
|
||||
(plen + m.end()) as u16,
|
||||
),
|
||||
);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
params.set_tail(req.path().len() as u16);
|
||||
Some(params)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
PatternType::Prefix(ref s) => if !path.starts_with(s) {
|
||||
None
|
||||
} else {
|
||||
Some(Params::with_url(req.url()))
|
||||
},
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Is the given path a prefix match and do the parameters match against this resource?
|
||||
pub fn match_prefix_with_params<S>(
|
||||
&self, req: &mut HttpRequest<S>, plen: usize,
|
||||
) -> Option<usize> {
|
||||
let mut segments: SmallVec<[ParamItem; 5]> = SmallVec::new();
|
||||
pub fn match_prefix_with_params(
|
||||
&self, req: &Request, plen: usize,
|
||||
) -> Option<Params> {
|
||||
let path = &req.path()[plen..];
|
||||
let path = if path.is_empty() { "/" } else { path };
|
||||
|
||||
let (names, tail_len) = {
|
||||
let path = &req.path()[plen..];
|
||||
let path = if path.is_empty() { "/" } else { path };
|
||||
match self.tp {
|
||||
PatternType::Static(ref s) => if s == path {
|
||||
Some(Params::with_url(req.url()))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
PatternType::Dynamic(ref re, ref names, len) => {
|
||||
if let Some(captures) = re.captures(path) {
|
||||
let mut params = Params::with_url(req.url());
|
||||
let mut pos = 0;
|
||||
let mut passed = false;
|
||||
let mut idx = 0;
|
||||
for capture in captures.iter() {
|
||||
if let Some(ref m) = capture {
|
||||
if !passed {
|
||||
passed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
match self.tp {
|
||||
PatternType::Static(ref s) => if s == path {
|
||||
return Some(s.len());
|
||||
} else {
|
||||
return None;
|
||||
},
|
||||
PatternType::Dynamic(ref re, ref names, len) => {
|
||||
if let Some(captures) = re.captures(path) {
|
||||
let mut pos = 0;
|
||||
let mut passed = false;
|
||||
for capture in captures.iter() {
|
||||
if let Some(ref m) = capture {
|
||||
if !passed {
|
||||
passed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
segments.push(ParamItem::UrlSegment(
|
||||
params.add(
|
||||
names[idx].clone(),
|
||||
ParamItem::UrlSegment(
|
||||
(plen + m.start()) as u16,
|
||||
(plen + m.end()) as u16,
|
||||
));
|
||||
pos = m.end();
|
||||
}
|
||||
),
|
||||
);
|
||||
idx += 1;
|
||||
pos = m.end();
|
||||
}
|
||||
(names, pos + len)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
PatternType::Prefix(ref s) => {
|
||||
return if path == s {
|
||||
Some(s.len())
|
||||
} else if path.starts_with(s)
|
||||
&& (s.ends_with('/')
|
||||
|| path.split_at(s.len()).1.starts_with('/'))
|
||||
{
|
||||
if s.ends_with('/') {
|
||||
Some(s.len() - 1)
|
||||
} else {
|
||||
Some(s.len())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
params.set_tail((plen + pos + len) as u16);
|
||||
Some(params)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let params = req.match_info_mut();
|
||||
params.set_tail(tail_len as u16);
|
||||
for (idx, segment) in segments.into_iter().enumerate() {
|
||||
params.add(names[idx].clone(), segment);
|
||||
PatternType::Prefix(ref s) => {
|
||||
let len = if path == s {
|
||||
s.len()
|
||||
} else if path.starts_with(s)
|
||||
&& (s.ends_with('/') || path.split_at(s.len()).1.starts_with('/'))
|
||||
{
|
||||
if s.ends_with('/') {
|
||||
s.len() - 1
|
||||
} else {
|
||||
s.len()
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let mut params = Params::with_url(req.url());
|
||||
params.set_tail((plen + len) as u16);
|
||||
Some(params)
|
||||
}
|
||||
}
|
||||
Some(tail_len)
|
||||
}
|
||||
|
||||
/// Build resource path.
|
||||
pub fn resource_path<U, I>(
|
||||
&self, router: &Router, elements: U,
|
||||
&self, elements: U, prefix: &str,
|
||||
) -> Result<String, UrlGenerationError>
|
||||
where
|
||||
U: IntoIterator<Item = I>,
|
||||
@ -402,7 +498,6 @@ impl Resource {
|
||||
};
|
||||
|
||||
if self.rtp != ResourceType::External {
|
||||
let prefix = router.prefix();
|
||||
if prefix.ends_with('/') {
|
||||
if path.starts_with('/') {
|
||||
path.insert_str(0, &prefix[..prefix.len() - 1]);
|
||||
@ -546,44 +641,57 @@ mod tests {
|
||||
Some(ResourceHandler::default()),
|
||||
),
|
||||
];
|
||||
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
|
||||
let (rec, _) = Router::new::<()>("", routes);
|
||||
|
||||
let mut req = TestRequest::with_uri("/name").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(0));
|
||||
let req = TestRequest::with_uri("/name").finish();
|
||||
assert_eq!(rec.recognize(&req).unwrap().0, 0);
|
||||
assert!(req.match_info().is_empty());
|
||||
|
||||
let mut req = TestRequest::with_uri("/name/value").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(1));
|
||||
let req = TestRequest::with_uri("/name/value").finish();
|
||||
let info = rec.recognize(&req).unwrap().1;
|
||||
let req = req.with_route_info(info);
|
||||
assert_eq!(req.match_info().get("val").unwrap(), "value");
|
||||
assert_eq!(&req.match_info()["val"], "value");
|
||||
|
||||
let mut req = TestRequest::with_uri("/name/value2/index.html").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(2));
|
||||
let req = TestRequest::with_uri("/name/value2/index.html").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 2);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(req.match_info().get("val").unwrap(), "value2");
|
||||
|
||||
let mut req = TestRequest::with_uri("/file/file.gz").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(3));
|
||||
let req = TestRequest::with_uri("/file/file.gz").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 3);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(req.match_info().get("file").unwrap(), "file");
|
||||
assert_eq!(req.match_info().get("ext").unwrap(), "gz");
|
||||
|
||||
let mut req = TestRequest::with_uri("/vtest/ttt/index.html").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(4));
|
||||
let req = TestRequest::with_uri("/vtest/ttt/index.html").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 4);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(req.match_info().get("val").unwrap(), "test");
|
||||
assert_eq!(req.match_info().get("val2").unwrap(), "ttt");
|
||||
|
||||
let mut req = TestRequest::with_uri("/v/blah-blah/index.html").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(5));
|
||||
let req = TestRequest::with_uri("/v/blah-blah/index.html").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 5);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(
|
||||
req.match_info().get("tail").unwrap(),
|
||||
"blah-blah/index.html"
|
||||
);
|
||||
|
||||
let mut req = TestRequest::with_uri("/test2/index.html").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(6));
|
||||
let req = TestRequest::with_uri("/test2/index.html").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 6);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(req.match_info().get("test").unwrap(), "index");
|
||||
|
||||
let mut req = TestRequest::with_uri("/bbb/index.html").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(7));
|
||||
let req = TestRequest::with_uri("/bbb/index.html").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 7);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(req.match_info().get("test").unwrap(), "bbb");
|
||||
}
|
||||
|
||||
@ -599,13 +707,13 @@ mod tests {
|
||||
Some(ResourceHandler::default()),
|
||||
),
|
||||
];
|
||||
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
|
||||
let (rec, _) = Router::new::<()>("", routes);
|
||||
|
||||
let mut req = TestRequest::with_uri("/index.json").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(0));
|
||||
let req = TestRequest::with_uri("/index.json").finish();
|
||||
assert_eq!(rec.recognize(&req).unwrap().0, 0);
|
||||
|
||||
let mut req = TestRequest::with_uri("/test.json").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(1));
|
||||
let req = TestRequest::with_uri("/test.json").finish();
|
||||
assert_eq!(rec.recognize(&req).unwrap().0, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -617,16 +725,18 @@ mod tests {
|
||||
Some(ResourceHandler::default()),
|
||||
),
|
||||
];
|
||||
let (rec, _) = Router::new::<()>("/test", ServerSettings::default(), routes);
|
||||
let (rec, _) = Router::new::<()>("/test", routes);
|
||||
|
||||
let mut req = TestRequest::with_uri("/name").finish();
|
||||
assert!(rec.recognize(&mut req).is_none());
|
||||
let req = TestRequest::with_uri("/name").finish();
|
||||
assert!(rec.recognize(&req).is_none());
|
||||
|
||||
let mut req = TestRequest::with_uri("/test/name").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(0));
|
||||
let req = TestRequest::with_uri("/test/name").finish();
|
||||
assert_eq!(rec.recognize(&req).unwrap().0, 0);
|
||||
|
||||
let mut req = TestRequest::with_uri("/test/name/value").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(1));
|
||||
let req = TestRequest::with_uri("/test/name/value").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 1);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(req.match_info().get("val").unwrap(), "value");
|
||||
assert_eq!(&req.match_info()["val"], "value");
|
||||
|
||||
@ -638,16 +748,18 @@ mod tests {
|
||||
Some(ResourceHandler::default()),
|
||||
),
|
||||
];
|
||||
let (rec, _) = Router::new::<()>("/test2", ServerSettings::default(), routes);
|
||||
let (rec, _) = Router::new::<()>("/test2", routes);
|
||||
|
||||
let mut req = TestRequest::with_uri("/name").finish();
|
||||
assert!(rec.recognize(&mut req).is_none());
|
||||
let mut req = TestRequest::with_uri("/test2/name").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(0));
|
||||
let mut req = TestRequest::with_uri("/test2/name-test").finish();
|
||||
assert!(rec.recognize(&mut req).is_none());
|
||||
let mut req = TestRequest::with_uri("/test2/name/ttt").finish();
|
||||
assert_eq!(rec.recognize(&mut req), Some(1));
|
||||
let req = TestRequest::with_uri("/name").finish();
|
||||
assert!(rec.recognize(&req).is_none());
|
||||
let req = TestRequest::with_uri("/test2/name").finish();
|
||||
assert_eq!(rec.recognize(&req).unwrap().0, 0);
|
||||
let req = TestRequest::with_uri("/test2/name-test").finish();
|
||||
assert!(rec.recognize(&req).is_none());
|
||||
let req = TestRequest::with_uri("/test2/name/ttt").finish();
|
||||
let info = rec.recognize(&req).unwrap();
|
||||
assert_eq!(info.0, 1);
|
||||
let req = req.with_route_info(info.1);
|
||||
assert_eq!(&req.match_info()["val"], "ttt");
|
||||
}
|
||||
|
||||
@ -681,29 +793,23 @@ mod tests {
|
||||
assert!(!re.is_match("/user/2345/"));
|
||||
assert!(!re.is_match("/user/2345/sdg"));
|
||||
|
||||
let mut req = TestRequest::with_uri("/user/profile").finish();
|
||||
let url = req.url().clone();
|
||||
req.match_info_mut().set_url(url);
|
||||
assert!(re.match_with_params(&mut req, 0, true));
|
||||
assert_eq!(req.match_info().get("id").unwrap(), "profile");
|
||||
let req = TestRequest::with_uri("/user/profile").finish();
|
||||
let info = re.match_with_params(&req, 0, true).unwrap();
|
||||
assert_eq!(info.get("id").unwrap(), "profile");
|
||||
|
||||
let mut req = TestRequest::with_uri("/user/1245125").finish();
|
||||
let url = req.url().clone();
|
||||
req.match_info_mut().set_url(url);
|
||||
assert!(re.match_with_params(&mut req, 0, true));
|
||||
assert_eq!(req.match_info().get("id").unwrap(), "1245125");
|
||||
let req = TestRequest::with_uri("/user/1245125").finish();
|
||||
let info = re.match_with_params(&req, 0, true).unwrap();
|
||||
assert_eq!(info.get("id").unwrap(), "1245125");
|
||||
|
||||
let re = Resource::new("test", "/v{version}/resource/{id}");
|
||||
assert!(re.is_match("/v1/resource/320120"));
|
||||
assert!(!re.is_match("/v/resource/1"));
|
||||
assert!(!re.is_match("/resource"));
|
||||
|
||||
let mut req = TestRequest::with_uri("/v151/resource/adahg32").finish();
|
||||
let url = req.url().clone();
|
||||
req.match_info_mut().set_url(url);
|
||||
assert!(re.match_with_params(&mut req, 0, true));
|
||||
assert_eq!(req.match_info().get("version").unwrap(), "151");
|
||||
assert_eq!(req.match_info().get("id").unwrap(), "adahg32");
|
||||
let req = TestRequest::with_uri("/v151/resource/adahg32").finish();
|
||||
let info = re.match_with_params(&req, 0, true).unwrap();
|
||||
assert_eq!(info.get("version").unwrap(), "151");
|
||||
assert_eq!(info.get("id").unwrap(), "adahg32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -728,18 +834,15 @@ mod tests {
|
||||
assert!(re.is_match("/name/gs"));
|
||||
assert!(!re.is_match("/name"));
|
||||
|
||||
let mut req = TestRequest::with_uri("/test2/").finish();
|
||||
let url = req.url().clone();
|
||||
req.match_info_mut().set_url(url);
|
||||
assert!(re.match_with_params(&mut req, 0, true));
|
||||
assert_eq!(&req.match_info()["name"], "test2");
|
||||
let req = TestRequest::with_uri("/test2/").finish();
|
||||
let info = re.match_with_params(&req, 0, true).unwrap();
|
||||
assert_eq!(&info["name"], "test2");
|
||||
assert_eq!(&info[0], "test2");
|
||||
|
||||
let mut req =
|
||||
TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish();
|
||||
let url = req.url().clone();
|
||||
req.match_info_mut().set_url(url);
|
||||
assert!(re.match_with_params(&mut req, 0, true));
|
||||
assert_eq!(&req.match_info()["name"], "test2");
|
||||
let req = TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish();
|
||||
let info = re.match_with_params(&req, 0, true).unwrap();
|
||||
assert_eq!(&info["name"], "test2");
|
||||
assert_eq!(&info[0], "test2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -754,18 +857,18 @@ mod tests {
|
||||
Some(ResourceHandler::default()),
|
||||
),
|
||||
];
|
||||
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes);
|
||||
let (router, _) = Router::new::<()>("", routes);
|
||||
|
||||
let mut req =
|
||||
TestRequest::with_uri("/index.json").finish_with_router(router.clone());
|
||||
assert_eq!(router.recognize(&mut req), Some(0));
|
||||
let resource = req.resource().unwrap();
|
||||
let req = TestRequest::with_uri("/index.json").finish();
|
||||
assert_eq!(router.recognize(&req).unwrap().0, 0);
|
||||
let info = router.recognize(&req).unwrap().1;
|
||||
let resource = info.resource().unwrap();
|
||||
assert_eq!(resource.name(), "r1");
|
||||
|
||||
let mut req =
|
||||
TestRequest::with_uri("/test.json").finish_with_router(router.clone());
|
||||
assert_eq!(router.recognize(&mut req), Some(1));
|
||||
let resource = req.resource().unwrap();
|
||||
let req = TestRequest::with_uri("/test.json").finish();
|
||||
assert_eq!(router.recognize(&req).unwrap().0, 1);
|
||||
let info = router.recognize(&req).unwrap().1;
|
||||
let resource = info.resource().unwrap();
|
||||
assert_eq!(resource.name(), "r2");
|
||||
}
|
||||
}
|
||||
|
228
src/scope.rs
228
src/scope.rs
@ -14,8 +14,9 @@ use middleware::{
|
||||
Started as MiddlewareStarted,
|
||||
};
|
||||
use pred::Predicate;
|
||||
use resource::ResourceHandler;
|
||||
use resource::{ResourceHandler, RouteId};
|
||||
use router::Resource;
|
||||
use server::Request;
|
||||
|
||||
type ScopeResource<S> = Rc<ResourceHandler<S>>;
|
||||
type Route<S> = Box<RouteHandler<S>>;
|
||||
@ -114,7 +115,7 @@ impl<S: 'static> Scope<S> {
|
||||
///
|
||||
/// struct AppState;
|
||||
///
|
||||
/// fn index(req: HttpRequest<AppState>) -> &'static str {
|
||||
/// fn index(req: &HttpRequest<AppState>) -> &'static str {
|
||||
/// "Welcome!"
|
||||
/// }
|
||||
///
|
||||
@ -159,7 +160,7 @@ impl<S: 'static> Scope<S> {
|
||||
///
|
||||
/// struct AppState;
|
||||
///
|
||||
/// fn index(req: HttpRequest<AppState>) -> &'static str {
|
||||
/// fn index(req: &HttpRequest<AppState>) -> &'static str {
|
||||
/// "Welcome!"
|
||||
/// }
|
||||
///
|
||||
@ -334,75 +335,61 @@ impl<S: 'static> Scope<S> {
|
||||
}
|
||||
|
||||
impl<S: 'static> RouteHandler<S> for Scope<S> {
|
||||
fn handle(&self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
let tail = req.match_info().tail as usize;
|
||||
|
||||
// recognize resources
|
||||
for &(ref pattern, ref resource) in self.resources.iter() {
|
||||
if pattern.match_with_params(&mut req, tail, false) {
|
||||
if self.middlewares.is_empty() {
|
||||
return match resource.handle(req) {
|
||||
Ok(result) => result,
|
||||
Err(req) => {
|
||||
if let Some(ref default) = self.default {
|
||||
match default.handle(req) {
|
||||
Ok(result) => result,
|
||||
Err(_) => AsyncResult::ok(HttpResponse::new(
|
||||
StatusCode::NOT_FOUND,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return AsyncResult::async(Box::new(Compose::new(
|
||||
req,
|
||||
Rc::clone(&self.middlewares),
|
||||
Rc::clone(&resource),
|
||||
)));
|
||||
if let Some(params) = pattern.match_with_params(req, tail, false) {
|
||||
let req2 = req.with_route_info(req.route().merge(params));
|
||||
if let Some(id) = resource.get_route_id(&req2) {
|
||||
if self.middlewares.is_empty() {
|
||||
return resource.handle(id, &req2);
|
||||
} else {
|
||||
return AsyncResult::async(Box::new(Compose::new(
|
||||
id,
|
||||
req2,
|
||||
Rc::clone(&self.middlewares),
|
||||
Rc::clone(&resource),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nested scopes
|
||||
let len = req.prefix_len() as usize;
|
||||
let len = req.route().prefix_len() as usize;
|
||||
'outer: for &(ref prefix, ref handler, ref filters) in &self.nested {
|
||||
if let Some(prefix_len) = prefix.match_prefix_with_params(&mut req, len) {
|
||||
if let Some(params) = prefix.match_prefix_with_params(req, tail) {
|
||||
let req2 = req.with_route_info(req.route().merge(params));
|
||||
|
||||
let state = req.state();
|
||||
for filter in filters {
|
||||
if !filter.check(&mut req) {
|
||||
if !filter.check(&req2, state) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
let url = req.url().clone();
|
||||
let prefix_len = (len + prefix_len) as u16;
|
||||
req.set_prefix_len(prefix_len);
|
||||
req.match_info_mut().set_tail(prefix_len);
|
||||
req.match_info_mut().set_url(url);
|
||||
return handler.handle(req);
|
||||
return handler.handle(&req2);
|
||||
}
|
||||
}
|
||||
|
||||
// default handler
|
||||
if self.middlewares.is_empty() {
|
||||
if let Some(ref default) = self.default {
|
||||
match default.handle(req) {
|
||||
Ok(result) => result,
|
||||
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
|
||||
if let Some(ref resource) = self.default {
|
||||
if let Some(id) = resource.get_route_id(req) {
|
||||
if self.middlewares.is_empty() {
|
||||
return resource.handle(id, req);
|
||||
} else {
|
||||
return AsyncResult::async(Box::new(Compose::new(
|
||||
id,
|
||||
req.clone(),
|
||||
Rc::clone(&self.middlewares),
|
||||
Rc::clone(resource),
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
|
||||
}
|
||||
} else if let Some(ref default) = self.default {
|
||||
AsyncResult::async(Box::new(Compose::new(
|
||||
req,
|
||||
Rc::clone(&self.middlewares),
|
||||
Rc::clone(default),
|
||||
)))
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
|
||||
}
|
||||
|
||||
fn has_default_resource(&self) -> bool {
|
||||
@ -420,8 +407,9 @@ struct Wrapper<S: 'static> {
|
||||
}
|
||||
|
||||
impl<S: 'static, S2: 'static> RouteHandler<S2> for Wrapper<S> {
|
||||
fn handle(&self, req: HttpRequest<S2>) -> AsyncResult<HttpResponse> {
|
||||
self.scope.handle(req.change_state(Rc::clone(&self.state)))
|
||||
fn handle(&self, req: &HttpRequest<S2>) -> AsyncResult<HttpResponse> {
|
||||
let req = req.with_state(Rc::clone(&self.state));
|
||||
self.scope.handle(&req)
|
||||
}
|
||||
}
|
||||
|
||||
@ -431,10 +419,9 @@ struct FiltersWrapper<S: 'static> {
|
||||
}
|
||||
|
||||
impl<S: 'static, S2: 'static> Predicate<S2> for FiltersWrapper<S> {
|
||||
fn check(&self, req: &mut HttpRequest<S2>) -> bool {
|
||||
let mut req = req.change_state(Rc::clone(&self.state));
|
||||
fn check(&self, req: &Request, _: &S2) -> bool {
|
||||
for filter in &self.filters {
|
||||
if !filter.check(&mut req) {
|
||||
if !filter.check(&req, &self.state) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -450,6 +437,7 @@ struct Compose<S: 'static> {
|
||||
|
||||
struct ComposeInfo<S: 'static> {
|
||||
count: usize,
|
||||
id: RouteId,
|
||||
req: HttpRequest<S>,
|
||||
mws: Rc<Vec<Box<Middleware<S>>>>,
|
||||
resource: Rc<ResourceHandler<S>>,
|
||||
@ -477,14 +465,15 @@ impl<S: 'static> ComposeState<S> {
|
||||
|
||||
impl<S: 'static> Compose<S> {
|
||||
fn new(
|
||||
req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>,
|
||||
id: RouteId, req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>,
|
||||
resource: Rc<ResourceHandler<S>>,
|
||||
) -> Self {
|
||||
let mut info = ComposeInfo {
|
||||
count: 0,
|
||||
req,
|
||||
id,
|
||||
mws,
|
||||
req,
|
||||
resource,
|
||||
count: 0,
|
||||
};
|
||||
let state = StartMiddlewares::init(&mut info);
|
||||
|
||||
@ -522,27 +511,27 @@ type Fut = Box<Future<Item = Option<HttpResponse>, Error = Error>>;
|
||||
impl<S: 'static> StartMiddlewares<S> {
|
||||
fn init(info: &mut ComposeInfo<S>) -> ComposeState<S> {
|
||||
let len = info.mws.len();
|
||||
|
||||
loop {
|
||||
if info.count == len {
|
||||
let reply = {
|
||||
let req = info.req.clone();
|
||||
info.resource.handle(req).unwrap()
|
||||
};
|
||||
let reply = info.resource.handle(info.id, &info.req);
|
||||
return WaitingResponse::init(info, reply);
|
||||
} else {
|
||||
let state = info.mws[info.count].start(&mut info.req);
|
||||
match state {
|
||||
let result = info.mws[info.count].start(&info.req);
|
||||
match result {
|
||||
Ok(MiddlewareStarted::Done) => info.count += 1,
|
||||
Ok(MiddlewareStarted::Response(resp)) => {
|
||||
return RunMiddlewares::init(info, resp)
|
||||
return RunMiddlewares::init(info, resp);
|
||||
}
|
||||
Ok(MiddlewareStarted::Future(fut)) => {
|
||||
return ComposeState::Starting(StartMiddlewares {
|
||||
fut: Some(fut),
|
||||
_s: PhantomData,
|
||||
})
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
return RunMiddlewares::init(info, err.into());
|
||||
}
|
||||
Err(err) => return RunMiddlewares::init(info, err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -550,24 +539,25 @@ impl<S: 'static> StartMiddlewares<S> {
|
||||
|
||||
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
|
||||
let len = info.mws.len();
|
||||
|
||||
'outer: loop {
|
||||
match self.fut.as_mut().unwrap().poll() {
|
||||
Ok(Async::NotReady) => return None,
|
||||
Ok(Async::NotReady) => {
|
||||
return None;
|
||||
}
|
||||
Ok(Async::Ready(resp)) => {
|
||||
info.count += 1;
|
||||
|
||||
if let Some(resp) = resp {
|
||||
return Some(RunMiddlewares::init(info, resp));
|
||||
}
|
||||
loop {
|
||||
if info.count == len {
|
||||
let reply = {
|
||||
let req = info.req.clone();
|
||||
info.resource.handle(req).unwrap()
|
||||
};
|
||||
let reply = { info.resource.handle(info.id, &info.req) };
|
||||
return Some(WaitingResponse::init(info, reply));
|
||||
} else {
|
||||
let state = info.mws[info.count].start(&mut info.req);
|
||||
match state {
|
||||
let result = info.mws[info.count].start(&info.req);
|
||||
match result {
|
||||
Ok(MiddlewareStarted::Done) => info.count += 1,
|
||||
Ok(MiddlewareStarted::Response(resp)) => {
|
||||
return Some(RunMiddlewares::init(info, resp));
|
||||
@ -577,13 +567,15 @@ impl<S: 'static> StartMiddlewares<S> {
|
||||
continue 'outer;
|
||||
}
|
||||
Err(err) => {
|
||||
return Some(RunMiddlewares::init(info, err.into()))
|
||||
return Some(RunMiddlewares::init(info, err.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => return Some(RunMiddlewares::init(info, err.into())),
|
||||
Err(err) => {
|
||||
return Some(RunMiddlewares::init(info, err.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -613,7 +605,7 @@ impl<S: 'static> WaitingResponse<S> {
|
||||
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
|
||||
match self.fut.poll() {
|
||||
Ok(Async::NotReady) => None,
|
||||
Ok(Async::Ready(response)) => Some(RunMiddlewares::init(info, response)),
|
||||
Ok(Async::Ready(resp)) => Some(RunMiddlewares::init(info, resp)),
|
||||
Err(err) => Some(RunMiddlewares::init(info, err.into())),
|
||||
}
|
||||
}
|
||||
@ -632,7 +624,7 @@ impl<S: 'static> RunMiddlewares<S> {
|
||||
let len = info.mws.len();
|
||||
|
||||
loop {
|
||||
let state = info.mws[curr].response(&mut info.req, resp);
|
||||
let state = info.mws[curr].response(&info.req, resp);
|
||||
resp = match state {
|
||||
Err(err) => {
|
||||
info.count = curr + 1;
|
||||
@ -651,7 +643,7 @@ impl<S: 'static> RunMiddlewares<S> {
|
||||
curr,
|
||||
fut: Some(fut),
|
||||
_s: PhantomData,
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -675,7 +667,7 @@ impl<S: 'static> RunMiddlewares<S> {
|
||||
if self.curr == len {
|
||||
return Some(FinishingMiddlewares::init(info, resp));
|
||||
} else {
|
||||
let state = info.mws[self.curr].response(&mut info.req, resp);
|
||||
let state = info.mws[self.curr].response(&info.req, resp);
|
||||
match state {
|
||||
Err(err) => {
|
||||
return Some(FinishingMiddlewares::init(info, err.into()))
|
||||
@ -745,7 +737,7 @@ impl<S: 'static> FinishingMiddlewares<S> {
|
||||
|
||||
info.count -= 1;
|
||||
let state = info.mws[info.count as usize]
|
||||
.finish(&mut info.req, self.resp.as_ref().unwrap());
|
||||
.finish(&info.req, self.resp.as_ref().unwrap());
|
||||
match state {
|
||||
MiddlewareFinished::Done => {
|
||||
if info.count == 0 {
|
||||
@ -794,7 +786,7 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1").finish();
|
||||
let req = TestRequest::with_uri("/app/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
@ -809,11 +801,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app").finish();
|
||||
let req = TestRequest::with_uri("/app").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/").finish();
|
||||
let req = TestRequest::with_uri("/app/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
@ -826,11 +818,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app").finish();
|
||||
let req = TestRequest::with_uri("/app").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/").finish();
|
||||
let req = TestRequest::with_uri("/app/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
@ -843,11 +835,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app").finish();
|
||||
let req = TestRequest::with_uri("/app").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/").finish();
|
||||
let req = TestRequest::with_uri("/app/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -866,19 +858,19 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1").finish();
|
||||
let req = TestRequest::with_uri("/app/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::DELETE)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::POST)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -895,13 +887,13 @@ mod tests {
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::POST)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::GET)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
@ -919,7 +911,7 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/ab-project1/path1").finish();
|
||||
let req = TestRequest::with_uri("/ab-project1/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
@ -931,7 +923,7 @@ mod tests {
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
let req = TestRequest::with_uri("/aa-project1/path1").finish();
|
||||
let req = TestRequest::with_uri("/aa-project1/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -948,7 +940,7 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/path1").finish();
|
||||
let req = TestRequest::with_uri("/app/t1/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
@ -967,11 +959,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1").finish();
|
||||
let req = TestRequest::with_uri("/app/t1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/").finish();
|
||||
let req = TestRequest::with_uri("/app/t1/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
@ -988,11 +980,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1").finish();
|
||||
let req = TestRequest::with_uri("/app/t1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/").finish();
|
||||
let req = TestRequest::with_uri("/app/t1/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
@ -1009,11 +1001,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1").finish();
|
||||
let req = TestRequest::with_uri("/app/t1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/").finish();
|
||||
let req = TestRequest::with_uri("/app/t1/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -1034,13 +1026,13 @@ mod tests {
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/path1")
|
||||
.method(Method::POST)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/path1")
|
||||
.method(Method::GET)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
@ -1055,7 +1047,7 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/path1").finish();
|
||||
let req = TestRequest::with_uri("/app/t1/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
@ -1072,11 +1064,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1").finish();
|
||||
let req = TestRequest::with_uri("/app/t1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/").finish();
|
||||
let req = TestRequest::with_uri("/app/t1/").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
@ -1095,13 +1087,13 @@ mod tests {
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/path1")
|
||||
.method(Method::POST)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/path1")
|
||||
.method(Method::GET)
|
||||
.finish();
|
||||
.request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
@ -1123,7 +1115,7 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/project_1/path1").finish();
|
||||
let req = TestRequest::with_uri("/app/project_1/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
|
||||
@ -1156,7 +1148,7 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/test/1/path1").finish();
|
||||
let req = TestRequest::with_uri("/app/test/1/path1").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
|
||||
@ -1168,7 +1160,7 @@ mod tests {
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
let req = TestRequest::with_uri("/app/test/1/path2").finish();
|
||||
let req = TestRequest::with_uri("/app/test/1/path2").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -1183,11 +1175,11 @@ mod tests {
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/path2").finish();
|
||||
let req = TestRequest::with_uri("/app/path2").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let req = TestRequest::with_uri("/path2").finish();
|
||||
let req = TestRequest::with_uri("/path2").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
@ -1202,15 +1194,15 @@ mod tests {
|
||||
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/non-exist").finish();
|
||||
let req = TestRequest::with_uri("/non-exist").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
|
||||
let req = TestRequest::with_uri("/app1/non-exist").finish();
|
||||
let req = TestRequest::with_uri("/app1/non-exist").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let req = TestRequest::with_uri("/app2/non-exist").finish();
|
||||
let req = TestRequest::with_uri("/app2/non-exist").request();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
25
src/server/error.rs
Normal file
25
src/server/error.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use futures::{Async, Poll};
|
||||
|
||||
use super::{helpers, HttpHandlerTask, Writer};
|
||||
use http::{StatusCode, Version};
|
||||
use httpresponse::HttpResponse;
|
||||
use Error;
|
||||
|
||||
pub(crate) struct ServerError(Version, StatusCode);
|
||||
|
||||
impl ServerError {
|
||||
pub fn err(ver: Version, status: StatusCode) -> Box<HttpHandlerTask> {
|
||||
Box::new(ServerError(ver, status))
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpHandlerTask for ServerError {
|
||||
fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error> {
|
||||
{
|
||||
let mut bytes = io.buffer();
|
||||
helpers::write_status_line(self.0, self.1.as_u16(), bytes);
|
||||
}
|
||||
io.set_date();
|
||||
Ok(Async::Ready(true))
|
||||
}
|
||||
}
|
148
src/server/h1.rs
148
src/server/h1.rs
@ -8,11 +8,13 @@ use futures::{Async, Future, Poll};
|
||||
use tokio_timer::Delay;
|
||||
|
||||
use error::{Error, PayloadError};
|
||||
use http::{StatusCode, Version};
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
use payload::{Payload, PayloadStatus, PayloadWriter};
|
||||
use pipeline::Pipeline;
|
||||
|
||||
use super::error::ServerError;
|
||||
use super::h1decoder::{DecoderError, H1Decoder, Message};
|
||||
use super::h1writer::H1Writer;
|
||||
use super::input::PayloadType;
|
||||
@ -180,9 +182,7 @@ where
|
||||
&& self.tasks.len() < MAX_PIPELINED_MESSAGES
|
||||
&& self.can_read()
|
||||
{
|
||||
let res = self.stream.get_mut().read_available(&mut self.buf);
|
||||
match res {
|
||||
//self.stream.get_mut().read_available(&mut self.buf) {
|
||||
match self.stream.get_mut().read_available(&mut self.buf) {
|
||||
Ok(Async::Ready(disconnected)) => {
|
||||
if disconnected {
|
||||
// notify all tasks
|
||||
@ -363,22 +363,19 @@ where
|
||||
|
||||
if payload {
|
||||
let (ps, pl) = Payload::new(false);
|
||||
msg.get_mut().payload = Some(pl);
|
||||
self.payload =
|
||||
Some(PayloadType::new(&msg.get_ref().headers, ps));
|
||||
*msg.inner.payload.borrow_mut() = Some(pl);
|
||||
self.payload = Some(PayloadType::new(&msg.inner.headers, ps));
|
||||
}
|
||||
|
||||
let mut req = HttpRequest::from_message(msg);
|
||||
|
||||
// set remote addr
|
||||
req.set_peer_addr(self.addr);
|
||||
msg.inner.addr = self.addr;
|
||||
|
||||
// stop keepalive timer
|
||||
self.keepalive_timer.take();
|
||||
|
||||
// search handler for request
|
||||
for h in self.settings.handlers().iter_mut() {
|
||||
req = match h.handle(req) {
|
||||
msg = match h.handle(msg) {
|
||||
Ok(mut pipe) => {
|
||||
if self.tasks.is_empty() {
|
||||
match pipe.poll_io(&mut self.stream) {
|
||||
@ -415,15 +412,16 @@ where
|
||||
});
|
||||
continue 'outer;
|
||||
}
|
||||
Err(req) => req,
|
||||
Err(msg) => msg,
|
||||
}
|
||||
}
|
||||
|
||||
// handler is not found
|
||||
self.tasks.push_back(Entry {
|
||||
pipe: EntryPipe::Error(
|
||||
Pipeline::error(HttpResponse::NotFound()),
|
||||
),
|
||||
pipe: EntryPipe::Error(ServerError::err(
|
||||
Version::HTTP_11,
|
||||
StatusCode::NOT_FOUND,
|
||||
)),
|
||||
flags: EntryFlags::empty(),
|
||||
});
|
||||
}
|
||||
@ -475,12 +473,11 @@ mod tests {
|
||||
use application::HttpApplication;
|
||||
use httpmessage::HttpMessage;
|
||||
use server::h1decoder::Message;
|
||||
use server::helpers::SharedHttpInnerMessage;
|
||||
use server::settings::WorkerSettings;
|
||||
use server::KeepAlive;
|
||||
use server::settings::{ServerSettings, WorkerSettings};
|
||||
use server::{KeepAlive, Request};
|
||||
|
||||
impl Message {
|
||||
fn message(self) -> SharedHttpInnerMessage {
|
||||
fn message(self) -> Request {
|
||||
match self {
|
||||
Message::Message { msg, payload: _ } => msg,
|
||||
_ => panic!("error"),
|
||||
@ -509,9 +506,9 @@ mod tests {
|
||||
macro_rules! parse_ready {
|
||||
($e:expr) => {{
|
||||
let settings: WorkerSettings<HttpApplication> =
|
||||
WorkerSettings::new(Vec::new(), KeepAlive::Os);
|
||||
WorkerSettings::new(Vec::new(), KeepAlive::Os, ServerSettings::default());
|
||||
match H1Decoder::new().decode($e, &settings) {
|
||||
Ok(Some(msg)) => HttpRequest::from_message(msg.message()),
|
||||
Ok(Some(msg)) => msg.message(),
|
||||
Ok(_) => unreachable!("Eof during parsing http request"),
|
||||
Err(err) => unreachable!("Error during parsing http request: {:?}", err),
|
||||
}
|
||||
@ -521,7 +518,7 @@ mod tests {
|
||||
macro_rules! expect_parse_err {
|
||||
($e:expr) => {{
|
||||
let settings: WorkerSettings<HttpApplication> =
|
||||
WorkerSettings::new(Vec::new(), KeepAlive::Os);
|
||||
WorkerSettings::new(Vec::new(), KeepAlive::Os, ServerSettings::default());
|
||||
|
||||
match H1Decoder::new().decode($e, &settings) {
|
||||
Err(err) => match err {
|
||||
@ -605,6 +602,7 @@ mod tests {
|
||||
let settings = Rc::new(WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
));
|
||||
|
||||
let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf);
|
||||
@ -620,6 +618,7 @@ mod tests {
|
||||
let settings = Rc::new(WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
));
|
||||
|
||||
let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf);
|
||||
@ -631,12 +630,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n");
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
Ok(Some(msg)) => {
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
assert_eq!(req.version(), Version::HTTP_11);
|
||||
assert_eq!(*req.method(), Method::GET);
|
||||
assert_eq!(req.path(), "/test");
|
||||
@ -648,7 +651,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_partial() {
|
||||
let mut buf = BytesMut::from("PUT /test HTTP/1");
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
@ -659,7 +666,7 @@ mod tests {
|
||||
buf.extend(b".1\r\n\r\n");
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
Ok(Some(msg)) => {
|
||||
let mut req = HttpRequest::from_message(msg.message());
|
||||
let mut req = msg.message();
|
||||
assert_eq!(req.version(), Version::HTTP_11);
|
||||
assert_eq!(*req.method(), Method::PUT);
|
||||
assert_eq!(req.path(), "/test");
|
||||
@ -671,12 +678,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_post() {
|
||||
let mut buf = BytesMut::from("POST /test2 HTTP/1.0\r\n\r\n");
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
Ok(Some(msg)) => {
|
||||
let mut req = HttpRequest::from_message(msg.message());
|
||||
let mut req = msg.message();
|
||||
assert_eq!(req.version(), Version::HTTP_10);
|
||||
assert_eq!(*req.method(), Method::POST);
|
||||
assert_eq!(req.path(), "/test2");
|
||||
@ -689,12 +700,16 @@ mod tests {
|
||||
fn test_parse_body() {
|
||||
let mut buf =
|
||||
BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
Ok(Some(msg)) => {
|
||||
let mut req = HttpRequest::from_message(msg.message());
|
||||
let mut req = msg.message();
|
||||
assert_eq!(req.version(), Version::HTTP_11);
|
||||
assert_eq!(*req.method(), Method::GET);
|
||||
assert_eq!(req.path(), "/test");
|
||||
@ -716,12 +731,16 @@ mod tests {
|
||||
fn test_parse_body_crlf() {
|
||||
let mut buf =
|
||||
BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
Ok(Some(msg)) => {
|
||||
let mut req = HttpRequest::from_message(msg.message());
|
||||
let mut req = msg.message();
|
||||
assert_eq!(req.version(), Version::HTTP_11);
|
||||
assert_eq!(*req.method(), Method::GET);
|
||||
assert_eq!(req.path(), "/test");
|
||||
@ -742,14 +761,18 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_partial_eof() {
|
||||
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
let mut reader = H1Decoder::new();
|
||||
assert!(reader.decode(&mut buf, &settings).unwrap().is_none());
|
||||
|
||||
buf.extend(b"\r\n");
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
Ok(Some(msg)) => {
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
assert_eq!(req.version(), Version::HTTP_11);
|
||||
assert_eq!(*req.method(), Method::GET);
|
||||
assert_eq!(req.path(), "/test");
|
||||
@ -761,7 +784,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_headers_split_field() {
|
||||
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
assert!{ reader.decode(&mut buf, &settings).unwrap().is_none() }
|
||||
@ -775,7 +802,7 @@ mod tests {
|
||||
buf.extend(b"t: value\r\n\r\n");
|
||||
match reader.decode(&mut buf, &settings) {
|
||||
Ok(Some(msg)) => {
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
assert_eq!(req.version(), Version::HTTP_11);
|
||||
assert_eq!(*req.method(), Method::GET);
|
||||
assert_eq!(req.path(), "/test");
|
||||
@ -792,10 +819,14 @@ mod tests {
|
||||
Set-Cookie: c1=cookie1\r\n\
|
||||
Set-Cookie: c2=cookie2\r\n\r\n",
|
||||
);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
let mut reader = H1Decoder::new();
|
||||
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
|
||||
let val: Vec<_> = req
|
||||
.headers()
|
||||
@ -988,7 +1019,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_http_request_upgrade() {
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
let mut buf = BytesMut::from(
|
||||
"GET /test HTTP/1.1\r\n\
|
||||
connection: upgrade\r\n\
|
||||
@ -998,7 +1033,7 @@ mod tests {
|
||||
let mut reader = H1Decoder::new();
|
||||
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
|
||||
assert!(msg.is_payload());
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
assert!(!req.keep_alive());
|
||||
assert!(req.upgrade());
|
||||
assert_eq!(
|
||||
@ -1054,12 +1089,16 @@ mod tests {
|
||||
"GET /test HTTP/1.1\r\n\
|
||||
transfer-encoding: chunked\r\n\r\n",
|
||||
);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
|
||||
assert!(msg.is_payload());
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
assert!(req.chunked().unwrap());
|
||||
|
||||
buf.extend(b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n");
|
||||
@ -1090,11 +1129,15 @@ mod tests {
|
||||
"GET /test HTTP/1.1\r\n\
|
||||
transfer-encoding: chunked\r\n\r\n",
|
||||
);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
let mut reader = H1Decoder::new();
|
||||
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
|
||||
assert!(msg.is_payload());
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
assert!(req.chunked().unwrap());
|
||||
|
||||
buf.extend(
|
||||
@ -1112,7 +1155,7 @@ mod tests {
|
||||
|
||||
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
|
||||
assert!(msg.is_payload());
|
||||
let req2 = HttpRequest::from_message(msg.message());
|
||||
let req2 = msg.message();
|
||||
assert!(req2.chunked().unwrap());
|
||||
assert_eq!(*req2.method(), Method::POST);
|
||||
assert!(req2.chunked().unwrap());
|
||||
@ -1124,12 +1167,16 @@ mod tests {
|
||||
"GET /test HTTP/1.1\r\n\
|
||||
transfer-encoding: chunked\r\n\r\n",
|
||||
);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
|
||||
assert!(msg.is_payload());
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
let req = msg.message();
|
||||
assert!(req.chunked().unwrap());
|
||||
|
||||
buf.extend(b"4\r\n1111\r\n");
|
||||
@ -1171,13 +1218,16 @@ mod tests {
|
||||
&"GET /test HTTP/1.1\r\n\
|
||||
transfer-encoding: chunked\r\n\r\n"[..],
|
||||
);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<HttpApplication>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
|
||||
let mut reader = H1Decoder::new();
|
||||
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
|
||||
assert!(msg.is_payload());
|
||||
let req = HttpRequest::from_message(msg.message());
|
||||
assert!(req.chunked().unwrap());
|
||||
assert!(msg.message().chunked().unwrap());
|
||||
|
||||
buf.extend(b"4;test\r\ndata\r\n4\r\nline\r\n0\r\n\r\n"); // test: test\r\n\r\n")
|
||||
let chunk = reader.decode(&mut buf, &settings).unwrap().unwrap().chunk();
|
||||
|
@ -4,12 +4,11 @@ use bytes::{Bytes, BytesMut};
|
||||
use futures::{Async, Poll};
|
||||
use httparse;
|
||||
|
||||
use super::helpers::SharedHttpInnerMessage;
|
||||
use super::message::{MessageFlags, Request};
|
||||
use super::settings::WorkerSettings;
|
||||
use error::ParseError;
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
use http::{header, HttpTryFrom, Method, Uri, Version};
|
||||
use httprequest::MessageFlags;
|
||||
use uri::Url;
|
||||
|
||||
const MAX_BUFFER_SIZE: usize = 131_072;
|
||||
@ -20,10 +19,7 @@ pub(crate) struct H1Decoder {
|
||||
}
|
||||
|
||||
pub(crate) enum Message {
|
||||
Message {
|
||||
msg: SharedHttpInnerMessage,
|
||||
payload: bool,
|
||||
},
|
||||
Message { msg: Request, payload: bool },
|
||||
Chunk(Bytes),
|
||||
Eof,
|
||||
}
|
||||
@ -84,7 +80,7 @@ impl H1Decoder {
|
||||
|
||||
fn parse_message<H>(
|
||||
&self, buf: &mut BytesMut, settings: &WorkerSettings<H>,
|
||||
) -> Poll<(SharedHttpInnerMessage, Option<EncodingDecoder>), ParseError> {
|
||||
) -> Poll<(Request, Option<EncodingDecoder>), ParseError> {
|
||||
// Parse http message
|
||||
let mut has_upgrade = false;
|
||||
let mut chunked = false;
|
||||
@ -122,11 +118,12 @@ impl H1Decoder {
|
||||
let slice = buf.split_to(len).freeze();
|
||||
|
||||
// convert headers
|
||||
let mut msg = settings.get_http_message();
|
||||
let mut msg = settings.get_request_context();
|
||||
{
|
||||
let msg_mut = msg.get_mut();
|
||||
msg_mut
|
||||
let inner = &mut msg.inner;
|
||||
inner
|
||||
.flags
|
||||
.get_mut()
|
||||
.set(MessageFlags::KEEPALIVE, version != Version::HTTP_10);
|
||||
|
||||
for idx in headers[..headers_len].iter() {
|
||||
@ -177,20 +174,20 @@ impl H1Decoder {
|
||||
} else {
|
||||
false
|
||||
};
|
||||
msg_mut.flags.set(MessageFlags::KEEPALIVE, ka);
|
||||
inner.flags.get_mut().set(MessageFlags::KEEPALIVE, ka);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
msg_mut.headers.append(name, value);
|
||||
inner.headers.append(name, value);
|
||||
} else {
|
||||
return Err(ParseError::Header);
|
||||
}
|
||||
}
|
||||
|
||||
msg_mut.url = path;
|
||||
msg_mut.method = method;
|
||||
msg_mut.version = version;
|
||||
inner.url = path;
|
||||
inner.method = method;
|
||||
inner.version = version;
|
||||
}
|
||||
msg
|
||||
};
|
||||
@ -202,7 +199,7 @@ impl H1Decoder {
|
||||
} else if let Some(len) = content_length {
|
||||
// Content-Length
|
||||
Some(EncodingDecoder::length(len))
|
||||
} else if has_upgrade || msg.get_ref().method == Method::CONNECT {
|
||||
} else if has_upgrade || msg.inner.method == Method::CONNECT {
|
||||
// upgrade(websocket) or connect
|
||||
Some(EncodingDecoder::eof())
|
||||
} else {
|
||||
|
@ -7,14 +7,16 @@ use std::rc::Rc;
|
||||
use tokio_io::AsyncWrite;
|
||||
|
||||
use super::helpers;
|
||||
use super::output::Output;
|
||||
use super::output::{Output, ResponseInfo, ResponseLength};
|
||||
use super::settings::WorkerSettings;
|
||||
use super::Request;
|
||||
use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
|
||||
use body::{Binary, Body};
|
||||
use header::ContentEncoding;
|
||||
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE};
|
||||
use http::header::{
|
||||
HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
|
||||
};
|
||||
use http::{Method, Version};
|
||||
use httprequest::HttpInnerMessage;
|
||||
use httpresponse::HttpResponse;
|
||||
|
||||
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
|
||||
@ -101,8 +103,8 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_date(&self, dst: &mut BytesMut) {
|
||||
self.settings.set_date(dst, true)
|
||||
fn set_date(&mut self) {
|
||||
self.settings.set_date(self.buffer.as_mut(), true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -111,11 +113,11 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
|
||||
}
|
||||
|
||||
fn start(
|
||||
&mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse,
|
||||
encoding: ContentEncoding,
|
||||
&mut self, req: &Request, msg: &mut HttpResponse, encoding: ContentEncoding,
|
||||
) -> io::Result<WriterState> {
|
||||
// prepare task
|
||||
self.buffer.for_server(req, msg, encoding);
|
||||
let mut info = ResponseInfo::new(req.inner.method == Method::HEAD);
|
||||
self.buffer.for_server(&mut info, &req.inner, msg, encoding);
|
||||
if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) {
|
||||
self.flags = Flags::STARTED | Flags::KEEPALIVE;
|
||||
} else {
|
||||
@ -123,7 +125,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
|
||||
}
|
||||
|
||||
// Connection upgrade
|
||||
let version = msg.version().unwrap_or_else(|| req.version);
|
||||
let version = msg.version().unwrap_or_else(|| req.inner.version);
|
||||
if msg.upgrade() {
|
||||
self.flags.insert(Flags::UPGRADE);
|
||||
msg.headers_mut()
|
||||
@ -166,16 +168,29 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
|
||||
helpers::write_status_line(version, msg.status().as_u16(), &mut buffer);
|
||||
buffer.extend_from_slice(reason);
|
||||
|
||||
match body {
|
||||
Body::Empty => if req.method != Method::HEAD {
|
||||
buffer.extend_from_slice(b"\r\ncontent-length: 0\r\n");
|
||||
} else {
|
||||
buffer.extend_from_slice(b"\r\n");
|
||||
},
|
||||
Body::Binary(ref bytes) => {
|
||||
helpers::write_content_length(bytes.len(), &mut buffer)
|
||||
// content length
|
||||
match info.length {
|
||||
ResponseLength::Chunked => {
|
||||
buffer.extend_from_slice(b"\r\ntransfer-encoding: chunked\r\n")
|
||||
}
|
||||
_ => buffer.extend_from_slice(b"\r\n"),
|
||||
ResponseLength::Zero => {
|
||||
buffer.extend_from_slice(b"\r\ncontent-length: 0\r\n")
|
||||
}
|
||||
ResponseLength::Length(len) => {
|
||||
helpers::write_content_length(len, &mut buffer)
|
||||
}
|
||||
ResponseLength::Length64(len) => {
|
||||
let s = format!("{}", len);
|
||||
buffer.extend_from_slice(b"\r\ncontent-length: ");
|
||||
buffer.extend_from_slice(s.as_ref());
|
||||
buffer.extend_from_slice(b"\r\n");
|
||||
}
|
||||
ResponseLength::None => buffer.extend_from_slice(b"\r\n"),
|
||||
}
|
||||
if let Some(ce) = info.content_encoding {
|
||||
buffer.extend_from_slice(b"content-encoding: ");
|
||||
buffer.extend_from_slice(ce.as_ref());
|
||||
buffer.extend_from_slice(b"\r\n");
|
||||
}
|
||||
|
||||
// write headers
|
||||
@ -185,9 +200,13 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
|
||||
unsafe {
|
||||
let mut buf = &mut *(buffer.bytes_mut() as *mut [u8]);
|
||||
for (key, value) in msg.headers() {
|
||||
if is_bin && key == CONTENT_LENGTH {
|
||||
is_bin = false;
|
||||
continue;
|
||||
match *key {
|
||||
TRANSFER_ENCODING | CONTENT_ENCODING => continue,
|
||||
CONTENT_LENGTH => match info.length {
|
||||
ResponseLength::None => (),
|
||||
_ => continue,
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
has_date = has_date || key == DATE;
|
||||
let v = value.as_ref();
|
||||
|
@ -14,6 +14,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_timer::Delay;
|
||||
|
||||
use error::{Error, PayloadError};
|
||||
use http::{StatusCode, Version};
|
||||
use httpmessage::HttpMessage;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
@ -21,6 +22,7 @@ use payload::{Payload, PayloadStatus, PayloadWriter};
|
||||
use pipeline::Pipeline;
|
||||
use uri::Url;
|
||||
|
||||
use super::error::ServerError;
|
||||
use super::h2writer::H2Writer;
|
||||
use super::input::PayloadType;
|
||||
use super::settings::WorkerSettings;
|
||||
@ -331,34 +333,35 @@ impl<H: HttpHandler + 'static> Entry<H> {
|
||||
// Payload and Content-Encoding
|
||||
let (psender, payload) = Payload::new(false);
|
||||
|
||||
let mut msg = settings.get_http_message();
|
||||
msg.get_mut().url = Url::new(parts.uri);
|
||||
msg.get_mut().method = parts.method;
|
||||
msg.get_mut().version = parts.version;
|
||||
msg.get_mut().headers = parts.headers;
|
||||
msg.get_mut().payload = Some(payload);
|
||||
msg.get_mut().addr = addr;
|
||||
|
||||
let mut req = HttpRequest::from_message(msg);
|
||||
let mut msg = settings.get_request_context();
|
||||
msg.inner.url = Url::new(parts.uri);
|
||||
msg.inner.method = parts.method;
|
||||
msg.inner.version = parts.version;
|
||||
msg.inner.headers = parts.headers;
|
||||
*msg.inner.payload.borrow_mut() = Some(payload);
|
||||
msg.inner.addr = addr;
|
||||
|
||||
// Payload sender
|
||||
let psender = PayloadType::new(req.headers(), psender);
|
||||
let psender = PayloadType::new(msg.headers(), psender);
|
||||
|
||||
// start request processing
|
||||
let mut task = None;
|
||||
for h in settings.handlers().iter_mut() {
|
||||
req = match h.handle(req) {
|
||||
msg = match h.handle(msg) {
|
||||
Ok(t) => {
|
||||
task = Some(t);
|
||||
break;
|
||||
}
|
||||
Err(req) => req,
|
||||
Err(msg) => msg,
|
||||
}
|
||||
}
|
||||
|
||||
Entry {
|
||||
task: task.map(EntryPipe::Task).unwrap_or_else(|| {
|
||||
EntryPipe::Error(Pipeline::error(HttpResponse::NotFound()))
|
||||
EntryPipe::Error(ServerError::err(
|
||||
Version::HTTP_2,
|
||||
StatusCode::NOT_FOUND,
|
||||
))
|
||||
}),
|
||||
payload: psender,
|
||||
stream: H2Writer::new(resp, Rc::clone(settings)),
|
||||
|
@ -9,15 +9,15 @@ use std::rc::Rc;
|
||||
use std::{cmp, io};
|
||||
|
||||
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
|
||||
use http::{HttpTryFrom, Version};
|
||||
use http::{HttpTryFrom, Method, Version};
|
||||
|
||||
use super::helpers;
|
||||
use super::output::Output;
|
||||
use super::message::Request;
|
||||
use super::output::{Output, ResponseInfo};
|
||||
use super::settings::WorkerSettings;
|
||||
use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
|
||||
use body::{Binary, Body};
|
||||
use header::ContentEncoding;
|
||||
use httprequest::HttpInnerMessage;
|
||||
use httpresponse::HttpResponse;
|
||||
|
||||
const CHUNK_SIZE: usize = 16_384;
|
||||
@ -75,8 +75,8 @@ impl<H: 'static> Writer for H2Writer<H> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_date(&self, dst: &mut BytesMut) {
|
||||
self.settings.set_date(dst, true)
|
||||
fn set_date(&mut self) {
|
||||
self.settings.set_date(self.buffer.as_mut(), true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -85,12 +85,12 @@ impl<H: 'static> Writer for H2Writer<H> {
|
||||
}
|
||||
|
||||
fn start(
|
||||
&mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse,
|
||||
encoding: ContentEncoding,
|
||||
&mut self, req: &Request, msg: &mut HttpResponse, encoding: ContentEncoding,
|
||||
) -> io::Result<WriterState> {
|
||||
// prepare response
|
||||
self.flags.insert(Flags::STARTED);
|
||||
self.buffer.for_server(req, msg, encoding);
|
||||
let mut info = ResponseInfo::new(req.inner.method == Method::HEAD);
|
||||
self.buffer.for_server(&mut info, &req.inner, msg, encoding);
|
||||
|
||||
// http2 specific
|
||||
msg.headers_mut().remove(CONNECTION);
|
||||
|
@ -1,91 +1,7 @@
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use http::Version;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use httprequest::HttpInnerMessage;
|
||||
|
||||
/// Internal use only!
|
||||
pub(crate) struct SharedMessagePool(RefCell<VecDeque<Rc<HttpInnerMessage>>>);
|
||||
|
||||
impl SharedMessagePool {
|
||||
pub fn new() -> SharedMessagePool {
|
||||
SharedMessagePool(RefCell::new(VecDeque::with_capacity(128)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> Rc<HttpInnerMessage> {
|
||||
if let Some(msg) = self.0.borrow_mut().pop_front() {
|
||||
msg
|
||||
} else {
|
||||
Rc::new(HttpInnerMessage::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn release(&self, mut msg: Rc<HttpInnerMessage>) {
|
||||
let v = &mut self.0.borrow_mut();
|
||||
if v.len() < 128 {
|
||||
Rc::get_mut(&mut msg).unwrap().reset();
|
||||
v.push_front(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SharedHttpInnerMessage(
|
||||
Option<Rc<HttpInnerMessage>>,
|
||||
Option<Rc<SharedMessagePool>>,
|
||||
);
|
||||
|
||||
impl Drop for SharedHttpInnerMessage {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ref pool) = self.1 {
|
||||
if let Some(msg) = self.0.take() {
|
||||
if Rc::strong_count(&msg) == 1 {
|
||||
pool.release(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SharedHttpInnerMessage {
|
||||
fn clone(&self) -> SharedHttpInnerMessage {
|
||||
SharedHttpInnerMessage(self.0.clone(), self.1.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SharedHttpInnerMessage {
|
||||
fn default() -> SharedHttpInnerMessage {
|
||||
SharedHttpInnerMessage(Some(Rc::new(HttpInnerMessage::default())), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedHttpInnerMessage {
|
||||
pub fn from_message(msg: HttpInnerMessage) -> SharedHttpInnerMessage {
|
||||
SharedHttpInnerMessage(Some(Rc::new(msg)), None)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
msg: Rc<HttpInnerMessage>, pool: Rc<SharedMessagePool>,
|
||||
) -> SharedHttpInnerMessage {
|
||||
SharedHttpInnerMessage(Some(msg), Some(pool))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> &mut HttpInnerMessage {
|
||||
let r: &HttpInnerMessage = self.0.as_ref().unwrap().as_ref();
|
||||
unsafe { &mut *(r as *const _ as *mut _) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &HttpInnerMessage {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
|
||||
2021222324252627282930313233343536373839\
|
||||
4041424344454647484950515253545556575859\
|
||||
|
220
src/server/message.rs
Normal file
220
src/server/message.rs
Normal file
@ -0,0 +1,220 @@
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
use std::collections::VecDeque;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use http::{header, HeaderMap, Method, Uri, Version};
|
||||
|
||||
use extensions::Extensions;
|
||||
use httpmessage::HttpMessage;
|
||||
use info::ConnectionInfo;
|
||||
use payload::Payload;
|
||||
use server::ServerSettings;
|
||||
use uri::Url as InnerUrl;
|
||||
|
||||
bitflags! {
|
||||
pub(crate) struct MessageFlags: u8 {
|
||||
const KEEPALIVE = 0b0000_0001;
|
||||
const CONN_INFO = 0b0000_0010;
|
||||
}
|
||||
}
|
||||
|
||||
/// Request's context
|
||||
pub struct Request {
|
||||
pub(crate) inner: Box<InnerRequest>,
|
||||
}
|
||||
|
||||
pub(crate) struct InnerRequest {
|
||||
pub(crate) version: Version,
|
||||
pub(crate) method: Method,
|
||||
pub(crate) url: InnerUrl,
|
||||
pub(crate) flags: Cell<MessageFlags>,
|
||||
pub(crate) headers: HeaderMap,
|
||||
pub(crate) extensions: RefCell<Extensions>,
|
||||
pub(crate) addr: Option<SocketAddr>,
|
||||
pub(crate) info: RefCell<ConnectionInfo>,
|
||||
pub(crate) payload: RefCell<Option<Payload>>,
|
||||
pub(crate) settings: ServerSettings,
|
||||
}
|
||||
|
||||
impl HttpMessage for Request {
|
||||
type Stream = Payload;
|
||||
|
||||
fn headers(&self) -> &HeaderMap {
|
||||
&self.inner.headers
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn payload(&self) -> Payload {
|
||||
if let Some(payload) = self.inner.payload.borrow_mut().take() {
|
||||
payload
|
||||
} else {
|
||||
Payload::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Create new RequestContext instance
|
||||
pub fn new(settings: ServerSettings) -> Request {
|
||||
Request {
|
||||
inner: Box::new(InnerRequest {
|
||||
settings,
|
||||
method: Method::GET,
|
||||
url: InnerUrl::default(),
|
||||
version: Version::HTTP_11,
|
||||
headers: HeaderMap::with_capacity(16),
|
||||
flags: Cell::new(MessageFlags::empty()),
|
||||
addr: None,
|
||||
info: RefCell::new(ConnectionInfo::default()),
|
||||
payload: RefCell::new(None),
|
||||
extensions: RefCell::new(Extensions::new()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn url(&self) -> &InnerUrl {
|
||||
&self.inner.url
|
||||
}
|
||||
|
||||
/// Read the Request Uri.
|
||||
#[inline]
|
||||
pub fn uri(&self) -> &Uri {
|
||||
self.inner.url.uri()
|
||||
}
|
||||
|
||||
/// Read the Request method.
|
||||
#[inline]
|
||||
pub fn method(&self) -> &Method {
|
||||
&self.inner.method
|
||||
}
|
||||
|
||||
/// Read the Request Version.
|
||||
#[inline]
|
||||
pub fn version(&self) -> Version {
|
||||
self.inner.version
|
||||
}
|
||||
|
||||
/// The target path of this Request.
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
self.inner.url.path()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns Request's headers.
|
||||
pub fn headers(&self) -> &HeaderMap {
|
||||
&self.inner.headers
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns mutable Request's headers.
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||
&mut self.inner.headers
|
||||
}
|
||||
|
||||
/// Peer socket address
|
||||
///
|
||||
/// Peer address is actual socket address, if proxy is used in front of
|
||||
/// actix http server, then peer address would be address of this proxy.
|
||||
///
|
||||
/// To get client connection information `connection_info()` method should
|
||||
/// be used.
|
||||
pub fn peer_addr(&self) -> Option<SocketAddr> {
|
||||
self.inner.addr
|
||||
}
|
||||
|
||||
/// Checks if a connection should be kept alive.
|
||||
#[inline]
|
||||
pub fn keep_alive(&self) -> bool {
|
||||
self.inner.flags.get().contains(MessageFlags::KEEPALIVE)
|
||||
}
|
||||
|
||||
/// Request extensions
|
||||
#[inline]
|
||||
pub fn extensions(&self) -> Ref<Extensions> {
|
||||
self.inner.extensions.borrow()
|
||||
}
|
||||
|
||||
/// Mutable reference to a the request's extensions
|
||||
#[inline]
|
||||
pub fn extensions_mut(&self) -> RefMut<Extensions> {
|
||||
self.inner.extensions.borrow_mut()
|
||||
}
|
||||
|
||||
/// Check if request requires connection upgrade
|
||||
pub fn upgrade(&self) -> bool {
|
||||
if let Some(conn) = self.inner.headers.get(header::CONNECTION) {
|
||||
if let Ok(s) = conn.to_str() {
|
||||
return s.to_lowercase().contains("upgrade");
|
||||
}
|
||||
}
|
||||
self.inner.method == Method::CONNECT
|
||||
}
|
||||
|
||||
/// Get *ConnectionInfo* for the correct request.
|
||||
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
|
||||
if self.inner.flags.get().contains(MessageFlags::CONN_INFO) {
|
||||
self.inner.info.borrow()
|
||||
} else {
|
||||
let mut flags = self.inner.flags.get();
|
||||
flags.insert(MessageFlags::CONN_INFO);
|
||||
self.inner.flags.set(flags);
|
||||
self.inner.info.borrow_mut().update(self);
|
||||
self.inner.info.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
/// Server settings
|
||||
#[inline]
|
||||
pub fn server_settings(&self) -> &ServerSettings {
|
||||
&self.inner.settings
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.inner.headers.clear();
|
||||
self.inner.extensions.borrow_mut().clear();
|
||||
self.inner.flags.set(MessageFlags::empty());
|
||||
*self.inner.payload.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RequestPool(RefCell<VecDeque<Request>>, RefCell<ServerSettings>);
|
||||
|
||||
thread_local!(static POOL: &'static RequestPool = RequestPool::create());
|
||||
|
||||
impl RequestPool {
|
||||
fn create() -> &'static RequestPool {
|
||||
let pool = RequestPool(
|
||||
RefCell::new(VecDeque::with_capacity(128)),
|
||||
RefCell::new(ServerSettings::default()),
|
||||
);
|
||||
Box::leak(Box::new(pool))
|
||||
}
|
||||
|
||||
pub fn pool(settings: ServerSettings) -> &'static RequestPool {
|
||||
POOL.with(|p| {
|
||||
*p.1.borrow_mut() = settings;
|
||||
*p
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self) -> Request {
|
||||
if let Some(msg) = self.0.borrow_mut().pop_front() {
|
||||
msg
|
||||
} else {
|
||||
Request::new(self.1.borrow().clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn release(&self, mut msg: Request) {
|
||||
let v = &mut self.0.borrow_mut();
|
||||
if v.len() < 128 {
|
||||
msg.reset();
|
||||
v.push_front(msg);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_tcp::TcpStream;
|
||||
|
||||
mod channel;
|
||||
mod error;
|
||||
pub(crate) mod h1;
|
||||
pub(crate) mod h1decoder;
|
||||
mod h1writer;
|
||||
@ -15,11 +16,13 @@ mod h2;
|
||||
mod h2writer;
|
||||
pub(crate) mod helpers;
|
||||
pub(crate) mod input;
|
||||
pub(crate) mod message;
|
||||
pub(crate) mod output;
|
||||
pub(crate) mod settings;
|
||||
mod srv;
|
||||
mod worker;
|
||||
|
||||
pub use self::message::Request;
|
||||
pub use self::settings::ServerSettings;
|
||||
pub use self::srv::HttpServer;
|
||||
|
||||
@ -30,7 +33,7 @@ use actix::Message;
|
||||
use body::Binary;
|
||||
use error::Error;
|
||||
use header::ContentEncoding;
|
||||
use httprequest::{HttpInnerMessage, HttpRequest};
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
|
||||
/// max buffer size 64k
|
||||
@ -128,13 +131,13 @@ pub trait HttpHandler: 'static {
|
||||
type Task: HttpHandlerTask;
|
||||
|
||||
/// Handle request
|
||||
fn handle(&self, req: HttpRequest) -> Result<Self::Task, HttpRequest>;
|
||||
fn handle(&self, req: Request) -> Result<Self::Task, Request>;
|
||||
}
|
||||
|
||||
impl HttpHandler for Box<HttpHandler<Task = Box<HttpHandlerTask>>> {
|
||||
type Task = Box<HttpHandlerTask>;
|
||||
|
||||
fn handle(&self, req: HttpRequest) -> Result<Box<HttpHandlerTask>, HttpRequest> {
|
||||
fn handle(&self, req: Request) -> Result<Box<HttpHandlerTask>, Request> {
|
||||
self.as_ref().handle(req)
|
||||
}
|
||||
}
|
||||
@ -165,13 +168,13 @@ pub trait IntoHttpHandler {
|
||||
type Handler: HttpHandler;
|
||||
|
||||
/// Convert into `HttpHandler` object.
|
||||
fn into_handler(self, settings: ServerSettings) -> Self::Handler;
|
||||
fn into_handler(self) -> Self::Handler;
|
||||
}
|
||||
|
||||
impl<T: HttpHandler> IntoHttpHandler for T {
|
||||
type Handler = T;
|
||||
|
||||
fn into_handler(self, _: ServerSettings) -> Self::Handler {
|
||||
fn into_handler(self) -> Self::Handler {
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -190,14 +193,13 @@ pub trait Writer {
|
||||
fn written(&self) -> u64;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn set_date(&self, st: &mut BytesMut);
|
||||
fn set_date(&mut self);
|
||||
|
||||
#[doc(hidden)]
|
||||
fn buffer(&mut self) -> &mut BytesMut;
|
||||
|
||||
fn start(
|
||||
&mut self, req: &mut HttpInnerMessage, resp: &mut HttpResponse,
|
||||
encoding: ContentEncoding,
|
||||
&mut self, req: &Request, resp: &mut HttpResponse, encoding: ContentEncoding,
|
||||
) -> io::Result<WriterState>;
|
||||
|
||||
fn write(&mut self, payload: &Binary) -> io::Result<WriterState>;
|
||||
|
@ -15,11 +15,37 @@ use http::header::{
|
||||
};
|
||||
use http::{HttpTryFrom, Method, Version};
|
||||
|
||||
use super::message::{InnerRequest, Request};
|
||||
use body::{Binary, Body};
|
||||
use header::ContentEncoding;
|
||||
use httprequest::HttpInnerMessage;
|
||||
use httpresponse::HttpResponse;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ResponseLength {
|
||||
Chunked,
|
||||
Zero,
|
||||
Length(usize),
|
||||
Length64(u64),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ResponseInfo {
|
||||
head: bool,
|
||||
pub length: ResponseLength,
|
||||
pub content_encoding: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl ResponseInfo {
|
||||
pub fn new(head: bool) -> Self {
|
||||
ResponseInfo {
|
||||
head,
|
||||
length: ResponseLength::None,
|
||||
content_encoding: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Output {
|
||||
Empty(BytesMut),
|
||||
@ -119,13 +145,12 @@ impl Output {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_server(
|
||||
&mut self, req: &HttpInnerMessage, resp: &mut HttpResponse,
|
||||
pub(crate) fn for_server(
|
||||
&mut self, info: &mut ResponseInfo, req: &InnerRequest, resp: &mut HttpResponse,
|
||||
response_encoding: ContentEncoding,
|
||||
) {
|
||||
let buf = self.take();
|
||||
let version = resp.version().unwrap_or_else(|| req.version);
|
||||
let is_head = req.method == Method::HEAD;
|
||||
let mut len = 0;
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
|
||||
@ -158,10 +183,7 @@ impl Output {
|
||||
encoding => encoding,
|
||||
};
|
||||
if encoding.is_compression() {
|
||||
resp.headers_mut().insert(
|
||||
CONTENT_ENCODING,
|
||||
HeaderValue::from_static(encoding.as_str()),
|
||||
);
|
||||
info.content_encoding = Some(encoding.as_str());
|
||||
}
|
||||
encoding
|
||||
} else {
|
||||
@ -173,8 +195,8 @@ impl Output {
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
|
||||
let transfer = match resp.body() {
|
||||
&Body::Empty => {
|
||||
if req.method != Method::HEAD {
|
||||
resp.headers_mut().remove(CONTENT_LENGTH);
|
||||
if !info.head {
|
||||
info.length = ResponseLength::Zero;
|
||||
}
|
||||
*self = Output::Empty(buf);
|
||||
return;
|
||||
@ -216,13 +238,8 @@ impl Output {
|
||||
}
|
||||
}
|
||||
|
||||
if is_head {
|
||||
let mut b = BytesMut::new();
|
||||
let _ = write!(b, "{}", len);
|
||||
resp.headers_mut().insert(
|
||||
CONTENT_LENGTH,
|
||||
HeaderValue::try_from(b.freeze()).unwrap(),
|
||||
);
|
||||
info.length = ResponseLength::Length(len);
|
||||
if info.head {
|
||||
*self = Output::Empty(buf);
|
||||
} else {
|
||||
*self = Output::Buffer(buf);
|
||||
@ -236,7 +253,7 @@ impl Output {
|
||||
}
|
||||
if encoding != ContentEncoding::Identity {
|
||||
encoding = ContentEncoding::Identity;
|
||||
resp.headers_mut().remove(CONTENT_ENCODING);
|
||||
info.content_encoding.take();
|
||||
}
|
||||
TransferEncoding::eof(buf)
|
||||
} else {
|
||||
@ -245,12 +262,12 @@ impl Output {
|
||||
{
|
||||
resp.headers_mut().remove(CONTENT_LENGTH);
|
||||
}
|
||||
Output::streaming_encoding(buf, version, resp)
|
||||
Output::streaming_encoding(info, buf, version, resp)
|
||||
}
|
||||
}
|
||||
};
|
||||
// check for head response
|
||||
if is_head {
|
||||
if info.head {
|
||||
resp.set_body(Body::Empty);
|
||||
*self = Output::Empty(transfer.buf.unwrap());
|
||||
return;
|
||||
@ -277,18 +294,17 @@ impl Output {
|
||||
}
|
||||
|
||||
fn streaming_encoding(
|
||||
buf: BytesMut, version: Version, resp: &mut HttpResponse,
|
||||
info: &mut ResponseInfo, buf: BytesMut, version: Version,
|
||||
resp: &mut HttpResponse,
|
||||
) -> TransferEncoding {
|
||||
match resp.chunked() {
|
||||
Some(true) => {
|
||||
// Enable transfer encoding
|
||||
resp.headers_mut().remove(CONTENT_LENGTH);
|
||||
if version == Version::HTTP_2 {
|
||||
resp.headers_mut().remove(TRANSFER_ENCODING);
|
||||
info.length = ResponseLength::None;
|
||||
TransferEncoding::eof(buf)
|
||||
} else {
|
||||
resp.headers_mut()
|
||||
.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
|
||||
info.length = ResponseLength::Chunked;
|
||||
TransferEncoding::chunked(buf)
|
||||
}
|
||||
}
|
||||
@ -315,6 +331,7 @@ impl Output {
|
||||
|
||||
if !chunked {
|
||||
if let Some(len) = len {
|
||||
info.length = ResponseLength::Length64(len);
|
||||
TransferEncoding::length(len, buf)
|
||||
} else {
|
||||
TransferEncoding::eof(buf)
|
||||
@ -323,14 +340,11 @@ impl Output {
|
||||
// Enable transfer encoding
|
||||
match version {
|
||||
Version::HTTP_11 => {
|
||||
resp.headers_mut().insert(
|
||||
TRANSFER_ENCODING,
|
||||
HeaderValue::from_static("chunked"),
|
||||
);
|
||||
info.length = ResponseLength::Chunked;
|
||||
TransferEncoding::chunked(buf)
|
||||
}
|
||||
_ => {
|
||||
resp.headers_mut().remove(TRANSFER_ENCODING);
|
||||
info.length = ResponseLength::None;
|
||||
TransferEncoding::eof(buf)
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use time;
|
||||
|
||||
use super::channel::Node;
|
||||
use super::helpers;
|
||||
use super::message::{Request, RequestPool};
|
||||
use super::KeepAlive;
|
||||
use body::Body;
|
||||
use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool};
|
||||
@ -156,14 +157,17 @@ pub(crate) struct WorkerSettings<H> {
|
||||
keep_alive: u64,
|
||||
ka_enabled: bool,
|
||||
bytes: Rc<SharedBytesPool>,
|
||||
messages: Rc<helpers::SharedMessagePool>,
|
||||
messages: &'static RequestPool,
|
||||
channels: Cell<usize>,
|
||||
node: Box<Node<()>>,
|
||||
date: UnsafeCell<Date>,
|
||||
settings: ServerSettings,
|
||||
}
|
||||
|
||||
impl<H> WorkerSettings<H> {
|
||||
pub(crate) fn new(h: Vec<H>, keep_alive: KeepAlive) -> WorkerSettings<H> {
|
||||
pub(crate) fn new(
|
||||
h: Vec<H>, keep_alive: KeepAlive, settings: ServerSettings,
|
||||
) -> WorkerSettings<H> {
|
||||
let (keep_alive, ka_enabled) = match keep_alive {
|
||||
KeepAlive::Timeout(val) => (val as u64, true),
|
||||
KeepAlive::Os | KeepAlive::Tcp(_) => (0, true),
|
||||
@ -171,14 +175,15 @@ impl<H> WorkerSettings<H> {
|
||||
};
|
||||
|
||||
WorkerSettings {
|
||||
keep_alive,
|
||||
ka_enabled,
|
||||
h: RefCell::new(h),
|
||||
bytes: Rc::new(SharedBytesPool::new()),
|
||||
messages: Rc::new(helpers::SharedMessagePool::new()),
|
||||
messages: RequestPool::pool(settings.clone()),
|
||||
channels: Cell::new(0),
|
||||
node: Box::new(Node::head()),
|
||||
date: UnsafeCell::new(Date::new()),
|
||||
keep_alive,
|
||||
ka_enabled,
|
||||
settings,
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,11 +215,8 @@ impl<H> WorkerSettings<H> {
|
||||
self.bytes.release_bytes(bytes)
|
||||
}
|
||||
|
||||
pub fn get_http_message(&self) -> helpers::SharedHttpInnerMessage {
|
||||
helpers::SharedHttpInnerMessage::new(
|
||||
self.messages.get(),
|
||||
Rc::clone(&self.messages),
|
||||
)
|
||||
pub fn get_request_context(&self) -> Request {
|
||||
self.messages.get()
|
||||
}
|
||||
|
||||
pub fn add_channel(&self) {
|
||||
@ -316,7 +318,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_date() {
|
||||
let settings = WorkerSettings::<()>::new(Vec::new(), KeepAlive::Os);
|
||||
let settings = WorkerSettings::<()>::new(
|
||||
Vec::new(),
|
||||
KeepAlive::Os,
|
||||
ServerSettings::default(),
|
||||
);
|
||||
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
|
||||
settings.set_date(&mut buf1, true);
|
||||
let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
|
||||
|
@ -358,12 +358,10 @@ where
|
||||
|
||||
let addr = Arbiter::start(move |ctx: &mut Context<_>| {
|
||||
let s = ServerSettings::from_parts(parts);
|
||||
let apps: Vec<_> = (*factory)()
|
||||
.into_iter()
|
||||
.map(|h| h.into_handler(s.clone()))
|
||||
.collect();
|
||||
let apps: Vec<_> =
|
||||
(*factory)().into_iter().map(|h| h.into_handler()).collect();
|
||||
ctx.add_message_stream(rx);
|
||||
Worker::new(apps, socks, ka)
|
||||
Worker::new(apps, socks, ka, s)
|
||||
});
|
||||
workers.push((idx, tx));
|
||||
self.workers.push((idx, addr));
|
||||
@ -404,7 +402,7 @@ impl<H: IntoHttpHandler> HttpServer<H> {
|
||||
/// fn main() {
|
||||
/// let sys = actix::System::new("example"); // <- create Actix system
|
||||
///
|
||||
/// server::new(|| App::new().resource("/", |r| r.h(|_| HttpResponse::Ok())))
|
||||
/// server::new(|| App::new().resource("/", |r| r.h(|_: &_| HttpResponse::Ok())))
|
||||
/// .bind("127.0.0.1:0")
|
||||
/// .expect("Can not bind to 127.0.0.1:0")
|
||||
/// .start();
|
||||
@ -559,9 +557,13 @@ impl<H: IntoHttpHandler> HttpServer<H> {
|
||||
let settings = ServerSettings::new(Some(addr), &self.host, secure);
|
||||
let apps: Vec<_> = (*self.factory)()
|
||||
.into_iter()
|
||||
.map(|h| h.into_handler(settings.clone()))
|
||||
.map(|h| h.into_handler())
|
||||
.collect();
|
||||
self.h = Some(Rc::new(WorkerSettings::new(apps, self.keep_alive)));
|
||||
self.h = Some(Rc::new(WorkerSettings::new(
|
||||
apps,
|
||||
self.keep_alive,
|
||||
settings,
|
||||
)));
|
||||
|
||||
// start server
|
||||
let signals = self.subscribe_to_signals();
|
||||
@ -645,12 +647,10 @@ impl<H: IntoHttpHandler> StreamHandler2<ServerCommand, ()> for HttpServer<H> {
|
||||
|
||||
let addr = Arbiter::start(move |ctx: &mut Context<_>| {
|
||||
let settings = ServerSettings::new(Some(addr), &host, false);
|
||||
let apps: Vec<_> = (*factory)()
|
||||
.into_iter()
|
||||
.map(|h| h.into_handler(settings.clone()))
|
||||
.collect();
|
||||
let apps: Vec<_> =
|
||||
(*factory)().into_iter().map(|h| h.into_handler()).collect();
|
||||
ctx.add_message_stream(rx);
|
||||
Worker::new(apps, socks, ka)
|
||||
Worker::new(apps, socks, ka, settings)
|
||||
});
|
||||
for item in &self.accept {
|
||||
let _ = item.1.send(Command::Worker(new_idx, tx.clone()));
|
||||
|
@ -25,7 +25,7 @@ use actix::msgs::StopArbiter;
|
||||
use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message, Response};
|
||||
|
||||
use server::channel::HttpChannel;
|
||||
use server::settings::WorkerSettings;
|
||||
use server::settings::{ServerSettings, WorkerSettings};
|
||||
use server::{HttpHandler, KeepAlive};
|
||||
|
||||
#[derive(Message)]
|
||||
@ -68,6 +68,7 @@ where
|
||||
impl<H: HttpHandler + 'static> Worker<H> {
|
||||
pub(crate) fn new(
|
||||
h: Vec<H>, socks: Slab<SocketInfo>, keep_alive: KeepAlive,
|
||||
settings: ServerSettings,
|
||||
) -> Worker<H> {
|
||||
let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive {
|
||||
Some(time::Duration::new(val as u64, 0))
|
||||
@ -76,7 +77,7 @@ impl<H: HttpHandler + 'static> Worker<H> {
|
||||
};
|
||||
|
||||
Worker {
|
||||
settings: Rc::new(WorkerSettings::new(h, keep_alive)),
|
||||
settings: Rc::new(WorkerSettings::new(h, keep_alive, settings)),
|
||||
socks,
|
||||
tcp_ka,
|
||||
}
|
||||
|
72
src/test.rs
72
src/test.rs
@ -22,14 +22,15 @@ use client::{ClientConnector, ClientRequest, ClientRequestBuilder};
|
||||
use error::Error;
|
||||
use handler::{AsyncResultItem, Handler, Responder};
|
||||
use header::{Header, IntoHeaderValue};
|
||||
use httprequest::HttpRequest;
|
||||
use httprequest::{HttpRequest, RouterResource};
|
||||
use httpresponse::HttpResponse;
|
||||
use middleware::Middleware;
|
||||
use param::Params;
|
||||
use payload::Payload;
|
||||
use resource::ResourceHandler;
|
||||
use router::Router;
|
||||
use server::{HttpServer, IntoHttpHandler, ServerSettings};
|
||||
use server::{HttpServer, IntoHttpHandler, Request, ServerSettings};
|
||||
use uri::Url as InnerUrl;
|
||||
use ws;
|
||||
|
||||
/// The `TestServer` type.
|
||||
@ -43,7 +44,7 @@ use ws;
|
||||
/// # extern crate actix_web;
|
||||
/// # use actix_web::*;
|
||||
/// #
|
||||
/// # fn my_handler(req: HttpRequest) -> HttpResponse {
|
||||
/// # fn my_handler(req: &HttpRequest) -> HttpResponse {
|
||||
/// # HttpResponse::Ok().into()
|
||||
/// # }
|
||||
/// #
|
||||
@ -330,8 +331,12 @@ impl<S: 'static> TestApp<S> {
|
||||
}
|
||||
|
||||
/// Register handler for "/"
|
||||
pub fn handler<H: Handler<S>>(&mut self, handler: H) {
|
||||
self.app = Some(self.app.take().unwrap().resource("/", |r| r.h(handler)));
|
||||
pub fn handler<F, R>(&mut self, handler: F)
|
||||
where
|
||||
F: Fn(&HttpRequest<S>) -> R + 'static,
|
||||
R: Responder + 'static,
|
||||
{
|
||||
self.app = Some(self.app.take().unwrap().resource("/", |r| r.f(handler)));
|
||||
}
|
||||
|
||||
/// Register middleware
|
||||
@ -357,8 +362,8 @@ impl<S: 'static> TestApp<S> {
|
||||
impl<S: 'static> IntoHttpHandler for TestApp<S> {
|
||||
type Handler = HttpApplication<S>;
|
||||
|
||||
fn into_handler(mut self, settings: ServerSettings) -> HttpApplication<S> {
|
||||
self.app.take().unwrap().into_handler(settings)
|
||||
fn into_handler(mut self) -> HttpApplication<S> {
|
||||
self.app.take().unwrap().into_handler()
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,7 +389,7 @@ impl<S: 'static> Iterator for TestApp<S> {
|
||||
/// # use actix_web::*;
|
||||
/// use actix_web::test::TestRequest;
|
||||
///
|
||||
/// fn index(req: HttpRequest) -> HttpResponse {
|
||||
/// fn index(req: &HttpRequest) -> HttpResponse {
|
||||
/// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
|
||||
/// HttpResponse::Ok().into()
|
||||
/// } else {
|
||||
@ -533,11 +538,19 @@ impl<S: 'static> TestRequest<S> {
|
||||
cookies,
|
||||
payload,
|
||||
} = self;
|
||||
let mut req = HttpRequest::new(method, uri, version, headers, payload);
|
||||
let (router, _) = Router::new::<S>("/", Vec::new());
|
||||
|
||||
let mut req = Request::new(ServerSettings::default());
|
||||
req.inner.method = method;
|
||||
req.inner.url = InnerUrl::new(uri);
|
||||
req.inner.version = version;
|
||||
req.inner.headers = headers;
|
||||
*req.inner.payload.borrow_mut() = payload;
|
||||
|
||||
let mut req =
|
||||
HttpRequest::new(req, Rc::new(state), router.route_info_params(params, 0));
|
||||
req.set_cookies(cookies);
|
||||
req.as_mut().params = params;
|
||||
let (router, _) = Router::new::<S>("/", ServerSettings::default(), Vec::new());
|
||||
req.with_state(Rc::new(state), router)
|
||||
req
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -554,10 +567,37 @@ impl<S: 'static> TestRequest<S> {
|
||||
payload,
|
||||
} = self;
|
||||
|
||||
let mut req = HttpRequest::new(method, uri, version, headers, payload);
|
||||
let mut req = Request::new(ServerSettings::default());
|
||||
req.inner.method = method;
|
||||
req.inner.url = InnerUrl::new(uri);
|
||||
req.inner.version = version;
|
||||
req.inner.headers = headers;
|
||||
*req.inner.payload.borrow_mut() = payload;
|
||||
let mut req =
|
||||
HttpRequest::new(req, Rc::new(state), router.route_info_params(params, 0));
|
||||
req.set_cookies(cookies);
|
||||
req.as_mut().params = params;
|
||||
req.with_state(Rc::new(state), router)
|
||||
req
|
||||
}
|
||||
|
||||
/// Complete request creation and generate server `Request` instance
|
||||
pub fn request(self) -> Request {
|
||||
let TestRequest {
|
||||
state,
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers,
|
||||
params,
|
||||
cookies,
|
||||
payload,
|
||||
} = self;
|
||||
let mut req = Request::new(ServerSettings::default());
|
||||
req.inner.method = method;
|
||||
req.inner.url = InnerUrl::new(uri);
|
||||
req.inner.version = version;
|
||||
req.inner.headers = headers;
|
||||
*req.inner.payload.borrow_mut() = payload;
|
||||
req
|
||||
}
|
||||
|
||||
/// This method generates `HttpRequest` instance and runs handler
|
||||
@ -566,7 +606,7 @@ impl<S: 'static> TestRequest<S> {
|
||||
/// This method panics is handler returns actor or async result.
|
||||
pub fn run<H: Handler<S>>(self, h: &H) -> Result<HttpResponse, Error> {
|
||||
let req = self.finish();
|
||||
let resp = h.handle(req.clone());
|
||||
let resp = h.handle(&req);
|
||||
|
||||
match resp.respond_to(&req) {
|
||||
Ok(resp) => match resp.into().into() {
|
||||
|
@ -42,9 +42,9 @@ where
|
||||
{
|
||||
type Result = AsyncResult<HttpResponse>;
|
||||
|
||||
fn handle(&self, req: HttpRequest<S>) -> Self::Result {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
|
||||
let mut fut = WithHandlerFut {
|
||||
req,
|
||||
req: req.clone(),
|
||||
started: false,
|
||||
hnd: Rc::clone(&self.hnd),
|
||||
cfg: self.cfg.clone(),
|
||||
@ -167,9 +167,9 @@ where
|
||||
{
|
||||
type Result = AsyncResult<HttpResponse>;
|
||||
|
||||
fn handle(&self, req: HttpRequest<S>) -> Self::Result {
|
||||
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
|
||||
let mut fut = WithAsyncHandlerFut {
|
||||
req,
|
||||
req: req.clone(),
|
||||
started: false,
|
||||
hnd: Rc::clone(&self.hnd),
|
||||
cfg: Rc::clone(&self.cfg),
|
||||
|
@ -24,7 +24,7 @@ use payload::PayloadHelper;
|
||||
|
||||
use client::{
|
||||
ClientConnector, ClientRequest, ClientRequestBuilder, ClientResponse,
|
||||
HttpResponseParserError, SendRequest, SendRequestError,
|
||||
HttpResponseParserError, Pipeline, SendRequest, SendRequestError,
|
||||
};
|
||||
|
||||
use super::frame::Frame;
|
||||
@ -275,7 +275,7 @@ impl Client {
|
||||
|
||||
struct Inner {
|
||||
tx: UnboundedSender<Bytes>,
|
||||
rx: PayloadHelper<ClientResponse>,
|
||||
rx: PayloadHelper<Box<Pipeline>>,
|
||||
closed: bool,
|
||||
}
|
||||
|
||||
@ -431,7 +431,7 @@ impl Future for ClientHandshake {
|
||||
|
||||
let inner = Inner {
|
||||
tx: self.tx.take().unwrap(),
|
||||
rx: PayloadHelper::new(resp),
|
||||
rx: PayloadHelper::new(resp.payload()),
|
||||
closed: false,
|
||||
};
|
||||
|
||||
|
196
src/ws/mod.rs
196
src/ws/mod.rs
@ -13,7 +13,7 @@
|
||||
//! use actix_web::{ws, HttpRequest, HttpResponse};
|
||||
//!
|
||||
//! // do websocket handshake and start actor
|
||||
//! fn ws_index(req: HttpRequest) -> Result<HttpResponse> {
|
||||
//! fn ws_index(req: &HttpRequest) -> Result<HttpResponse> {
|
||||
//! ws::start(req, Ws)
|
||||
//! }
|
||||
//!
|
||||
@ -171,15 +171,15 @@ pub enum Message {
|
||||
}
|
||||
|
||||
/// Do websocket handshake and start actor
|
||||
pub fn start<A, S>(req: HttpRequest<S>, actor: A) -> Result<HttpResponse, Error>
|
||||
pub fn start<A, S>(req: &HttpRequest<S>, actor: A) -> Result<HttpResponse, Error>
|
||||
where
|
||||
A: Actor<Context = WebsocketContext<A, S>> + StreamHandler<Message, ProtocolError>,
|
||||
S: 'static,
|
||||
{
|
||||
let mut resp = handshake(&req)?;
|
||||
let stream = WsStream::new(req.clone());
|
||||
let mut resp = handshake(req)?;
|
||||
let stream = WsStream::new(req.payload());
|
||||
|
||||
let mut ctx = WebsocketContext::new(req, actor);
|
||||
let mut ctx = WebsocketContext::new(req.clone(), actor);
|
||||
ctx.add_stream(stream);
|
||||
|
||||
Ok(resp.body(ctx))
|
||||
@ -359,162 +359,116 @@ pub trait WsWriter {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::*;
|
||||
use http::{header, HeaderMap, Method, Uri, Version};
|
||||
use std::str::FromStr;
|
||||
use test::TestRequest;
|
||||
|
||||
#[test]
|
||||
fn test_handshake() {
|
||||
let req = HttpRequest::new(
|
||||
Method::POST,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default().method(Method::POST).finish();
|
||||
assert_eq!(
|
||||
HandshakeError::GetMethodRequired,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default().finish();
|
||||
assert_eq!(
|
||||
HandshakeError::NoWebsocketUpgrade,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(header::UPGRADE, header::HeaderValue::from_static("test"));
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(header::UPGRADE, header::HeaderValue::from_static("test"))
|
||||
.finish();
|
||||
assert_eq!(
|
||||
HandshakeError::NoWebsocketUpgrade,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
)
|
||||
.finish();
|
||||
assert_eq!(
|
||||
HandshakeError::NoConnectionUpgrade,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
);
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
)
|
||||
.header(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
)
|
||||
.finish();
|
||||
assert_eq!(
|
||||
HandshakeError::NoVersionHeader,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
);
|
||||
headers.insert(
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("5"),
|
||||
);
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
)
|
||||
.header(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
)
|
||||
.header(
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("5"),
|
||||
)
|
||||
.finish();
|
||||
assert_eq!(
|
||||
HandshakeError::UnsupportedVersion,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
);
|
||||
headers.insert(
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("13"),
|
||||
);
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
)
|
||||
.header(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
)
|
||||
.header(
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("13"),
|
||||
)
|
||||
.finish();
|
||||
assert_eq!(
|
||||
HandshakeError::BadWebsocketKey,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
);
|
||||
headers.insert(
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("13"),
|
||||
);
|
||||
headers.insert(
|
||||
header::SEC_WEBSOCKET_KEY,
|
||||
header::HeaderValue::from_static("13"),
|
||||
);
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
headers,
|
||||
None,
|
||||
);
|
||||
let req = TestRequest::default()
|
||||
.header(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
)
|
||||
.header(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
)
|
||||
.header(
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("13"),
|
||||
)
|
||||
.header(
|
||||
header::SEC_WEBSOCKET_KEY,
|
||||
header::HeaderValue::from_static("13"),
|
||||
)
|
||||
.finish();
|
||||
assert_eq!(
|
||||
StatusCode::SWITCHING_PROTOCOLS,
|
||||
handshake(&req).unwrap().finish().status()
|
||||
|
@ -67,7 +67,7 @@ fn test_simple() {
|
||||
#[test]
|
||||
fn test_with_query_parameter() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| match req.query().get("qp") {
|
||||
app.handler(|req: &HttpRequest| match req.query().get("qp") {
|
||||
Some(_) => HttpResponse::Ok().finish(),
|
||||
None => HttpResponse::BadRequest().finish(),
|
||||
})
|
||||
@ -110,7 +110,7 @@ fn test_no_decompress() {
|
||||
#[test]
|
||||
fn test_client_gzip_encoding() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -140,7 +140,7 @@ fn test_client_gzip_encoding_large() {
|
||||
let data = STR.repeat(10);
|
||||
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -173,7 +173,7 @@ fn test_client_gzip_encoding_large_random() {
|
||||
.collect::<String>();
|
||||
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -202,7 +202,7 @@ fn test_client_gzip_encoding_large_random() {
|
||||
#[test]
|
||||
fn test_client_brotli_encoding() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -236,7 +236,7 @@ fn test_client_brotli_encoding_large_random() {
|
||||
.collect::<String>();
|
||||
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(move |bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -266,7 +266,7 @@ fn test_client_brotli_encoding_large_random() {
|
||||
#[test]
|
||||
fn test_client_deflate_encoding() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -300,7 +300,7 @@ fn test_client_deflate_encoding_large_random() {
|
||||
.collect::<String>();
|
||||
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -328,7 +328,7 @@ fn test_client_deflate_encoding_large_random() {
|
||||
#[test]
|
||||
fn test_client_streaming_explicit() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.map_err(Error::from)
|
||||
.and_then(|body| {
|
||||
@ -393,7 +393,7 @@ fn test_client_cookie_handling() {
|
||||
let mut srv = test::TestServer::new(move |app| {
|
||||
let cookie1 = cookie1b.clone();
|
||||
let cookie2 = cookie2b.clone();
|
||||
app.handler(move |req: HttpRequest| {
|
||||
app.handler(move |req: &HttpRequest| {
|
||||
// Check cookies were sent correctly
|
||||
req.cookie("cookie1").ok_or_else(err)
|
||||
.and_then(|c1| if c1.value() == "value1" {
|
||||
|
@ -20,21 +20,21 @@ struct MiddlewareTest {
|
||||
}
|
||||
|
||||
impl<S> middleware::Middleware<S> for MiddlewareTest {
|
||||
fn start(&self, _: &mut HttpRequest<S>) -> Result<middleware::Started> {
|
||||
fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started> {
|
||||
self.start
|
||||
.store(self.start.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
Ok(middleware::Started::Done)
|
||||
}
|
||||
|
||||
fn response(
|
||||
&self, _: &mut HttpRequest<S>, resp: HttpResponse,
|
||||
&self, _: &HttpRequest<S>, resp: HttpResponse,
|
||||
) -> Result<middleware::Response> {
|
||||
self.response
|
||||
.store(self.response.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
Ok(middleware::Response::Done(resp))
|
||||
}
|
||||
|
||||
fn finish(&self, _: &mut HttpRequest<S>, _: &HttpResponse) -> middleware::Finished {
|
||||
fn finish(&self, _: &HttpRequest<S>, _: &HttpResponse) -> middleware::Finished {
|
||||
self.finish
|
||||
.store(self.finish.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
middleware::Finished::Done
|
||||
@ -331,7 +331,7 @@ fn test_scope_middleware_async_handler() {
|
||||
assert_eq!(num3.load(Ordering::Relaxed), 1);
|
||||
}
|
||||
|
||||
fn index_test_middleware_async_error(_: HttpRequest) -> FutureResponse<HttpResponse> {
|
||||
fn index_test_middleware_async_error(_: &HttpRequest) -> FutureResponse<HttpResponse> {
|
||||
future::result(Err(error::ErrorBadRequest("TEST"))).responder()
|
||||
}
|
||||
|
||||
@ -412,7 +412,7 @@ fn test_resource_middleware_async_error() {
|
||||
|
||||
App::new().resource("/test", move |r| {
|
||||
r.middleware(mw);
|
||||
r.h(index_test_middleware_async_error);
|
||||
r.f(index_test_middleware_async_error);
|
||||
})
|
||||
});
|
||||
|
||||
@ -432,7 +432,7 @@ struct MiddlewareAsyncTest {
|
||||
}
|
||||
|
||||
impl<S> middleware::Middleware<S> for MiddlewareAsyncTest {
|
||||
fn start(&self, _: &mut HttpRequest<S>) -> Result<middleware::Started> {
|
||||
fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started> {
|
||||
let to = Delay::new(Instant::now() + Duration::from_millis(10));
|
||||
|
||||
let start = Arc::clone(&self.start);
|
||||
@ -445,7 +445,7 @@ impl<S> middleware::Middleware<S> for MiddlewareAsyncTest {
|
||||
}
|
||||
|
||||
fn response(
|
||||
&self, _: &mut HttpRequest<S>, resp: HttpResponse,
|
||||
&self, _: &HttpRequest<S>, resp: HttpResponse,
|
||||
) -> Result<middleware::Response> {
|
||||
let to = Delay::new(Instant::now() + Duration::from_millis(10));
|
||||
|
||||
@ -458,7 +458,7 @@ impl<S> middleware::Middleware<S> for MiddlewareAsyncTest {
|
||||
)))
|
||||
}
|
||||
|
||||
fn finish(&self, _: &mut HttpRequest<S>, _: &HttpResponse) -> middleware::Finished {
|
||||
fn finish(&self, _: &HttpRequest<S>, _: &HttpResponse) -> middleware::Finished {
|
||||
let to = Delay::new(Instant::now() + Duration::from_millis(10));
|
||||
|
||||
let finish = Arc::clone(&self.finish);
|
||||
@ -697,7 +697,7 @@ fn test_async_resource_middleware() {
|
||||
};
|
||||
App::new().resource("/test", move |r| {
|
||||
r.middleware(mw);
|
||||
r.h(|_| HttpResponse::Ok());
|
||||
r.f(|_| HttpResponse::Ok());
|
||||
})
|
||||
});
|
||||
|
||||
@ -736,7 +736,7 @@ fn test_async_resource_middleware_multiple() {
|
||||
App::new().resource("/test", move |r| {
|
||||
r.middleware(mw1);
|
||||
r.middleware(mw2);
|
||||
r.h(|_| HttpResponse::Ok());
|
||||
r.f(|_| HttpResponse::Ok());
|
||||
})
|
||||
});
|
||||
|
||||
@ -775,7 +775,7 @@ fn test_async_sync_resource_middleware_multiple() {
|
||||
App::new().resource("/test", move |r| {
|
||||
r.middleware(mw1);
|
||||
r.middleware(mw2);
|
||||
r.h(|_| HttpResponse::Ok());
|
||||
r.f(|_| HttpResponse::Ok());
|
||||
})
|
||||
});
|
||||
|
||||
@ -793,7 +793,7 @@ fn test_async_sync_resource_middleware_multiple() {
|
||||
struct MiddlewareWithErr;
|
||||
|
||||
impl<S> middleware::Middleware<S> for MiddlewareWithErr {
|
||||
fn start(&self, _req: &mut HttpRequest<S>) -> Result<middleware::Started, Error> {
|
||||
fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started, Error> {
|
||||
Err(ErrorInternalServerError("middleware error"))
|
||||
}
|
||||
}
|
||||
@ -801,7 +801,7 @@ impl<S> middleware::Middleware<S> for MiddlewareWithErr {
|
||||
struct MiddlewareAsyncWithErr;
|
||||
|
||||
impl<S> middleware::Middleware<S> for MiddlewareAsyncWithErr {
|
||||
fn start(&self, _req: &mut HttpRequest<S>) -> Result<middleware::Started, Error> {
|
||||
fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started, Error> {
|
||||
Ok(middleware::Started::Future(Box::new(future::err(
|
||||
ErrorInternalServerError("middleware error"),
|
||||
))))
|
||||
@ -827,7 +827,7 @@ fn test_middleware_chain_with_error() {
|
||||
App::new()
|
||||
.middleware(mw1)
|
||||
.middleware(MiddlewareWithErr)
|
||||
.resource("/test", |r| r.h(|_| HttpResponse::Ok()))
|
||||
.resource("/test", |r| r.f(|_| HttpResponse::Ok()))
|
||||
});
|
||||
|
||||
let request = srv.get().uri(srv.url("/test")).finish().unwrap();
|
||||
@ -857,7 +857,7 @@ fn test_middleware_async_chain_with_error() {
|
||||
App::new()
|
||||
.middleware(mw1)
|
||||
.middleware(MiddlewareAsyncWithErr)
|
||||
.resource("/test", |r| r.h(|_| HttpResponse::Ok()))
|
||||
.resource("/test", |r| r.f(|_| HttpResponse::Ok()))
|
||||
});
|
||||
|
||||
let request = srv.get().uri(srv.url("/test")).finish().unwrap();
|
||||
@ -888,7 +888,7 @@ fn test_scope_middleware_chain_with_error() {
|
||||
scope
|
||||
.middleware(mw1)
|
||||
.middleware(MiddlewareWithErr)
|
||||
.resource("/test", |r| r.h(|_| HttpResponse::Ok()))
|
||||
.resource("/test", |r| r.f(|_| HttpResponse::Ok()))
|
||||
})
|
||||
});
|
||||
|
||||
@ -920,7 +920,7 @@ fn test_scope_middleware_async_chain_with_error() {
|
||||
scope
|
||||
.middleware(mw1)
|
||||
.middleware(MiddlewareAsyncWithErr)
|
||||
.resource("/test", |r| r.h(|_| HttpResponse::Ok()))
|
||||
.resource("/test", |r| r.f(|_| HttpResponse::Ok()))
|
||||
})
|
||||
});
|
||||
|
||||
@ -951,7 +951,7 @@ fn test_resource_middleware_chain_with_error() {
|
||||
App::new().resource("/test", move |r| {
|
||||
r.middleware(mw1);
|
||||
r.middleware(MiddlewareWithErr);
|
||||
r.h(|_| HttpResponse::Ok());
|
||||
r.f(|_| HttpResponse::Ok());
|
||||
})
|
||||
});
|
||||
|
||||
@ -982,7 +982,7 @@ fn test_resource_middleware_async_chain_with_error() {
|
||||
App::new().resource("/test", move |r| {
|
||||
r.middleware(mw1);
|
||||
r.middleware(MiddlewareAsyncWithErr);
|
||||
r.h(|_| HttpResponse::Ok());
|
||||
r.f(|_| HttpResponse::Ok());
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -368,8 +368,8 @@ fn test_head_empty() {
|
||||
}
|
||||
|
||||
// read response
|
||||
//let bytes = srv.execute(response.body()).unwrap();
|
||||
//assert!(bytes.is_empty());
|
||||
// let bytes = srv.execute(response.body()).unwrap();
|
||||
// assert!(bytes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -524,7 +524,7 @@ fn test_body_brotli() {
|
||||
#[test]
|
||||
fn test_gzip_encoding() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -557,7 +557,7 @@ fn test_gzip_encoding() {
|
||||
fn test_gzip_encoding_large() {
|
||||
let data = STR.repeat(10);
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -594,7 +594,7 @@ fn test_reading_gzip_encoding_large_random() {
|
||||
.collect::<String>();
|
||||
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -627,7 +627,7 @@ fn test_reading_gzip_encoding_large_random() {
|
||||
#[test]
|
||||
fn test_reading_deflate_encoding() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -660,7 +660,7 @@ fn test_reading_deflate_encoding() {
|
||||
fn test_reading_deflate_encoding_large() {
|
||||
let data = STR.repeat(10);
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -697,7 +697,7 @@ fn test_reading_deflate_encoding_large_random() {
|
||||
.collect::<String>();
|
||||
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -731,7 +731,7 @@ fn test_reading_deflate_encoding_large_random() {
|
||||
#[test]
|
||||
fn test_brotli_encoding() {
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
@ -765,7 +765,7 @@ fn test_brotli_encoding() {
|
||||
fn test_brotli_encoding_large() {
|
||||
let data = STR.repeat(10);
|
||||
let mut srv = test::TestServer::new(|app| {
|
||||
app.handler(|req: HttpRequest| {
|
||||
app.handler(|req: &HttpRequest| {
|
||||
req.body()
|
||||
.and_then(|bytes: Bytes| {
|
||||
Ok(HttpResponse::Ok()
|
||||
|
Loading…
Reference in New Issue
Block a user