1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
//! A CORS middleware for Iron. //! //! See https://www.html5rocks.com/static/images/cors_server_flowchart.png for //! reference. //! //! The middleware will return `HTTP 400 Bad Request` if the Origin host is //! missing or not allowed. //! //! Preflight requests are not yet supported. //! //! # Usage //! //! There are two modes available: //! //! ## Mode 1: Whitelist //! //! The user of the middleware must specify a list of allowed hosts (port or //! protocol aren't being checked by the middleware). The wrapped handler will only //! be executed if the hostname in the `Origin` header matches one of the allowed //! hosts. Requests without an `Origin` header will be rejected. //! //! Initialize the middleware with a `HashSet` of allowed host strings: //! //! ```rust //! use std::collections::HashSet; //! use iron_cors::CorsMiddleware; //! //! let allowed_hosts = ["example.com"].iter() //! .map(ToString::to_string) //! .collect::<HashSet<_>>(); //! let middleware = CorsMiddleware::with_whitelist(allowed_hosts); //! ``` //! //! See //! [`examples/whitelist.rs`](https://github.com/dbrgn/iron-cors-rs/blob/master/examples/whitelist.rs) //! for a full usage example. //! //! ## Mode 2: Allow Any //! //! Alternatively, the user of the middleware can choose to allow requests from //! any origin. //! //! ```rust //! use iron_cors::CorsMiddleware; //! //! let middleware = CorsMiddleware::with_allow_any(true); //! ``` //! //! The boolean flag specifies whether requests without an `Origin` header are //! acceptable. When set to `false`, requests without that header will be //! answered with a HTTP 400 response. //! //! See //! [`examples/allow_any.rs`](https://github.com/dbrgn/iron-cors-rs/blob/master/examples/allow_any.rs) //! for a full usage example. extern crate iron; #[macro_use] extern crate log; use std::collections::HashSet; use iron::{Request, Response, IronResult, AroundMiddleware, Handler}; use iron::{headers, status}; /// The struct that holds the CORS configuration. pub struct CorsMiddleware { allowed_hosts: Option<HashSet<String>>, allow_invalid: bool, } impl CorsMiddleware { /// Specify which origin hosts are allowed to access the resource. /// /// Requests without an `Origin` header will be rejected. pub fn with_whitelist(allowed_hosts: HashSet<String>) -> Self { CorsMiddleware { allowed_hosts: Some(allowed_hosts), allow_invalid: false, } } /// Allow all origins to access the resource. The /// `Access-Control-Allow-Origin` header of the response will be set to /// `*`. /// /// The `allow_invalid` parameter specifies whether requests without an /// `Origin` header should be accepted or not. When set to `false`, /// requests without that header will be answered with a HTTP 400 /// response. pub fn with_allow_any(allow_invalid: bool) -> Self { CorsMiddleware { allowed_hosts: None, allow_invalid: allow_invalid, } } } impl AroundMiddleware for CorsMiddleware { fn around(self, handler: Box<Handler>) -> Box<Handler> { match self.allowed_hosts { Some(allowed_hosts) => Box::new(CorsHandlerWhitelist { handler: handler, allowed_hosts: allowed_hosts, }), None => Box::new(CorsHandlerAllowAny { handler: handler, allow_invalid: self.allow_invalid, }), } } } /// Handler for whitelist based rules. struct CorsHandlerWhitelist { handler: Box<Handler>, allowed_hosts: HashSet<String>, } /// Handler if allowing any origin. struct CorsHandlerAllowAny { handler: Box<Handler>, allow_invalid: bool, } /// The handler that acts as an AroundMiddleware. /// /// It first checks an incoming request for appropriate CORS headers. If the /// request is allowed, then process it as usual. If not, return a proper error /// response. impl Handler for CorsHandlerWhitelist { fn handle(&self, req: &mut Request) -> IronResult<Response> { // Extract origin header let origin = match req.headers.get::<headers::Origin>() { Some(origin) => origin.clone(), None => { warn!("Not a valid CORS request: Missing Origin header"); return Ok(Response::with((status::BadRequest, "Invalid CORS request: Origin header missing"))); } }; // Verify origin header let may_process = self.allowed_hosts.contains(&origin.host.hostname); // Process request if may_process { // Everything OK, process request let mut res = try!(self.handler.handle(req)); // Add Access-Control-Allow-Origin header to response let header = match origin.host.port { Some(port) => format!("{}://{}:{}", &origin.scheme, &origin.host.hostname, &port), None => format!("{}://{}", &origin.scheme, &origin.host.hostname), }; res.headers.set(headers::AccessControlAllowOrigin::Value(header)); Ok(res) } else { warn!("Got disallowed CORS request from {}", &origin.host.hostname); Ok(Response::with((status::BadRequest, "Invalid CORS request: Origin not allowed"))) } } } /// The handler that acts as an AroundMiddleware. /// /// It first checks an incoming request for appropriate CORS headers. If the /// `Origin` header is present, or if invalid CORS requests are allowed, then /// process it as usual. If not, return a proper error response. impl Handler for CorsHandlerAllowAny { fn handle(&self, req: &mut Request) -> IronResult<Response> { // Extract origin header match req.headers.get::<headers::Origin>() { // If `Origin` wasn't set, abort if the user disallows invalid // CORS requests. None if !self.allow_invalid => { warn!("Not a valid CORS request: Missing Origin header"); return Ok(Response::with((status::BadRequest, "Invalid CORS request: Origin header missing"))); }, _ => {}, } // Everything OK, process request let mut res = try!(self.handler.handle(req)); // Add Access-Control-Allow-Origin header to response res.headers.set(headers::AccessControlAllowOrigin::Value("*".into())); Ok(res) } }