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/>.
|
||||
|
||||
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<Endpoints>,
|
||||
fetcher: Arc<ContentFetcher>,
|
||||
}
|
||||
|
||||
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 {
|
||||
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<Handler> {
|
||||
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<Handler> {
|
||||
Box::new(RestApiRouter::new(self.clone(), path, control))
|
||||
}
|
||||
}
|
||||
|
||||
struct RestApiRouter {
|
||||
api: RestApi,
|
||||
path: Option<EndpointPath>,
|
||||
control: Option<Control>,
|
||||
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 {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -42,7 +42,9 @@ pub type Handler = server::Handler<net::HttpStream> + Send;
|
||||
pub trait Endpoint : Send + Sync {
|
||||
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> {
|
||||
self.to_handler(path)
|
||||
|
@ -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));
|
||||
|
@ -91,7 +91,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
(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<A: Authorization> Router<A> {
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
) -> 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,
|
||||
|
@ -38,10 +38,6 @@ struct 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> {
|
||||
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
||||
Box::new(ServerHandler::new(
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// 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]
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user