use std::fmt;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::io;
use std::fs::File;
use std::io::{Read, Write};
use std::convert;
use hyper;
use hyper::uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star};
use hyper::header::{Headers, ContentLength, ContentType, Cookie};
use hyper::mime::Mime;
use hyper::method::Method;
use url::UrlParser;
use url::form_urlencoded;
use formdata::uploaded_file::UploadedFile;
use rustc_serialize::json;
use typemap::TypeMap;
use app::Pencil;
use datastructures::MultiDict;
use httputils::{get_name_by_http_code, get_content_type, get_host_value};
use httputils::get_status_from_code;
use routing::Rule;
use types::ViewArgs;
use http_errors::{HTTPError, NotFound};
use formparser::FormDataParser;
pub struct Request<'r, 'a, 'b: 'a> {
pub app: &'r Pencil,
pub request: hyper::server::request::Request<'a, 'b>,
pub url_rule: Option<Rule>,
pub view_args: ViewArgs,
pub routing_error: Option<HTTPError>,
pub extensions_data: TypeMap,
args: Option<MultiDict<String>>,
form: Option<MultiDict<String>>,
files: Option<MultiDict<UploadedFile>>,
cached_json: Option<Option<json::Json>>
}
impl<'r, 'a, 'b: 'a> Request<'r, 'a, 'b> {
pub fn new(app: &'r Pencil, request: hyper::server::request::Request<'a, 'b>) -> Request<'r, 'a, 'b> {
Request {
app: app,
request: request,
url_rule: None,
view_args: HashMap::new(),
routing_error: None,
extensions_data: TypeMap::new(),
args: None,
form: None,
files: None,
cached_json: None,
}
}
pub fn match_request(&mut self) {
match self.path() {
Some(path) => {
let url_adapter = self.app.url_map.bind(path, self.method());
match url_adapter.matched() {
Ok((rule, view_args)) => {
self.url_rule = Some(rule);
self.view_args = view_args;
},
Err(e) => {
self.routing_error = Some(e);
},
}
},
None => {
self.routing_error = Some(NotFound);
}
}
}
pub fn endpoint(&self) -> Option<String> {
match self.url_rule {
Some(ref rule) => Some(rule.endpoint.clone()),
None => None,
}
}
pub fn module_name(&self) -> Option<String> {
if let Some(endpoint) = self.endpoint() {
if endpoint.contains(".") {
let v: Vec<&str> = endpoint.rsplitn(2, '.').collect();
return Some(v[1].to_string());
}
}
None
}
pub fn args(&mut self) -> &MultiDict<String> {
if self.args.is_none() {
let mut args = MultiDict::new();
let query_pairs = self.query_string().map(|query| form_urlencoded::parse(query.as_bytes()));
match query_pairs {
Some(pairs) => {
for (k, v) in pairs {
args.add(k, v);
}
},
None => {},
}
self.args = Some(args);
}
return self.args.as_ref().unwrap();
}
fn content_type(&self) -> Option<ContentType> {
let content_type: Option<&ContentType> = self.request.headers.get();
content_type.map(|c| c.clone())
}
pub fn get_json(&mut self) -> &Option<json::Json> {
if self.cached_json.is_none() {
let mut data = String::from("");
let rv = match self.request.read_to_string(&mut data) {
Ok(_) => {
match json::Json::from_str(&data) {
Ok(json) => Some(json),
Err(_) => None
}
},
Err(_) => {
None
}
};
self.cached_json = Some(rv);
}
return self.cached_json.as_ref().unwrap();
}
fn load_form_data(&mut self) {
if self.form.is_some() {
return
}
let (form, files) = match self.content_type() {
Some(ContentType(mimetype)) => {
let parser = FormDataParser::new();
parser.parse(&mut self.request, &mimetype)
},
None => {
(MultiDict::new(), MultiDict::new())
}
};
self.form = Some(form);
self.files = Some(files);
}
pub fn form(&mut self) -> &MultiDict<String> {
self.load_form_data();
self.form.as_ref().unwrap()
}
pub fn files(&mut self) -> &MultiDict<UploadedFile> {
self.load_form_data();
self.files.as_ref().unwrap()
}
pub fn headers(&self) -> &Headers {
&self.request.headers
}
pub fn path(&self) -> Option<String> {
match self.request.uri {
AbsolutePath(ref path) => {
Some(path.splitn(2, '?').next().unwrap().to_string())
},
AbsoluteUri(ref url) => {
url.serialize_path()
},
Authority(_) | Star => None
}
}
pub fn full_path(&self) -> Option<String> {
let path = self.path();
let query_string = self.query_string();
if path.is_some() && query_string.is_some() {
return Some(path.unwrap() + "?" + &query_string.unwrap());
} else {
return path;
}
}
pub fn host(&self) -> Option<String> {
let host: Option<&hyper::header::Host> = self.request.headers.get();
host.map(|host| get_host_value(host))
}
pub fn query_string(&self) -> Option<String> {
match self.request.uri {
AbsolutePath(ref path) => {
match UrlParser::new().parse_path(path) {
Ok((_, query, _)) => {
query
},
Err(_) => None
}
},
AbsoluteUri(ref url) => {
url.query.clone()
},
Authority(_) | Star => None
}
}
pub fn cookies(&self) -> Option<&Cookie> {
self.request.headers.get()
}
pub fn method(&self) -> Method {
self.request.method.clone()
}
pub fn remote_addr(&self) -> SocketAddr {
self.request.remote_addr.clone()
}
pub fn scheme(&self) -> String {
match self.request.uri {
AbsoluteUri(ref url) => {
return url.scheme.clone();
},
_ => {}
}
String::from("http")
}
pub fn host_url(&self) -> Option<String> {
match self.host() {
Some(host) => {
Some(self.scheme() + "://" + &host + "/")
},
None => None,
}
}
pub fn url(&self) -> Option<String> {
let host_url = self.host_url();
let full_path = self.full_path();
if host_url.is_some() && full_path.is_some() {
Some(host_url.unwrap() + &(full_path.unwrap()).trim_left_matches('/'))
} else {
None
}
}
pub fn base_url(&self) -> Option<String> {
let host_url = self.host_url();
let path = self.path();
if host_url.is_some() && path.is_some() {
Some(host_url.unwrap() + &path.unwrap().trim_left_matches('/'))
} else {
None
}
}
pub fn is_secure(&self) -> bool {
self.scheme() == "https".to_string()
}
}
impl<'r, 'a, 'b: 'a> fmt::Debug for Request<'r, 'a, 'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.url() {
Some(url) => {
write!(f, "<Pencil Request '{}' {}>", url, self.method())
},
None => {
write!(f, "<Pencil Request>")
}
}
}
}
impl<'r, 'a, 'b: 'a> Read for Request<'r, 'a, 'b> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.request.read(buf)
}
}
pub struct ResponseBody<'a>(Box<Write + 'a>);
impl<'a> ResponseBody<'a> {
pub fn new<W: Write + 'a>(writer: W) -> ResponseBody<'a> {
ResponseBody(Box::new(writer))
}
}
impl<'a> Write for ResponseBody<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
pub trait BodyWrite: Send {
fn write_body(&mut self, body: &mut ResponseBody) -> io::Result<()>;
}
impl BodyWrite for Vec<u8> {
fn write_body(&mut self, body: &mut ResponseBody) -> io::Result<()> {
body.write_all(self)
}
}
impl<'a> BodyWrite for &'a [u8] {
fn write_body(&mut self, body: &mut ResponseBody) -> io::Result<()> {
body.write_all(self)
}
}
impl BodyWrite for String {
fn write_body(&mut self, body: &mut ResponseBody) -> io::Result<()> {
self.as_bytes().write_body(body)
}
}
impl<'a> BodyWrite for &'a str {
fn write_body(&mut self, body: &mut ResponseBody) -> io::Result<()> {
self.as_bytes().write_body(body)
}
}
impl BodyWrite for File {
fn write_body(&mut self, body: &mut ResponseBody) -> io::Result<()> {
io::copy(self, body).map(|_| ())
}
}
pub struct Response {
pub status_code: u16,
pub headers: Headers,
pub body: Option<Box<BodyWrite>>,
}
impl Response {
pub fn new<T: 'static + BodyWrite>(body: T) -> Response {
let mut response = Response {
status_code: 200,
headers: Headers::new(),
body: Some(Box::new(body)),
};
let mime: Mime = "text/html; charset=UTF-8".parse().unwrap();
let content_type = ContentType(mime);
response.headers.set(content_type);
return response;
}
pub fn new_empty() -> Response {
Response {
status_code: 200,
headers: Headers::new(),
body: None,
}
}
pub fn status_name(&self) -> &str {
match get_name_by_http_code(self.status_code) {
Some(name) => name,
None => "UNKNOWN",
}
}
pub fn content_type(&self) -> Option<&ContentType> {
self.headers.get()
}
pub fn set_content_type(&mut self, mimetype: &str) {
let mimetype = get_content_type(mimetype, "UTF-8");
let mime: Mime = (&mimetype).parse().unwrap();
let content_type = ContentType(mime);
self.headers.set(content_type);
}
pub fn content_length(&self) -> Option<usize> {
let content_length: Option<&ContentLength> = self.headers.get();
match content_length {
Some(&ContentLength(length)) => Some(length as usize),
None => None,
}
}
pub fn set_content_length(&mut self, value: usize) {
let content_length = ContentLength(value as u64);
self.headers.set(content_length);
}
pub fn set_cookie(&mut self, cookie: hyper::header::SetCookie) {
self.headers.set(cookie);
}
#[doc(hidden)]
pub fn write(self, request_method: Method, mut res: hyper::server::Response) {
let status_code = self.status_code;
*res.status_mut() = get_status_from_code(status_code);
*res.headers_mut() = self.headers;
if request_method == Method::Head ||
(100 <= status_code && status_code < 200) || status_code == 204 || status_code == 304 {
res.headers_mut().set(ContentLength(0));
try_return!(res.start().and_then(|w| w.end()));
} else {
match self.body {
Some(mut body) => {
let mut res = try_return!(res.start());
try_return!(body.write_body(&mut ResponseBody::new(&mut res)));
try_return!(res.end());
},
None => {
res.headers_mut().set(ContentLength(0));
try_return!(res.start().and_then(|w| w.end()));
}
}
}
}
}
impl fmt::Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Pencil Response [{}]>", self.status_code)
}
}
impl convert::From<Vec<u8>> for Response {
fn from(bytes: Vec<u8>) -> Response {
let content_length = bytes.len();
let mut response = Response::new(bytes);
response.set_content_length(content_length);
return response;
}
}
impl<'a> convert::From<&'a [u8]> for Response {
fn from(bytes: &'a [u8]) -> Response {
bytes.to_vec().into()
}
}
impl<'a> convert::From<&'a str> for Response {
fn from(s: &'a str) -> Response {
s.to_owned().into()
}
}
impl convert::From<String> for Response {
fn from(s: String) -> Response {
s.into_bytes().into()
}
}
impl convert::From<File> for Response {
fn from(f: File) -> Response {
let content_length = match f.metadata() {
Ok(metadata) => {
Some(metadata.len())
},
Err(_) => None
};
let mut response = Response::new(f);
if let Some(content_length) = content_length {
response.set_content_length(content_length as usize);
}
return response;
}
}