mirror of
https://github.com/actix/actix-web.git
synced 2024-11-20 08:31:09 +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
|
/// Write payload
|
||||||
pub fn write(&mut self, data: Bytes) {
|
pub fn write<B: Into<Bytes>>(&mut self, data: B) {
|
||||||
self.stream.push_back(Frame::Payload(Some(data)))
|
self.stream.push_back(Frame::Payload(Some(data.into())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicate end of streamimng payload
|
/// Indicate end of streamimng payload
|
||||||
|
|
|
@ -12,10 +12,29 @@ use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
|
||||||
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
||||||
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
|
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
|
||||||
pub const HTTPNoContent: StaticResponse = StaticResponse(StatusCode::NO_CONTENT);
|
pub const HTTPNoContent: StaticResponse = StaticResponse(StatusCode::NO_CONTENT);
|
||||||
|
|
||||||
pub const HTTPBadRequest: StaticResponse = StaticResponse(StatusCode::BAD_REQUEST);
|
pub const HTTPBadRequest: StaticResponse = StaticResponse(StatusCode::BAD_REQUEST);
|
||||||
pub const HTTPNotFound: StaticResponse = StaticResponse(StatusCode::NOT_FOUND);
|
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 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 =
|
pub const HTTPInternalServerError: StaticResponse =
|
||||||
StaticResponse(StatusCode::INTERNAL_SERVER_ERROR);
|
StaticResponse(StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
|
@ -34,6 +53,9 @@ impl StaticResponse {
|
||||||
resp.set_reason(reason);
|
resp.set_reason(reason);
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
pub fn with_body(self, body: Body) -> HttpResponse {
|
||||||
|
HttpResponse::new(self.0, body)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> RouteHandler<S> for StaticResponse {
|
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 actix::Actor;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use http::{header, Version};
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
|
@ -11,7 +12,8 @@ use context::HttpContext;
|
||||||
use resource::Reply;
|
use resource::Reply;
|
||||||
use payload::Payload;
|
use payload::Payload;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::{Body, HttpResponse};
|
||||||
|
use httpcodes::HTTPExpectationFailed;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -31,11 +33,39 @@ pub trait RouteHandler<S>: 'static {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actors with ability to handle http requests
|
/// Actors with ability to handle http requests
|
||||||
|
#[allow(unused_variables)]
|
||||||
pub trait Route: Actor {
|
pub trait Route: Actor {
|
||||||
/// Route shared state. State is shared with all routes within same application and could be
|
/// Route shared state. State is shared with all routes within same application and could be
|
||||||
/// accessed with `HttpContext::state()` method.
|
/// accessed with `HttpContext::state()` method.
|
||||||
type State;
|
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
|
/// Handle incoming request. Route actor can return
|
||||||
/// result immediately with `Reply::reply` or `Reply::with`.
|
/// result immediately with `Reply::reply` or `Reply::with`.
|
||||||
/// Actor itself could be returned for handling streaming request/response.
|
/// 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
|
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<A::State>) -> Task
|
||||||
{
|
{
|
||||||
let mut ctx = HttpContext::new(state);
|
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)
|
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,
|
buffer: BytesMut,
|
||||||
upgrade: bool,
|
upgrade: bool,
|
||||||
keepalive: bool,
|
keepalive: bool,
|
||||||
|
prepared: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
|
@ -92,6 +93,7 @@ impl Task {
|
||||||
buffer: BytesMut::new(),
|
buffer: BytesMut::new(),
|
||||||
upgrade: false,
|
upgrade: false,
|
||||||
keepalive: false,
|
keepalive: false,
|
||||||
|
prepared: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +109,7 @@ impl Task {
|
||||||
buffer: BytesMut::new(),
|
buffer: BytesMut::new(),
|
||||||
upgrade: false,
|
upgrade: false,
|
||||||
keepalive: false,
|
keepalive: false,
|
||||||
|
prepared: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +125,7 @@ impl Task {
|
||||||
let body = msg.replace_body(Body::Empty);
|
let body = msg.replace_body(Body::Empty);
|
||||||
let version = msg.version().unwrap_or_else(|| req.version);
|
let version = msg.version().unwrap_or_else(|| req.version);
|
||||||
self.keepalive = msg.keep_alive().unwrap_or_else(|| req.keep_alive);
|
self.keepalive = msg.keep_alive().unwrap_or_else(|| req.keep_alive);
|
||||||
|
self.prepared = true;
|
||||||
|
|
||||||
match body {
|
match body {
|
||||||
Body::Empty => {
|
Body::Empty => {
|
||||||
|
@ -248,8 +252,13 @@ impl Task {
|
||||||
Frame::Payload(chunk) => {
|
Frame::Payload(chunk) => {
|
||||||
match chunk {
|
match chunk {
|
||||||
Some(chunk) => {
|
Some(chunk) => {
|
||||||
// TODO: add warning, write after EOF
|
if self.prepared {
|
||||||
self.encoder.encode(&mut self.buffer, chunk.as_ref());
|
// 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 => {
|
None => {
|
||||||
// TODO: add error "not eof""
|
// TODO: add error "not eof""
|
||||||
|
|
Loading…
Reference in a new issue