mirror of
https://github.com/actix/actix-web.git
synced 2024-12-29 19:40:34 +00:00
add expect/continue support
This commit is contained in:
parent
94c8aa6a54
commit
f1d6c61c5c
4 changed files with 74 additions and 6 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
39
src/route.rs
39
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<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)
|
||||
}
|
||||
}
|
||||
|
|
13
src/task.rs
13
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""
|
||||
|
|
Loading…
Reference in a new issue