1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 16:02:59 +01:00

Merge branch 'master' into websocket_close_reason

This commit is contained in:
Nikolay Kim 2018-04-25 20:17:19 -07:00 committed by GitHub
commit 927f2e594e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 142 additions and 72 deletions

View File

@ -1,9 +1,15 @@
# Changes # Changes
## 0.5.5 (2018-04-xx) ## 0.5.6 (2018-04-24)
* Make flate2 crate optional #200
## 0.5.5 (2018-04-24)
* Fix panic when Websocket is closed with no error code #191 * Fix panic when Websocket is closed with no error code #191
* Allow to use rust backend for flate2 crate #199
## 0.5.4 (2018-04-19) ## 0.5.4 (2018-04-19)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "0.5.5" version = "0.5.6"
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"
@ -26,7 +26,7 @@ name = "actix_web"
path = "src/lib.rs" path = "src/lib.rs"
[features] [features]
default = ["session", "brotli"] default = ["session", "brotli", "flate2-c"]
# tls # tls
tls = ["native-tls", "tokio-tls"] tls = ["native-tls", "tokio-tls"]
@ -34,19 +34,24 @@ tls = ["native-tls", "tokio-tls"]
# openssl # openssl
alpn = ["openssl", "tokio-openssl"] alpn = ["openssl", "tokio-openssl"]
# sessions # sessions feature, session require "ring" crate and c compiler
session = ["cookie/secure"] session = ["cookie/secure"]
# brotli encoding # brotli encoding, requires c compiler
brotli = ["brotli2"] brotli = ["brotli2"]
# miniz-sys backend for flate2 crate
flate2-c = ["flate2/miniz-sys"]
# rust backend for flate2 crate
flate2-rust = ["flate2/rust_backend"]
[dependencies] [dependencies]
actix = "^0.5.5" actix = "^0.5.5"
base64 = "0.9" base64 = "0.9"
bitflags = "1.0" bitflags = "1.0"
failure = "0.1.1" failure = "0.1.1"
flate2 = "1.0"
h2 = "0.1" h2 = "0.1"
http = "^0.1.5" http = "^0.1.5"
httparse = "1.2" httparse = "1.2"
@ -71,6 +76,7 @@ lazy_static = "1.0"
url = { version="1.7", features=["query_encoding"] } url = { version="1.7", features=["query_encoding"] }
cookie = { version="0.10", features=["percent-encode"] } cookie = { version="0.10", features=["percent-encode"] }
brotli2 = { version="^0.3.2", optional = true } brotli2 = { version="^0.3.2", optional = true }
flate2 = { version="1.0", optional = true, default-features = false }
# io # io
mio = "^0.6.13" mio = "^0.6.13"

View File

@ -54,9 +54,11 @@ fn main() {
* [Stateful](https://github.com/actix/examples/tree/master/state/) * [Stateful](https://github.com/actix/examples/tree/master/state/)
* [Protobuf support](https://github.com/actix/examples/tree/master/protobuf/) * [Protobuf support](https://github.com/actix/examples/tree/master/protobuf/)
* [Multipart streams](https://github.com/actix/examples/tree/master/multipart/) * [Multipart streams](https://github.com/actix/examples/tree/master/multipart/)
* [Simple websocket session](https://github.com/actix/examples/tree/master/websocket/) * [Simple websocket](https://github.com/actix/examples/tree/master/websocket/)
* [Tera templates](https://github.com/actix/examples/tree/master/template_tera/) * [Tera](https://github.com/actix/examples/tree/master/template_tera/) /
[Askama](https://github.com/actix/examples/tree/master/template_askama/) templates
* [Diesel integration](https://github.com/actix/examples/tree/master/diesel/) * [Diesel integration](https://github.com/actix/examples/tree/master/diesel/)
* [r2d2](https://github.com/actix/examples/tree/master/r2d2/)
* [SSL / HTTP/2.0](https://github.com/actix/examples/tree/master/tls/) * [SSL / HTTP/2.0](https://github.com/actix/examples/tree/master/tls/)
* [Tcp/Websocket chat](https://github.com/actix/examples/tree/master/websocket-chat/) * [Tcp/Websocket chat](https://github.com/actix/examples/tree/master/websocket-chat/)
* [Json](https://github.com/actix/examples/tree/master/json/) * [Json](https://github.com/actix/examples/tree/master/json/)

View File

@ -7,8 +7,10 @@ use std::io::{self, Write};
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
use brotli2::write::BrotliEncoder; use brotli2::write::BrotliEncoder;
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use flate2::Compression; #[cfg(feature = "flate2")]
use flate2::write::{DeflateEncoder, GzEncoder}; use flate2::write::{DeflateEncoder, GzEncoder};
#[cfg(feature = "flate2")]
use flate2::Compression;
use futures::{Async, Poll}; use futures::{Async, Poll};
use http::header::{HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE, use http::header::{HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE,
TRANSFER_ENCODING}; TRANSFER_ENCODING};
@ -18,9 +20,9 @@ use tokio_io::AsyncWrite;
use body::{Binary, Body}; use body::{Binary, Body};
use header::ContentEncoding; use header::ContentEncoding;
use server::WriterState;
use server::encoding::{ContentEncoder, TransferEncoding}; use server::encoding::{ContentEncoder, TransferEncoding};
use server::shared::SharedBytes; use server::shared::SharedBytes;
use server::WriterState;
use client::ClientRequest; use client::ClientRequest;
@ -70,7 +72,7 @@ impl HttpClientWriter {
// !self.flags.contains(Flags::UPGRADE) } // !self.flags.contains(Flags::UPGRADE) }
fn write_to_stream<T: AsyncWrite>( fn write_to_stream<T: AsyncWrite>(
&mut self, stream: &mut T &mut self, stream: &mut T,
) -> io::Result<WriterState> { ) -> io::Result<WriterState> {
while !self.buffer.is_empty() { while !self.buffer.is_empty() {
match stream.write(self.buffer.as_ref()) { match stream.write(self.buffer.as_ref()) {
@ -191,7 +193,7 @@ impl HttpClientWriter {
#[inline] #[inline]
pub fn poll_completed<T: AsyncWrite>( pub fn poll_completed<T: AsyncWrite>(
&mut self, stream: &mut T, shutdown: bool &mut self, stream: &mut T, shutdown: bool,
) -> Poll<(), io::Error> { ) -> Poll<(), io::Error> {
match self.write_to_stream(stream) { match self.write_to_stream(stream) {
Ok(WriterState::Done) => { Ok(WriterState::Done) => {
@ -222,9 +224,11 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
let tmp = SharedBytes::default(); let tmp = SharedBytes::default();
let transfer = TransferEncoding::eof(tmp.clone()); let transfer = TransferEncoding::eof(tmp.clone());
let mut enc = match encoding { let mut enc = match encoding {
#[cfg(feature = "flate2")]
ContentEncoding::Deflate => ContentEncoder::Deflate( ContentEncoding::Deflate => ContentEncoder::Deflate(
DeflateEncoder::new(transfer, Compression::default()), DeflateEncoder::new(transfer, Compression::default()),
), ),
#[cfg(feature = "flate2")]
ContentEncoding::Gzip => ContentEncoder::Gzip(GzEncoder::new( ContentEncoding::Gzip => ContentEncoder::Gzip(GzEncoder::new(
transfer, transfer,
Compression::default(), Compression::default(),
@ -283,10 +287,12 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
req.replace_body(body); req.replace_body(body);
match encoding { match encoding {
#[cfg(feature = "flate2")]
ContentEncoding::Deflate => ContentEncoder::Deflate(DeflateEncoder::new( ContentEncoding::Deflate => ContentEncoder::Deflate(DeflateEncoder::new(
transfer, transfer,
Compression::default(), Compression::default(),
)), )),
#[cfg(feature = "flate2")]
ContentEncoding::Gzip => { ContentEncoding::Gzip => {
ContentEncoder::Gzip(GzEncoder::new(transfer, Compression::default())) ContentEncoder::Gzip(GzEncoder::new(transfer, Compression::default()))
} }
@ -299,7 +305,7 @@ fn content_encoder(buf: SharedBytes, req: &mut ClientRequest) -> ContentEncoder
} }
fn streaming_encoding( fn streaming_encoding(
buf: SharedBytes, version: Version, req: &mut ClientRequest buf: SharedBytes, version: Version, req: &mut ClientRequest,
) -> TransferEncoding { ) -> TransferEncoding {
if req.chunked() { if req.chunked() {
// Enable transfer encoding // Enable transfer encoding

View File

@ -6,8 +6,8 @@ use std::str::FromStr;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use mime::Mime; use mime::Mime;
use modhttp::Error as HttpError;
use modhttp::header::GetAll; use modhttp::header::GetAll;
use modhttp::Error as HttpError;
pub use modhttp::header::*; pub use modhttp::header::*;
@ -116,8 +116,10 @@ pub enum ContentEncoding {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
Br, Br,
/// A format using the zlib structure with deflate algorithm /// A format using the zlib structure with deflate algorithm
#[cfg(feature = "flate2")]
Deflate, Deflate,
/// Gzip algorithm /// Gzip algorithm
#[cfg(feature = "flate2")]
Gzip, Gzip,
/// Indicates the identity function (i.e. no compression, nor modification) /// Indicates the identity function (i.e. no compression, nor modification)
Identity, Identity,
@ -137,7 +139,9 @@ impl ContentEncoding {
match *self { match *self {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
ContentEncoding::Br => "br", ContentEncoding::Br => "br",
#[cfg(feature = "flate2")]
ContentEncoding::Gzip => "gzip", ContentEncoding::Gzip => "gzip",
#[cfg(feature = "flate2")]
ContentEncoding::Deflate => "deflate", ContentEncoding::Deflate => "deflate",
ContentEncoding::Identity | ContentEncoding::Auto => "identity", ContentEncoding::Identity | ContentEncoding::Auto => "identity",
} }
@ -149,7 +153,9 @@ impl ContentEncoding {
match *self { match *self {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
ContentEncoding::Br => 1.1, ContentEncoding::Br => 1.1,
#[cfg(feature = "flate2")]
ContentEncoding::Gzip => 1.0, ContentEncoding::Gzip => 1.0,
#[cfg(feature = "flate2")]
ContentEncoding::Deflate => 0.9, ContentEncoding::Deflate => 0.9,
ContentEncoding::Identity | ContentEncoding::Auto => 0.1, ContentEncoding::Identity | ContentEncoding::Auto => 0.1,
} }
@ -159,10 +165,12 @@ impl ContentEncoding {
// TODO: remove memory allocation // TODO: remove memory allocation
impl<'a> From<&'a str> for ContentEncoding { impl<'a> From<&'a str> for ContentEncoding {
fn from(s: &'a str) -> ContentEncoding { fn from(s: &'a str) -> ContentEncoding {
match s.trim().to_lowercase().as_ref() { match AsRef::<str>::as_ref(&s.trim().to_lowercase()) {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
"br" => ContentEncoding::Br, "br" => ContentEncoding::Br,
#[cfg(feature = "flate2")]
"gzip" => ContentEncoding::Gzip, "gzip" => ContentEncoding::Gzip,
#[cfg(feature = "flate2")]
"deflate" => ContentEncoding::Deflate, "deflate" => ContentEncoding::Deflate,
_ => ContentEncoding::Identity, _ => ContentEncoding::Identity,
} }
@ -202,7 +210,7 @@ impl fmt::Write for Writer {
#[doc(hidden)] #[doc(hidden)]
/// Reads a comma-delimited raw header into a Vec. /// Reads a comma-delimited raw header into a Vec.
pub fn from_comma_delimited<T: FromStr>( pub fn from_comma_delimited<T: FromStr>(
all: GetAll<HeaderValue> all: GetAll<HeaderValue>,
) -> Result<Vec<T>, ParseError> { ) -> Result<Vec<T>, ParseError> {
let mut result = Vec::new(); let mut result = Vec::new();
for h in all { for h in all {

View File

@ -37,6 +37,7 @@ pub struct HttpInnerMessage {
pub addr: Option<SocketAddr>, pub addr: Option<SocketAddr>,
pub payload: Option<Payload>, pub payload: Option<Payload>,
pub info: Option<ConnectionInfo<'static>>, pub info: Option<ConnectionInfo<'static>>,
pub keep_alive: bool,
resource: RouterResource, resource: RouterResource,
} }
@ -56,11 +57,12 @@ impl Default for HttpInnerMessage {
params: Params::new(), params: Params::new(),
query: Params::new(), query: Params::new(),
query_loaded: false, query_loaded: false,
cookies: None,
addr: None, addr: None,
cookies: None,
payload: None, payload: None,
extensions: Extensions::new(), extensions: Extensions::new(),
info: None, info: None,
keep_alive: true,
resource: RouterResource::Notset, resource: RouterResource::Notset,
} }
} }
@ -70,20 +72,7 @@ impl HttpInnerMessage {
/// Checks if a connection should be kept alive. /// Checks if a connection should be kept alive.
#[inline] #[inline]
pub fn keep_alive(&self) -> bool { pub fn keep_alive(&self) -> bool {
if let Some(conn) = self.headers.get(header::CONNECTION) { self.keep_alive
if let Ok(conn) = conn.to_str() {
if self.version == Version::HTTP_10 && conn.contains("keep-alive") {
true
} else {
self.version == Version::HTTP_11
&& !(conn.contains("close") || conn.contains("upgrade"))
}
} else {
false
}
} else {
self.version != Version::HTTP_10
}
} }
#[inline] #[inline]
@ -91,12 +80,12 @@ impl HttpInnerMessage {
self.headers.clear(); self.headers.clear();
self.extensions.clear(); self.extensions.clear();
self.params.clear(); self.params.clear();
self.query.clear();
self.query_loaded = false;
self.cookies = None;
self.addr = None; self.addr = None;
self.info = None; self.info = None;
self.query_loaded = false;
self.cookies = None;
self.payload = None; self.payload = None;
self.keep_alive = true;
self.resource = RouterResource::Notset; self.resource = RouterResource::Notset;
} }
} }
@ -126,10 +115,11 @@ impl HttpRequest<()> {
params: Params::new(), params: Params::new(),
query: Params::new(), query: Params::new(),
query_loaded: false, query_loaded: false,
extensions: Extensions::new(),
cookies: None, cookies: None,
addr: None, addr: None,
extensions: Extensions::new(),
info: None, info: None,
keep_alive: true,
resource: RouterResource::Notset, resource: RouterResource::Notset,
}), }),
None, None,
@ -377,13 +367,13 @@ impl<S> HttpRequest<S> {
/// To get client connection information `connection_info()` method should /// To get client connection information `connection_info()` method should
/// be used. /// be used.
#[inline] #[inline]
pub fn peer_addr(&self) -> Option<&SocketAddr> { pub fn peer_addr(&self) -> Option<SocketAddr> {
self.as_ref().addr.as_ref() self.as_ref().addr
} }
#[inline] #[inline]
pub(crate) fn set_peer_addr(&mut self, addr: Option<SocketAddr>) { pub(crate) fn set_peer_addr(&mut self, addr: Option<SocketAddr>) {
self.as_mut().addr = addr self.as_mut().addr = addr;
} }
/// Get a reference to the Params object. /// Get a reference to the Params object.
@ -392,6 +382,7 @@ impl<S> HttpRequest<S> {
if !self.as_ref().query_loaded { if !self.as_ref().query_loaded {
let params: &mut Params = let params: &mut Params =
unsafe { mem::transmute(&mut self.as_mut().query) }; unsafe { mem::transmute(&mut self.as_mut().query) };
params.clear();
self.as_mut().query_loaded = true; self.as_mut().query_loaded = true;
for (key, val) in form_urlencoded::parse(self.query_string().as_ref()) { for (key, val) in form_urlencoded::parse(self.query_string().as_ref()) {
params.add(key, val); params.add(key, val);
@ -425,9 +416,9 @@ impl<S> HttpRequest<S> {
} }
} }
} }
msg.cookies = Some(cookies) msg.cookies = Some(cookies);
} }
Ok(self.as_ref().cookies.as_ref().unwrap()) Ok(&self.as_ref().cookies.as_ref().unwrap())
} }
/// Return request cookie. /// Return request cookie.

View File

@ -64,8 +64,10 @@
#![cfg_attr(actix_nightly, feature( #![cfg_attr(actix_nightly, feature(
specialization, // for impl ErrorResponse for std::error::Error specialization, // for impl ErrorResponse for std::error::Error
))] ))]
#![cfg_attr(feature = "cargo-clippy", #![cfg_attr(
allow(decimal_literal_representation, suspicious_arithmetic_impl))] feature = "cargo-clippy",
allow(decimal_literal_representation, suspicious_arithmetic_impl)
)]
#[macro_use] #[macro_use]
extern crate log; extern crate log;
@ -103,6 +105,7 @@ extern crate serde;
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
extern crate brotli2; extern crate brotli2;
extern crate encoding; extern crate encoding;
#[cfg(feature = "flate2")]
extern crate flate2; extern crate flate2;
extern crate h2 as http2; extern crate h2 as http2;
extern crate num_cpus; extern crate num_cpus;

View File

@ -6,11 +6,14 @@ use std::{cmp, io, mem};
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
use brotli2::write::{BrotliDecoder, BrotliEncoder}; use brotli2::write::{BrotliDecoder, BrotliEncoder};
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{BufMut, Bytes, BytesMut};
use flate2::Compression; #[cfg(feature = "flate2")]
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
#[cfg(feature = "flate2")]
use flate2::write::{DeflateDecoder, DeflateEncoder, GzEncoder}; use flate2::write::{DeflateDecoder, DeflateEncoder, GzEncoder};
use http::header::{HeaderMap, HeaderValue, ACCEPT_ENCODING, CONNECTION, #[cfg(feature = "flate2")]
CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING}; use flate2::Compression;
use http::header::{HeaderMap, HeaderValue, ACCEPT_ENCODING, CONTENT_ENCODING,
CONTENT_LENGTH, TRANSFER_ENCODING};
use http::{HttpTryFrom, Method, Version}; use http::{HttpTryFrom, Method, Version};
use body::{Binary, Body}; use body::{Binary, Body};
@ -144,7 +147,9 @@ impl PayloadWriter for EncodedPayload {
} }
pub(crate) enum Decoder { pub(crate) enum Decoder {
#[cfg(feature = "flate2")]
Deflate(Box<DeflateDecoder<Writer>>), Deflate(Box<DeflateDecoder<Writer>>),
#[cfg(feature = "flate2")]
Gzip(Option<Box<GzDecoder<Wrapper>>>), Gzip(Option<Box<GzDecoder<Wrapper>>>),
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
Br(Box<BrotliDecoder<Writer>>), Br(Box<BrotliDecoder<Writer>>),
@ -223,9 +228,11 @@ impl PayloadStream {
ContentEncoding::Br => { ContentEncoding::Br => {
Decoder::Br(Box::new(BrotliDecoder::new(Writer::new()))) Decoder::Br(Box::new(BrotliDecoder::new(Writer::new())))
} }
#[cfg(feature = "flate2")]
ContentEncoding::Deflate => { ContentEncoding::Deflate => {
Decoder::Deflate(Box::new(DeflateDecoder::new(Writer::new()))) Decoder::Deflate(Box::new(DeflateDecoder::new(Writer::new())))
} }
#[cfg(feature = "flate2")]
ContentEncoding::Gzip => Decoder::Gzip(None), ContentEncoding::Gzip => Decoder::Gzip(None),
_ => Decoder::Identity, _ => Decoder::Identity,
}; };
@ -251,6 +258,7 @@ impl PayloadStream {
} }
Err(e) => Err(e), Err(e) => Err(e),
}, },
#[cfg(feature = "flate2")]
Decoder::Gzip(ref mut decoder) => { Decoder::Gzip(ref mut decoder) => {
if let Some(ref mut decoder) = *decoder { if let Some(ref mut decoder) = *decoder {
decoder.as_mut().get_mut().eof = true; decoder.as_mut().get_mut().eof = true;
@ -267,6 +275,7 @@ impl PayloadStream {
Ok(None) Ok(None)
} }
} }
#[cfg(feature = "flate2")]
Decoder::Deflate(ref mut decoder) => match decoder.try_finish() { Decoder::Deflate(ref mut decoder) => match decoder.try_finish() {
Ok(_) => { Ok(_) => {
let b = decoder.get_mut().take(); let b = decoder.get_mut().take();
@ -297,6 +306,7 @@ impl PayloadStream {
} }
Err(e) => Err(e), Err(e) => Err(e),
}, },
#[cfg(feature = "flate2")]
Decoder::Gzip(ref mut decoder) => { Decoder::Gzip(ref mut decoder) => {
if decoder.is_none() { if decoder.is_none() {
*decoder = Some(Box::new(GzDecoder::new(Wrapper { *decoder = Some(Box::new(GzDecoder::new(Wrapper {
@ -334,6 +344,7 @@ impl PayloadStream {
} }
} }
} }
#[cfg(feature = "flate2")]
Decoder::Deflate(ref mut decoder) => match decoder.write_all(&data) { Decoder::Deflate(ref mut decoder) => match decoder.write_all(&data) {
Ok(_) => { Ok(_) => {
decoder.flush()?; decoder.flush()?;
@ -352,7 +363,9 @@ impl PayloadStream {
} }
pub(crate) enum ContentEncoder { pub(crate) enum ContentEncoder {
#[cfg(feature = "flate2")]
Deflate(DeflateEncoder<TransferEncoding>), Deflate(DeflateEncoder<TransferEncoding>),
#[cfg(feature = "flate2")]
Gzip(GzEncoder<TransferEncoding>), Gzip(GzEncoder<TransferEncoding>),
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
Br(BrotliEncoder<TransferEncoding>), Br(BrotliEncoder<TransferEncoding>),
@ -422,9 +435,11 @@ impl ContentEncoder {
let tmp = SharedBytes::default(); let tmp = SharedBytes::default();
let transfer = TransferEncoding::eof(tmp.clone()); let transfer = TransferEncoding::eof(tmp.clone());
let mut enc = match encoding { let mut enc = match encoding {
#[cfg(feature = "flate2")]
ContentEncoding::Deflate => ContentEncoder::Deflate( ContentEncoding::Deflate => ContentEncoder::Deflate(
DeflateEncoder::new(transfer, Compression::fast()), DeflateEncoder::new(transfer, Compression::fast()),
), ),
#[cfg(feature = "flate2")]
ContentEncoding::Gzip => ContentEncoder::Gzip(GzEncoder::new( ContentEncoding::Gzip => ContentEncoder::Gzip(GzEncoder::new(
transfer, transfer,
Compression::fast(), Compression::fast(),
@ -459,9 +474,6 @@ impl ContentEncoder {
if resp.upgrade() { if resp.upgrade() {
if version == Version::HTTP_2 { if version == Version::HTTP_2 {
error!("Connection upgrade is forbidden for HTTP/2"); error!("Connection upgrade is forbidden for HTTP/2");
} else {
resp.headers_mut()
.insert(CONNECTION, HeaderValue::from_static("upgrade"));
} }
if encoding != ContentEncoding::Identity { if encoding != ContentEncoding::Identity {
encoding = ContentEncoding::Identity; encoding = ContentEncoding::Identity;
@ -481,10 +493,12 @@ impl ContentEncoder {
} }
match encoding { match encoding {
#[cfg(feature = "flate2")]
ContentEncoding::Deflate => ContentEncoder::Deflate(DeflateEncoder::new( ContentEncoding::Deflate => ContentEncoder::Deflate(DeflateEncoder::new(
transfer, transfer,
Compression::fast(), Compression::fast(),
)), )),
#[cfg(feature = "flate2")]
ContentEncoding::Gzip => { ContentEncoding::Gzip => {
ContentEncoder::Gzip(GzEncoder::new(transfer, Compression::fast())) ContentEncoder::Gzip(GzEncoder::new(transfer, Compression::fast()))
} }
@ -497,7 +511,7 @@ impl ContentEncoder {
} }
fn streaming_encoding( fn streaming_encoding(
buf: SharedBytes, version: Version, resp: &mut HttpResponse buf: SharedBytes, version: Version, resp: &mut HttpResponse,
) -> TransferEncoding { ) -> TransferEncoding {
match resp.chunked() { match resp.chunked() {
Some(true) => { Some(true) => {
@ -566,7 +580,9 @@ impl ContentEncoder {
match *self { match *self {
#[cfg(feature = "brotli")] #[cfg(feature = "brotli")]
ContentEncoder::Br(ref encoder) => encoder.get_ref().is_eof(), ContentEncoder::Br(ref encoder) => encoder.get_ref().is_eof(),
#[cfg(feature = "flate2")]
ContentEncoder::Deflate(ref encoder) => encoder.get_ref().is_eof(), ContentEncoder::Deflate(ref encoder) => encoder.get_ref().is_eof(),
#[cfg(feature = "flate2")]
ContentEncoder::Gzip(ref encoder) => encoder.get_ref().is_eof(), ContentEncoder::Gzip(ref encoder) => encoder.get_ref().is_eof(),
ContentEncoder::Identity(ref encoder) => encoder.is_eof(), ContentEncoder::Identity(ref encoder) => encoder.is_eof(),
} }
@ -590,6 +606,7 @@ impl ContentEncoder {
} }
Err(err) => Err(err), Err(err) => Err(err),
}, },
#[cfg(feature = "flate2")]
ContentEncoder::Gzip(encoder) => match encoder.finish() { ContentEncoder::Gzip(encoder) => match encoder.finish() {
Ok(mut writer) => { Ok(mut writer) => {
writer.encode_eof(); writer.encode_eof();
@ -598,6 +615,7 @@ impl ContentEncoder {
} }
Err(err) => Err(err), Err(err) => Err(err),
}, },
#[cfg(feature = "flate2")]
ContentEncoder::Deflate(encoder) => match encoder.finish() { ContentEncoder::Deflate(encoder) => match encoder.finish() {
Ok(mut writer) => { Ok(mut writer) => {
writer.encode_eof(); writer.encode_eof();
@ -628,6 +646,7 @@ impl ContentEncoder {
} }
} }
} }
#[cfg(feature = "flate2")]
ContentEncoder::Gzip(ref mut encoder) => { ContentEncoder::Gzip(ref mut encoder) => {
match encoder.write_all(data.as_ref()) { match encoder.write_all(data.as_ref()) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
@ -637,6 +656,7 @@ impl ContentEncoder {
} }
} }
} }
#[cfg(feature = "flate2")]
ContentEncoder::Deflate(ref mut encoder) => { ContentEncoder::Deflate(ref mut encoder) => {
match encoder.write_all(data.as_ref()) { match encoder.write_all(data.as_ref()) {
Ok(_) => Ok(()), Ok(_) => Ok(()),

View File

@ -510,9 +510,10 @@ impl Reader {
buf: &mut BytesMut, settings: &WorkerSettings<H> buf: &mut BytesMut, settings: &WorkerSettings<H>
) -> Poll<(HttpRequest, Option<PayloadInfo>), ParseError> { ) -> Poll<(HttpRequest, Option<PayloadInfo>), ParseError> {
// Parse http message // Parse http message
let mut has_te = false;
let mut has_upgrade = false; let mut has_upgrade = false;
let mut has_length = false; let mut chunked = false;
let mut content_length = None;
let msg = { let msg = {
let bytes_ptr = buf.as_ref().as_ptr() as usize; let bytes_ptr = buf.as_ref().as_ptr() as usize;
let mut headers: [httparse::Header; MAX_HEADERS] = let mut headers: [httparse::Header; MAX_HEADERS] =
@ -546,10 +547,10 @@ impl Reader {
let msg = settings.get_http_message(); let msg = settings.get_http_message();
{ {
let msg_mut = msg.get_mut(); let msg_mut = msg.get_mut();
msg_mut.keep_alive = version != Version::HTTP_10;
for header in headers[..headers_len].iter() { for header in headers[..headers_len].iter() {
if let Ok(name) = HeaderName::from_bytes(header.name.as_bytes()) { if let Ok(name) = HeaderName::from_bytes(header.name.as_bytes()) {
has_te = has_te || name == header::TRANSFER_ENCODING;
has_length = has_length || name == header::CONTENT_LENGTH;
has_upgrade = has_upgrade || name == header::UPGRADE; has_upgrade = has_upgrade || name == header::UPGRADE;
let v_start = header.value.as_ptr() as usize - bytes_ptr; let v_start = header.value.as_ptr() as usize - bytes_ptr;
let v_end = v_start + header.value.len(); let v_end = v_start + header.value.len();
@ -558,6 +559,47 @@ impl Reader {
slice.slice(v_start, v_end), slice.slice(v_start, v_end),
) )
}; };
match name {
header::CONTENT_LENGTH => {
if let Ok(s) = value.to_str() {
if let Ok(len) = s.parse::<u64>() {
content_length = Some(len)
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header);
}
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header);
}
},
// transfer-encoding
header::TRANSFER_ENCODING => {
if let Ok(s) = value.to_str() {
chunked = s.to_lowercase().contains("chunked");
} else {
return Err(ParseError::Header)
}
},
// connection keep-alive state
header::CONNECTION => {
msg_mut.keep_alive = if let Ok(conn) = value.to_str() {
if version == Version::HTTP_10
&& conn.contains("keep-alive")
{
true
} else {
version == Version::HTTP_11
&& !(conn.contains("close")
|| conn.contains("upgrade"))
}
} else {
false
};
},
_ => (),
}
msg_mut.headers.append(name, value); msg_mut.headers.append(name, value);
} else { } else {
return Err(ParseError::Header); return Err(ParseError::Header);
@ -572,26 +614,12 @@ impl Reader {
}; };
// https://tools.ietf.org/html/rfc7230#section-3.3.3 // https://tools.ietf.org/html/rfc7230#section-3.3.3
let decoder = if has_te && chunked(&msg.get_mut().headers)? { let decoder = if chunked {
// Chunked encoding // Chunked encoding
Some(Decoder::chunked()) Some(Decoder::chunked())
} else if has_length { } else if let Some(len) = content_length {
// Content-Length // Content-Length
let len = msg.get_ref() Some(Decoder::length(len))
.headers
.get(header::CONTENT_LENGTH)
.unwrap();
if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<u64>() {
Some(Decoder::length(len))
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header);
}
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header);
}
} else if has_upgrade || msg.get_ref().method == Method::CONNECT { } else if has_upgrade || msg.get_ref().method == Method::CONNECT {
// upgrade(websocket) or connect // upgrade(websocket) or connect
Some(Decoder::eof()) Some(Decoder::eof())

View File

@ -2,8 +2,6 @@
use bytes::BufMut; use bytes::BufMut;
use futures::{Async, Poll}; use futures::{Async, Poll};
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE};
use http::{Method, Version};
use std::rc::Rc; use std::rc::Rc;
use std::{io, mem}; use std::{io, mem};
use tokio_io::AsyncWrite; use tokio_io::AsyncWrite;
@ -17,6 +15,8 @@ use body::{Binary, Body};
use header::ContentEncoding; use header::ContentEncoding;
use httprequest::HttpInnerMessage; use httprequest::HttpInnerMessage;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use http::{Method, Version};
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE};
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific