use common::{Header, HTTPVersion, StatusCode};
use std::ascii::AsciiExt;
use std::cmp::Ordering;
use std::sync::mpsc::Receiver;
use std::io::{self, Read, Write, Cursor};
use std::io::Result as IoResult;
use std::fs::File;
use std::str::FromStr;
pub struct Response<R> where R: Read {
reader: R,
status_code: StatusCode,
headers: Vec<Header>,
data_length: Option<usize>,
}
pub type ResponseBox = Response<Box<Read + Send>>;
enum TransferEncoding {
Identity,
Chunked,
}
impl FromStr for TransferEncoding {
type Err = ();
fn from_str(input: &str) -> Result<TransferEncoding, ()> {
if input.eq_ignore_ascii_case("identity") {
Ok(TransferEncoding::Identity)
} else if input.eq_ignore_ascii_case("chunked") {
Ok(TransferEncoding::Chunked)
} else {
Err(())
}
}
}
fn build_date_header() -> Header {
Header::from_bytes(&b"Date"[..], &b"Wed, 15 Nov 1995 06:25:24 GMT"[..]).unwrap()
}
fn write_message_header<W>(mut writer: W, http_version: &HTTPVersion,
status_code: &StatusCode, headers: &[Header])
-> IoResult<()> where W: Write
{
try!(write!(&mut writer, "HTTP/{} {} {}\r\n",
http_version,
status_code.0,
status_code.get_default_reason_phrase()
));
for header in headers.iter() {
try!(write!(&mut writer, "{}: {}\r\n", header.field.as_str().as_str(),
header.value.as_str()));
}
try!(write!(&mut writer, "\r\n"));
Ok(())
}
fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
entity_length: &Option<usize>, has_additional_headers: bool)
-> TransferEncoding
{
use util;
if *http_version <= (1, 0) {
return TransferEncoding::Identity;
}
let user_request = request_headers.iter()
.find(|h| h.field.equiv(&"TE"))
.map(|h| h.value.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 }
match <TransferEncoding as FromStr>::from_str(value.0) {
Ok(te) => return Some(te),
_ => () };
}
None
});
if user_request.is_some() {
return user_request.unwrap();
}
if has_additional_headers {
return TransferEncoding::Chunked;
}
let chunks_threshold = 32768;
if entity_length.as_ref().map_or(true, |val| *val >= chunks_threshold) {
return TransferEncoding::Chunked;
}
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: status_code,
headers: Vec::new(),
data_length: data_length,
};
for h in headers.into_iter() {
response.add_header(h)
}
if additional_headers.is_some() {
for h in additional_headers.unwrap().iter() {
response.add_header(h)
}
}
response
}
pub fn add_header<H>(&mut self, header: H) where H: Into<Header> {
let header = header.into();
if header.field.equiv(&"Accept-Ranges") ||
header.field.equiv(&"Connection") ||
header.field.equiv(&"Content-Range") ||
header.field.equiv(&"Trailer") ||
header.field.equiv(&"Transfer-Encoding") ||
header.field.equiv(&"Upgrade")
{
return;
}
if header.field.equiv(&"Content-Length") {
match <usize as FromStr>::from_str(header.value.as_str()) {
Ok(val) => self.data_length = Some(val),
Err(_) => () };
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: reader,
headers: self.headers,
status_code: self.status_code,
data_length: data_length,
}
}
pub fn raw_print<W: Write>(mut self, mut writer: W, http_version: HTTPVersion,
request_headers: &[Header], do_not_send_body: bool,
upgrade: Option<&str>)
-> IoResult<()>
{
let mut transfer_encoding = Some(choose_transfer_encoding(request_headers,
&http_version, &self.data_length, false ));
if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
self.headers.insert(0, build_date_header());
}
if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
self.headers.insert(0,
FromStr::from_str("Server: tiny-http (Rust)").unwrap()
);
}
if upgrade.is_some() {
let upgrade = upgrade.unwrap();
self.headers.insert(0, FromStr::from_str(&format!("Upgrade: {}", upgrade)).unwrap());
self.headers.insert(0, FromStr::from_str("Connection: upgrade").unwrap());
transfer_encoding = None;
}
let do_not_send_body = do_not_send_body ||
match self.status_code.0 {
100...199 | 204 | 304 => true,
_ => false
};
match transfer_encoding {
Some(TransferEncoding::Chunked) => {
self.headers.push(
FromStr::from_str("Transfer-Encoding: chunked").unwrap()
)
},
Some(TransferEncoding::Identity) => {
assert!(self.data_length.is_some());
let data_length = self.data_length.unwrap();
self.headers.push(
FromStr::from_str(&format!("Content-Length: {}", data_length)).unwrap()
)
},
_ => ()
};
try!(write_message_header(writer.by_ref(), &http_version,
&self.status_code, &self.headers));
if !do_not_send_body {
match transfer_encoding {
Some(TransferEncoding::Chunked) => {
use chunked_transfer::Encoder;
let mut writer = Encoder::new(writer);
try!(io::copy(&mut self.reader, &mut writer));
},
Some(TransferEncoding::Identity) => {
use util::EqualReader;
assert!(self.data_length.is_some());
let data_length = self.data_length.unwrap();
if data_length >= 1 {
let (mut equ_reader, _) =
EqualReader::new(self.reader.by_ref(), data_length);
try!(io::copy(&mut equ_reader, &mut writer));
}
},
_ => ()
}
}
Ok(())
}
}
impl<R> Response<R> where R: Read + Send + 'static {
pub fn boxed(self) -> ResponseBox {
Response {
reader: Box::new(self.reader) as Box<Read + Send>,
status_code: self.status_code,
headers: self.headers,
data_length: self.data_length,
}
}
}
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::new(),
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(200),
Vec::new(),
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(200),
vec!(
FromStr::from_str("Content-Type: text/plain; charset=UTF-8").unwrap()
),
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::new(),
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.clone(),
headers: self.headers.clone(),
data_length: self.data_length.clone(),
}
}
}