diff --git a/.travis.yml b/.travis.yml index bebb6e62c..b902c9796 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: rust rust: - - 1.18.0 - - 1.19.0 - 1.20.0 - nightly diff --git a/README.md b/README.md index f0e832f11..547033e7f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Actix http is a http framework for Actix framework. * [API Documentation](http://fafhrd91.github.io/actix-http/actix_http/) * Cargo package: [actix-http](https://crates.io/crates/actix-http) +* Minimum supported Rust version: 1.20 or later --- diff --git a/src/application.rs b/src/application.rs index 83fb1e90b..904384dc7 100644 --- a/src/application.rs +++ b/src/application.rs @@ -39,6 +39,8 @@ impl HttpApplication where S: 'static } impl HttpApplication<()> { + + /// Create `HttpApplication` with no state pub fn no_state() -> Self { HttpApplication { state: (), @@ -50,6 +52,9 @@ impl HttpApplication<()> { impl HttpApplication where S: 'static { + /// Create http application with specific state. State is shared with all + /// routes within same application and could be + /// accessed with `HttpContext::state()` method. pub fn new(state: S) -> HttpApplication { HttpApplication { state: state, @@ -58,6 +63,7 @@ impl HttpApplication where S: 'static { } } + /// Add resource by path. pub fn add(&mut self, path: P) -> &mut HttpResource { let path = path.to_string(); @@ -70,7 +76,7 @@ impl HttpApplication where S: 'static { self.resources.get_mut(&path).unwrap() } - /// Default resource + /// Default resource is used if no matched route could be found. pub fn default(&mut self) -> &mut HttpResource { &mut self.default } diff --git a/src/context.rs b/src/context.rs index fd225b453..88bd1b650 100644 --- a/src/context.rs +++ b/src/context.rs @@ -9,13 +9,13 @@ use actix::fut::ActorFuture; use actix::dev::{AsyncContextApi, ActorAddressCell}; use route::{Route, Frame}; -use httpmessage::HttpMessage; +use httpmessage::HttpResponse; /// Actor execution context pub struct HttpContext where A: Actor> + Route, { - act: A, + act: Option, state: ActorState, items: Vec>>, address: ActorAddressCell, @@ -60,6 +60,7 @@ impl AsyncActorContext for HttpContext where A: Actor + R } } +#[doc(hidden)] impl AsyncContextApi for HttpContext where A: Actor + Route { fn address_cell(&mut self) -> &mut ActorAddressCell { &mut self.address @@ -68,10 +69,10 @@ impl AsyncContextApi for HttpContext where A: Actor + Rou impl HttpContext where A: Actor + Route { - pub(crate) fn new(act: A, state: Rc<::State>) -> HttpContext + pub(crate) fn new(state: Rc<::State>) -> HttpContext { HttpContext { - act: act, + act: None, state: ActorState::Started, items: Vec::new(), address: ActorAddressCell::new(), @@ -80,8 +81,8 @@ impl HttpContext where A: Actor + Route { } } - pub(crate) fn replace_actor(&mut self, srv: A) -> A { - std::mem::replace(&mut self.act, srv) + pub(crate) fn set_actor(&mut self, act: A) { + self.act = Some(act) } } @@ -93,7 +94,7 @@ impl HttpContext where A: Actor + Route { } /// Start response processing - pub fn start(&mut self, response: HttpMessage) { + pub fn start(&mut self, response: HttpResponse) { self.stream.push_back(Frame::Message(response)) } @@ -102,18 +103,26 @@ impl HttpContext where A: Actor + Route { self.stream.push_back(Frame::Payload(Some(data))) } - /// Completed + /// Indicate end of streamimng payload pub fn write_eof(&mut self) { self.stream.push_back(Frame::Payload(None)) } } +#[doc(hidden)] impl Stream for HttpContext where A: Actor + Route { type Item = Frame; type Error = std::io::Error; fn poll(&mut self) -> Poll, Self::Error> { + if self.act.is_none() { + return Ok(Async::NotReady) + } + + let act: &mut A = unsafe { + std::mem::transmute(self.act.as_mut().unwrap() as &mut A) + }; let ctx: &mut HttpContext = unsafe { std::mem::transmute(self as &mut HttpContext) }; @@ -121,11 +130,11 @@ impl Stream for HttpContext where A: Actor + Route // update state match self.state { ActorState::Started => { - Actor::started(&mut self.act, ctx); + Actor::started(act, ctx); self.state = ActorState::Running; }, ActorState::Stopping => { - Actor::stopping(&mut self.act, ctx); + Actor::stopping(act, ctx); } _ => () } @@ -134,7 +143,7 @@ impl Stream for HttpContext where A: Actor + Route loop { let mut not_ready = true; - if let Ok(Async::Ready(_)) = self.address.poll(&mut self.act, ctx) { + if let Ok(Async::Ready(_)) = self.address.poll(act, ctx) { not_ready = false } @@ -146,7 +155,7 @@ impl Stream for HttpContext where A: Actor + Route break } - let (drop, item) = match self.items[idx].poll(&mut self.act, ctx) { + let (drop, item) = match self.items[idx].poll(act, ctx) { Ok(val) => match val { Async::Ready(_) => { not_ready = false; @@ -194,7 +203,7 @@ impl Stream for HttpContext where A: Actor + Route match self.state { ActorState::Stopped => { self.state = ActorState::Stopped; - Actor::stopped(&mut self.act, ctx); + Actor::stopped(act, ctx); return Ok(Async::Ready(None)) }, ActorState::Stopping => { @@ -204,11 +213,11 @@ impl Stream for HttpContext where A: Actor + Route continue } else { self.state = ActorState::Stopped; - Actor::stopped(&mut self.act, ctx); + Actor::stopped(act, ctx); return Ok(Async::Ready(None)) } } else { - Actor::stopping(&mut self.act, ctx); + Actor::stopping(act, ctx); prep_stop = true; continue } @@ -216,7 +225,7 @@ impl Stream for HttpContext where A: Actor + Route ActorState::Running => { if !self.address.connected() && self.items.is_empty() { self.state = ActorState::Stopping; - Actor::stopping(&mut self.act, ctx); + Actor::stopping(act, ctx); prep_stop = true; continue } diff --git a/src/httpcodes.rs b/src/httpcodes.rs index 3f3d75bb1..517d0de2a 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -5,7 +5,7 @@ use http::StatusCode; use task::Task; use route::{Payload, RouteHandler}; -use httpmessage::{Body, HttpRequest, HttpMessage, IntoHttpMessage}; +use httpmessage::{Body, HttpRequest, HttpResponse, IntoHttpResponse}; pub struct StaticResponse(StatusCode); @@ -20,12 +20,12 @@ pub const HTTPMethodNotAllowed: StaticResponse = StaticResponse(StatusCode::METH impl RouteHandler for StaticResponse { fn handle(&self, req: HttpRequest, _: Option, _: Rc) -> Task { - Task::reply(HttpMessage::new(req, self.0, Body::Empty), None) + Task::reply(HttpResponse::new(req, self.0, Body::Empty), None) } } -impl IntoHttpMessage for StaticResponse { - fn into_response(self, req: HttpRequest) -> HttpMessage { - HttpMessage::new(req, self.0, Body::Empty) +impl IntoHttpResponse for StaticResponse { + fn into_response(self, req: HttpRequest) -> HttpResponse { + HttpResponse::new(req, self.0, Body::Empty) } } diff --git a/src/httpmessage.rs b/src/httpmessage.rs index ec41cb1f4..b7faa4a87 100644 --- a/src/httpmessage.rs +++ b/src/httpmessage.rs @@ -161,15 +161,22 @@ impl HttpRequest { } } +/// Represents various types of http message body. #[derive(Debug)] pub enum Body { + /// Empty response. `Content-Length` header is set to `0` Empty, + /// Specific response body. `Content-Length` header is set to length of bytes. Binary(Bytes), + /// Streaming response body with specified length. Length(u64), + /// Unspecified streaming response. Developer is responsible for setting + /// right `Content-Length` or `Transfer-Encoding` headers. Streaming, } impl Body { + /// Does this body have payload. pub fn has_body(&self) -> bool { match *self { Body::Length(_) | Body::Streaming => true, @@ -178,13 +185,15 @@ impl Body { } } -pub trait IntoHttpMessage { - fn into_response(self, req: HttpRequest) -> HttpMessage; +/// Implements by something that can be converted to `HttpMessage` +pub trait IntoHttpResponse { + /// Convert into response. + fn into_response(self, req: HttpRequest) -> HttpResponse; } #[derive(Debug)] /// An HTTP Response -pub struct HttpMessage { +pub struct HttpResponse { request: HttpRequest, pub version: Version, pub headers: Headers, @@ -195,7 +204,7 @@ pub struct HttpMessage { compression: Option, } -impl Message for HttpMessage { +impl Message for HttpResponse { fn version(&self) -> Version { self.version } @@ -204,12 +213,12 @@ impl Message for HttpMessage { } } -impl HttpMessage { - /// Constructs a default response +impl HttpResponse { + /// Constructs a response #[inline] - pub fn new(request: HttpRequest, status: StatusCode, body: Body) -> HttpMessage { + pub fn new(request: HttpRequest, status: StatusCode, body: Body) -> HttpResponse { let version = request.version; - HttpMessage { + HttpResponse { request: request, version: version, headers: Default::default(), @@ -296,10 +305,12 @@ impl HttpMessage { } } + /// Get body os this response pub fn body(&self) -> &Body { &self.body } + /// Set a body and return previous body value pub fn set_body>(&mut self, body: B) -> Body { mem::replace(&mut self.body, body.into()) } diff --git a/src/lib.rs b/src/lib.rs index 7bf89e0b9..3d71fb6a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,10 +30,10 @@ mod server; pub mod httpcodes; pub use application::HttpApplication; -pub use route::{Route, RouteFactory, Payload, PayloadItem, Frame}; -pub use resource::{HttpResource, HttpResponse}; +pub use route::{Route, RouteFactory, RouteHandler, Payload, PayloadItem}; +pub use resource::{HttpMessage, HttpResource}; pub use server::HttpServer; pub use context::HttpContext; pub use router::RoutingMap; pub use route_recognizer::Params; -pub use httpmessage::{HttpRequest, HttpMessage, IntoHttpMessage}; +pub use httpmessage::{HttpRequest, HttpResponse, IntoHttpResponse}; diff --git a/src/main.rs b/src/main.rs index 4a7c128aa..66c8c290e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,13 +21,13 @@ impl Route for MyRoute { fn request(req: HttpRequest, payload: Option, - ctx: &mut HttpContext) -> HttpResponse + ctx: &mut HttpContext) -> HttpMessage { if let Some(pl) = payload { ctx.add_stream(pl); - HttpResponse::Stream(MyRoute{req: Some(req)}) + HttpMessage::Stream(MyRoute{req: Some(req)}) } else { - HttpResponse::Reply(req, httpcodes::HTTPOk) + HttpMessage::Reply(req, httpcodes::HTTPOk) } } } diff --git a/src/resource.rs b/src/resource.rs index c6a4ea81e..679e8d3b2 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -1,4 +1,3 @@ -use std::mem; use std::rc::Rc; use std::marker::PhantomData; use std::collections::HashMap; @@ -11,7 +10,7 @@ use task::Task; use route::{Route, Payload, RouteHandler}; use context::HttpContext; use httpcodes::HTTPMethodNotAllowed; -use httpmessage::{HttpRequest, HttpMessage, IntoHttpMessage}; +use httpmessage::{HttpRequest, HttpResponse, IntoHttpResponse}; /// Resource pub struct HttpResource { @@ -32,6 +31,7 @@ impl Default for HttpResource { impl HttpResource where S: 'static { + /// Register handler for specified method. pub fn handler(&mut self, method: Method, handler: H) -> &mut Self where H: RouteHandler { @@ -39,6 +39,8 @@ impl HttpResource where S: 'static { self } + /// Default handler is used if no matched route found. + /// By default `HTTPMethodNotAllowed` is used. pub fn default_handler(&mut self, handler: H) -> &mut Self where H: RouteHandler { @@ -46,21 +48,25 @@ impl HttpResource where S: 'static { self } + /// Handler for `GET` method. pub fn get(&mut self) -> &mut Self where A: Route { self.handler(Method::GET, A::factory()) } + /// Handler for `POST` method. pub fn post(&mut self) -> &mut Self where A: Route { self.handler(Method::POST, A::factory()) } + /// Handler for `PUR` method. pub fn put(&mut self) -> &mut Self where A: Route { self.handler(Method::PUT, A::factory()) } + /// Handler for `METHOD` method. pub fn delete(&mut self) -> &mut Self where A: Route { self.handler(Method::DELETE, A::factory()) @@ -81,40 +87,40 @@ impl RouteHandler for HttpResource { #[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))] -enum HttpResponseItem where A: Actor> + Route { - Message(HttpMessage, Option), +enum HttpMessageItem where A: Actor> + Route { + Message(HttpResponse, Option), Actor(A), } -pub struct HttpResponse> + Route> (HttpResponseItem); +pub struct HttpMessage> + Route> (HttpMessageItem); -impl HttpResponse where A: Actor> + Route +impl HttpMessage where A: Actor> + Route { /// Create async response #[allow(non_snake_case)] pub fn Stream(act: A) -> Self { - HttpResponse(HttpResponseItem::Actor(act)) + HttpMessage(HttpMessageItem::Actor(act)) } #[allow(non_snake_case)] pub fn Reply(req: HttpRequest, msg: I) -> Self - where I: IntoHttpMessage + where I: IntoHttpResponse { - HttpResponse(HttpResponseItem::Message(msg.into_response(req), None)) + HttpMessage(HttpMessageItem::Message(msg.into_response(req), None)) } #[allow(non_snake_case)] - pub fn ReplyMessage(msg: HttpMessage, body: Option) -> Self { - HttpResponse(HttpResponseItem::Message(msg, body)) + pub fn ReplyMessage(msg: HttpResponse, body: Option) -> Self { + HttpMessage(HttpMessageItem::Message(msg, body)) } pub(crate) fn into(self, mut ctx: HttpContext) -> Task { match self.0 { - HttpResponseItem::Message(msg, body) => - Task::reply(msg, body), - HttpResponseItem::Actor(act) => { - let old = ctx.replace_actor(act); - mem::forget(old); + HttpMessageItem::Message(msg, body) => { + Task::reply(msg, body) + }, + HttpMessageItem::Actor(act) => { + ctx.set_actor(act); Task::with_stream(ctx) } } diff --git a/src/route.rs b/src/route.rs index c954a508e..cbbbaae28 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,4 +1,3 @@ -use std; use std::rc::Rc; use std::marker::PhantomData; @@ -8,34 +7,41 @@ use futures::unsync::mpsc::Receiver; use task::Task; use context::HttpContext; -use resource::HttpResponse; -use httpmessage::{HttpRequest, HttpMessage}; +use resource::HttpMessage; +use httpmessage::{HttpRequest, HttpResponse}; +/// Stream of `PayloadItem`'s pub type Payload = Receiver; +/// `PayloadItem` represents one payload item #[derive(Debug)] pub enum PayloadItem { + /// Indicates end of payload stream Eof, + /// Chunk of bytes Chunk(Bytes) } impl PayloadItem { + /// Is item an eof pub fn is_eof(&self) -> bool { match *self { PayloadItem::Eof => true, _ => false, } } + /// Is item a chunk pub fn is_chunk(&self) -> bool { !self.is_eof() } } +#[doc(hidden)] #[derive(Debug)] #[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))] pub enum Frame { - Message(HttpMessage), + Message(HttpResponse), Payload(Option), } @@ -43,12 +49,13 @@ pub trait RouteHandler: 'static { fn handle(&self, req: HttpRequest, payload: Option, state: Rc) -> Task; } +/// Actors with ability to handle http requests pub trait Route: Actor> { type State; fn request(req: HttpRequest, payload: Option, - ctx: &mut HttpContext) -> HttpResponse; + ctx: &mut HttpContext) -> HttpMessage; fn factory() -> RouteFactory { RouteFactory(PhantomData) @@ -64,7 +71,7 @@ impl RouteHandler for RouteFactory { fn handle(&self, req: HttpRequest, payload: Option, state: Rc) -> Task { - let mut ctx = HttpContext::new(unsafe{std::mem::uninitialized()}, state); + let mut ctx = HttpContext::new(state); A::request(req, payload, &mut ctx).into(ctx) } } diff --git a/src/router.rs b/src/router.rs index 8aebab707..08baab08d 100644 --- a/src/router.rs +++ b/src/router.rs @@ -8,7 +8,7 @@ use route::{Payload, RouteHandler}; use resource::HttpResource; use application::HttpApplication; use httpcodes::HTTPNotFound; -use httpmessage::{HttpRequest, IntoHttpMessage}; +use httpmessage::{HttpRequest, IntoHttpResponse}; pub trait HttpHandler: 'static { fn handle(&self, req: HttpRequest, payload: Option) -> Task; @@ -91,7 +91,7 @@ impl Router { } } - Task::reply(IntoHttpMessage::into_response(HTTPNotFound, req), None) + Task::reply(IntoHttpResponse::into_response(HTTPNotFound, req), None) } } } diff --git a/src/server.rs b/src/server.rs index e32845db8..f0f38a536 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,6 +10,7 @@ use task::Task; use reader::Reader; use router::{Router, RoutingMap}; +/// An HTTP Server. pub struct HttpServer { router: Rc, } @@ -19,10 +20,12 @@ impl Actor for HttpServer { } impl HttpServer { + /// Create new http server with specified `RoutingMap` pub fn new(routes: RoutingMap) -> Self { HttpServer {router: Rc::new(routes.into_router())} } + /// Start listening for incomming connections. pub fn serve(self, addr: &net::SocketAddr) -> io::Result where Self: ActorAddress { diff --git a/src/task.rs b/src/task.rs index 777453359..047896d8b 100644 --- a/src/task.rs +++ b/src/task.rs @@ -13,7 +13,7 @@ use hyper::header::{Date, Connection, ContentType, use date; use route::Frame; -use httpmessage::{Body, HttpMessage}; +use httpmessage::{Body, HttpResponse}; type FrameStream = Stream; const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific @@ -57,7 +57,7 @@ pub struct Task { impl Task { - pub(crate) fn reply(msg: HttpMessage, body: Option) -> Self { + pub(crate) fn reply(msg: HttpResponse, body: Option) -> Self { let mut frames = VecDeque::new(); if let Some(body) = body { frames.push_back(Frame::Message(msg)); @@ -90,7 +90,7 @@ impl Task { } } - fn prepare(&mut self, mut msg: HttpMessage) + fn prepare(&mut self, mut msg: HttpResponse) { trace!("Prepare message status={:?}", msg.status);