use crate::Headers;
use crate::common::{HEADER_CONNECTION, HEADER_CONTENT_TYPE, HEADER_SERVER, HEADER_TRANSFER_ENCODING, HTTPVersion, Header, StatusCode};
use httpdate::HttpDate;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::sync::mpsc::Receiver;
use std::io::Result as IoResult;
use std::io::{self, Cursor, Read, Write};
use std::fs::File;
use std::str::FromStr;
use std::time::SystemTime;
pub struct Response<R>
{
reader: R,
status_code: StatusCode,
headers: Headers,
data_length: Option<usize>,
chunked_threshold: Option<usize>,
}
pub type ResponseBox = Response<Box<dyn Read + Send>>;
#[derive(Copy, Clone)]
enum TransferEncoding
{
Identity,
Chunked,
}
impl FromStr for TransferEncoding
{
type Err = ();
fn from_str(input: &str) -> Result<TransferEncoding, ()>
{
if input.eq_ignore_ascii_case("identity") == true
{
Ok(TransferEncoding::Identity)
}
else if input.eq_ignore_ascii_case("chunked") == true
{
Ok(TransferEncoding::Chunked)
}
else
{
Err(())
}
}
}
fn build_date_header() -> Header
{
let d = HttpDate::from(SystemTime::now());
Header::from_str("Date", d.to_string()).unwrap()
}
fn write_message_header<W>(
mut writer: W,
http_version: &HTTPVersion,
status_code: &StatusCode,
headers: &Headers,
) -> IoResult<()>
where
W: Write,
{
write!(
&mut writer,
"HTTP/{}.{} {} {}\r\n",
http_version.0,
http_version.1,
status_code.0,
status_code.default_reason_phrase()
)?;
headers.write_headers(&mut writer)?;
write!(&mut writer, "\r\n")?;
Ok(())
}
fn choose_transfer_encoding(
status_code: StatusCode,
request_headers_opt: Option<&Headers>,
http_version: &HTTPVersion,
entity_length: &Option<usize>,
has_additional_headers: bool,
chunked_threshold: usize,
) -> TransferEncoding
{
use crate::util;
if *http_version <= (1, 0)
{
return TransferEncoding::Identity;
}
if status_code < StatusCode::OK || status_code == StatusCode::NO_CONTENT
{
return TransferEncoding::Identity;
}
let user_request =
if let Some(request_headers) = request_headers_opt
{
request_headers
.get("TE")
.map(|h| h.clone())
.and_then(
|value|
{
let mut parse = util::parse_header_value(value.as_str());
parse.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
for value in parse.iter() {
if value.1 <= 0.0 {
continue;
}
if let Ok(te) = TransferEncoding::from_str(value.0)
{
return Some(te);
}
}
None
}
)
}
else
{
None
};
if let Some(user_request) = user_request
{
return user_request;
}
if has_additional_headers
{
return TransferEncoding::Chunked;
}
if entity_length
.as_ref()
.map_or(true, |val| *val >= chunked_threshold)
{
return TransferEncoding::Chunked;
}
return TransferEncoding::Identity;
}
impl<R> Response<R>
where
R: Read,
{
pub
fn new(
status_code: StatusCode,
headers: Vec<Header>,
data: R,
data_length: Option<usize>,
additional_headers: Option<Receiver<Header>>,
) -> Response<R>
{
let mut response =
Response
{
reader: data,
status_code,
headers: Headers::with_capacity(16),
data_length,
chunked_threshold: None,
};
for h in headers
{
response.add_header(h)
}
if let Some(additional_headers) = additional_headers {
for h in additional_headers.iter() {
response.add_header(h)
}
}
response
}
pub fn with_chunked_threshold(mut self, length: usize) -> Response<R> {
self.chunked_threshold = Some(length);
self
}
pub fn into_reader(self) -> R {
self.reader
}
pub fn chunked_threshold(&self) -> usize {
self.chunked_threshold.unwrap_or(32768)
}
pub fn add_header<H>(&mut self, header: H)
where
H: Into<Header>,
{
let header = header.into();
if header.field == "Connection" || header.field == "Trailer" ||
header.field == "Transfer-Encoding" || header.field == "Upgrade"
{
return;
}
if header.field == "Content-Length"
{
if let Ok(val) = usize::from_str(header.value.as_str())
{
self.data_length = Some(val)
}
return;
}
else if header.field == "Content-Type"
{
if let Some(content_type_header) =
self
.headers
.get_header_mut("Content-Type")
{
content_type_header.value = header.value;
return;
}
}
self.headers.push(header);
}
#[inline]
pub fn with_header<H>(mut self, header: H) -> Response<R>
where
H: Into<Header>,
{
self.add_header(header.into());
self
}
#[inline]
pub fn with_status_code<S>(mut self, code: S) -> Response<R>
where
S: Into<StatusCode>,
{
self.status_code = code.into();
self
}
pub fn with_data<S>(self, reader: S, data_length: Option<usize>) -> Response<S>
where
S: Read,
{
Response
{
reader,
headers: self.headers,
status_code: self.status_code,
data_length,
chunked_threshold: self.chunked_threshold,
}
}
pub fn raw_print<W: Write>(
mut self,
mut writer: W,
http_version: HTTPVersion,
request_headers: Option<&Headers>,
do_not_send_body: bool,
upgrade: Option<&str>,
) -> IoResult<()> {
let mut transfer_encoding =
Some(
choose_transfer_encoding(
self.status_code,
request_headers,
&http_version,
&self.data_length,
false,
self.chunked_threshold(),
)
);
if self.headers.contains("Date") == false
{
self.headers.insert_header(0, build_date_header());
}
if self.headers.contains("Server") == false
{
self.headers.insert_header(0, Header::from(&HEADER_SERVER));
}
if let Some(upgrade) = upgrade
{
self.headers.insert_header(
0,
Header::from_str("Upgrade", upgrade).unwrap(),
);
self.headers.insert_header(0,Header::from(&HEADER_CONNECTION));
transfer_encoding = None;
}
let (mut reader, data_length): (Box<dyn Read>, _) =
match (self.data_length, transfer_encoding)
{
(Some(l), _) =>
(Box::new(self.reader), Some(l)),
(None, Some(TransferEncoding::Identity)) =>
{
let mut buf = Vec::new();
self.reader.read_to_end(&mut buf)?;
let l = buf.len();
(Box::new(Cursor::new(buf)), Some(l))
}
_ =>
(Box::new(self.reader), None),
};
let do_not_send_body =
do_not_send_body == true ||
(self.status_code.in_range_inc(StatusCode::CONTINUE, StatusCode(199)) == true ||
self.status_code == StatusCode::NO_CONTENT || self.status_code == StatusCode::NOT_MODIFIED);
match transfer_encoding
{
Some(TransferEncoding::Chunked) =>
self
.headers
.push(Header::from(&HEADER_TRANSFER_ENCODING)),
Some(TransferEncoding::Identity) =>
{
assert!(data_length.is_some());
let data_length = data_length.unwrap();
self.headers.push(
Header::from_str(
"Content-Length",
data_length.to_string(), )
.unwrap(),
)
}
_ => (),
};
write_message_header(
writer.by_ref(),
&http_version,
&self.status_code,
&self.headers,
)?;
if do_not_send_body == false
{
match transfer_encoding
{
Some(TransferEncoding::Chunked) =>
{
use chunked_transfer::Encoder;
let mut writer = Encoder::new(writer);
io::copy(&mut reader, &mut writer)?;
}
Some(TransferEncoding::Identity) =>
{
assert!(data_length.is_some());
let data_length = data_length.unwrap();
if data_length >= 1 {
io::copy(&mut reader, &mut writer)?;
}
}
_ => (),
}
}
Ok(())
}
pub
fn status_code(&self) -> StatusCode
{
self.status_code
}
pub
fn data_length(&self) -> Option<usize>
{
self.data_length
}
pub
fn headers(&self) -> &Headers
{
&self.headers
}
}
impl<R> Response<R>
where
R: Read + Send + 'static,
{
pub fn boxed(self) -> ResponseBox
{
Response
{
reader: Box::new(self.reader) as Box<dyn Read + Send>,
status_code: self.status_code,
headers: self.headers,
data_length: self.data_length,
chunked_threshold: self.chunked_threshold,
}
}
}
impl Response<File>
{
pub
fn from_file(file: File) -> Response<File>
{
let file_size = file.metadata().ok().map(|v| v.len() as usize);
Response::new(
StatusCode(200),
Vec::with_capacity(0),
file,
file_size,
None,
)
}
}
impl Response<Cursor<Vec<u8>>>
{
pub
fn from_data<D>(data: D) -> Response<Cursor<Vec<u8>>>
where
D: Into<Vec<u8>>,
{
let data = data.into();
let data_len = data.len();
Response::new(
StatusCode::OK,
Vec::with_capacity(0),
Cursor::new(data),
Some(data_len),
None,
)
}
pub fn from_string<S>(data: S) -> Response<Cursor<Vec<u8>>>
where
S: Into<String>,
{
let data = data.into();
let data_len = data.len();
Response::new(
StatusCode::OK,
vec![
Header::from(&HEADER_CONTENT_TYPE)
],
Cursor::new(data.into_bytes()),
Some(data_len),
None,
)
}
}
impl Response<io::Empty> {
pub fn empty<S>(status_code: S) -> Response<io::Empty>
where
S: Into<StatusCode>,
{
Response::new(
status_code.into(),
Vec::with_capacity(0),
io::empty(),
Some(0),
None,
)
}
pub fn new_empty(status_code: StatusCode) -> Response<io::Empty> {
Response::empty(status_code)
}
}
impl Clone for Response<io::Empty>
{
fn clone(&self) -> Response<io::Empty>
{
Response
{
reader: io::empty(),
status_code: self.status_code,
headers: self.headers.clone(),
data_length: self.data_length,
chunked_threshold: self.chunked_threshold,
}
}
}