1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 01:32:57 +01:00

Send HTTP/1.1 100 Continue if request contains expect: continue header #634

This commit is contained in:
Nikolay Kim 2018-12-23 10:19:12 -08:00
parent e9fe3879df
commit 477bf0d8ae
4 changed files with 64 additions and 23 deletions

View File

@ -1,11 +1,13 @@
# Changes # Changes
## [0.7.17] - 2018-xx-xx ## [0.7.17] - 2018-12-23
### Added ### Added
* Support for custom content types in `JsonConfig`. #637 * Support for custom content types in `JsonConfig`. #637
* Send `HTTP/1.1 100 Continue` if request contains `expect: continue` header #634
### Fixed ### Fixed
* HTTP1 decoder should perform case-insentive comparison for client requests (e.g. `Keep-Alive`). #631 * HTTP1 decoder should perform case-insentive comparison for client requests (e.g. `Keep-Alive`). #631

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "0.7.16" version = "0.7.17"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md" readme = "README.md"
@ -62,7 +62,7 @@ cell = ["actix-net/cell"]
[dependencies] [dependencies]
actix = "0.7.9" actix = "0.7.9"
actix-net = "0.2.2" actix-net = "0.2.6"
askama_escape = "0.1.0" askama_escape = "0.1.0"
base64 = "0.10" base64 = "0.10"
@ -105,7 +105,7 @@ slab = "0.4"
tokio = "0.1" tokio = "0.1"
tokio-io = "0.1" tokio-io = "0.1"
tokio-tcp = "0.1" tokio-tcp = "0.1"
tokio-timer = "0.2" tokio-timer = "0.2.8"
tokio-reactor = "0.1" tokio-reactor = "0.1"
tokio-current-thread = "0.1" tokio-current-thread = "0.1"

View File

@ -7,6 +7,7 @@ use futures::{Async, Future, Poll};
use tokio_current_thread::spawn; use tokio_current_thread::spawn;
use tokio_timer::Delay; use tokio_timer::Delay;
use body::Binary;
use error::{Error, PayloadError}; use error::{Error, PayloadError};
use http::{StatusCode, Version}; use http::{StatusCode, Version};
use payload::{Payload, PayloadStatus, PayloadWriter}; use payload::{Payload, PayloadStatus, PayloadWriter};
@ -50,32 +51,40 @@ pub struct Http1Dispatcher<T: IoStream, H: HttpHandler + 'static> {
} }
enum Entry<H: HttpHandler> { enum Entry<H: HttpHandler> {
Task(H::Task), Task(H::Task, Option<()>),
Error(Box<HttpHandlerTask>), Error(Box<HttpHandlerTask>),
} }
impl<H: HttpHandler> Entry<H> { impl<H: HttpHandler> Entry<H> {
fn into_task(self) -> H::Task { fn into_task(self) -> H::Task {
match self { match self {
Entry::Task(task) => task, Entry::Task(task, _) => task,
Entry::Error(_) => panic!(), Entry::Error(_) => panic!(),
} }
} }
fn disconnected(&mut self) { fn disconnected(&mut self) {
match *self { match *self {
Entry::Task(ref mut task) => task.disconnected(), Entry::Task(ref mut task, _) => task.disconnected(),
Entry::Error(ref mut task) => task.disconnected(), Entry::Error(ref mut task) => task.disconnected(),
} }
} }
fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error> { fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error> {
match *self { match *self {
Entry::Task(ref mut task) => task.poll_io(io), Entry::Task(ref mut task, ref mut except) => {
match except {
Some(_) => {
let _ = io.write(&Binary::from("HTTP/1.1 100 Continue\r\n\r\n"));
}
_ => (),
};
task.poll_io(io)
}
Entry::Error(ref mut task) => task.poll_io(io), Entry::Error(ref mut task) => task.poll_io(io),
} }
} }
fn poll_completed(&mut self) -> Poll<(), Error> { fn poll_completed(&mut self) -> Poll<(), Error> {
match *self { match *self {
Entry::Task(ref mut task) => task.poll_completed(), Entry::Task(ref mut task, _) => task.poll_completed(),
Entry::Error(ref mut task) => task.poll_completed(), Entry::Error(ref mut task) => task.poll_completed(),
} }
} }
@ -463,7 +472,11 @@ where
'outer: loop { 'outer: loop {
match self.decoder.decode(&mut self.buf, &self.settings) { match self.decoder.decode(&mut self.buf, &self.settings) {
Ok(Some(Message::Message { mut msg, payload })) => { Ok(Some(Message::Message {
mut msg,
mut expect,
payload,
})) => {
updated = true; updated = true;
self.flags.insert(Flags::STARTED); self.flags.insert(Flags::STARTED);
@ -484,6 +497,12 @@ where
match self.settings.handler().handle(msg) { match self.settings.handler().handle(msg) {
Ok(mut task) => { Ok(mut task) => {
if self.tasks.is_empty() { if self.tasks.is_empty() {
if expect {
expect = false;
let _ = self.stream.write(&Binary::from(
"HTTP/1.1 100 Continue\r\n\r\n",
));
}
match task.poll_io(&mut self.stream) { match task.poll_io(&mut self.stream) {
Ok(Async::Ready(ready)) => { Ok(Async::Ready(ready)) => {
// override keep-alive state // override keep-alive state
@ -510,7 +529,10 @@ where
} }
} }
} }
self.tasks.push_back(Entry::Task(task)); self.tasks.push_back(Entry::Task(
task,
if expect { Some(()) } else { None },
));
continue 'outer; continue 'outer;
} }
Err(_) => { Err(_) => {
@ -608,13 +630,13 @@ mod tests {
impl Message { impl Message {
fn message(self) -> Request { fn message(self) -> Request {
match self { match self {
Message::Message { msg, payload: _ } => msg, Message::Message { msg, .. } => msg,
_ => panic!("error"), _ => panic!("error"),
} }
} }
fn is_payload(&self) -> bool { fn is_payload(&self) -> bool {
match *self { match *self {
Message::Message { msg: _, payload } => payload, Message::Message { payload, .. } => payload,
_ => panic!("error"), _ => panic!("error"),
} }
} }

View File

@ -20,7 +20,11 @@ pub(crate) struct H1Decoder {
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum Message { pub(crate) enum Message {
Message { msg: Request, payload: bool }, Message {
msg: Request,
payload: bool,
expect: bool,
},
Chunk(Bytes), Chunk(Bytes),
Eof, Eof,
} }
@ -63,10 +67,11 @@ impl H1Decoder {
.parse_message(src, settings) .parse_message(src, settings)
.map_err(DecoderError::Error)? .map_err(DecoderError::Error)?
{ {
Async::Ready((msg, decoder)) => { Async::Ready((msg, expect, decoder)) => {
self.decoder = decoder; self.decoder = decoder;
Ok(Some(Message::Message { Ok(Some(Message::Message {
msg, msg,
expect,
payload: self.decoder.is_some(), payload: self.decoder.is_some(),
})) }))
} }
@ -85,11 +90,12 @@ impl H1Decoder {
&self, &self,
buf: &mut BytesMut, buf: &mut BytesMut,
settings: &ServiceConfig<H>, settings: &ServiceConfig<H>,
) -> Poll<(Request, Option<EncodingDecoder>), ParseError> { ) -> Poll<(Request, bool, Option<EncodingDecoder>), ParseError> {
// Parse http message // Parse http message
let mut has_upgrade = false; let mut has_upgrade = false;
let mut chunked = false; let mut chunked = false;
let mut content_length = None; let mut content_length = None;
let mut expect_continue = false;
let msg = { let msg = {
// Unsafe: we read only this data only after httparse parses headers into. // Unsafe: we read only this data only after httparse parses headers into.
@ -165,11 +171,17 @@ impl H1Decoder {
} }
// connection keep-alive state // connection keep-alive state
header::CONNECTION => { header::CONNECTION => {
let ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim()) { let ka = if let Ok(conn) =
if version == Version::HTTP_10 && conn.eq_ignore_ascii_case("keep-alive") { value.to_str().map(|conn| conn.trim())
{
if version == Version::HTTP_10
&& conn.eq_ignore_ascii_case("keep-alive")
{
true true
} else { } else {
version == Version::HTTP_11 && !(conn.eq_ignore_ascii_case("close") || conn.eq_ignore_ascii_case("upgrade")) version == Version::HTTP_11
&& !(conn.eq_ignore_ascii_case("close")
|| conn.eq_ignore_ascii_case("upgrade"))
} }
} else { } else {
false false
@ -186,6 +198,11 @@ impl H1Decoder {
} }
} }
} }
header::EXPECT => {
if value == "100-continue" {
expect_continue = true
}
}
_ => (), _ => (),
} }
@ -216,7 +233,7 @@ impl H1Decoder {
None None
}; };
Ok(Async::Ready((msg, decoder))) Ok(Async::Ready((msg, expect_continue, decoder)))
} }
} }