handler.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. use config::Config;
  2. use mime::Mime;
  3. use std::io;
  4. use std::collections::HashSet;
  5. use hyper::Get;
  6. use hyper::Client;
  7. use hyper::client;
  8. use hyper::header::{ContentType, ETag, EntityTag, IfNoneMatch, Headers};
  9. use hyper::method::Method;
  10. use hyper::server::{Handler, Request, Response};
  11. use hyper::status::StatusCode::{InternalServerError, NotFound, NotModified, Unauthorized};
  12. use hyper::uri::RequestUri::AbsolutePath;
  13. use hyper::Url;
  14. use rustc_serialize::json;
  15. use rustc_serialize::json::Json;
  16. use sha1::Sha1;
  17. const API_ENDPOINT: &'static str = "https://api.cloudflare.com/client/v4";
  18. const INDEX_HTML: &'static str = include_str!("../../index.html");
  19. const BUNDLE_JS: &'static str = include_str!("../../assets/bundle.js");
  20. struct Etags {
  21. index: EntityTag,
  22. bundle: EntityTag
  23. }
  24. pub struct SiteHandler {
  25. cfg: Config,
  26. client: Client,
  27. etags: Etags,
  28. whitelist: HashSet<String>
  29. }
  30. impl SiteHandler {
  31. fn make_headers(&self) -> Headers {
  32. let mut headers = Headers::new();
  33. headers.set_raw("x-auth-email", vec![self.cfg.email.as_bytes().to_vec()]);
  34. headers.set_raw("x-auth-key", vec![self.cfg.token.as_bytes().to_vec()]);
  35. headers.set(ContentType::json());
  36. headers
  37. }
  38. fn request(&self, method: Method, url: &str, mut body: Option<Request>) -> client::Response {
  39. let url = API_ENDPOINT.to_owned() + url;
  40. let mut url = Url::parse(&url).unwrap();
  41. if method == Get {
  42. url.query_pairs_mut().append_pair("per_page", "999");
  43. }
  44. let request = self.client.request(method, url).headers(self.make_headers());
  45. let request = match body.as_mut() {
  46. Some(body) => request.body(body),
  47. None => request
  48. };
  49. request.send().unwrap()
  50. }
  51. fn has_whitelist(&self) -> bool {
  52. self.cfg.whitelist.is_some()
  53. }
  54. fn is_valid(&self, path: &str) -> bool {
  55. if self.has_whitelist() {
  56. let zone: String = path.chars()
  57. .skip(1)
  58. .skip_while(|c| *c != '/')
  59. .skip(1)
  60. .take_while(|c| *c != '/')
  61. .collect();
  62. self.whitelist.contains(&zone)
  63. }
  64. else {
  65. true
  66. }
  67. }
  68. }
  69. fn make_etag(source: &str) -> EntityTag {
  70. let mut m = Sha1::new();
  71. m.update(source.as_bytes());
  72. let digest = m.digest().to_string();
  73. EntityTag::new(false, digest)
  74. }
  75. pub fn new(cfg: Config) -> SiteHandler {
  76. let mut handler = SiteHandler {
  77. cfg: cfg,
  78. client: Client::new(),
  79. etags: Etags {
  80. index: make_etag(INDEX_HTML),
  81. bundle: make_etag(BUNDLE_JS)
  82. },
  83. whitelist: HashSet::new()
  84. };
  85. let mut response = handler.request(Get, "/zones", None);
  86. if let Some(ref domain_whitelist) = handler.cfg.whitelist {
  87. let mut whitelist: HashSet<String> = HashSet::new();
  88. match Json::from_reader(&mut response) {
  89. Ok(body) => {
  90. let zones = body.find("result").and_then(|result| result.as_array()).unwrap();
  91. for zone in zones {
  92. let id = zone.find("id").and_then(|id| id.as_string()).unwrap();
  93. let name = zone.find("name").and_then(Json::as_string).unwrap();
  94. if domain_whitelist.into_iter().any(|domain| domain == name) {
  95. whitelist.insert(id.to_owned());
  96. }
  97. }
  98. },
  99. Err(error) => {
  100. println!("Error: {}", error);
  101. }
  102. }
  103. handler.whitelist = whitelist;
  104. }
  105. handler
  106. }
  107. fn serve(req: &Request, mut res: Response, content: &str, etag: &EntityTag, mime: Mime) {
  108. let empty_vec = vec!();
  109. let etags = req.headers.get::<IfNoneMatch>();
  110. let etags: &Vec<EntityTag> = match etags {
  111. Some(&IfNoneMatch::Items(ref items)) => items,
  112. _ => &empty_vec
  113. };
  114. let is_cached = etags.iter().find(|&etag_b| etag.weak_eq(etag_b)).is_some();
  115. res.headers_mut().set(ContentType(mime));
  116. res.headers_mut().set(ETag(etag.to_owned()));
  117. if is_cached {
  118. *res.status_mut() = NotModified;
  119. }
  120. else {
  121. res.send(content.as_bytes()).unwrap();
  122. }
  123. }
  124. impl Handler for SiteHandler {
  125. fn handle(&self, req: Request, mut res: Response) {
  126. let uri = req.uri.clone();
  127. let method = req.method.clone();
  128. match uri {
  129. AbsolutePath(ref path) => match (&method, &path[..]) {
  130. (&Get, "/") =>
  131. serve(&req, res, INDEX_HTML, &self.etags.index, mime!(Text/Html; Charset=Utf8)),
  132. (&Get, "/assets/bundle.js") =>
  133. serve(&req, res, BUNDLE_JS, &self.etags.bundle, mime!(Application/Javascript; Charset=Utf8)),
  134. (method, url) if path.starts_with("/api") => {
  135. let method = method.clone();
  136. let path: String = url.chars().skip(4).collect();
  137. let whitelist = &self.whitelist;
  138. if path == "/zones" {
  139. let mut proxy_res = self.request(method, &path, None);
  140. if self.has_whitelist() {
  141. match Json::from_reader(&mut proxy_res) {
  142. Ok(mut body) => {
  143. // filter out non-whitelisted domains
  144. body.as_object_mut()
  145. .and_then(|mut root| root.get_mut("result"))
  146. .and_then(|mut result| result.as_array_mut())
  147. .map(|mut zones| {
  148. zones.retain(|zone| {
  149. let id = zone.find("id").and_then(|id| id.as_string()).unwrap();
  150. whitelist.contains(id)
  151. });
  152. });
  153. let json = json::encode(&body).unwrap();
  154. res.headers_mut().set(ContentType(mime!(Application/Json; Charset=Utf8)));
  155. res.send(json.as_bytes()).unwrap();
  156. },
  157. Err(error) => {
  158. println!("Error: {}", error);
  159. *res.status_mut() = InternalServerError;
  160. res.send(b"Unexpected response from CloudFlare").unwrap();
  161. }
  162. };
  163. }
  164. else {
  165. res.headers_mut().set(ContentType(mime!(Application/Json; Charset=Utf8)));
  166. let mut res = res.start().unwrap();
  167. io::copy(&mut proxy_res, &mut res).ok().expect("Failed to proxy");
  168. res.end().unwrap();
  169. }
  170. }
  171. else if self.is_valid(&path) {
  172. let mut proxy_res = self.request(method, &path, Some(req));
  173. res.headers_mut().set(ContentType(mime!(Application/Json; Charset=Utf8)));
  174. let mut res = res.start().unwrap();
  175. io::copy(&mut proxy_res, &mut res).ok().expect("Failed to proxy");
  176. res.end().unwrap();
  177. }
  178. else {
  179. *res.status_mut() = Unauthorized;
  180. res.send(b"Unauthorized").unwrap();
  181. }
  182. },
  183. (&Get, _) =>
  184. serve(&req, res, INDEX_HTML, &self.etags.index, mime!(Text/Html; Charset=Utf8)),
  185. _ => {
  186. *res.status_mut() = NotFound;
  187. res.send(b"Not Found").unwrap();
  188. }
  189. },
  190. _ => {
  191. *res.status_mut() = NotFound;
  192. res.send(b"Not Found").unwrap();
  193. }
  194. }
  195. }
  196. }