diff --git a/src/context.rs b/src/context.rs
index 5e5985ed9..bcf59122b 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -108,8 +108,8 @@ impl HttpContext where A: Actor + Route {
}
/// Write payload
- pub fn write(&mut self, data: Bytes) {
- self.stream.push_back(Frame::Payload(Some(data)))
+ pub fn write>(&mut self, data: B) {
+ self.stream.push_back(Frame::Payload(Some(data.into())))
}
/// Indicate end of streamimng payload
diff --git a/src/httpcodes.rs b/src/httpcodes.rs
index 7ffa1a661..54d70ea5c 100644
--- a/src/httpcodes.rs
+++ b/src/httpcodes.rs
@@ -12,10 +12,29 @@ use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
pub const HTTPNoContent: StaticResponse = StaticResponse(StatusCode::NO_CONTENT);
+
pub const HTTPBadRequest: StaticResponse = StaticResponse(StatusCode::BAD_REQUEST);
pub const HTTPNotFound: StaticResponse = StaticResponse(StatusCode::NOT_FOUND);
+pub const HTTPUnauthorized: StaticResponse = StaticResponse(StatusCode::UNAUTHORIZED);
+pub const HTTPPaymentRequired: StaticResponse = StaticResponse(StatusCode::PAYMENT_REQUIRED);
pub const HTTPForbidden: StaticResponse = StaticResponse(StatusCode::FORBIDDEN);
-pub const HTTPMethodNotAllowed: StaticResponse = StaticResponse(StatusCode::METHOD_NOT_ALLOWED);
+
+pub const HTTPMethodNotAllowed: StaticResponse =
+ StaticResponse(StatusCode::METHOD_NOT_ALLOWED);
+pub const HTTPNotAcceptable: StaticResponse = StaticResponse(StatusCode::NOT_ACCEPTABLE);
+pub const HTTPProxyAuthenticationRequired: StaticResponse =
+ StaticResponse(StatusCode::PROXY_AUTHENTICATION_REQUIRED);
+pub const HTTPRequestTimeout: StaticResponse = StaticResponse(StatusCode::REQUEST_TIMEOUT);
+pub const HTTPConflict: StaticResponse = StaticResponse(StatusCode::CONFLICT);
+pub const HTTPGone: StaticResponse = StaticResponse(StatusCode::GONE);
+pub const HTTPLengthRequired: StaticResponse = StaticResponse(StatusCode::LENGTH_REQUIRED);
+pub const HTTPPreconditionFailed: StaticResponse =
+ StaticResponse(StatusCode::PRECONDITION_FAILED);
+pub const HTTPPayloadTooLarge: StaticResponse = StaticResponse(StatusCode::PAYLOAD_TOO_LARGE);
+pub const HTTPUriTooLong: StaticResponse = StaticResponse(StatusCode::URI_TOO_LONG);
+pub const HTTPExpectationFailed: StaticResponse =
+ StaticResponse(StatusCode::EXPECTATION_FAILED);
+
pub const HTTPInternalServerError: StaticResponse =
StaticResponse(StatusCode::INTERNAL_SERVER_ERROR);
@@ -34,6 +53,9 @@ impl StaticResponse {
resp.set_reason(reason);
resp
}
+ pub fn with_body(self, body: Body) -> HttpResponse {
+ HttpResponse::new(self.0, body)
+ }
}
impl RouteHandler for StaticResponse {
diff --git a/src/route.rs b/src/route.rs
index f549379b6..e2cfcaf7f 100644
--- a/src/route.rs
+++ b/src/route.rs
@@ -4,6 +4,7 @@ use std::marker::PhantomData;
use actix::Actor;
use bytes::Bytes;
+use http::{header, Version};
use futures::Stream;
use task::Task;
@@ -11,7 +12,8 @@ use context::HttpContext;
use resource::Reply;
use payload::Payload;
use httprequest::HttpRequest;
-use httpresponse::HttpResponse;
+use httpresponse::{Body, HttpResponse};
+use httpcodes::HTTPExpectationFailed;
#[doc(hidden)]
#[derive(Debug)]
@@ -31,11 +33,39 @@ pub trait RouteHandler: 'static {
}
/// Actors with ability to handle http requests
+#[allow(unused_variables)]
pub trait Route: Actor {
/// Route shared state. State is shared with all routes within same application and could be
/// accessed with `HttpContext::state()` method.
type State;
+ /// Handle `EXPECT` header. By default respond with `HTTP/1.1 100 Continue`
+ fn expect(req: &HttpRequest, ctx: &mut Self::Context) -> Result<(), HttpResponse>
+ where Self: Actor>
+ {
+ // handle expect header only for HTTP/1.1
+ if req.version() == Version::HTTP_11 {
+ if let Some(expect) = req.headers().get(header::EXPECT) {
+ if let Ok(expect) = expect.to_str() {
+ if expect.to_lowercase() == "100-continue" {
+ ctx.write("HTTP/1.1 100 Continue\r\n\r\n");
+ Ok(())
+ } else {
+ Err(HTTPExpectationFailed.with_body(
+ Body::Binary("Unknown Expect".into())))
+ }
+ } else {
+ Err(HTTPExpectationFailed.with_body(
+ Body::Binary("Unknown Expect".into())))
+ }
+ } else {
+ Ok(())
+ }
+ } else {
+ Ok(())
+ }
+ }
+
/// Handle incoming request. Route actor can return
/// result immediately with `Reply::reply` or `Reply::with`.
/// Actor itself could be returned for handling streaming request/response.
@@ -58,6 +88,13 @@ impl RouteHandler for RouteFactory
fn handle(&self, req: HttpRequest, payload: Payload, 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) {
+ return Task::reply(resp)
+ }
+ }
A::request(req, payload, &mut ctx).into(ctx)
}
}
diff --git a/src/task.rs b/src/task.rs
index 2fdd569cd..fd1523bf7 100644
--- a/src/task.rs
+++ b/src/task.rs
@@ -74,6 +74,7 @@ pub struct Task {
buffer: BytesMut,
upgrade: bool,
keepalive: bool,
+ prepared: bool,
}
impl Task {
@@ -92,6 +93,7 @@ impl Task {
buffer: BytesMut::new(),
upgrade: false,
keepalive: false,
+ prepared: false,
}
}
@@ -107,6 +109,7 @@ impl Task {
buffer: BytesMut::new(),
upgrade: false,
keepalive: false,
+ prepared: false,
}
}
@@ -122,6 +125,7 @@ impl Task {
let body = msg.replace_body(Body::Empty);
let version = msg.version().unwrap_or_else(|| req.version);
self.keepalive = msg.keep_alive().unwrap_or_else(|| req.keep_alive);
+ self.prepared = true;
match body {
Body::Empty => {
@@ -248,8 +252,13 @@ impl Task {
Frame::Payload(chunk) => {
match chunk {
Some(chunk) => {
- // TODO: add warning, write after EOF
- self.encoder.encode(&mut self.buffer, chunk.as_ref());
+ if self.prepared {
+ // TODO: add warning, write after EOF
+ self.encoder.encode(&mut self.buffer, chunk.as_ref());
+ } else {
+ // might be response for EXCEPT
+ self.buffer.extend(chunk)
+ }
}
None => {
// TODO: add error "not eof""