2016-12-15 08:47:31 +00:00
|
|
|
use std::{io, fmt, str};
|
2016-12-15 20:37:17 +00:00
|
|
|
use std::borrow::Cow;
|
2016-10-25 11:03:50 +00:00
|
|
|
|
2016-12-15 20:37:17 +00:00
|
|
|
use http::{Header, HeaderMap};
|
2016-12-15 08:47:31 +00:00
|
|
|
use http::Status;
|
|
|
|
|
|
|
|
pub const DEFAULT_CHUNK_SIZE: u64 = 4096;
|
|
|
|
|
|
|
|
pub enum Body<T> {
|
|
|
|
Sized(T, u64),
|
|
|
|
Chunked(T, u64)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Body<T> {
|
|
|
|
pub fn as_mut(&mut self) -> Body<&mut T> {
|
|
|
|
match *self {
|
|
|
|
Body::Sized(ref mut b, n) => Body::Sized(b, n),
|
|
|
|
Body::Chunked(ref mut b, n) => Body::Chunked(b, n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Body<U> {
|
|
|
|
match self {
|
|
|
|
Body::Sized(b, n) => Body::Sized(f(b), n),
|
|
|
|
Body::Chunked(b, n) => Body::Chunked(f(b), n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: io::Read> Body<T> {
|
|
|
|
pub fn to_string(self) -> Option<String> {
|
|
|
|
let (mut body, mut string) = match self {
|
|
|
|
Body::Sized(b, size) => (b, String::with_capacity(size as usize)),
|
|
|
|
Body::Chunked(b, _) => (b, String::new())
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Err(e) = body.read_to_string(&mut string) {
|
|
|
|
error_!("Error reading body: {:?}", e);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(string)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> fmt::Debug for Body<T> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match *self {
|
|
|
|
Body::Sized(_, n) => writeln!(f, "Sized Body [{} bytes]", n),
|
|
|
|
Body::Chunked(_, n) => writeln!(f, "Chunked Body [{} bytes]", n),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ResponseBuilder<'r> {
|
|
|
|
response: Response<'r>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'r> ResponseBuilder<'r> {
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn new(base: Response<'r>) -> ResponseBuilder<'r> {
|
|
|
|
ResponseBuilder {
|
|
|
|
response: base
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn status(&mut self, status: Status) -> &mut ResponseBuilder<'r> {
|
|
|
|
self.response.set_status(status);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn raw_status(&mut self, code: u16, reason: &'static str)
|
|
|
|
-> &mut ResponseBuilder<'r> {
|
|
|
|
self.response.set_raw_status(code, reason);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn header<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r>
|
|
|
|
where H: Into<Header<'h>>
|
|
|
|
{
|
|
|
|
self.response.set_header(header);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn header_adjoin<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r>
|
|
|
|
where H: Into<Header<'h>>
|
|
|
|
{
|
|
|
|
self.response.adjoin_header(header);
|
|
|
|
self
|
|
|
|
}
|
2016-10-25 11:03:50 +00:00
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 08:47:31 +00:00
|
|
|
pub fn raw_header<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V)
|
|
|
|
-> &mut ResponseBuilder<'r>
|
|
|
|
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
|
|
|
|
{
|
|
|
|
self.response.set_raw_header(name, value);
|
|
|
|
self
|
2016-10-25 11:03:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 08:47:31 +00:00
|
|
|
pub fn raw_header_adjoin<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V)
|
|
|
|
-> &mut ResponseBuilder<'r>
|
|
|
|
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
|
|
|
|
{
|
|
|
|
self.response.adjoin_raw_header(name, value);
|
|
|
|
self
|
2016-10-25 11:03:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 08:47:31 +00:00
|
|
|
pub fn sized_body<B>(&mut self, body: B) -> &mut ResponseBuilder<'r>
|
|
|
|
where B: io::Read + io::Seek + 'r
|
|
|
|
{
|
|
|
|
self.response.set_sized_body(body);
|
|
|
|
self
|
2016-10-25 11:03:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 08:47:31 +00:00
|
|
|
pub fn streamed_body<B>(&mut self, body: B) -> &mut ResponseBuilder<'r>
|
|
|
|
where B: io::Read + 'r
|
|
|
|
{
|
|
|
|
self.response.set_streamed_body(body);
|
|
|
|
self
|
2016-10-25 11:03:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 08:47:31 +00:00
|
|
|
pub fn chunked_body<B: io::Read + 'r>(&mut self, body: B, chunk_size: u64)
|
|
|
|
-> &mut ResponseBuilder<'r>
|
|
|
|
{
|
|
|
|
self.response.set_chunked_body(body, chunk_size);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn merge(&mut self, other: Response<'r>) -> &mut ResponseBuilder<'r> {
|
|
|
|
self.response.merge(other);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn join(&mut self, other: Response<'r>) -> &mut ResponseBuilder<'r> {
|
|
|
|
self.response.join(other);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn finalize(&mut self) -> Response<'r> {
|
|
|
|
::std::mem::replace(&mut self.response, Response::new())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn ok<T>(&mut self) -> Result<Response<'r>, T> {
|
|
|
|
Ok(self.finalize())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// `join`? Maybe one does one thing, the other does another? IE: `merge`
|
|
|
|
// replaces, `join` adds. One more thing that could be done: we could make it
|
|
|
|
// some that _some_ headers default to replacing, and other to joining.
|
|
|
|
/// Return type of a thing.
|
|
|
|
pub struct Response<'r> {
|
|
|
|
status: Option<Status>,
|
2016-12-15 20:37:17 +00:00
|
|
|
headers: HeaderMap<'r>,
|
2016-12-15 08:47:31 +00:00
|
|
|
body: Option<Body<Box<io::Read + 'r>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'r> Response<'r> {
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn new() -> Response<'r> {
|
|
|
|
Response {
|
|
|
|
status: None,
|
2016-12-15 20:37:17 +00:00
|
|
|
headers: HeaderMap::new(),
|
2016-12-15 08:47:31 +00:00
|
|
|
body: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn build() -> ResponseBuilder<'r> {
|
|
|
|
Response::build_from(Response::new())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn build_from(other: Response<'r>) -> ResponseBuilder<'r> {
|
|
|
|
ResponseBuilder::new(other)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn status(&self) -> Status {
|
|
|
|
self.status.unwrap_or(Status::Ok)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn set_status(&mut self, status: Status) {
|
|
|
|
self.status = Some(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn set_raw_status(&mut self, code: u16, reason: &'static str) {
|
|
|
|
self.status = Some(Status::new(code, reason));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn headers<'a>(&'a self) -> impl Iterator<Item=Header<'a>> {
|
2016-12-15 20:37:17 +00:00
|
|
|
self.headers.iter()
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn get_header_values<'h>(&'h self, name: &str)
|
|
|
|
-> impl Iterator<Item=&'h str> {
|
2016-12-15 20:37:17 +00:00
|
|
|
self.headers.get(name)
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 20:37:17 +00:00
|
|
|
pub fn set_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) -> bool {
|
|
|
|
self.headers.replace(header)
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 20:37:17 +00:00
|
|
|
pub fn set_raw_header<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V) -> bool
|
2016-12-15 08:47:31 +00:00
|
|
|
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
|
|
|
|
{
|
2016-12-15 20:37:17 +00:00
|
|
|
self.set_header(Header::new(name, value))
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn adjoin_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
|
2016-12-15 20:37:17 +00:00
|
|
|
self.headers.add(header)
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn adjoin_raw_header<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V)
|
|
|
|
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
|
|
|
|
{
|
|
|
|
self.adjoin_header(Header::new(name, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2016-12-15 20:37:17 +00:00
|
|
|
pub fn remove_header(&mut self, name: &str) {
|
2016-12-15 08:47:31 +00:00
|
|
|
self.headers.remove(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn body(&mut self) -> Option<Body<&mut io::Read>> {
|
|
|
|
// Looks crazy, right? Needed so Rust infers lifetime correctly. Weird.
|
|
|
|
match self.body.as_mut() {
|
|
|
|
Some(body) => Some(match body.as_mut() {
|
|
|
|
Body::Sized(b, u64) => Body::Sized(b, u64),
|
|
|
|
Body::Chunked(b, u64) => Body::Chunked(b, u64),
|
|
|
|
}),
|
|
|
|
None => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn take_body(&mut self) -> Option<Body<Box<io::Read + 'r>>> {
|
|
|
|
self.body.take()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn set_sized_body<B>(&mut self, mut body: B)
|
|
|
|
where B: io::Read + io::Seek + 'r
|
|
|
|
{
|
|
|
|
let size = body.seek(io::SeekFrom::End(0))
|
|
|
|
.expect("Attempted to retrieve size by seeking, but failed.");
|
|
|
|
body.seek(io::SeekFrom::Start(0))
|
|
|
|
.expect("Attempted to reset body by seeking after getting size.");
|
|
|
|
self.body = Some(Body::Sized(Box::new(body), size));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn set_streamed_body<B>(&mut self, body: B) where B: io::Read + 'r {
|
|
|
|
self.set_chunked_body(body, DEFAULT_CHUNK_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn set_chunked_body<B>(&mut self, body: B, chunk_size: u64)
|
|
|
|
where B: io::Read + 'r {
|
|
|
|
self.body = Some(Body::Chunked(Box::new(body), chunk_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Replaces this response's status and body with that of `other`, if they
|
|
|
|
// exist. Any headers that exist in `other` replace the ones in `self`. Any
|
|
|
|
// in `self` that aren't in `other` remain.
|
|
|
|
pub fn merge(&mut self, other: Response<'r>) {
|
|
|
|
if let Some(status) = other.status {
|
|
|
|
self.status = Some(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(body) = other.body {
|
|
|
|
self.body = Some(body);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (name, values) in other.headers.into_iter() {
|
2016-12-15 20:37:17 +00:00
|
|
|
self.headers.replace_all(name, values);
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sets `self`'s status and body to that of `other` if they are not already
|
|
|
|
// set in `self`. Any headers present in both `other` and `self` are
|
|
|
|
// adjoined.
|
|
|
|
pub fn join(&mut self, other: Response<'r>) {
|
|
|
|
if self.status.is_none() {
|
|
|
|
self.status = other.status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.body.is_none() {
|
|
|
|
self.body = other.body;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (name, mut values) in other.headers.into_iter() {
|
2016-12-15 20:37:17 +00:00
|
|
|
self.headers.add_all(name, &mut values);
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'r> fmt::Debug for Response<'r> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
writeln!(f, "{}", self.status())?;
|
|
|
|
|
|
|
|
for header in self.headers() {
|
|
|
|
writeln!(f, "{}", header)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
match self.body {
|
|
|
|
Some(ref body) => writeln!(f, "{:?}", body),
|
|
|
|
None => writeln!(f, "Empty Body")
|
|
|
|
}
|
2016-10-25 11:03:50 +00:00
|
|
|
}
|
|
|
|
}
|