lynx 0.2.0

Small and lightweight HTTP client
Documentation
use std::io::{self, Read};

use http::header::HeaderMap;
#[cfg(feature = "compress")]
use http::header::CONTENT_ENCODING;
#[cfg(feature = "compress")]
use libflate::{deflate, gzip};

#[cfg(feature = "compress")]
use crate::error::HttpError;
use crate::error::HttpResult;
use crate::parsing::body_reader::BodyReader;

pub enum CompressedReader {
    Plain(BodyReader),
    #[cfg(feature = "compress")]
    Deflate(deflate::Decoder<BodyReader>),
    #[cfg(feature = "compress")]
    Gzip(gzip::Decoder<BodyReader>),
}

impl CompressedReader {
    #[cfg(feature = "compress")]
    pub fn new(headers: &HeaderMap, reader: BodyReader) -> HttpResult<CompressedReader> {
        if let Some(content_encoding) = headers.get(CONTENT_ENCODING).map(|v| v.as_bytes()) {
            match content_encoding {
                b"deflate" => Ok(CompressedReader::Deflate(deflate::Decoder::new(reader))),
                b"gzip" => Ok(CompressedReader::Gzip(gzip::Decoder::new(reader)?)),
                _ => Err(HttpError::InvalidResponse("invalid Content-Encoding header")),
            }
        } else {
            Ok(CompressedReader::Plain(reader))
        }
    }

    #[cfg(not(feature = "compress"))]
    pub fn new(_: &HeaderMap, reader: BodyReader) -> HttpResult<CompressedReader> {
        Ok(CompressedReader::Plain(reader))
    }
}

impl Read for CompressedReader {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            CompressedReader::Plain(s) => s.read(buf),
            #[cfg(feature = "compress")]
            CompressedReader::Deflate(s) => s.read(buf),
            #[cfg(feature = "compress")]
            CompressedReader::Gzip(s) => s.read(buf),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io::prelude::*;

    #[cfg(feature = "compress")]
    use libflate::{deflate, gzip};

    use crate::parsing::response::parse_response;
    use crate::streams::BaseStream;
    use crate::Request;

    #[test]
    fn test_stream_plain() {
        let payload = b"Hello world!!!!!!!!";

        let mut buf: Vec<u8> = Vec::new();
        let _ = write!(buf, "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n", payload.len());
        buf.extend(payload);

        let req = Request::get("http://google.ca");

        let sock = BaseStream::mock(buf);
        let (_, _, response) = parse_response(sock, &req).unwrap();
        assert_eq!(response.string().unwrap(), "Hello world!!!!!!!!");
    }

    #[test]
    #[cfg(feature = "compress")]
    fn test_stream_deflate() {
        let mut payload = Vec::new();
        let mut enc = deflate::Encoder::new(&mut payload);
        enc.write_all(b"Hello world!!!!!!!!").unwrap();
        enc.finish();

        let mut buf: Vec<u8> = Vec::new();
        let _ = write!(
            buf,
            "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nContent-Encoding: deflate\r\n\r\n",
            payload.len()
        );
        buf.extend(payload);

        let req = Request::get("http://google.ca");

        let sock = BaseStream::mock(buf);
        let (_, _, response) = parse_response(sock, &req).unwrap();
        assert_eq!(response.string().unwrap(), "Hello world!!!!!!!!");
    }

    #[test]
    #[cfg(feature = "compress")]
    fn test_stream_gzip() {
        let mut payload = Vec::new();
        let mut enc = gzip::Encoder::new(&mut payload).unwrap();
        enc.write_all(b"Hello world!!!!!!!!").unwrap();
        enc.finish();

        let mut buf: Vec<u8> = Vec::new();
        let _ = write!(
            buf,
            "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nContent-Encoding: gzip\r\n\r\n",
            payload.len()
        );
        buf.extend(payload);

        let req = Request::get("http://google.ca");

        let sock = BaseStream::mock(buf);
        let (_, _, response) = parse_response(sock, &req).unwrap();

        assert_eq!(response.string().unwrap(), "Hello world!!!!!!!!");
    }
}