From 862feb71722f11bed6d2f914145feaf4a8bcf4a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 22 Sep 2016 18:05:36 +0200 Subject: [PATCH] Serving content at /api/content/ (#2248) --- dapps/src/api/api.rs | 53 +++++++++++++++++++++++++++++++---------- dapps/src/endpoint.rs | 4 +++- dapps/src/lib.rs | 5 +++- dapps/src/router/mod.rs | 4 ++-- dapps/src/rpc.rs | 4 ---- dapps/src/tests/api.rs | 23 +++++++++++++++++- 6 files changed, 71 insertions(+), 22 deletions(-) diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs index 84db93f63..9a8dfef95 100644 --- a/dapps/src/api/api.rs +++ b/dapps/src/api/api.rs @@ -15,23 +15,26 @@ // along with Parity. If not, see . use std::sync::Arc; -use hyper::{server, net, Decoder, Encoder, Next}; +use hyper::{server, net, Decoder, Encoder, Next, Control}; use api::types::{App, ApiError}; use api::response::{as_json, as_json_error, ping_response}; use handlers::extract_url; use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; +use apps::fetcher::ContentFetcher; #[derive(Clone)] pub struct RestApi { local_domain: String, endpoints: Arc, + fetcher: Arc, } impl RestApi { - pub fn new(local_domain: String, endpoints: Arc) -> Box { + pub fn new(local_domain: String, endpoints: Arc, fetcher: Arc) -> Box { Box::new(RestApi { local_domain: local_domain, endpoints: endpoints, + fetcher: fetcher, }) } @@ -43,23 +46,42 @@ impl RestApi { } impl Endpoint for RestApi { - fn to_handler(&self, _path: EndpointPath) -> Box { - Box::new(RestApiRouter { - api: self.clone(), - handler: as_json_error(&ApiError { - code: "404".into(), - title: "Not Found".into(), - detail: "Resource you requested has not been found.".into(), - }), - }) + fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box { + Box::new(RestApiRouter::new(self.clone(), path, control)) } } struct RestApiRouter { api: RestApi, + path: Option, + control: Option, handler: Box, } +impl RestApiRouter { + fn new(api: RestApi, path: EndpointPath, control: Control) -> Self { + RestApiRouter { + path: Some(path), + control: Some(control), + api: api, + handler: as_json_error(&ApiError { + code: "404".into(), + title: "Not Found".into(), + detail: "Resource you requested has not been found.".into(), + }), + } + } + + fn resolve_content(&self, hash: Option<&str>, path: EndpointPath, control: Control) -> Option> { + match hash { + Some(hash) if self.api.fetcher.contains(hash) => { + Some(self.api.fetcher.to_async_handler(path, control)) + }, + _ => None + } + } +} + impl server::Handler for RestApiRouter { fn on_request(&mut self, request: server::Request) -> Next { @@ -69,13 +91,18 @@ impl server::Handler for RestApiRouter { return Next::write(); } - let url = url.expect("Check for None is above; qed"); + let url = url.expect("Check for None early-exists above; qed"); + let path = self.path.take().expect("on_request called only once, and path is always defined in new; qed"); + let control = self.control.take().expect("on_request called only once, and control is always defined in new; qed"); + let endpoint = url.path.get(1).map(|v| v.as_str()); + let hash = url.path.get(2).map(|v| v.as_str()); let handler = endpoint.and_then(|v| match v { "apps" => Some(as_json(&self.api.list_apps())), "ping" => Some(ping_response(&self.api.local_domain)), - _ => None, + "content" => self.resolve_content(hash, path, control), + _ => None }); // Overwrite default diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index 51e863f19..eea7a872f 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -42,7 +42,9 @@ pub type Handler = server::Handler + Send; pub trait Endpoint : Send + Sync { fn info(&self) -> Option<&EndpointInfo> { None } - fn to_handler(&self, path: EndpointPath) -> Box; + fn to_handler(&self, _path: EndpointPath) -> Box { + panic!("This Endpoint is asynchronous and requires Control object."); + } fn to_async_handler(&self, path: EndpointPath, _control: hyper::Control) -> Box { self.to_handler(path) diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 4dcf53a44..edc0bebe5 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -196,8 +196,11 @@ impl Server { let special = Arc::new({ let mut special = HashMap::new(); special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone())); - special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone())); special.insert(router::SpecialEndpoint::Utils, apps::utils()); + special.insert( + router::SpecialEndpoint::Api, + api::RestApi::new(format!("{}", addr), endpoints.clone(), content_fetcher.clone()) + ); special }); let hosts = Self::allowed_hosts(hosts, format!("{}", addr)); diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index f54d6bf3d..e3ff6e64f 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -91,7 +91,7 @@ impl server::Handler for Router { (Some(ref path), _) if self.fetch.contains(&path.app_id) => { self.fetch.to_async_handler(path.clone(), control) }, - // Redirection to main page (maybe 404 instead?) + // 404 for non-existent content (Some(ref path), _) if *req.method() == hyper::method::Method::Get => { let address = apps::redirection_address(path.using_dapps_domains, self.main_page); Box::new(ContentHandler::error( @@ -143,7 +143,7 @@ impl Router { allowed_hosts: Option>, ) -> Self { - let handler = special.get(&SpecialEndpoint::Api).unwrap().to_handler(EndpointPath::default()); + let handler = special.get(&SpecialEndpoint::Utils).unwrap().to_handler(EndpointPath::default()); Router { control: Some(control), main_page: main_page, diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs index 4fbc37772..649d283ce 100644 --- a/dapps/src/rpc.rs +++ b/dapps/src/rpc.rs @@ -38,10 +38,6 @@ struct RpcEndpoint { } impl Endpoint for RpcEndpoint { - fn to_handler(&self, _path: EndpointPath) -> Box { - panic!("RPC Endpoint is asynchronous and requires Control object."); - } - fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box { let panic_handler = PanicHandler { handler: self.panic_handler.clone() }; Box::new(ServerHandler::new( diff --git a/dapps/src/tests/api.rs b/dapps/src/tests/api.rs index ab0d33726..fc255ec20 100644 --- a/dapps/src/tests/api.rs +++ b/dapps/src/tests/api.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use tests::helpers::{serve, request}; +use tests::helpers::{serve, serve_with_registrar, request}; #[test] fn should_return_error() { @@ -82,3 +82,24 @@ fn should_handle_ping() { assert_eq!(response.body, "0\n\n".to_owned()); } + +#[test] +fn should_try_to_resolve_dapp() { + // given + let (server, registrar) = serve_with_registrar(); + + // when + let response = request(server, + "\ + GET /api/content/1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d HTTP/1.1\r\n\ + Host: home.parity\r\n\ + Connection: close\r\n\ + \r\n\ + " + ); + + // then + assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); + assert_eq!(registrar.calls.lock().len(), 2); +} +