1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-13 02:39:32 +00:00

add expect/continue support

This commit is contained in:
Nikolay Kim 2017-10-15 15:52:52 -07:00
parent 94c8aa6a54
commit f1d6c61c5c
4 changed files with 74 additions and 6 deletions

View file

@ -108,8 +108,8 @@ impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
}
/// Write payload
pub fn write(&mut self, data: Bytes) {
self.stream.push_back(Frame::Payload(Some(data)))
pub fn write<B: Into<Bytes>>(&mut self, data: B) {
self.stream.push_back(Frame::Payload(Some(data.into())))
}
/// Indicate end of streamimng payload

View file

@ -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<S> RouteHandler<S> for StaticResponse {

View file

@ -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<S>: '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<Context=HttpContext<Self>>
{
// 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<A, S> RouteHandler<S> for RouteFactory<A, S>
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<A::State>) -> 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)
}
}

View file

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