use core::file;
use futures::prelude::*;
use http::response::Parts;
use hyper::{
self,
header::{HeaderMap, HeaderValue, CONTENT_LENGTH, CONTENT_TYPE, LOCATION},
Body,
StatusCode,
Version,
};
use mime_guess::guess_mime_type;
use std::{fmt::Debug, path::Path};
use tokio::{fs::File, io::ErrorKind};
#[derive(Debug)]
pub struct Response {
pub(crate) parts: Parts,
pub(crate) body: Body,
}
impl Response {
pub fn new() -> Self {
Response::default()
}
#[inline]
pub fn version(&self) -> Version {
self.parts.version
}
#[inline]
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
&self.parts.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
&mut self.parts.headers
}
#[inline]
pub fn status(&self) -> StatusCode {
self.parts.status
}
pub fn status_mut(&mut self) -> &mut StatusCode {
&mut self.parts.status
}
#[inline]
pub fn set_status(&mut self, status: u16) {
self.parts.status = StatusCode::from_u16(status).unwrap();
}
#[inline]
pub fn with_status(mut self, status: u16) -> Self {
self.parts.status = StatusCode::from_u16(status).unwrap();
self
}
#[inline]
pub fn text<T: Into<String>>(&mut self, body: T) {
let body = body.into();
self.headers_mut().insert(
CONTENT_LENGTH,
HeaderValue::from_str(&body.len().to_string()).unwrap(),
);
self.body = Body::from(body);
}
#[inline]
pub fn with_text<T: Into<String>>(mut self, body: T) -> Self {
self.text(body);
self
}
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.parts.extensions.get::<T>()
}
pub fn set<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
self.parts.extensions.insert::<T>(value)
}
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.parts.extensions.remove::<T>()
}
pub fn with_file<P>(mut self, pathbuf: P) -> impl Future<Item = Response, Error = Response>
where
P: AsRef<Path> + Send + Clone + Debug + 'static,
{
let path_clone = pathbuf.clone();
File::open(pathbuf)
.and_then(file::metadata)
.then(move |result| {
match result {
Ok((file, meta)) => {
let mime_type = guess_mime_type(path_clone.clone());
self.headers_mut().insert(
CONTENT_LENGTH,
HeaderValue::from_str(&meta.len().to_string()).unwrap(),
);
self.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_str(mime_type.as_ref()).unwrap(),
);
self.body = Body::wrap_stream(file::stream(file));
Ok(self)
}
Err(err) => {
error!("Error responding with a file: {}", err);
match err.kind() {
ErrorKind::NotFound => self.set_status(404),
_ => self.set_status(500),
};
Err(self)
}
}
})
}
#[inline]
pub fn with_body<T: Into<Body>>(mut self, body: T) -> Self {
self.body = body.into();
self
}
#[inline]
pub fn set_body<T: Into<Body>>(&mut self, body: T) {
self.body = body.into();
}
#[inline]
pub fn body_ref(&self) -> &Body {
&self.body
}
#[inline]
pub fn body(self) -> Body {
self.body
}
pub fn redirect(mut self, url: &'static str) -> Response {
self.set_status(301);
self.headers_mut()
.insert(LOCATION, HeaderValue::from_static(url));
self
}
pub fn unauthorized(self) -> Self {
self.with_status(401)
}
pub fn badRequest(self) -> Self {
self.with_status(400)
}
pub fn forbidden(self) -> Self {
self.with_status(403)
}
pub fn methodNotAllowed(self) -> Self {
self.with_status(405)
}
pub fn notAcceptable(self) -> Self {
self.with_status(406)
}
pub fn requestTimeout(self) -> Self {
self.with_status(408)
}
pub fn internalServerError(self) -> Self {
self.with_status(500)
}
pub fn badGateway(self) -> Self {
self.with_status(502)
}
pub fn serviceUnavailable(self) -> Self {
self.with_status(503)
}
}
impl Default for Response {
fn default() -> Response {
let (parts, body) = hyper::Response::new(Body::empty()).into_parts();
Response { parts, body }
}
}
pub fn res() -> Response {
Response::default()
}