1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-23 16:21:06 +01:00
This commit is contained in:
Nikolay Kim 2017-10-06 23:14:13 -07:00
parent 0b5f0c4f22
commit a505be9321
13 changed files with 106 additions and 65 deletions

View File

@ -1,7 +1,5 @@
language: rust
rust:
- 1.18.0
- 1.19.0
- 1.20.0
- nightly

View File

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

View File

@ -39,6 +39,8 @@ impl<S> HttpApplication<S> where S: 'static
}
impl HttpApplication<()> {
/// Create `HttpApplication` with no state
pub fn no_state() -> Self {
HttpApplication {
state: (),
@ -50,6 +52,9 @@ impl HttpApplication<()> {
impl<S> HttpApplication<S> 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<S> {
HttpApplication {
state: state,
@ -58,6 +63,7 @@ impl<S> HttpApplication<S> where S: 'static {
}
}
/// Add resource by path.
pub fn add<P: ToString>(&mut self, path: P) -> &mut HttpResource<S>
{
let path = path.to_string();
@ -70,7 +76,7 @@ impl<S> HttpApplication<S> 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<S> {
&mut self.default
}

View File

@ -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<A> where A: Actor<Context=HttpContext<A>> + Route,
{
act: A,
act: Option<A>,
state: ActorState,
items: Vec<Box<ActorFuture<Item=(), Error=(), Actor=A>>>,
address: ActorAddressCell<A>,
@ -60,6 +60,7 @@ impl<A> AsyncActorContext<A> for HttpContext<A> where A: Actor<Context=Self> + R
}
}
#[doc(hidden)]
impl<A> AsyncContextApi<A> for HttpContext<A> where A: Actor<Context=Self> + Route {
fn address_cell(&mut self) -> &mut ActorAddressCell<A> {
&mut self.address
@ -68,10 +69,10 @@ impl<A> AsyncContextApi<A> for HttpContext<A> where A: Actor<Context=Self> + Rou
impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
pub(crate) fn new(act: A, state: Rc<<A as Route>::State>) -> HttpContext<A>
pub(crate) fn new(state: Rc<<A as Route>::State>) -> HttpContext<A>
{
HttpContext {
act: act,
act: None,
state: ActorState::Started,
items: Vec::new(),
address: ActorAddressCell::new(),
@ -80,8 +81,8 @@ impl<A> HttpContext<A> where A: Actor<Context=Self> + 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<A> HttpContext<A> where A: Actor<Context=Self> + 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<A> HttpContext<A> where A: Actor<Context=Self> + 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<A> Stream for HttpContext<A> where A: Actor<Context=Self> + Route
{
type Item = Frame;
type Error = std::io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, 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<A> = unsafe {
std::mem::transmute(self as &mut HttpContext<A>)
};
@ -121,11 +130,11 @@ impl<A> Stream for HttpContext<A> where A: Actor<Context=Self> + 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<A> Stream for HttpContext<A> where A: Actor<Context=Self> + 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<A> Stream for HttpContext<A> where A: Actor<Context=Self> + 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<A> Stream for HttpContext<A> where A: Actor<Context=Self> + 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<A> Stream for HttpContext<A> where A: Actor<Context=Self> + 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<A> Stream for HttpContext<A> where A: Actor<Context=Self> + 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
}

View File

@ -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<S> RouteHandler<S> for StaticResponse {
fn handle(&self, req: HttpRequest, _: Option<Payload>, _: Rc<S>) -> 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)
}
}

View File

@ -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<Encoding>,
}
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<B: Into<Body>>(&mut self, body: B) -> Body {
mem::replace(&mut self.body, body.into())
}

View File

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

View File

@ -21,13 +21,13 @@ impl Route for MyRoute {
fn request(req: HttpRequest,
payload: Option<Payload>,
ctx: &mut HttpContext<Self>) -> HttpResponse<Self>
ctx: &mut HttpContext<Self>) -> HttpMessage<Self>
{
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)
}
}
}

View File

@ -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<S=()> {
@ -32,6 +31,7 @@ impl<S> Default for HttpResource<S> {
impl<S> HttpResource<S> where S: 'static {
/// Register handler for specified method.
pub fn handler<H>(&mut self, method: Method, handler: H) -> &mut Self
where H: RouteHandler<S>
{
@ -39,6 +39,8 @@ impl<S> HttpResource<S> where S: 'static {
self
}
/// Default handler is used if no matched route found.
/// By default `HTTPMethodNotAllowed` is used.
pub fn default_handler<H>(&mut self, handler: H) -> &mut Self
where H: RouteHandler<S>
{
@ -46,21 +48,25 @@ impl<S> HttpResource<S> where S: 'static {
self
}
/// Handler for `GET` method.
pub fn get<A>(&mut self) -> &mut Self where A: Route<State=S>
{
self.handler(Method::GET, A::factory())
}
/// Handler for `POST` method.
pub fn post<A>(&mut self) -> &mut Self where A: Route<State=S>
{
self.handler(Method::POST, A::factory())
}
/// Handler for `PUR` method.
pub fn put<A>(&mut self) -> &mut Self where A: Route<State=S>
{
self.handler(Method::PUT, A::factory())
}
/// Handler for `METHOD` method.
pub fn delete<A>(&mut self) -> &mut Self where A: Route<State=S>
{
self.handler(Method::DELETE, A::factory())
@ -81,40 +87,40 @@ impl<S: 'static> RouteHandler<S> for HttpResource<S> {
#[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))]
enum HttpResponseItem<A> where A: Actor<Context=HttpContext<A>> + Route {
Message(HttpMessage, Option<Bytes>),
enum HttpMessageItem<A> where A: Actor<Context=HttpContext<A>> + Route {
Message(HttpResponse, Option<Bytes>),
Actor(A),
}
pub struct HttpResponse<A: Actor<Context=HttpContext<A>> + Route> (HttpResponseItem<A>);
pub struct HttpMessage<A: Actor<Context=HttpContext<A>> + Route> (HttpMessageItem<A>);
impl<A> HttpResponse<A> where A: Actor<Context=HttpContext<A>> + Route
impl<A> HttpMessage<A> where A: Actor<Context=HttpContext<A>> + 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<I>(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<Bytes>) -> Self {
HttpResponse(HttpResponseItem::Message(msg, body))
pub fn ReplyMessage(msg: HttpResponse, body: Option<Bytes>) -> Self {
HttpMessage(HttpMessageItem::Message(msg, body))
}
pub(crate) fn into(self, mut ctx: HttpContext<A>) -> 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)
}
}

View File

@ -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>;
/// `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<Bytes>),
}
@ -43,12 +49,13 @@ pub trait RouteHandler<S>: 'static {
fn handle(&self, req: HttpRequest, payload: Option<Payload>, state: Rc<S>) -> Task;
}
/// Actors with ability to handle http requests
pub trait Route: Actor<Context=HttpContext<Self>> {
type State;
fn request(req: HttpRequest,
payload: Option<Payload>,
ctx: &mut HttpContext<Self>) -> HttpResponse<Self>;
ctx: &mut HttpContext<Self>) -> HttpMessage<Self>;
fn factory() -> RouteFactory<Self, Self::State> {
RouteFactory(PhantomData)
@ -64,7 +71,7 @@ impl<A, S> RouteHandler<S> for RouteFactory<A, S>
{
fn handle(&self, req: HttpRequest, payload: Option<Payload>, state: Rc<A::State>) -> 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)
}
}

View File

@ -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<Payload>) -> Task;
@ -91,7 +91,7 @@ impl Router {
}
}
Task::reply(IntoHttpMessage::into_response(HTTPNotFound, req), None)
Task::reply(IntoHttpResponse::into_response(HTTPNotFound, req), None)
}
}
}

View File

@ -10,6 +10,7 @@ use task::Task;
use reader::Reader;
use router::{Router, RoutingMap};
/// An HTTP Server.
pub struct HttpServer {
router: Rc<Router>,
}
@ -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<Addr>(self, addr: &net::SocketAddr) -> io::Result<Addr>
where Self: ActorAddress<Self, Addr>
{

View File

@ -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<Item=Frame, Error=io::Error>;
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<Bytes>) -> Self {
pub(crate) fn reply(msg: HttpResponse, body: Option<Bytes>) -> 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);