use std::io::{Read, Write}; use std::fs::File; use std::fmt; use http::mime::{Mime, TopLevel, SubLevel}; use http::hyper::{header, FreshHyperResponse, StatusCode}; use outcome::{self, IntoOutcome}; use outcome::Outcome::*; /// Type alias for the `Outcome` of a `Responder`. pub type Outcome<'a> = outcome::Outcome<(), (), (StatusCode, FreshHyperResponse<'a>)>; impl<'a, T, E> IntoOutcome<(), (), (StatusCode, FreshHyperResponse<'a>)> for Result { fn into_outcome(self) -> Outcome<'a> { match self { Ok(_) => Success(()), Err(_) => Failure(()) } } } /// Trait implemented by types that send a response to clients. /// /// Types that implement this trait can be used as the return type of a handler, /// as illustrated below: /// /// ```rust,ignore /// #[get("/")] /// fn index() -> T { ... } /// ``` /// /// In this example, `T` can be any type that implements `Responder`. /// /// # Outcomes /// /// The returned [Outcome](/rocket/outcome/index.html) of a `respond` call /// determines how the response will be processed, if at all. /// /// * **Success** /// /// An `Outcome` of `Success` indicates that the responder was successful in /// sending the response to the client. No further processing will occur as a /// result. /// /// * **Failure** /// /// An `Outcome` of `Failure` indicates that the responder failed after /// beginning a response. The response is incomplete, and there is no way to /// salvage the response. No further processing will occur. /// /// * **Forward**(StatusCode, FreshHyperResponse<'a>) /// /// If the `Outcome` is `Forward`, the response will be forwarded to the /// designated error [Catcher](/rocket/struct.Catcher.html) for the given /// `StatusCode`. This requires that a response wasn't started and thus is /// still fresh. /// /// # Implementation Tips /// /// This section describes a few best practices to take into account when /// implementing `Responder`. /// /// ## Debug /// /// A type implementing `Responder` should implement the `Debug` trait when /// possible. This is because the `Responder` implementation for `Result` /// requires its `Err` type to implement `Debug`. Therefore, a type implementing /// `Debug` can more easily be composed. /// /// ## Check Before Changing /// /// Unless a given type is explicitly designed to change some information in /// ther esponse, it should first _check_ that some information hasn't been set /// before _changing_ that information. For example, before setting the /// `Content-Type` header of a response, first check that the header hasn't been /// set. pub trait Responder { /// Attempts to write a response to `res`. /// /// If writing the response successfully completes, an outcome of `Success` /// is returned. If writing the response begins but fails, an outcome of /// `Failure` is returned. If writing a response fails before writing /// anything out, an outcome of `Forward` can be returned, which causes the /// response to be written by the appropriate error catcher instead. fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a>; } impl<'a> Responder for &'a str { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> { if res.headers().get::().is_none() { let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![]); res.headers_mut().set(header::ContentType(mime)); } res.send(self.as_bytes()).into_outcome() } } impl Responder for String { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> { if res.headers().get::().is_none() { let mime = Mime(TopLevel::Text, SubLevel::Html, vec![]); res.headers_mut().set(header::ContentType(mime)); } res.send(self.as_bytes()).into_outcome() } } impl Responder for File { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> { let size = match self.metadata() { Ok(md) => md.len(), Err(e) => { error_!("Failed to read file metadata: {:?}", e); return Forward((StatusCode::InternalServerError, res)); } }; let mut v = Vec::new(); if let Err(e) = self.read_to_end(&mut v) { error_!("Failed to read file: {:?}", e); return Forward((StatusCode::InternalServerError, res)); } res.headers_mut().set(header::ContentLength(size)); res.start().and_then(|mut stream| stream.write_all(&v)).into_outcome() } } impl Responder for Option { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> { if let Some(ref mut val) = *self { val.respond(res) } else { warn_!("Response was `None`."); Forward((StatusCode::NotFound, res)) } } } impl Responder for Result { // prepend with `default` when using impl specialization default fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> { match *self { Ok(ref mut val) => val.respond(res), Err(ref e) => { error_!("{:?}", e); Forward((StatusCode::InternalServerError, res)) } } } } impl Responder for Result { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> { match *self { Ok(ref mut responder) => responder.respond(res), Err(ref mut responder) => responder.respond(res), } } }