diff --git a/README.md b/README.md index e91c3be36..b4c3773f3 100644 --- a/README.md +++ b/README.md @@ -83,14 +83,14 @@ impl Actor for MyWebSocket { impl Route for MyWebSocket { type State = (); - fn request(req: &mut HttpRequest, ctx: &mut HttpContext) -> RouteResult + fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult { // websocket handshake - let resp = ws::handshake(req)?; + let resp = ws::handshake(&req)?; // send HttpResponse back to peer ctx.start(resp); // convert bytes stream to a stream of `ws::Message` and handle stream - ctx.add_stream(ws::WsStream::new(req)); + ctx.add_stream(ws::WsStream::new(&mut req)); Reply::async(MyWebSocket) } } diff --git a/examples/basic.rs b/examples/basic.rs index b79cd3f3e..f91048371 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -12,7 +12,7 @@ use actix_web::middlewares::RequestSession; use futures::stream::{once, Once}; /// somple handle -fn index(req: &mut HttpRequest, state: &()) -> Result { +fn index(mut req: HttpRequest, state: &()) -> Result { println!("{:?}", req); if let Ok(ch) = req.payload_mut().readany() { if let futures::Async::Ready(Some(d)) = ch { @@ -32,7 +32,7 @@ fn index(req: &mut HttpRequest, state: &()) -> Result { } /// somple handle -fn index_async(req: &mut HttpRequest, state: &()) -> Once +fn index_async(req: HttpRequest, state: &()) -> Once { println!("{:?}", req); @@ -44,7 +44,7 @@ fn index_async(req: &mut HttpRequest, state: &()) -> Once Result +fn with_param(req: HttpRequest, state: &()) -> Result { println!("{:?}", req); diff --git a/examples/state.rs b/examples/state.rs index debd7f785..22550a568 100644 --- a/examples/state.rs +++ b/examples/state.rs @@ -15,7 +15,7 @@ struct AppState { } /// somple handle -fn index(req: &mut HttpRequest, state: &AppState) -> HttpResponse { +fn index(req: HttpRequest, state: &AppState) -> HttpResponse { println!("{:?}", req); state.counter.set(state.counter.get() + 1); httpcodes::HTTPOk.with_body( @@ -36,11 +36,11 @@ impl Route for MyWebSocket { /// Shared application state type State = AppState; - fn request(req: &mut HttpRequest, ctx: &mut HttpContext) -> RouteResult + fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult { - let resp = ws::handshake(req)?; + let resp = ws::handshake(&req)?; ctx.start(resp); - ctx.add_stream(ws::WsStream::new(req)); + ctx.add_stream(ws::WsStream::new(&mut req)); Reply::async(MyWebSocket{counter: 0}) } } diff --git a/examples/websocket.rs b/examples/websocket.rs index 70e40cd6d..59e363b8e 100644 --- a/examples/websocket.rs +++ b/examples/websocket.rs @@ -22,14 +22,14 @@ impl Actor for MyWebSocket { impl Route for MyWebSocket { type State = (); - fn request(req: &mut HttpRequest, ctx: &mut HttpContext) -> RouteResult + fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult { // websocket handshake - let resp = ws::handshake(req)?; + let resp = ws::handshake(&req)?; // send HttpResponse back to peer ctx.start(resp); // convert bytes stream to a stream of `ws::Message` and register it - ctx.add_stream(ws::WsStream::new(req)); + ctx.add_stream(ws::WsStream::new(&mut req)); Reply::async(MyWebSocket) } } diff --git a/src/application.rs b/src/application.rs index 30e976be9..399cac8dc 100644 --- a/src/application.rs +++ b/src/application.rs @@ -24,7 +24,7 @@ pub struct Application { impl Application { - fn run(&self, req: &mut HttpRequest) -> Task { + fn run(&self, mut req: HttpRequest) -> Task { if let Some((params, h)) = self.router.recognize(req.path()) { if let Some(params) = params { req.set_match_info(params); @@ -48,8 +48,7 @@ impl HttpHandler for Application { } fn handle(&self, req: HttpRequest) -> Pipeline { - Pipeline::new(req, Rc::clone(&self.middlewares), - &|req: &mut HttpRequest| {self.run(req)}) + Pipeline::new(req, Rc::clone(&self.middlewares), &|req: HttpRequest| {self.run(req)}) } } @@ -139,7 +138,7 @@ impl ApplicationBuilder where S: 'static { /// impl Route for MyRoute { /// type State = (); /// - /// fn request(req: &mut HttpRequest, ctx: &mut HttpContext) -> RouteResult { + /// fn request(req: HttpRequest, ctx: &mut HttpContext) -> RouteResult { /// Reply::reply(httpcodes::HTTPOk) /// } /// } @@ -202,7 +201,7 @@ impl ApplicationBuilder where S: 'static { /// } /// ``` pub fn handler(&mut self, path: P, handler: F) -> &mut Self - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Into + 'static, P: Into, { diff --git a/src/httpcodes.rs b/src/httpcodes.rs index 3e8f5c831..bb5905aaf 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -70,7 +70,7 @@ impl StaticResponse { } impl RouteHandler for StaticResponse { - fn handle(&self, _: &mut HttpRequest, _: Rc) -> Task { + fn handle(&self, _: HttpRequest, _: Rc) -> Task { Task::reply(HttpResponse::new(self.0, Body::Empty)) } } diff --git a/src/httprequest.rs b/src/httprequest.rs index 8a408694e..75db9b939 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -1,5 +1,6 @@ //! HTTP Request message related code. use std::{str, fmt, mem}; +use std::rc::Rc; use std::net::SocketAddr; use std::collections::HashMap; use bytes::BytesMut; @@ -13,45 +14,24 @@ use payload::Payload; use multipart::Multipart; use error::{ParseError, PayloadError, MultipartError, CookieParseError, HttpRangeError}; - -/// An HTTP Request -pub struct HttpRequest { +struct HttpMessage { version: Version, method: Method, path: String, query: String, headers: HeaderMap, + extensions: Extensions, params: Params, cookies: Vec>, cookies_loaded: bool, - extensions: Extensions, addr: Option, payload: Payload, } -impl HttpRequest { - /// Construct a new Request. - #[inline] - pub fn new(method: Method, path: String, - version: Version, headers: HeaderMap, query: String, payload: Payload) -> Self - { - HttpRequest { - method: method, - path: path, - query: query, - version: version, - headers: headers, - params: Params::empty(), - cookies: Vec::new(), - cookies_loaded: false, - extensions: Extensions::new(), - addr: None, - payload: payload, - } - } +impl Default for HttpMessage { - pub(crate) fn for_error() -> HttpRequest { - HttpRequest { + fn default() -> HttpMessage { + HttpMessage { method: Method::GET, path: String::new(), query: String::new(), @@ -60,38 +40,75 @@ impl HttpRequest { params: Params::empty(), cookies: Vec::new(), cookies_loaded: false, - extensions: Extensions::new(), addr: None, payload: Payload::empty(), + extensions: Extensions::new(), } } +} + +/// An HTTP Request +pub struct HttpRequest(Rc); + +impl HttpRequest { + /// Construct a new Request. + #[inline] + pub fn new(method: Method, path: String, version: Version, + headers: HeaderMap, query: String, payload: Payload) -> HttpRequest + { + HttpRequest( + Rc::new(HttpMessage { + method: method, + path: path, + query: query, + version: version, + headers: headers, + params: Params::empty(), + cookies: Vec::new(), + cookies_loaded: false, + addr: None, + payload: payload, + extensions: Extensions::new(), + }) + ) + } + + pub(crate) fn for_error() -> HttpRequest { + HttpRequest(Rc::new(HttpMessage::default())) + } + + fn as_mut(&mut self) -> &mut HttpMessage { + let r: &HttpMessage = self.0.as_ref(); + #[allow(mutable_transmutes)] + unsafe{mem::transmute(r)} + } /// Protocol extensions. #[inline] pub fn extensions(&mut self) -> &mut Extensions { - &mut self.extensions + &mut self.as_mut().extensions } /// Read the Request method. #[inline] - pub fn method(&self) -> &Method { &self.method } + pub fn method(&self) -> &Method { &self.0.method } /// Read the Request Version. #[inline] pub fn version(&self) -> Version { - self.version + self.0.version } /// Read the Request Headers. #[inline] pub fn headers(&self) -> &HeaderMap { - &self.headers + &self.0.headers } /// The target path of this Request. #[inline] pub fn path(&self) -> &str { - &self.path + &self.0.path } /// Remote IP of client initiated HTTP request. @@ -103,18 +120,18 @@ impl HttpRequest { /// - peername of opened socket #[inline] pub fn remote(&self) -> Option<&SocketAddr> { - self.addr.as_ref() + self.0.addr.as_ref() } pub(crate) fn set_remove_addr(&mut self, addr: Option) { - self.addr = addr + self.as_mut().addr = addr } /// Return a new iterator that yields pairs of `Cow` for query parameters #[inline] pub fn query(&self) -> HashMap { let mut q: HashMap = HashMap::new(); - for (key, val) in form_urlencoded::parse(self.query.as_ref()) { + for (key, val) in form_urlencoded::parse(self.0.query.as_ref()) { q.insert(key.to_string(), val.to_string()); } q @@ -125,17 +142,17 @@ impl HttpRequest { /// E.g., id=10 #[inline] pub fn query_string(&self) -> &str { - &self.query + &self.0.query } /// Return request cookies. pub fn cookies(&self) -> &Vec> { - &self.cookies + &self.0.cookies } /// Return request cookie. pub fn cookie(&self, name: &str) -> Option<&Cookie> { - for cookie in &self.cookies { + for cookie in &self.0.cookies { if cookie.name() == name { return Some(cookie) } @@ -146,17 +163,18 @@ impl HttpRequest { /// Load cookies pub fn load_cookies(&mut self) -> Result<&Vec>, CookieParseError> { - if !self.cookies_loaded { - self.cookies_loaded = true; - if let Some(val) = self.headers.get(header::COOKIE) { + if !self.0.cookies_loaded { + let msg = self.as_mut(); + msg.cookies_loaded = true; + if let Some(val) = msg.headers.get(header::COOKIE) { let s = str::from_utf8(val.as_bytes()) .map_err(CookieParseError::from)?; for cookie in s.split("; ") { - self.cookies.push(Cookie::parse_encoded(cookie)?.into_owned()); + msg.cookies.push(Cookie::parse_encoded(cookie)?.into_owned()); } } } - Ok(&self.cookies) + Ok(&self.0.cookies) } /// Get a reference to the Params object. @@ -164,34 +182,34 @@ impl HttpRequest { /// Route supports glob patterns: * for a single wildcard segment and :param /// for matching storing that segment of the request url in the Params object. #[inline] - pub fn match_info(&self) -> &Params { &self.params } + pub fn match_info(&self) -> &Params { &self.0.params } /// Set request Params. pub fn set_match_info(&mut self, params: Params) { - self.params = params; + self.as_mut().params = params; } /// Checks if a connection should be kept alive. pub fn keep_alive(&self) -> bool { - if let Some(conn) = self.headers.get(header::CONNECTION) { + if let Some(conn) = self.0.headers.get(header::CONNECTION) { if let Ok(conn) = conn.to_str() { - if self.version == Version::HTTP_10 && conn.contains("keep-alive") { + if self.0.version == Version::HTTP_10 && conn.contains("keep-alive") { true } else { - self.version == Version::HTTP_11 && + self.0.version == Version::HTTP_11 && !(conn.contains("close") || conn.contains("upgrade")) } } else { false } } else { - self.version != Version::HTTP_10 + self.0.version != Version::HTTP_10 } } /// Read the request content type pub fn content_type(&self) -> &str { - if let Some(content_type) = self.headers.get(header::CONTENT_TYPE) { + if let Some(content_type) = self.0.headers.get(header::CONTENT_TYPE) { if let Ok(content_type) = content_type.to_str() { return content_type } @@ -201,17 +219,17 @@ impl HttpRequest { /// Check if request requires connection upgrade pub(crate) fn upgrade(&self) -> bool { - if let Some(conn) = self.headers.get(header::CONNECTION) { + if let Some(conn) = self.0.headers.get(header::CONNECTION) { if let Ok(s) = conn.to_str() { return s.to_lowercase().contains("upgrade") } } - self.method == Method::CONNECT + self.0.method == Method::CONNECT } /// Check if request has chunked transfer encoding pub fn chunked(&self) -> Result { - if let Some(encodings) = self.headers.get(header::TRANSFER_ENCODING) { + if let Some(encodings) = self.0.headers.get(header::TRANSFER_ENCODING) { if let Ok(s) = encodings.to_str() { Ok(s.to_lowercase().contains("chunked")) } else { @@ -225,7 +243,7 @@ impl HttpRequest { /// Parses Range HTTP header string as per RFC 2616. /// `size` is full size of response (file). pub fn range(&self, size: u64) -> Result, HttpRangeError> { - if let Some(range) = self.headers().get(header::RANGE) { + if let Some(range) = self.0.headers.get(header::RANGE) { HttpRange::parse(unsafe{str::from_utf8_unchecked(range.as_bytes())}, size) .map_err(|e| e.into()) } else { @@ -236,25 +254,25 @@ impl HttpRequest { /// Returns reference to the associated http payload. #[inline] pub fn payload(&self) -> &Payload { - &self.payload + &self.0.payload } /// Returns mutable reference to the associated http payload. #[inline] pub fn payload_mut(&mut self) -> &mut Payload { - &mut self.payload + &mut self.as_mut().payload } /// Return payload pub fn take_payload(&mut self) -> Payload { - mem::replace(&mut self.payload, Payload::empty()) + mem::replace(&mut self.as_mut().payload, Payload::empty()) } /// Return stream to process BODY as multipart. /// /// Content-type: multipart/form-data; pub fn multipart(&self, payload: Payload) -> Result { - Ok(Multipart::new(Multipart::boundary(&self.headers)?, payload)) + Ok(Multipart::new(Multipart::boundary(&self.0.headers)?, payload)) } /// Parse `application/x-www-form-urlencoded` encoded body. @@ -287,7 +305,7 @@ impl HttpRequest { } } - if let Some(content_type) = self.headers().get(header::CONTENT_TYPE) { + if let Some(content_type) = self.0.headers.get(header::CONTENT_TYPE) { if let Ok(content_type) = content_type.to_str() { if content_type.to_lowercase() == "application/x-www-form-urlencoded" { return Ok(UrlEncoded{pl: payload, body: BytesMut::new()}) @@ -299,19 +317,25 @@ impl HttpRequest { } } +impl Clone for HttpRequest { + fn clone(&self) -> HttpRequest { + HttpRequest(Rc::clone(&self.0)) + } +} + impl fmt::Debug for HttpRequest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let res = write!(f, "\nHttpRequest {:?} {}:{}\n", - self.version, self.method, self.path); + self.0.version, self.0.method, self.0.path); if !self.query_string().is_empty() { let _ = write!(f, " query: ?{:?}\n", self.query_string()); } - if !self.params.is_empty() { - let _ = write!(f, " params: {:?}\n", self.params); + if !self.0.params.is_empty() { + let _ = write!(f, " params: {:?}\n", self.0.params); } let _ = write!(f, " headers:\n"); - for key in self.headers.keys() { - let vals: Vec<_> = self.headers.get_all(key).iter().collect(); + for key in self.0.headers.keys() { + let vals: Vec<_> = self.0.headers.get_all(key).iter().collect(); if vals.len() > 1 { let _ = write!(f, " {:?}: {:?}\n", key, vals); } else { diff --git a/src/pipeline.rs b/src/pipeline.rs index ef6706cd0..518a82130 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -10,8 +10,8 @@ use h1writer::Writer; use httprequest::HttpRequest; use httpresponse::HttpResponse; -type Handler = Fn(&mut HttpRequest) -> Task; -pub(crate) type PipelineHandler<'a> = &'a Fn(&mut HttpRequest) -> Task; +type Handler = Fn(HttpRequest) -> Task; +pub(crate) type PipelineHandler<'a> = &'a Fn(HttpRequest) -> Task; pub struct Pipeline(PipelineState); @@ -26,10 +26,10 @@ enum PipelineState { impl Pipeline { - pub fn new(mut req: HttpRequest, - mw: Rc>>, handler: PipelineHandler) -> Pipeline { + pub fn new(req: HttpRequest, mw: Rc>>, handler: PipelineHandler) -> Pipeline + { if mw.is_empty() { - let task = (handler)(&mut req); + let task = (handler)(req.clone()); Pipeline(PipelineState::Task(Box::new((task, req)))) } else { match Start::init(mw, req, handler) { @@ -191,7 +191,7 @@ impl Start { let len = self.middlewares.len(); loop { if self.idx == len { - let task = (unsafe{&*self.hnd})(&mut req); + let task = (unsafe{&*self.hnd})(req.clone()); return Ok(StartResult::Ready( Box::new(Handle::new(self.idx-1, req, self.prepare(task), self.middlewares)))) } else { @@ -243,7 +243,7 @@ impl Start { self.prepare(Task::reply(resp)), Rc::clone(&self.middlewares))))) } if self.idx == len { - let task = (unsafe{&*self.hnd})(&mut req); + let task = (unsafe{&*self.hnd})(req.clone()); return Ok(Async::Ready(Box::new(Handle::new( self.idx-1, req, self.prepare(task), Rc::clone(&self.middlewares))))) } else { diff --git a/src/resource.rs b/src/resource.rs index 234238fd2..54f7c43b1 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -64,7 +64,7 @@ impl Resource where S: 'static { /// Register handler for specified method. pub fn handler(&mut self, method: Method, handler: F) - where F: Fn(&mut HttpRequest, &S) -> Result + 'static, + where F: Fn(HttpRequest, &S) -> Result + 'static, R: Into + 'static, { self.routes.insert(method, Box::new(FnHandler::new(handler))); @@ -72,7 +72,7 @@ impl Resource where S: 'static { /// Register async handler for specified method. pub fn async(&mut self, method: Method, handler: F) - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Stream + 'static, { self.routes.insert(method, Box::new(StreamHandler::new(handler))); @@ -125,7 +125,7 @@ impl Resource where S: 'static { impl RouteHandler for Resource { - fn handle(&self, req: &mut HttpRequest, state: Rc) -> Task { + fn handle(&self, req: HttpRequest, state: Rc) -> Task { if let Some(handler) = self.routes.get(req.method()) { handler.handle(req, state) } else { diff --git a/src/route.rs b/src/route.rs index 34c8df905..3a1ccfd8f 100644 --- a/src/route.rs +++ b/src/route.rs @@ -33,7 +33,7 @@ impl Frame { #[allow(unused_variables)] pub trait RouteHandler: 'static { /// Handle request - fn handle(&self, req: &mut HttpRequest, state: Rc) -> Task; + fn handle(&self, req: HttpRequest, state: Rc) -> Task; /// Set route prefix fn set_prefix(&mut self, prefix: String) {} @@ -50,7 +50,7 @@ pub trait Route: Actor { type State; /// Handle `EXPECT` header. By default respones with `HTTP/1.1 100 Continue` - fn expect(req: &HttpRequest, ctx: &mut Self::Context) -> Result<(), Error> + fn expect(req: &mut HttpRequest, ctx: &mut Self::Context) -> Result<(), Error> where Self: Actor> { // handle expect header only for HTTP/1.1 @@ -80,7 +80,7 @@ pub trait Route: Actor { /// request/response or websocket connection. /// In that case `HttpContext::start` and `HttpContext::write` has to be used /// for writing response. - fn request(req: &mut HttpRequest, ctx: &mut Self::Context) -> RouteResult; + fn request(req: HttpRequest, ctx: &mut Self::Context) -> RouteResult; /// This method creates `RouteFactory` for this actor. fn factory() -> RouteFactory { @@ -95,12 +95,12 @@ impl RouteHandler for RouteFactory where A: Actor> + Route, S: 'static { - fn handle(&self, req: &mut HttpRequest, state: Rc) -> Task { + fn handle(&self, mut req: HttpRequest, state: Rc) -> Task { let mut ctx = HttpContext::new(state); // handle EXPECT header if req.headers().contains_key(header::EXPECT) { - if let Err(resp) = A::expect(req, &mut ctx) { + if let Err(resp) = A::expect(&mut req, &mut ctx) { return Task::reply(resp) } } @@ -114,7 +114,7 @@ impl RouteHandler for RouteFactory /// Fn() route handler pub(crate) struct FnHandler - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Into, S: 'static, { @@ -123,7 +123,7 @@ struct FnHandler } impl FnHandler - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Into + 'static, S: 'static, { @@ -133,11 +133,11 @@ impl FnHandler } impl RouteHandler for FnHandler - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Into + 'static, S: 'static, { - fn handle(&self, req: &mut HttpRequest, state: Rc) -> Task { + fn handle(&self, req: HttpRequest, state: Rc) -> Task { Task::reply((self.f)(req, &state).into()) } } @@ -145,7 +145,7 @@ impl RouteHandler for FnHandler /// Async route handler pub(crate) struct StreamHandler - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Stream + 'static, S: 'static, { @@ -154,7 +154,7 @@ struct StreamHandler } impl StreamHandler - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Stream + 'static, S: 'static, { @@ -164,11 +164,11 @@ impl StreamHandler } impl RouteHandler for StreamHandler - where F: Fn(&mut HttpRequest, &S) -> R + 'static, + where F: Fn(HttpRequest, &S) -> R + 'static, R: Stream + 'static, S: 'static, { - fn handle(&self, req: &mut HttpRequest, state: Rc) -> Task { + fn handle(&self, req: HttpRequest, state: Rc) -> Task { Task::with_stream((self.f)(req, &state)) } } diff --git a/src/staticfiles.rs b/src/staticfiles.rs index cfc9b7a10..97cd44070 100644 --- a/src/staticfiles.rs +++ b/src/staticfiles.rs @@ -137,7 +137,7 @@ impl RouteHandler for StaticFiles { } } - fn handle(&self, req: &mut HttpRequest, _: Rc) -> Task { + fn handle(&self, req: HttpRequest, _: Rc) -> Task { if !self.accessible { Task::reply(HTTPNotFound) } else { diff --git a/src/ws.rs b/src/ws.rs index 97769e03d..910248192 100644 --- a/src/ws.rs +++ b/src/ws.rs @@ -22,14 +22,14 @@ //! impl Route for WsRoute { //! type State = (); //! -//! fn request(req: &mut HttpRequest, ctx: &mut HttpContext) -> RouteResult +//! fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult //! { //! // WebSocket handshake //! let resp = ws::handshake(&req)?; //! // Send handshake response to peer //! ctx.start(resp); //! // Map Payload into WsStream -//! ctx.add_stream(ws::WsStream::new(req)); +//! ctx.add_stream(ws::WsStream::new(&mut req)); //! // Start ws messages processing //! Reply::async(WsRoute) //! }