| // Copyright (c) 2016 The Rouille developers |
| // Licensed under the Apache License, Version 2.0 |
| // <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT |
| // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
| // at your option. All files in the project carrying such |
| // notice may not be copied, modified, or distributed except |
| // according to those terms. |
| |
| //! The rouille library is very easy to get started with. |
| //! |
| //! Listening to a port is done by calling the [`start_server`](fn.start_server.html) function: |
| //! |
| //! ```no_run |
| //! use rouille::Request; |
| //! use rouille::Response; |
| //! |
| //! rouille::start_server("0.0.0.0:80", move |request| { |
| //! Response::text("hello world") |
| //! }); |
| //! ``` |
| //! |
| //! Whenever an HTTP request is received on the address passed as first parameter, the closure |
| //! passed as second parameter is called. This closure must then return a |
| //! [`Response`](struct.Response.html) that will be sent back to the client. |
| //! |
| //! See the documentation of [`start_server`](fn.start_server.html) for more details. |
| //! |
| //! # Analyzing the request |
| //! |
| //! The parameter that the closure receives is a [`Request`](struct.Request.html) object that |
| //! represents the request made by the client. |
| //! |
| //! The `Request` object itself provides some getters, but most advanced functionnalities are |
| //! provided by other modules of this crate. |
| //! |
| //! - In order to dispatch between various code depending on the URL, you can use the |
| //! [`router!`](macro.router.html) macro. |
| //! - In order to analyze the body of the request, like handling JSON input, form input, etc. you |
| //! can take a look at [the `input` module](input/index.html). |
| //! |
| //! # Returning a response |
| //! |
| //! Once you analyzed the request, it is time to return a response by returning a |
| //! [`Response`](struct.Response.html) object. |
| //! |
| //! All the members of `Response` are public, so you can customize it as you want. There are also |
| //! several constructors that you build a basic `Response` which can then modify. |
| //! |
| //! In order to serve static files, take a look at |
| //! [the `match_assets` function](fn.match_assets.html). |
| |
| #![deny(unsafe_code)] |
| |
| extern crate base64; |
| #[cfg(feature = "brotli2")] |
| extern crate brotli2; |
| extern crate chrono; |
| extern crate filetime; |
| #[cfg(feature = "gzip")] |
| extern crate deflate; |
| extern crate multipart; |
| extern crate rand; |
| extern crate serde; |
| #[macro_use] |
| extern crate serde_derive; |
| extern crate serde_json; |
| extern crate sha1; |
| extern crate time; |
| extern crate tiny_http; |
| pub extern crate url; |
| extern crate threadpool; |
| extern crate num_cpus; |
| |
| pub use assets::extension_to_mime; |
| pub use assets::match_assets; |
| pub use log::{log, log_custom}; |
| pub use response::{Response, ResponseBody}; |
| pub use tiny_http::ReadWrite; |
| |
| use std::error::Error; |
| use std::io::Cursor; |
| use std::io::Result as IoResult; |
| use std::io::Read; |
| use std::marker::PhantomData; |
| use std::net::SocketAddr; |
| use std::net::ToSocketAddrs; |
| use std::panic; |
| use std::panic::AssertUnwindSafe; |
| use std::slice::Iter as SliceIter; |
| use std::sync::Arc; |
| use std::sync::Mutex; |
| use std::thread; |
| use std::fmt; |
| |
| // The AsciiExt import is needed for Rust older than 1.23.0. These two lines can |
| // be removed when supporting older Rust is no longer needed. |
| #[allow(unused_imports)] |
| use std::ascii::AsciiExt; |
| |
| pub mod cgi; |
| pub mod content_encoding; |
| pub mod input; |
| pub mod proxy; |
| pub mod session; |
| pub mod websocket; |
| |
| mod assets; |
| mod find_route; |
| mod log; |
| mod response; |
| mod router; |
| #[doc(hidden)] |
| pub mod try_or_400; |
| |
| /// This macro assumes that the current function returns a `Response` and takes a `Result`. |
| /// If the expression you pass to the macro is an error, then a 404 response is returned. |
| #[macro_export] |
| macro_rules! try_or_404 { |
| ($result:expr) => ( |
| match $result { |
| Ok(r) => r, |
| Err(_) => return $crate::Response::empty_404(), |
| } |
| ); |
| } |
| |
| /// This macro assumes that the current function returns a `Response`. If the condition you pass |
| /// to the macro is false, then a 400 response is returned. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// # #[macro_use] extern crate rouille; |
| /// # fn main() { |
| /// use rouille::Request; |
| /// use rouille::Response; |
| /// |
| /// fn handle_something(request: &Request) -> Response { |
| /// let data = try_or_400!(post_input!(request, { |
| /// field1: u32, |
| /// field2: String, |
| /// })); |
| /// |
| /// assert_or_400!(data.field1 >= 2); |
| /// Response::text("hello") |
| /// } |
| /// # } |
| /// ``` |
| #[macro_export] |
| macro_rules! assert_or_400 { |
| ($cond:expr) => ( |
| if !$cond { |
| return $crate::Response::empty_400(); |
| } |
| ); |
| } |
| |
| /// Starts a server and uses the given requests handler. |
| /// |
| /// The request handler takes a `&Request` and must return a `Response` to send to the user. |
| /// |
| /// > **Note**: `start_server` is meant to be an easy-to-use function. If you want more control, |
| /// > see [the `Server` struct](struct.Server.html). |
| /// |
| /// # Common mistakes |
| /// |
| /// The handler must capture its environment by value and not by reference (`'static`). If you |
| /// use closure, don't forget to put `move` in front of the closure. |
| /// |
| /// The handler must also be thread-safe (`Send` and `Sync`). |
| /// For example this handler isn't thread-safe: |
| /// |
| /// ```should_fail |
| /// let mut requests_counter = 0; |
| /// |
| /// rouille::start_server("localhost:80", move |request| { |
| /// requests_counter += 1; |
| /// |
| /// // ... rest of the handler ... |
| /// # panic!() |
| /// }) |
| /// ``` |
| /// |
| /// Multiple requests can be processed simultaneously, therefore you can't mutably access |
| /// variables from the outside. |
| /// |
| /// Instead you must use a `Mutex`: |
| /// |
| /// ```no_run |
| /// use std::sync::Mutex; |
| /// let requests_counter = Mutex::new(0); |
| /// |
| /// rouille::start_server("localhost:80", move |request| { |
| /// *requests_counter.lock().unwrap() += 1; |
| /// |
| /// // rest of the handler |
| /// # panic!() |
| /// }) |
| /// ``` |
| /// |
| /// # Panic handling in the handler |
| /// |
| /// If your request handler panicks, a 500 error will automatically be sent to the client. |
| /// |
| /// # Panic |
| /// |
| /// This function will panic if the server starts to fail (for example if you use a port that is |
| /// already occupied) or if the socket is force-closed by the operating system. |
| /// |
| /// If you need to handle these situations, please see `Server`. |
| pub fn start_server<A, F>(addr: A, handler: F) -> ! |
| where A: ToSocketAddrs, |
| F: Send + Sync + 'static + Fn(&Request) -> Response |
| { |
| Server::new(addr, handler).expect("Failed to start server").run(); |
| panic!("The server socket closed unexpectedly") |
| } |
| |
| |
| /// Identical to `start_server` but uses a `ThreadPool` of the given size. |
| /// |
| /// When `pool_size` is `None`, the thread pool size will default to `8 * num-cpus`. |
| /// `pool_size` must be greater than zero or this function will panic. |
| pub fn start_server_with_pool<A, F>(addr: A, pool_size: Option<usize>, handler: F) -> ! |
| where A: ToSocketAddrs, |
| F: Send + Sync + 'static + Fn(&Request) -> Response |
| { |
| Server::new(addr, handler).expect("Failed to start server") |
| .pool_size(pool_size.unwrap_or_else(|| 8 * num_cpus::get())) |
| .run(); |
| panic!("The server socket closed unexpectedly") |
| } |
| |
| |
| /// Executes a function in either a thread of a thread pool |
| enum Executor { |
| Threaded, |
| Pooled { |
| pool: threadpool::ThreadPool, |
| } |
| } |
| impl Executor { |
| /// `size` must be greater than zero or the call to `ThreadPool::new` will panic. |
| fn with_size(size: usize) -> Self { |
| let pool = threadpool::ThreadPool::new(size); |
| Executor::Pooled { pool } |
| } |
| |
| #[inline] |
| fn execute<F: FnOnce() + Send + 'static>(&self, f: F) { |
| match *self { |
| Executor::Threaded => { thread::spawn(f); } |
| Executor::Pooled { ref pool } => { pool.execute(f); } |
| } |
| } |
| } |
| |
| |
| /// A listening server. |
| /// |
| /// This struct is the more manual server creation API of rouille and can be used as an alternative |
| /// to the `start_server` function. |
| /// |
| /// The `start_server` function is just a shortcut for `Server::new` followed with `run`. See the |
| /// documentation of the `start_server` function for more details about the handler. |
| /// |
| /// # Example |
| /// |
| /// ```no_run |
| /// use rouille::Server; |
| /// use rouille::Response; |
| /// |
| /// let server = Server::new("localhost:0", |request| { |
| /// Response::text("hello world") |
| /// }).unwrap(); |
| /// println!("Listening on {:?}", server.server_addr()); |
| /// server.run(); |
| /// ``` |
| pub struct Server<F> { |
| server: tiny_http::Server, |
| handler: Arc<AssertUnwindSafe<F>>, |
| executor: Executor, |
| } |
| |
| |
| impl<F> Server<F> where F: Send + Sync + 'static + Fn(&Request) -> Response { |
| /// Builds a new `Server` object. |
| /// |
| /// After this function returns, the HTTP server is listening. |
| /// |
| /// Returns an error if there was an error while creating the listening socket, for example if |
| /// the port is already in use. |
| pub fn new<A>(addr: A, handler: F) -> Result<Server<F>, Box<Error + Send + Sync>> |
| where A: ToSocketAddrs |
| { |
| let server = try!(tiny_http::Server::http(addr)); |
| Ok(Server { |
| server, |
| executor: Executor::Threaded, |
| handler: Arc::new(AssertUnwindSafe(handler)), // TODO: using AssertUnwindSafe here is wrong, but unwind safety has some usability problems in Rust in general |
| }) |
| } |
| |
| /// Builds a new `Server` object with SSL support. |
| /// |
| /// After this function returns, the HTTPS server is listening. |
| /// |
| /// Returns an error if there was an error while creating the listening socket, for example if |
| /// the port is already in use. |
| #[cfg(feature = "ssl")] |
| pub fn new_ssl<A>( |
| addr: A, |
| handler: F, |
| certificate: Vec<u8>, |
| private_key: Vec<u8>, |
| ) -> Result<Server<F>, Box<Error + Send + Sync>> where A: ToSocketAddrs { |
| let ssl_config = tiny_http::SslConfig { |
| certificate, |
| private_key, |
| }; |
| let server = try!(tiny_http::Server::https(addr, ssl_config)); |
| Ok(Server { |
| server, |
| executor: Executor::Threaded, |
| handler: Arc::new(AssertUnwindSafe(handler)), // TODO: using AssertUnwindSafe here is wrong, but unwind safety has some usability problems in Rust in general |
| }) |
| } |
| |
| /// Use a `ThreadPool` of the given size to process requests |
| /// |
| /// `pool_size` must be greater than zero or this function will panic. |
| pub fn pool_size(mut self, pool_size: usize) -> Self { |
| self.executor = Executor::with_size(pool_size); |
| self |
| } |
| |
| /// Returns the address of the listening socket. |
| #[inline] |
| pub fn server_addr(&self) -> SocketAddr { |
| self.server.server_addr() |
| } |
| |
| /// Runs the server forever, or until the listening socket is somehow force-closed by the |
| /// operating system. |
| #[inline] |
| pub fn run(self) { |
| for request in self.server.incoming_requests() { |
| self.process(request); |
| } |
| } |
| |
| /// Processes all the client requests waiting to be processed, then returns. |
| /// |
| /// This function executes very quickly, as each client requests that needs to be processed |
| /// is processed in a separate thread. |
| #[inline] |
| pub fn poll(&self) { |
| while let Ok(Some(request)) = self.server.try_recv() { |
| self.process(request); |
| } |
| } |
| |
| // Internal function, called when we got a request from tiny-http that needs to be processed. |
| fn process(&self, request: tiny_http::Request) { |
| // We spawn a thread so that requests are processed in parallel. |
| let handler = self.handler.clone(); |
| self.executor.execute(|| { |
| // Small helper struct that makes it possible to put |
| // a `tiny_http::Request` inside a `Box<Read>`. |
| struct RequestRead(Arc<Mutex<Option<tiny_http::Request>>>); |
| impl Read for RequestRead { |
| #[inline] |
| fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
| self.0.lock().unwrap().as_mut().unwrap().as_reader().read(buf) |
| } |
| } |
| |
| // Building the `Request` object. |
| let tiny_http_request; |
| let rouille_request = { |
| let url = request.url().to_owned(); |
| let method = request.method().as_str().to_owned(); |
| let headers = request.headers().iter().map(|h| (h.field.to_string(), h.value.clone().into())).collect(); |
| let remote_addr = *request.remote_addr(); |
| |
| tiny_http_request = Arc::new(Mutex::new(Some(request))); |
| let data = Arc::new(Mutex::new(Some(Box::new(RequestRead(tiny_http_request.clone())) as Box<_>))); |
| |
| Request { |
| url, |
| method, |
| headers, |
| https: false, |
| data, |
| remote_addr, |
| } |
| }; |
| |
| // Calling the handler ; this most likely takes a lot of time. |
| // If the handler panics, we build a dummy response. |
| let mut rouille_response = { |
| // We don't use the `rouille_request` anymore after the panic, so it's ok to assert |
| // it's unwind safe. |
| let rouille_request = AssertUnwindSafe(rouille_request); |
| let res = panic::catch_unwind(move || { |
| let rouille_request = rouille_request; |
| handler(&rouille_request) |
| }); |
| |
| match res { |
| Ok(r) => r, |
| Err(_) => { |
| Response::html("<h1>Internal Server Error</h1>\ |
| <p>An internal error has occurred on the server.</p>") |
| .with_status_code(500) |
| } |
| } |
| }; |
| |
| // writing the response |
| let (res_data, res_len) = rouille_response.data.into_reader_and_size(); |
| let mut response = tiny_http::Response::empty(rouille_response.status_code) |
| .with_data(res_data, res_len); |
| |
| let mut upgrade_header = "".into(); |
| |
| for (key, value) in rouille_response.headers { |
| if key.eq_ignore_ascii_case("Content-Length") { |
| continue; |
| } |
| |
| if key.eq_ignore_ascii_case("Upgrade") { |
| upgrade_header = value; |
| continue; |
| } |
| |
| if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes()) { |
| response.add_header(header); |
| } else { |
| // TODO: ? |
| } |
| } |
| |
| if let Some(ref mut upgrade) = rouille_response.upgrade { |
| let trq = tiny_http_request.lock().unwrap().take().unwrap(); |
| let socket = trq.upgrade(&upgrade_header, response); |
| upgrade.build(socket); |
| |
| } else { |
| // We don't really care if we fail to send the response to the client, as there's |
| // nothing we can do anyway. |
| let _ = tiny_http_request.lock().unwrap().take().unwrap().respond(response); |
| } |
| }); |
| } |
| } |
| |
| /// Trait for objects that can take ownership of a raw connection to the client data. |
| /// |
| /// The purpose of this trait is to be used with the `Connection: Upgrade` header, hence its name. |
| pub trait Upgrade { |
| /// Initializes the object with the given socket. |
| fn build(&mut self, socket: Box<ReadWrite + Send>); |
| } |
| |
| /// Represents a request that your handler must answer to. |
| /// |
| /// This can be either a real request (received by the HTTP server) or a mock object created with |
| /// one of the `fake_*` constructors. |
| pub struct Request { |
| method: String, |
| url: String, |
| headers: Vec<(String, String)>, |
| https: bool, |
| data: Arc<Mutex<Option<Box<Read + Send>>>>, |
| remote_addr: SocketAddr, |
| } |
| |
| impl fmt::Debug for Request { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| f.debug_struct("Request") |
| .field("method", &self.method) |
| .field("url", &self.url) |
| .field("headers", &self.headers) |
| .field("https", &self.https) |
| .field("remote_addr", &self.remote_addr) |
| .finish() |
| } |
| } |
| |
| impl Request { |
| /// Builds a fake HTTP request to be used during tests. |
| /// |
| /// The remote address of the client will be `127.0.0.1:12345`. Use `fake_http_from` to |
| /// specify what the client's address should be. |
| pub fn fake_http<U, M>(method: M, url: U, headers: Vec<(String, String)>, data: Vec<u8>) |
| -> Request where U: Into<String>, M: Into<String> |
| { |
| let data = Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))); |
| let remote_addr = "127.0.0.1:12345".parse().unwrap(); |
| |
| Request { |
| url: url.into(), |
| method: method.into(), |
| https: false, |
| data, |
| headers, |
| remote_addr, |
| } |
| } |
| |
| /// Builds a fake HTTP request to be used during tests. |
| pub fn fake_http_from<U, M>(from: SocketAddr, method: M, url: U, |
| headers: Vec<(String, String)>, data: Vec<u8>) |
| -> Request where U: Into<String>, M: Into<String> |
| { |
| let data = Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))); |
| |
| Request { |
| url: url.into(), |
| method: method.into(), |
| https: false, |
| data, |
| headers, |
| remote_addr: from, |
| } |
| } |
| |
| /// Builds a fake HTTPS request to be used during tests. |
| /// |
| /// The remote address of the client will be `127.0.0.1:12345`. Use `fake_https_from` to |
| /// specify what the client's address should be. |
| pub fn fake_https<U, M>(method: M, url: U, headers: Vec<(String, String)>, data: Vec<u8>) |
| -> Request where U: Into<String>, M: Into<String> |
| { |
| let data = Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))); |
| let remote_addr = "127.0.0.1:12345".parse().unwrap(); |
| |
| Request { |
| url: url.into(), |
| method: method.into(), |
| https: true, |
| data, |
| headers, |
| remote_addr, |
| } |
| } |
| |
| /// Builds a fake HTTPS request to be used during tests. |
| pub fn fake_https_from<U, M>(from: SocketAddr, method: M, url: U, |
| headers: Vec<(String, String)>, data: Vec<u8>) |
| -> Request where U: Into<String>, M: Into<String> |
| { |
| let data = Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))); |
| |
| Request { |
| url: url.into(), |
| method: method.into(), |
| https: true, |
| data, |
| headers, |
| remote_addr: from, |
| } |
| } |
| |
| /// If the decoded URL of the request starts with `prefix`, builds a new `Request` that is |
| /// the same as the original but without that prefix. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// # use rouille::Request; |
| /// # use rouille::Response; |
| /// fn handle(request: &Request) -> Response { |
| /// if let Some(request) = request.remove_prefix("/static") { |
| /// return rouille::match_assets(&request, "/static"); |
| /// } |
| /// |
| /// // ... |
| /// # panic!() |
| /// } |
| /// ``` |
| pub fn remove_prefix(&self, prefix: &str) -> Option<Request> { |
| if !self.url().starts_with(prefix) { |
| return None; |
| } |
| |
| // TODO: url-encoded characters in the prefix are not implemented |
| assert!(self.url.starts_with(prefix)); |
| Some(Request { |
| method: self.method.clone(), |
| url: self.url[prefix.len() ..].to_owned(), |
| headers: self.headers.clone(), // TODO: expensive |
| https: self.https, |
| data: self.data.clone(), |
| remote_addr: self.remote_addr, |
| }) |
| } |
| |
| /// Returns `true` if the request uses HTTPS, and `false` if it uses HTTP. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use rouille::{Request, Response}; |
| /// |
| /// fn handle(request: &Request) -> Response { |
| /// if !request.is_secure() { |
| /// return Response::redirect_303(format!("https://example.com")); |
| /// } |
| /// |
| /// // ... |
| /// # panic!() |
| /// } |
| /// ``` |
| #[inline] |
| pub fn is_secure(&self) -> bool { |
| self.https |
| } |
| |
| /// Returns the method of the request (`GET`, `POST`, etc.). |
| #[inline] |
| pub fn method(&self) -> &str { |
| &self.method |
| } |
| |
| /// Returns the raw URL requested by the client. It is not decoded and thus can contain strings |
| /// such as `%20`, and the query parameters such as `?p=hello`. |
| /// |
| /// See also `url()`. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use rouille::Request; |
| /// |
| /// let request = Request::fake_http("GET", "/hello%20world?foo=bar", vec![], vec![]); |
| /// assert_eq!(request.raw_url(), "/hello%20world?foo=bar"); |
| /// ``` |
| #[inline] |
| pub fn raw_url(&self) -> &str { |
| &self.url |
| } |
| |
| /// Returns the raw query string requested by the client. In other words, everything after the |
| /// first `?` in the raw url. |
| /// |
| /// Returns the empty string if no query string. |
| #[inline] |
| pub fn raw_query_string(&self) -> &str { |
| if let Some(pos) = self.url.bytes().position(|c| c == b'?') { |
| self.url.split_at(pos + 1).1 |
| } else { |
| "" |
| } |
| } |
| |
| /// Returns the URL requested by the client. |
| /// |
| /// Contrary to `raw_url`, special characters have been decoded and the query string |
| /// (eg `?p=hello`) has been removed. |
| /// |
| /// If there is any non-unicode character in the URL, it will be replaced with `U+FFFD`. |
| /// |
| /// > **Note**: This function will decode the token `%2F` will be decoded as `/`. However the |
| /// > official speficiations say that such a token must not count as a delimiter for URL paths. |
| /// > In other words, `/hello/world` is not the same as `/hello%2Fworld`. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use rouille::Request; |
| /// |
| /// let request = Request::fake_http("GET", "/hello%20world?foo=bar", vec![], vec![]); |
| /// assert_eq!(request.url(), "/hello world"); |
| /// ``` |
| pub fn url(&self) -> String { |
| let url = self.url.as_bytes(); |
| let url = if let Some(pos) = url.iter().position(|&c| c == b'?') { |
| &url[..pos] |
| } else { |
| url |
| }; |
| |
| url::percent_encoding::percent_decode(url).decode_utf8_lossy().into_owned() |
| } |
| |
| /// Returns the value of a GET parameter. |
| /// TODO: clumbsy |
| pub fn get_param(&self, param_name: &str) -> Option<String> { |
| let get_params = self.raw_query_string(); |
| |
| // TODO: `hello=5` will be matched for param name `lo` |
| |
| let param = match get_params.rfind(&format!("{}=", param_name)) { |
| Some(p) => p + param_name.len() + 1, |
| None => return None, |
| }; |
| |
| let value = match get_params.bytes().skip(param).position(|c| c == b'&') { |
| None => &get_params[param..], |
| Some(e) => &get_params[param .. e + param], |
| }; |
| |
| Some(url::percent_encoding::percent_decode(value.replace("+", " ").as_bytes()).decode_utf8_lossy().into_owned()) |
| } |
| |
| /// Returns the value of a header of the request. |
| /// |
| /// Returns `None` if no such header could be found. |
| #[inline] |
| pub fn header(&self, key: &str) -> Option<&str> { |
| self.headers.iter().find(|&&(ref k, _)| k.eq_ignore_ascii_case(key)).map(|&(_, ref v)| &v[..]) |
| } |
| |
| /// Returns a list of all the headers of the request. |
| #[inline] |
| pub fn headers(&self) -> HeadersIter { |
| HeadersIter { iter: self.headers.iter() } |
| } |
| |
| /// Returns the state of the `DNT` (Do Not Track) header. |
| /// |
| /// If the header is missing or is malformed, `None` is returned. If the header exists, |
| /// `Some(true)` is returned if `DNT` is `1` and `Some(false)` is returned if `DNT` is `0`. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use rouille::{Request, Response}; |
| /// |
| /// # fn track_user(request: &Request) {} |
| /// fn handle(request: &Request) -> Response { |
| /// if !request.do_not_track().unwrap_or(false) { |
| /// track_user(&request); |
| /// } |
| /// |
| /// // ... |
| /// # panic!() |
| /// } |
| /// ``` |
| pub fn do_not_track(&self) -> Option<bool> { |
| match self.header("DNT") { |
| Some(h) if h == "1" => Some(true), |
| Some(h) if h == "0" => Some(false), |
| _ => None |
| } |
| } |
| |
| /// Returns the body of the request. |
| /// |
| /// The body can only be retrieved once. Returns `None` is the body has already been retreived |
| /// before. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use std::io::Read; |
| /// use rouille::{Request, Response, ResponseBody}; |
| /// |
| /// fn echo(request: &Request) -> Response { |
| /// let mut data = request.data().expect("Oops, body already retrieved, problem \ |
| /// in the server"); |
| /// |
| /// let mut buf = Vec::new(); |
| /// match data.read_to_end(&mut buf) { |
| /// Ok(_) => (), |
| /// Err(_) => return Response::text("Failed to read body") |
| /// }; |
| /// |
| /// Response { |
| /// data: ResponseBody::from_data(buf), |
| /// .. Response::text("") |
| /// } |
| /// } |
| /// ``` |
| pub fn data(&self) -> Option<RequestBody> { |
| let reader = self.data.lock().unwrap().take(); |
| reader.map(|r| RequestBody { body: r, marker: PhantomData }) |
| } |
| |
| /// Returns the address of the client that made this request. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use rouille::{Request, Response}; |
| /// |
| /// fn handle(request: &Request) -> Response { |
| /// Response::text(format!("Your IP is: {:?}", request.remote_addr())) |
| /// } |
| /// ``` |
| #[inline] |
| pub fn remote_addr(&self) -> &SocketAddr { |
| &self.remote_addr |
| } |
| } |
| |
| /// Iterator to the list of headers in a request. |
| #[derive(Debug, Clone)] |
| pub struct HeadersIter<'a> { |
| iter: SliceIter<'a, (String, String)> |
| } |
| |
| impl<'a> Iterator for HeadersIter<'a> { |
| type Item = (&'a str, &'a str); |
| |
| #[inline] |
| fn next(&mut self) -> Option<Self::Item> { |
| self.iter.next().map(|&(ref k, ref v)| (&k[..], &v[..])) |
| } |
| |
| #[inline] |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| self.iter.size_hint() |
| } |
| } |
| |
| impl<'a> ExactSizeIterator for HeadersIter<'a> { |
| } |
| |
| /// Gives access to the body of a request. |
| /// |
| /// In order to obtain this object, call `request.data()`. |
| pub struct RequestBody<'a> { |
| body: Box<Read + Send>, |
| marker: PhantomData<&'a ()>, |
| } |
| |
| impl<'a> Read for RequestBody<'a> { |
| #[inline] |
| fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
| self.body.read(buf) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use Request; |
| |
| #[test] |
| fn header() { |
| let request = Request::fake_http("GET", "/", vec![("Host".to_owned(), "localhost".to_owned())], vec![]); |
| assert_eq!(request.header("Host"), Some("localhost")); |
| assert_eq!(request.header("host"), Some("localhost")); |
| } |
| |
| #[test] |
| fn get_param() { |
| let request = Request::fake_http("GET", "/?p=hello", vec![], vec![]); |
| assert_eq!(request.get_param("p"), Some("hello".to_owned())); |
| } |
| |
| #[test] |
| fn body_twice() { |
| let request = Request::fake_http("GET", "/", vec![], vec![62, 62, 62]); |
| assert!(request.data().is_some()); |
| assert!(request.data().is_none()); |
| } |
| |
| #[test] |
| fn url_strips_get_query() { |
| let request = Request::fake_http("GET", "/?p=hello", vec![], vec![]); |
| assert_eq!(request.url(), "/"); |
| } |
| |
| #[test] |
| fn urlencode_query_string() { |
| let request = Request::fake_http("GET", "/?p=hello%20world", vec![], vec![]); |
| assert_eq!(request.get_param("p"), Some("hello world".to_owned())); |
| } |
| |
| #[test] |
| fn plus_in_query_string() { |
| let request = Request::fake_http("GET", "/?p=hello+world", vec![], vec![]); |
| assert_eq!(request.get_param("p"), Some("hello world".to_owned())); |
| } |
| |
| #[test] |
| fn encoded_plus_in_query_string() { |
| let request = Request::fake_http("GET", "/?p=hello%2Bworld", vec![], vec![]); |
| assert_eq!(request.get_param("p"), Some("hello+world".to_owned())); |
| } |
| |
| #[test] |
| fn url_encode() { |
| let request = Request::fake_http("GET", "/hello%20world", vec![], vec![]); |
| assert_eq!(request.url(), "/hello world"); |
| } |
| |
| #[test] |
| fn plus_in_url() { |
| let request = Request::fake_http("GET", "/hello+world", vec![], vec![]); |
| assert_eq!(request.url(), "/hello+world"); |
| } |
| |
| #[test] |
| fn dnt() { |
| let request = Request::fake_http("GET", "/", vec![("DNT".to_owned(), "1".to_owned())], vec![]); |
| assert_eq!(request.do_not_track(), Some(true)); |
| |
| let request = Request::fake_http("GET", "/", vec![("DNT".to_owned(), "0".to_owned())], vec![]); |
| assert_eq!(request.do_not_track(), Some(false)); |
| |
| let request = Request::fake_http("GET", "/", vec![], vec![]); |
| assert_eq!(request.do_not_track(), None); |
| |
| let request = Request::fake_http("GET", "/", vec![("DNT".to_owned(), "malformed".to_owned())], vec![]); |
| assert_eq!(request.do_not_track(), None); |
| } |
| } |