mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-24 00:21:08 +01:00
Send HTTP/1.1 100 Continue if request contains expect: continue header #634
This commit is contained in:
parent
e9fe3879df
commit
477bf0d8ae
@ -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
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -874,13 +896,13 @@ mod tests {
|
|||||||
let settings = wrk_settings();
|
let settings = wrk_settings();
|
||||||
|
|
||||||
let mut reader = H1Decoder::new();
|
let mut reader = H1Decoder::new();
|
||||||
assert!{ reader.decode(&mut buf, &settings).unwrap().is_none() }
|
assert! { reader.decode(&mut buf, &settings).unwrap().is_none() }
|
||||||
|
|
||||||
buf.extend(b"t");
|
buf.extend(b"t");
|
||||||
assert!{ reader.decode(&mut buf, &settings).unwrap().is_none() }
|
assert! { reader.decode(&mut buf, &settings).unwrap().is_none() }
|
||||||
|
|
||||||
buf.extend(b"es");
|
buf.extend(b"es");
|
||||||
assert!{ reader.decode(&mut buf, &settings).unwrap().is_none() }
|
assert! { reader.decode(&mut buf, &settings).unwrap().is_none() }
|
||||||
|
|
||||||
buf.extend(b"t: value\r\n\r\n");
|
buf.extend(b"t: value\r\n\r\n");
|
||||||
match reader.decode(&mut buf, &settings) {
|
match reader.decode(&mut buf, &settings) {
|
||||||
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user