1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-30 18:34:36 +01:00

Merge pull request #348 from actix/request-mutability

Request mutability
This commit is contained in:
Nikolay Kim 2018-07-02 23:52:42 +06:00 committed by GitHub
commit b6d26c9faf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 2239 additions and 2156 deletions

View File

@ -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")));

View File

@ -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;

View File

@ -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)
}
}

View File

@ -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::*;

View File

@ -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),

View File

@ -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,

View File

@ -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"
);
}

View File

@ -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)),

View File

@ -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,14 +86,13 @@ 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() {
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 router.has_route(p.as_ref()) {
if req.route().has_route(p.as_ref()) {
let p = if !query.is_empty() {
p + "?" + query
} else {
@ -106,7 +105,7 @@ impl<S> Handler<S> for NormalizePath {
// merge slashes and append trailing slash
if self.append && !p.ends_with('/') {
let p = p.as_ref().to_owned() + "/";
if router.has_route(&p) {
if req.route().has_route(&p) {
let p = if !query.is_empty() {
p + "?" + query
} else {
@ -121,7 +120,7 @@ impl<S> Handler<S> for NormalizePath {
// 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(
@ -136,7 +135,7 @@ impl<S> Handler<S> for NormalizePath {
} else if p.ends_with('/') {
// try to remove trailing slash
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(
@ -152,7 +151,7 @@ impl<S> Handler<S> for NormalizePath {
// append trailing slash
if self.append && !req.path().ends_with('/') {
let p = req.path().to_owned() + "/";
if router.has_route(&p) {
if req.route().has_route(&p) {
let p = if !query.is_empty() {
p + "?" + query
} else {
@ -163,7 +162,6 @@ impl<S> Handler<S> for NormalizePath {
.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!(

View File

@ -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,35 +432,47 @@ 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 let Some(ref mut fut) = self.fut {
return fut.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);
}
} else {
return Err(PayloadError::UnknownLength);
}
} else {
return Err(PayloadError::UnknownLength);
}
}
// future
let limit = self.limit;
self.fut = Some(Box::new(
req.from_err()
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)
@ -448,29 +483,63 @@ where
})
.map(|body| body.freeze()),
));
}
self.fut
.as_mut()
.expect("UrlEncoded could not be used second time")
.poll()
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,37 +552,35 @@ 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 {
if let Some(ref mut fut) = self.fut {
return fut.poll();
}
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);
}
} 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
let encoding = self.encoding;
let fut = self
.stream
.take()
.expect("UrlEncoded could not be used second time")
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
@ -524,8 +591,7 @@ where
}
})
.and_then(move |body| {
let enc: *const Encoding = encoding as *const Encoding;
if enc == UTF_8 {
if (encoding as *const Encoding) == UTF_8 {
serde_urlencoded::from_bytes::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
} else {
@ -537,12 +603,7 @@ where
}
});
self.fut = Some(Box::new(fut));
}
self.fut
.as_mut()
.expect("UrlEncoded could not be used second time")
.poll()
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(
let req = TestRequest::default()
.header(
header::TRANSFER_ENCODING,
header::HeaderValue::from_shared(hdr).unwrap(),
);
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
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(
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.",
));
let mut r = Readlines::new(req);
))
.finish();
let mut r = Readlines::new(&req);
match r.poll().ok().unwrap() {
Async::Ready(Some(s)) => assert_eq!(
s,

View File

@ -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,
}
}
}
impl HttpInnerMessage {
/// Checks if a connection should be kept alive.
#[inline]
pub fn keep_alive(&self) -> bool {
self.flags.contains(MessageFlags::KEEPALIVE)
}
#[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))
}
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.as_ref().headers
self.req.headers()
}
#[inline]
fn payload(&self) -> Payload {
if let Some(payload) = self.req.inner.payload.borrow_mut().take() {
payload
} else {
Payload::empty()
}
}
}
impl<S> Deref for HttpRequest<S> {
type Target = Request;
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())
/// 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(),
}
/// 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()
}
/// 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(),

View File

@ -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
req.request()
.server_settings()
.get_response_builder(StatusCode::OK)
} else {
HttpResponse::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);

View File

@ -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,21 +67,17 @@ 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() {
if scheme.is_none() && req.server_settings().secure() {
scheme = Some("https")
}
}
}
}
}
// host
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(
let req = TestRequest::default()
.header(
header::FORWARDED,
HeaderValue::from_static(
"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");
}
}

View File

@ -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,39 +295,30 @@ 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);
if let Some(ref mut fut) = self.fut {
return fut.poll();
}
} 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);
if let Some(err) = self.err.take() {
return Err(err);
}
let limit = self.limit;
let fut = req
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")
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
@ -310,12 +330,7 @@ where
})
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?));
self.fut = Some(Box::new(fut));
}
self.fut
.as_mut()
.expect("JsonBody could not be used second time")
.poll()
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(
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(
let req = TestRequest::default()
.header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
);
req.headers_mut().insert(
)
.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(
let req = TestRequest::default()
.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\"}"));
)
.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())
}
}

View File

@ -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

View File

@ -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()

View File

@ -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());
}
}

View File

@ -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!(),
};

View File

@ -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"))
}
}

View File

@ -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,13 +180,9 @@ 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 {
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)
@ -194,11 +192,9 @@ impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
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,

View File

@ -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();

View File

@ -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
}
}

View File

@ -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());

View File

@ -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 {

View File

@ -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
}

View File

@ -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,11 +488,8 @@ 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,
) {
let result =
match io.start(&info.req, &mut self.resp, encoding) {
Ok(res) => res,
Err(err) => {
info.error = Some(err.into());
@ -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);

View File

@ -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(
let req = TestRequest::default()
.header(
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,
);
)
.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()));
}
}

View File

@ -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))
}
}
}

View File

@ -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 {

View File

@ -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 &params.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,12 +348,9 @@ 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 = {
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() {
@ -271,9 +363,15 @@ impl Resource {
};
match self.tp {
PatternType::Static(ref s) => return s == path,
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 {
@ -281,50 +379,49 @@ impl Resource {
passed = true;
continue;
}
segments.push(ParamItem::UrlSegment(
params.add(
names[idx].clone(),
ParamItem::UrlSegment(
(plen + m.start()) as u16,
(plen + m.end()) as u16,
));
),
);
idx += 1;
}
}
names
params.set_tail(req.path().len() as u16);
Some(params)
} else {
return false;
None
}
}
PatternType::Prefix(ref s) => return path.starts_with(s),
PatternType::Prefix(ref s) => if !path.starts_with(s) {
None
} else {
Some(Params::with_url(req.url()))
},
}
};
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);
}
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();
let (names, tail_len) = {
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 };
match self.tp {
PatternType::Static(ref s) => if s == path {
return Some(s.len());
Some(Params::with_url(req.url()))
} else {
return None;
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 {
@ -332,48 +429,47 @@ impl Resource {
continue;
}
segments.push(ParamItem::UrlSegment(
params.add(
names[idx].clone(),
ParamItem::UrlSegment(
(plen + m.start()) as u16,
(plen + m.end()) as u16,
));
),
);
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())
}
params.set_tail((plen + pos + len) as u16);
Some(params)
} else {
None
}
}
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 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);
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");
}
}

View File

@ -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,76 +335,62 @@ 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 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 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))
}
}
};
return resource.handle(id, &req2);
} else {
return AsyncResult::async(Box::new(Compose::new(
req,
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 let Some(ref resource) = self.default {
if let Some(id) = resource.get_route_id(req) {
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)),
}
return resource.handle(id, req);
} else {
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
}
} else if let Some(ref default) = self.default {
AsyncResult::async(Box::new(Compose::new(
req,
return AsyncResult::async(Box::new(Compose::new(
id,
req.clone(),
Rc::clone(&self.middlewares),
Rc::clone(default),
)))
} else {
unimplemented!()
Rc::clone(resource),
)));
}
}
}
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
}
fn has_default_resource(&self) -> bool {
self.default.is_some()
@ -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
View 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))
}
}

View File

@ -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();

View File

@ -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 {

View File

@ -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();

View File

@ -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)),

View File

@ -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);

View File

@ -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
View 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);
}
}
}

View File

@ -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>;

View File

@ -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)
}
}

View File

@ -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);

View File

@ -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()));

View File

@ -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,
}

View File

@ -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() {

View File

@ -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),

View File

@ -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,
};

View File

@ -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(
let req = TestRequest::default()
.header(
header::UPGRADE,
header::HeaderValue::from_static("websocket"),
);
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
)
.finish();
assert_eq!(
HandshakeError::NoConnectionUpgrade,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new();
headers.insert(
let req = TestRequest::default()
.header(
header::UPGRADE,
header::HeaderValue::from_static("websocket"),
);
headers.insert(
)
.header(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
)
.finish();
assert_eq!(
HandshakeError::NoVersionHeader,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new();
headers.insert(
let req = TestRequest::default()
.header(
header::UPGRADE,
header::HeaderValue::from_static("websocket"),
);
headers.insert(
)
.header(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
headers.insert(
)
.header(
header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("5"),
);
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
)
.finish();
assert_eq!(
HandshakeError::UnsupportedVersion,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new();
headers.insert(
let req = TestRequest::default()
.header(
header::UPGRADE,
header::HeaderValue::from_static("websocket"),
);
headers.insert(
)
.header(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
headers.insert(
)
.header(
header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"),
);
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
)
.finish();
assert_eq!(
HandshakeError::BadWebsocketKey,
handshake(&req).err().unwrap()
);
let mut headers = HeaderMap::new();
headers.insert(
let req = TestRequest::default()
.header(
header::UPGRADE,
header::HeaderValue::from_static("websocket"),
);
headers.insert(
)
.header(
header::CONNECTION,
header::HeaderValue::from_static("upgrade"),
);
headers.insert(
)
.header(
header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"),
);
headers.insert(
)
.header(
header::SEC_WEBSOCKET_KEY,
header::HeaderValue::from_static("13"),
);
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
)
.finish();
assert_eq!(
StatusCode::SWITCHING_PROTOCOLS,
handshake(&req).unwrap().finish().status()

View File

@ -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" {

View File

@ -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());
})
});

View File

@ -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()