Serving content at /api/content/<hash> (#2248)
This commit is contained in:
parent
368aca521b
commit
862feb7172
@ -15,23 +15,26 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::sync::Arc;
|
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::types::{App, ApiError};
|
||||||
use api::response::{as_json, as_json_error, ping_response};
|
use api::response::{as_json, as_json_error, ping_response};
|
||||||
use handlers::extract_url;
|
use handlers::extract_url;
|
||||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
||||||
|
use apps::fetcher::ContentFetcher;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RestApi {
|
pub struct RestApi {
|
||||||
local_domain: String,
|
local_domain: String,
|
||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
|
fetcher: Arc<ContentFetcher>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestApi {
|
impl RestApi {
|
||||||
pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
pub fn new(local_domain: String, endpoints: Arc<Endpoints>, fetcher: Arc<ContentFetcher>) -> Box<Endpoint> {
|
||||||
Box::new(RestApi {
|
Box::new(RestApi {
|
||||||
local_domain: local_domain,
|
local_domain: local_domain,
|
||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
|
fetcher: fetcher,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,23 +46,42 @@ impl RestApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for RestApi {
|
impl Endpoint for RestApi {
|
||||||
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<Handler> {
|
||||||
Box::new(RestApiRouter {
|
Box::new(RestApiRouter::new(self.clone(), path, control))
|
||||||
api: self.clone(),
|
|
||||||
handler: as_json_error(&ApiError {
|
|
||||||
code: "404".into(),
|
|
||||||
title: "Not Found".into(),
|
|
||||||
detail: "Resource you requested has not been found.".into(),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RestApiRouter {
|
struct RestApiRouter {
|
||||||
api: RestApi,
|
api: RestApi,
|
||||||
|
path: Option<EndpointPath>,
|
||||||
|
control: Option<Control>,
|
||||||
handler: Box<Handler>,
|
handler: Box<Handler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<Box<Handler>> {
|
||||||
|
match hash {
|
||||||
|
Some(hash) if self.api.fetcher.contains(hash) => {
|
||||||
|
Some(self.api.fetcher.to_async_handler(path, control))
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||||
|
|
||||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||||
@ -69,13 +91,18 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
|
|||||||
return Next::write();
|
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 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 {
|
let handler = endpoint.and_then(|v| match v {
|
||||||
"apps" => Some(as_json(&self.api.list_apps())),
|
"apps" => Some(as_json(&self.api.list_apps())),
|
||||||
"ping" => Some(ping_response(&self.api.local_domain)),
|
"ping" => Some(ping_response(&self.api.local_domain)),
|
||||||
_ => None,
|
"content" => self.resolve_content(hash, path, control),
|
||||||
|
_ => None
|
||||||
});
|
});
|
||||||
|
|
||||||
// Overwrite default
|
// Overwrite default
|
||||||
|
@ -42,7 +42,9 @@ pub type Handler = server::Handler<net::HttpStream> + Send;
|
|||||||
pub trait Endpoint : Send + Sync {
|
pub trait Endpoint : Send + Sync {
|
||||||
fn info(&self) -> Option<&EndpointInfo> { None }
|
fn info(&self) -> Option<&EndpointInfo> { None }
|
||||||
|
|
||||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler>;
|
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
||||||
|
panic!("This Endpoint is asynchronous and requires Control object.");
|
||||||
|
}
|
||||||
|
|
||||||
fn to_async_handler(&self, path: EndpointPath, _control: hyper::Control) -> Box<Handler> {
|
fn to_async_handler(&self, path: EndpointPath, _control: hyper::Control) -> Box<Handler> {
|
||||||
self.to_handler(path)
|
self.to_handler(path)
|
||||||
|
@ -196,8 +196,11 @@ impl Server {
|
|||||||
let special = Arc::new({
|
let special = Arc::new({
|
||||||
let mut special = HashMap::new();
|
let mut special = HashMap::new();
|
||||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
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::Utils, apps::utils());
|
||||||
|
special.insert(
|
||||||
|
router::SpecialEndpoint::Api,
|
||||||
|
api::RestApi::new(format!("{}", addr), endpoints.clone(), content_fetcher.clone())
|
||||||
|
);
|
||||||
special
|
special
|
||||||
});
|
});
|
||||||
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||||
|
@ -91,7 +91,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
(Some(ref path), _) if self.fetch.contains(&path.app_id) => {
|
(Some(ref path), _) if self.fetch.contains(&path.app_id) => {
|
||||||
self.fetch.to_async_handler(path.clone(), control)
|
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 => {
|
(Some(ref path), _) if *req.method() == hyper::method::Method::Get => {
|
||||||
let address = apps::redirection_address(path.using_dapps_domains, self.main_page);
|
let address = apps::redirection_address(path.using_dapps_domains, self.main_page);
|
||||||
Box::new(ContentHandler::error(
|
Box::new(ContentHandler::error(
|
||||||
@ -143,7 +143,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
allowed_hosts: Option<Vec<String>>,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Api).unwrap().to_handler(EndpointPath::default());
|
let handler = special.get(&SpecialEndpoint::Utils).unwrap().to_handler(EndpointPath::default());
|
||||||
Router {
|
Router {
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
main_page: main_page,
|
main_page: main_page,
|
||||||
|
@ -38,10 +38,6 @@ struct RpcEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for RpcEndpoint {
|
impl Endpoint for RpcEndpoint {
|
||||||
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
|
||||||
panic!("RPC Endpoint is asynchronous and requires Control object.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
||||||
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
||||||
Box::new(ServerHandler::new(
|
Box::new(ServerHandler::new(
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve, request};
|
use tests::helpers::{serve, serve_with_registrar, request};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_error() {
|
fn should_return_error() {
|
||||||
@ -82,3 +82,24 @@ fn should_handle_ping() {
|
|||||||
assert_eq!(response.body, "0\n\n".to_owned());
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user