/*!
A potentially non-blocking response decoder.
The decoder wraps a stream of chunks and produces a new stream of decompressed chunks.
The decompressed chunks aren't guaranteed to align to the compressed ones.
If the response is plaintext then no additional work is carried out.
Chunks are just passed along.
If the response is gzip, then the chunks are decompressed into a buffer.
Slices of that buffer are emitted as new chunks.
This module consists of a few main types:
- `ReadableChunks` is a `Read`-like wrapper around a stream
- `Decoder` is a layer over `ReadableChunks` that applies the right decompression
The following types directly support the gzip compression case:
- `Pending` is a non-blocking constructor for a `Decoder` in case the body needs to be checked for EOF
- `Peeked` is a buffer that keeps a few bytes available so `libflate`s `read_exact` calls won't fail
*/
use std::fmt;
use std::mem;
use std::cmp;
use std::io::{self, Read};
use std::marker::PhantomData;
use bytes::{BufMut, BytesMut};
use libflate::non_blocking::gzip;
use tokio_io::AsyncRead;
use tokio_io::io as async_io;
use futures::{Async, Future, Poll, Stream};
use futures::stream::Concat2;
use hyper::StatusCode;
use serde::de::DeserializeOwned;
use serde_json;
use url::Url;
use header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use super::{body, Body, Chunk};
use error;
const INIT_BUFFER_SIZE: usize = 8192;
/// A response decompressor over a non-blocking stream of chunks.
///
/// The inner decoder may be constructed asynchronously.
pub struct Decoder {
inner: Inner
}
enum Inner {
/// A `PlainText` decoder just returns the response content as is.
PlainText(Body),
/// A `Gzip` decoder will uncompress the gzipped response content before returning it.
Gzip(Gzip),
/// A decoder that doesn't have a value yet.
Pending(Pending)
}
enum Pending {
/// An unreachable internal state.
Empty,
/// A future attempt to poll the response body for EOF so we know whether to use gzip or not.
Gzip(ReadableChunks
)
}
/// A gzip decoder that reads from a `libflate::gzip::Decoder` into a `BytesMut` and emits the results
/// as a `Chunk`.
struct Gzip {
inner: gzip::Decoder>>,
buf: BytesMut,
}
impl fmt::Debug for Decoder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Decoder")
.finish()
}
}
impl Decoder {
#[inline]
fn plain_text(body: Body) -> Decoder {
Decoder {
inner: Inner::PlainText(body)
}
}
#[inline]
fn gzip(mut body: Body) -> Decoder {
Decoder {
inner: Inner::Pending(Pending::Gzip(ReadableChunks::new(body)))
}
}
}
impl Stream for Decoder {
type Item = Chunk;
type Error = error::Error;
fn poll(&mut self) -> Poll