use std::io::{self, Read, Write};
#[cfg(feature = "charsets")]
use encoding_rs::Encoding;
use http::header::HeaderMap;
#[cfg(feature = "charsets")]
use http::header::CONTENT_TYPE;
#[cfg(feature = "json")]
use serde::de::DeserializeOwned;
#[cfg(feature = "charsets")]
use crate::charsets::{self, Charset};
use crate::error::HttpResult;
#[cfg(feature = "charsets")]
use crate::parsing::buffers::trim_byte;
use crate::parsing::CompressedReader;
use crate::request::Request;
#[cfg(feature = "charsets")]
use crate::streams::StreamDecoder;
#[cfg(feature = "charsets")]
fn get_charset(headers: &HeaderMap, default_charset: Option<Charset>) -> Charset {
if let Some(value) = headers.get(CONTENT_TYPE) {
let bytes = value.as_bytes();
if let Some(scol) = bytes.iter().position(|&b| b == b';') {
let rhs = trim_byte(b' ', &bytes[scol + 1..]);
if rhs.starts_with(b"charset=") {
if let Some(enc) = Encoding::for_label(&rhs[8..]) {
return enc;
}
}
}
}
default_charset.unwrap_or(charsets::WINDOWS_1252)
}
pub struct ResponseReader {
inner: CompressedReader,
#[cfg(feature = "charsets")]
charset: Charset,
}
impl ResponseReader {
#[cfg(feature = "charsets")]
pub(crate) fn new(headers: &HeaderMap, request: &Request, reader: CompressedReader) -> ResponseReader {
ResponseReader {
inner: reader,
charset: get_charset(&headers, request.default_charset),
}
}
#[cfg(not(feature = "charsets"))]
pub(crate) fn new(_: &HeaderMap, _: &Request, reader: CompressedReader) -> ResponseReader {
ResponseReader { inner: reader }
}
pub fn write_to<W>(mut self, mut writer: W) -> HttpResult<u64>
where
W: Write,
{
let n = io::copy(&mut self.inner, &mut writer)?;
Ok(n)
}
pub fn bytes(self) -> HttpResult<Vec<u8>> {
let mut buf = Vec::new();
self.write_to(&mut buf)?;
Ok(buf)
}
#[cfg(not(feature = "charsets"))]
pub fn string(mut self) -> HttpResult<String> {
let mut contents = String::new();
self.inner.read_to_string(&mut contents)?;
Ok(contents)
}
#[cfg(feature = "charsets")]
pub fn string(self) -> HttpResult<String> {
let charset = self.charset;
self.string_with(charset)
}
#[cfg(feature = "charsets")]
pub fn string_with(self, charset: Charset) -> HttpResult<String> {
let mut decoder = StreamDecoder::new(charset);
self.write_to(&mut decoder)?;
Ok(decoder.take())
}
#[cfg(feature = "json")]
pub fn json<T>(self) -> HttpResult<T>
where
T: DeserializeOwned,
{
let text = self.string()?;
let obj = serde_json::from_str(&text)?;
Ok(obj)
}
}
impl Read for ResponseReader {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}
#[cfg(test)]
#[cfg(feature = "charsets")]
mod tests {
use http::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use super::get_charset;
use crate::charsets;
#[test]
fn test_get_charset_from_header() {
let mut headers = HeaderMap::new();
headers.insert(
CONTENT_TYPE,
HeaderValue::from_bytes(&b"text/html; charset=UTF-8"[..]).unwrap(),
);
assert_eq!(get_charset(&headers, None), charsets::UTF_8);
}
#[test]
fn test_get_charset_from_header_lowercase() {
let mut headers = HeaderMap::new();
headers.insert(
CONTENT_TYPE,
HeaderValue::from_bytes(&b"text/html; charset=utf8"[..]).unwrap(),
);
assert_eq!(get_charset(&headers, None), charsets::UTF_8);
}
#[test]
fn test_get_charset_from_default() {
let headers = HeaderMap::new();
assert_eq!(get_charset(&headers, Some(charsets::UTF_8)), charsets::UTF_8);
}
#[test]
fn test_get_charset_standard() {
let headers = HeaderMap::new();
assert_eq!(get_charset(&headers, None), charsets::WINDOWS_1252);
}
}