diff --git a/.travis.yml b/.travis.yml index 5dac530c..0419ed11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,7 @@ script: # Upload docs after_success: - | - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_RUST_VERSION" == "nightly-2018-01-03" ]]; then + if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_RUST_VERSION" == "1.20.0" ]]; then cargo doc --features alpn --no-deps && echo "" > target/doc/index.html && cargo install mdbook && diff --git a/CHANGES.md b/CHANGES.md index 22b42266..df1308f8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,7 @@ # Changes -## 0.3.0 (2017-xx-xx) +## 0.3.0 (2018-01-12) * HTTP/2 Support diff --git a/Cargo.toml b/Cargo.toml index 80a53a9c..cc87f724 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,10 +36,11 @@ alpn = ["openssl", "openssl/v102", "openssl/v110", "tokio-openssl"] log = "0.4" failure = "0.1" failure_derive = "0.1" -time = "0.1" +h2 = "0.1" http = "^0.1.2" httparse = "1.2" http-range = "0.1" +time = "0.1" mime = "0.3" mime_guess = "1.8" regex = "0.2" @@ -54,8 +55,7 @@ smallvec = "0.6" bitflags = "1.0" num_cpus = "1.0" -#flate2 = "1.0" -flate2 = { git="https://github.com/fafhrd91/flate2-rs.git" } +flate2 = "1.0" # temp solution # cookie = { version="0.10", features=["percent-encode", "secure"] } @@ -69,8 +69,6 @@ futures = "0.1" tokio-io = "0.1" tokio-core = "0.1" -h2 = { git = 'https://github.com/carllerche/h2' } - # native-tls native-tls = { version="0.1", optional = true } tokio-tls = { version="0.1", optional = true } diff --git a/src/server/encoding.rs b/src/server/encoding.rs index f9dbd64c..deb4a543 100644 --- a/src/server/encoding.rs +++ b/src/server/encoding.rs @@ -1,5 +1,5 @@ use std::{io, cmp, mem}; -use std::io::Write; +use std::io::{Read, Write}; use std::fmt::Write as FmtWrite; use std::str::FromStr; @@ -8,7 +8,8 @@ use http::header::{HeaderMap, HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING}; use flate2::Compression; -use flate2::write::{GzDecoder, GzEncoder, DeflateDecoder, DeflateEncoder}; +use flate2::read::GzDecoder; +use flate2::write::{GzEncoder, DeflateDecoder, DeflateEncoder}; use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::{Bytes, BytesMut, BufMut, Writer}; @@ -122,15 +123,50 @@ impl PayloadWriter for PayloadType { enum Decoder { Deflate(Box>>), - Gzip(Box>>), + Gzip(Option>>), Br(Box>>), Identity, } +// should go after write::GzDecoder get implemented +#[derive(Debug)] +struct Wrapper { + buf: BytesMut, + eof: bool, +} + +impl io::Read for Wrapper { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = cmp::min(buf.len(), self.buf.len()); + buf[..len].copy_from_slice(&self.buf[..len]); + self.buf.split_to(len); + if len == 0 { + if self.eof { + Ok(0) + } else { + Err(io::Error::new(io::ErrorKind::WouldBlock, "")) + } + } else { + Ok(len) + } + } +} + +impl io::Write for Wrapper { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buf.extend_from_slice(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + /// Payload wrapper with content decompression support pub(crate) struct EncodedPayload { inner: PayloadSender, decoder: Decoder, + dst: BytesMut, error: bool, } @@ -141,11 +177,10 @@ impl EncodedPayload { Box::new(BrotliDecoder::new(BytesMut::with_capacity(8192).writer()))), ContentEncoding::Deflate => Decoder::Deflate( Box::new(DeflateDecoder::new(BytesMut::with_capacity(8192).writer()))), - ContentEncoding::Gzip => Decoder::Gzip( - Box::new(GzDecoder::new(BytesMut::with_capacity(8192).writer()))), + ContentEncoding::Gzip => Decoder::Gzip(None), _ => Decoder::Identity, }; - EncodedPayload{ inner: inner, decoder: dec, error: false } + EncodedPayload{ inner: inner, decoder: dec, error: false, dst: BytesMut::new() } } } @@ -174,16 +209,28 @@ impl PayloadWriter for EncodedPayload { } }, Decoder::Gzip(ref mut decoder) => { - match decoder.try_finish() { - Ok(_) => { - let b = decoder.get_mut().get_mut().take().freeze(); - if !b.is_empty() { - self.inner.feed_data(b); + if let Some(ref mut decoder) = *decoder { + decoder.as_mut().get_mut().eof = true; + + loop { + self.dst.reserve(8192); + match decoder.read(unsafe{self.dst.bytes_mut()}) { + Ok(n) => { + if n == 0 { + self.inner.feed_eof(); + return + } else { + unsafe{self.dst.set_len(n)}; + self.inner.feed_data(self.dst.split_to(n).freeze()); + } + } + Err(err) => { + break Some(err); + } } - self.inner.feed_eof(); - return - }, - Err(err) => Some(err), + } + } else { + return } }, Decoder::Deflate(ref mut decoder) => { @@ -231,14 +278,33 @@ impl PayloadWriter for EncodedPayload { } Decoder::Gzip(ref mut decoder) => { - if decoder.write(&data).is_ok() && decoder.flush().is_ok() { - let b = decoder.get_mut().get_mut().take().freeze(); - if !b.is_empty() { - self.inner.feed_data(b); - } - return + if decoder.is_none() { + *decoder = Some( + Box::new(GzDecoder::new( + Wrapper{buf: BytesMut::from(data), eof: false}))); + } else { + let _ = decoder.as_mut().unwrap().write(&data); + } + + loop { + self.dst.reserve(8192); + match decoder.as_mut().as_mut().unwrap().read(unsafe{self.dst.bytes_mut()}) { + Ok(n) => { + if n == 0 { + return + } else { + unsafe{self.dst.set_len(n)}; + self.inner.feed_data(self.dst.split_to(n).freeze()); + } + } + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock { + return + } + break + } + } } - trace!("Error decoding gzip encoding"); } Decoder::Deflate(ref mut decoder) => {