mirror of
https://github.com/actix/actix-web.git
synced 2024-12-23 00:26:34 +00:00
Merge branch 'master' into websocket_close_reason
This commit is contained in:
commit
927f2e594e
10 changed files with 142 additions and 72 deletions
|
@ -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)
|
||||||
|
|
||||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -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"
|
||||||
|
|
|
@ -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/)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(()),
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue