Remove Iron dependency and update hyper to version 0.12 (#1241)
* Remove Iron dependecy and update hyper to version 0.12 #876 * REMOVE ME * Revert "REMOVE ME" This reverts commit e9a976eee98a2d5a4dfae5d9e1e4f5ed640c05d3. * Rebase and start updating libwallet Libwallet doesn't compile yet. * Wallet compiles * Grin compiles * No compilation errors in tests * All tests pass * Reeturn future from handler * Refactoring * Fix lifetime issue one more time I have to force push to rollback all the work done in last 2 days * Fix wallet send issue * Clean up
This commit is contained in:
+6
-64
@@ -18,13 +18,6 @@
|
||||
use failure::ResultExt;
|
||||
use libwallet::types::*;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
|
||||
use futures::{Future, Stream};
|
||||
use hyper::header::ContentType;
|
||||
use hyper::{self, Method, Request};
|
||||
use serde_json;
|
||||
use tokio_core::reactor;
|
||||
|
||||
use api;
|
||||
use error::{Error, ErrorKind};
|
||||
@@ -90,38 +83,9 @@ impl WalletClient for HTTPWalletClient {
|
||||
let url = format!("{}/v1/wallet/foreign/receive_tx", dest);
|
||||
debug!(LOGGER, "Posting transaction slate to {}", url);
|
||||
|
||||
let mut core = reactor::Core::new().context(libwallet::ErrorKind::ClientCallback(
|
||||
"Sending transaction: Initialise API",
|
||||
))?;
|
||||
let client = hyper::Client::new(&core.handle());
|
||||
|
||||
let url_pool = url.to_owned();
|
||||
|
||||
let mut req = Request::new(
|
||||
Method::Post,
|
||||
url_pool
|
||||
.parse::<hyper::Uri>()
|
||||
.context(libwallet::ErrorKind::ClientCallback(
|
||||
"Sending transaction: parsing URL",
|
||||
))?,
|
||||
);
|
||||
req.headers_mut().set(ContentType::json());
|
||||
let json = serde_json::to_string(&slate).context(libwallet::ErrorKind::ClientCallback(
|
||||
"Sending transaction: parsing response",
|
||||
))?;
|
||||
req.set_body(json);
|
||||
|
||||
let work = client.request(req).and_then(|res| {
|
||||
res.body().concat2().and_then(move |body| {
|
||||
let slate: Slate = serde_json::from_slice(&body)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
Ok(slate)
|
||||
})
|
||||
});
|
||||
let res = core.run(work)
|
||||
.context(libwallet::ErrorKind::ClientCallback(
|
||||
"Sending transaction: posting request",
|
||||
))?;
|
||||
let res = api::client::post(url.as_str(), slate).context(
|
||||
libwallet::ErrorKind::ClientCallback("Posting transaction slate"),
|
||||
)?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -134,7 +98,7 @@ impl WalletClient for HTTPWalletClient {
|
||||
} else {
|
||||
url = format!("{}/v1/pool/push", dest);
|
||||
}
|
||||
api::client::post(url.as_str(), tx).context(libwallet::ErrorKind::ClientCallback(
|
||||
api::client::post_no_ret(url.as_str(), tx).context(libwallet::ErrorKind::ClientCallback(
|
||||
"Posting transaction to node",
|
||||
))?;
|
||||
Ok(())
|
||||
@@ -247,30 +211,8 @@ pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Err
|
||||
|
||||
/// Makes a single request to the wallet API to create a new coinbase output.
|
||||
fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
|
||||
let mut core = reactor::Core::new().context(ErrorKind::GenericError(
|
||||
"Could not create reactor".to_owned(),
|
||||
let res = api::client::post(url, block_fees).context(ErrorKind::GenericError(
|
||||
"Posting create coinbase".to_string(),
|
||||
))?;
|
||||
let client = hyper::Client::new(&core.handle());
|
||||
|
||||
let mut req = Request::new(
|
||||
Method::Post,
|
||||
url.parse::<hyper::Uri>().context(ErrorKind::Uri)?,
|
||||
);
|
||||
req.headers_mut().set(ContentType::json());
|
||||
let json = serde_json::to_string(&block_fees).context(ErrorKind::Format)?;
|
||||
trace!(LOGGER, "Sending coinbase request: {:?}", json);
|
||||
req.set_body(json);
|
||||
|
||||
let work = client.request(req).and_then(|res| {
|
||||
res.body().concat2().and_then(move |body| {
|
||||
trace!(LOGGER, "Returned Body: {:?}", body);
|
||||
let coinbase: CbData =
|
||||
serde_json::from_slice(&body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
Ok(coinbase)
|
||||
})
|
||||
});
|
||||
|
||||
let res = core.run(work)
|
||||
.context(ErrorKind::GenericError("Could not run core".to_owned()))?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
+2
-4
@@ -27,7 +27,7 @@ extern crate serde_json;
|
||||
extern crate slog;
|
||||
extern crate chrono;
|
||||
extern crate term;
|
||||
extern crate urlencoded;
|
||||
extern crate url;
|
||||
extern crate uuid;
|
||||
|
||||
extern crate bodyparser;
|
||||
@@ -36,9 +36,7 @@ extern crate failure;
|
||||
extern crate failure_derive;
|
||||
extern crate futures;
|
||||
extern crate hyper;
|
||||
extern crate iron;
|
||||
#[macro_use]
|
||||
extern crate router;
|
||||
extern crate tokio;
|
||||
extern crate tokio_core;
|
||||
extern crate tokio_retry;
|
||||
|
||||
|
||||
+301
-304
@@ -15,18 +15,18 @@
|
||||
//! Controller for wallet.. instantiates and handles listeners (or single-run
|
||||
//! invocations) as needed.
|
||||
//! Still experimental
|
||||
use api::ApiServer;
|
||||
use api::{ApiServer, Handler, ResponseFuture, Router};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use bodyparser;
|
||||
use iron::prelude::{IronError, IronResult, Plugin, Request, Response};
|
||||
use iron::{status, Handler, Headers};
|
||||
use serde::Serialize;
|
||||
use futures::future::{err, ok};
|
||||
use futures::{Future, Stream};
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use urlencoded::UrlEncodedQuery;
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
use keychain::Keychain;
|
||||
use libtx::slate::Slate;
|
||||
@@ -35,6 +35,7 @@ use libwallet::types::{
|
||||
BlockFees, CbData, OutputData, SendTXArgs, TxLogEntry, WalletBackend, WalletClient, WalletInfo,
|
||||
};
|
||||
use libwallet::{Error, ErrorKind};
|
||||
use url::form_urlencoded;
|
||||
|
||||
use util::LOGGER;
|
||||
|
||||
@@ -64,72 +65,102 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
thread_local!(static OWNER_ROUTER: RefCell<Option<Router>> = RefCell::new(None));
|
||||
|
||||
/// Listener version, providing same API but listening for requests on a
|
||||
/// port and wrapping the calls
|
||||
pub fn owner_listener<T: ?Sized, C, K>(wallet: Box<T>, addr: &str) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
OwnerAPIGetHandler<T, C, K>: Handler,
|
||||
OwnerAPIPostHandler<T, C, K>: Handler,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
OwnerAPIHandler<T, C, K>: Handler,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
let wallet_arc = Arc::new(Mutex::new(wallet));
|
||||
let api_get_handler = OwnerAPIGetHandler::new(wallet_arc.clone());
|
||||
let api_post_handler = OwnerAPIPostHandler::new(wallet_arc);
|
||||
let api_options_handler = OwnerAPIOptionsHandler {};
|
||||
let api_handler = OwnerAPIHandler::new(wallet_arc);
|
||||
|
||||
let router = router!(
|
||||
owner_options: options "/wallet/owner/*" => api_options_handler,
|
||||
owner_get: get "/wallet/owner/*" => api_get_handler,
|
||||
owner_post: post "/wallet/owner/*" => api_post_handler,
|
||||
);
|
||||
let mut orouter = Router::new();
|
||||
orouter
|
||||
.add_route("/v1/wallet/owner/**", Box::new(api_handler))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
let mut apis = ApiServer::new("/v1".to_string());
|
||||
apis.register_handler(router);
|
||||
match apis.start(addr) {
|
||||
Err(e) => error!(
|
||||
LOGGER,
|
||||
"Failed to start Grin wallet owner API listener: {}.", e
|
||||
),
|
||||
Ok(_) => info!(LOGGER, "Grin wallet owner API listener started at {}", addr),
|
||||
};
|
||||
OWNER_ROUTER.with(move |router| {
|
||||
*router.borrow_mut() = Some(orouter);
|
||||
let mut apis = ApiServer::new();
|
||||
info!(LOGGER, "Starting HTTP Owner API server at {}.", addr);
|
||||
let socket_addr: SocketAddr = addr.parse().expect("unable to parse socket address");
|
||||
apis.start(socket_addr, &handle_owner).unwrap_or_else(|e| {
|
||||
error!(LOGGER, "Failed to start API HTTP server: {}.", e);
|
||||
})
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_owner(req: Request<Body>) -> ResponseFuture {
|
||||
OWNER_ROUTER.with(|router| match *router.borrow() {
|
||||
Some(ref h) => h.handle(req),
|
||||
None => {
|
||||
error!(LOGGER, "No HTTP API router configured");
|
||||
Box::new(ok(response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"No router configured",
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
thread_local!(static FOREIGN_ROUTER: RefCell<Option<Router>> = RefCell::new(None));
|
||||
|
||||
/// Listener version, providing same API but listening for requests on a
|
||||
/// port and wrapping the calls
|
||||
pub fn foreign_listener<T: ?Sized, C, K>(wallet: Box<T>, addr: &str) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
ForeignAPIHandler<T, C, K>: Handler,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
let api_handler = ForeignAPIHandler::new(Arc::new(Mutex::new(wallet)));
|
||||
|
||||
let router = router!(
|
||||
receive_tx: post "/wallet/foreign/*" => api_handler,
|
||||
);
|
||||
let mut router = Router::new();
|
||||
router
|
||||
.add_route("/v1/wallet/foreign/**", Box::new(api_handler))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
||||
FOREIGN_ROUTER.with(move |frouter| {
|
||||
*frouter.borrow_mut() = Some(router);
|
||||
let mut apis = ApiServer::new();
|
||||
info!(LOGGER, "Starting HTTP Foreign API server at {}.", addr);
|
||||
let socket_addr: SocketAddr = addr.parse().expect("unable to parse socket address");
|
||||
apis.start(socket_addr, &handle_foreign)
|
||||
.unwrap_or_else(|e| {
|
||||
error!(LOGGER, "Failed to start API HTTP server: {}.", e);
|
||||
});
|
||||
});
|
||||
|
||||
let mut apis = ApiServer::new("/v1".to_string());
|
||||
apis.register_handler(router);
|
||||
match apis.start(addr) {
|
||||
Err(e) => error!(
|
||||
LOGGER,
|
||||
"Failed to start Grin wallet foreign listener: {}.", e
|
||||
),
|
||||
Ok(_) => info!(LOGGER, "Grin wallet foreign listener started at {}", addr),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
/// API Handler/Wrapper for owner functions
|
||||
|
||||
pub struct OwnerAPIGetHandler<T: ?Sized, C, K>
|
||||
fn handle_foreign(req: Request<Body>) -> ResponseFuture {
|
||||
FOREIGN_ROUTER.with(|router| match *router.borrow() {
|
||||
Some(ref h) => h.handle(req),
|
||||
None => {
|
||||
error!(LOGGER, "No HTTP API router configured");
|
||||
Box::new(ok(response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"No router configured",
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type WalletResponseFuture = Box<Future<Item = Response<Body>, Error = Error> + Send>;
|
||||
|
||||
/// API Handler/Wrapper for owner functions
|
||||
pub struct OwnerAPIHandler<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<Box<T>>>,
|
||||
@@ -137,15 +168,15 @@ where
|
||||
phantom_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> OwnerAPIGetHandler<T, C, K>
|
||||
impl<T: ?Sized, C, K> OwnerAPIHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Create a new owner API handler for GET methods
|
||||
pub fn new(wallet: Arc<Mutex<Box<T>>>) -> OwnerAPIGetHandler<T, C, K> {
|
||||
OwnerAPIGetHandler {
|
||||
pub fn new(wallet: Arc<Mutex<Box<T>>>) -> OwnerAPIHandler<T, C, K> {
|
||||
OwnerAPIHandler {
|
||||
wallet,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
@@ -154,23 +185,23 @@ where
|
||||
|
||||
fn retrieve_outputs(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
req: &Request<Body>,
|
||||
api: APIOwner<T, C, K>,
|
||||
) -> Result<(bool, Vec<OutputData>), Error> {
|
||||
let mut update_from_node = false;
|
||||
let mut id = None;
|
||||
let mut show_spent = false;
|
||||
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
|
||||
if let Some(_) = params.get("refresh") {
|
||||
update_from_node = true;
|
||||
}
|
||||
if let Some(_) = params.get("show_spent") {
|
||||
show_spent = true;
|
||||
}
|
||||
if let Some(ids) = params.get("tx_id") {
|
||||
for i in ids {
|
||||
id = Some(i.parse().unwrap());
|
||||
}
|
||||
let params = parse_params(req);
|
||||
|
||||
if let Some(_) = params.get("refresh") {
|
||||
update_from_node = true;
|
||||
}
|
||||
if let Some(_) = params.get("show_spent") {
|
||||
show_spent = true;
|
||||
}
|
||||
if let Some(ids) = params.get("tx_id") {
|
||||
for i in ids {
|
||||
id = Some(i.parse().unwrap());
|
||||
}
|
||||
}
|
||||
api.retrieve_outputs(show_spent, update_from_node, id)
|
||||
@@ -178,19 +209,20 @@ where
|
||||
|
||||
fn retrieve_txs(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
req: &Request<Body>,
|
||||
api: APIOwner<T, C, K>,
|
||||
) -> Result<(bool, Vec<TxLogEntry>), Error> {
|
||||
let mut id = None;
|
||||
let mut update_from_node = false;
|
||||
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
|
||||
if let Some(_) = params.get("refresh") {
|
||||
update_from_node = true;
|
||||
}
|
||||
if let Some(ids) = params.get("id") {
|
||||
for i in ids {
|
||||
id = Some(i.parse().unwrap());
|
||||
}
|
||||
|
||||
let params = parse_params(req);
|
||||
|
||||
if let Some(_) = params.get("refresh") {
|
||||
update_from_node = true;
|
||||
}
|
||||
if let Some(ids) = params.get("id") {
|
||||
for i in ids {
|
||||
id = Some(i.parse().unwrap());
|
||||
}
|
||||
}
|
||||
api.retrieve_txs(update_from_node, id)
|
||||
@@ -198,212 +230,129 @@ where
|
||||
|
||||
fn retrieve_summary_info(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
req: &Request<Body>,
|
||||
mut api: APIOwner<T, C, K>,
|
||||
) -> Result<(bool, WalletInfo), Error> {
|
||||
let mut update_from_node = false;
|
||||
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
|
||||
if let Some(_) = params.get("refresh") {
|
||||
update_from_node = true;
|
||||
}
|
||||
}
|
||||
let update_from_node = param_exists(req, "refresh");
|
||||
api.retrieve_summary_info(update_from_node)
|
||||
}
|
||||
|
||||
fn node_height(
|
||||
&self,
|
||||
_req: &mut Request,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
_req: &Request<Body>,
|
||||
mut api: APIOwner<T, C, K>,
|
||||
) -> Result<(u64, bool), Error> {
|
||||
api.node_height()
|
||||
}
|
||||
|
||||
fn handle_request(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
) -> IronResult<Response> {
|
||||
let url = req.url.clone();
|
||||
let path_elems = url.path();
|
||||
match *path_elems.last().unwrap() {
|
||||
"retrieve_outputs" => json_response(&self.retrieve_outputs(req, api)
|
||||
.map_err(|e| IronError::new(Fail::compat(e), status::BadRequest))?),
|
||||
"retrieve_txs" => json_response(&self.retrieve_txs(req, api)
|
||||
.map_err(|e| IronError::new(Fail::compat(e), status::BadRequest))?),
|
||||
"retrieve_summary_info" => json_response(&self.retrieve_summary_info(req, api)
|
||||
.map_err(|e| IronError::new(Fail::compat(e), status::BadRequest))?),
|
||||
"node_height" => json_response(&self.node_height(req, api)
|
||||
.map_err(|e| IronError::new(Fail::compat(e), status::BadRequest))?),
|
||||
_ => Err(IronError::new(
|
||||
Fail::compat(ErrorKind::Hyper),
|
||||
status::BadRequest,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> Handler for OwnerAPIGetHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||
let mut api = APIOwner::new(self.wallet.clone());
|
||||
let mut resp_json = self.handle_request(req, &mut api);
|
||||
if !resp_json.is_err() {
|
||||
resp_json
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.headers
|
||||
.set_raw("access-control-allow-origin", vec![b"*".to_vec()]);
|
||||
}
|
||||
resp_json
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles all owner API POST requests
|
||||
pub struct OwnerAPIPostHandler<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<Box<T>>>,
|
||||
phantom: PhantomData<K>,
|
||||
phantom_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> OwnerAPIPostHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
{
|
||||
/// New POST handler
|
||||
pub fn new(wallet: Arc<Mutex<Box<T>>>) -> OwnerAPIPostHandler<T, C, K> {
|
||||
OwnerAPIPostHandler {
|
||||
wallet,
|
||||
phantom: PhantomData,
|
||||
phantom_c: PhantomData,
|
||||
}
|
||||
fn handle_get_request(&self, req: &Request<Body>) -> Result<Response<Body>, Error> {
|
||||
let api = APIOwner::new(self.wallet.clone());
|
||||
Ok(match req.uri()
|
||||
.path()
|
||||
.trim_right_matches("/")
|
||||
.rsplit("/")
|
||||
.next()
|
||||
.unwrap()
|
||||
{
|
||||
"retrieve_outputs" => json_response(&self.retrieve_outputs(req, api)?),
|
||||
"retrieve_summary_info" => json_response(&self.retrieve_summary_info(req, api)?),
|
||||
"node_height" => json_response(&self.node_height(req, api)?),
|
||||
"retrieve_txs" => json_response(&self.retrieve_txs(req, api)?),
|
||||
_ => response(StatusCode::BAD_REQUEST, ""),
|
||||
})
|
||||
}
|
||||
|
||||
fn issue_send_tx(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
) -> Result<Slate, Error> {
|
||||
let struct_body = req.get::<bodyparser::Struct<SendTXArgs>>();
|
||||
match struct_body {
|
||||
Ok(Some(args)) => api.issue_send_tx(
|
||||
req: Request<Body>,
|
||||
mut api: APIOwner<T, C, K>,
|
||||
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
||||
Box::new(parse_body(req).and_then(move |args: SendTXArgs| {
|
||||
api.issue_send_tx(
|
||||
args.amount,
|
||||
args.minimum_confirmations,
|
||||
&args.dest,
|
||||
args.max_outputs,
|
||||
args.selection_strategy_is_use_all,
|
||||
),
|
||||
Ok(None) => {
|
||||
error!(LOGGER, "Missing request body: issue_send_tx");
|
||||
Err(ErrorKind::GenericError(
|
||||
"Invalid request body: issue_send_tx".to_owned(),
|
||||
))?
|
||||
}
|
||||
Err(e) => {
|
||||
error!(LOGGER, "Invalid request body: issue_send_tx {:?}", e);
|
||||
Err(ErrorKind::GenericError(
|
||||
"Invalid request body: issue_send_tx".to_owned(),
|
||||
))?
|
||||
}
|
||||
}
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
fn issue_burn_tx(&self, _req: &mut Request, api: &mut APIOwner<T, C, K>) -> Result<(), Error> {
|
||||
// TODO: Args
|
||||
api.issue_burn_tx(60, 10, 1000)
|
||||
}
|
||||
|
||||
fn handle_request(
|
||||
fn issue_burn_tx(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIOwner<T, C, K>,
|
||||
) -> Result<String, Error> {
|
||||
let url = req.url.clone();
|
||||
let path_elems = url.path();
|
||||
match *path_elems.last().unwrap() {
|
||||
"issue_send_tx" => json_response_pretty(&self.issue_send_tx(req, api)?),
|
||||
"issue_burn_tx" => json_response_pretty(&self.issue_burn_tx(req, api)?),
|
||||
_ => Err(ErrorKind::GenericError(
|
||||
_req: Request<Body>,
|
||||
mut api: APIOwner<T, C, K>,
|
||||
) -> Box<Future<Item = (), Error = Error> + Send> {
|
||||
// TODO: Args
|
||||
Box::new(match api.issue_burn_tx(60, 10, 1000) {
|
||||
Ok(_) => ok(()),
|
||||
Err(e) => err(e),
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_post_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||
let api = APIOwner::new(self.wallet.clone());
|
||||
match req.uri()
|
||||
.path()
|
||||
.trim_right_matches("/")
|
||||
.rsplit("/")
|
||||
.next()
|
||||
.unwrap()
|
||||
{
|
||||
"issue_send_tx" => Box::new(
|
||||
self.issue_send_tx(req, api)
|
||||
.and_then(|slate| ok(json_response_pretty(&slate))),
|
||||
),
|
||||
"issue_burn_tx" => Box::new(
|
||||
self.issue_burn_tx(req, api)
|
||||
.and_then(|_| ok(response(StatusCode::OK, ""))),
|
||||
),
|
||||
_ => Box::new(err(ErrorKind::GenericError(
|
||||
"Unknown error handling post request".to_owned(),
|
||||
))?,
|
||||
).into())),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_error_response(&self, e: Error) -> IronResult<Response> {
|
||||
let mut headers = Headers::new();
|
||||
headers.set_raw("access-control-allow-origin", vec![b"*".to_vec()]);
|
||||
headers.set_raw(
|
||||
"access-control-allow-headers",
|
||||
vec![b"Content-Type".to_vec()],
|
||||
);
|
||||
let message = format!("{}", e.kind());
|
||||
let mut r = Response::with((status::InternalServerError, message));
|
||||
r.headers = headers;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn create_ok_response(&self, json: &str) -> IronResult<Response> {
|
||||
let mut headers = Headers::new();
|
||||
headers.set_raw("access-control-allow-origin", vec![b"*".to_vec()]);
|
||||
let mut r = Response::with((status::Ok, json));
|
||||
r.headers = headers;
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, C, K> Handler for OwnerAPIPostHandler<T, C, K>
|
||||
impl<T: ?Sized, C, K> Handler for OwnerAPIHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||
let mut api = APIOwner::new(self.wallet.clone());
|
||||
let resp = match self.handle_request(req, &mut api) {
|
||||
Ok(r) => self.create_ok_response(&r),
|
||||
fn get(&self, req: Request<Body>) -> ResponseFuture {
|
||||
match self.handle_get_request(&req) {
|
||||
Ok(r) => Box::new(ok(r)),
|
||||
Err(e) => {
|
||||
error!(LOGGER, "Request Error: {:?}", e);
|
||||
self.create_error_response(e)
|
||||
Box::new(ok(create_error_response(e)))
|
||||
}
|
||||
};
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(
|
||||
self.handle_post_request(req)
|
||||
.and_then(|r| ok(r))
|
||||
.or_else(|e| {
|
||||
error!(LOGGER, "Request Error: {:?}", e);
|
||||
ok(create_error_response(e))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(ok(create_ok_response("{}")))
|
||||
}
|
||||
}
|
||||
|
||||
/// Options handler
|
||||
pub struct OwnerAPIOptionsHandler {}
|
||||
|
||||
impl Handler for OwnerAPIOptionsHandler where {
|
||||
fn handle(&self, _req: &mut Request) -> IronResult<Response> {
|
||||
let mut resp_json = Ok(Response::with((status::Ok, "{}")));
|
||||
let mut headers = Headers::new();
|
||||
headers.set_raw("access-control-allow-origin", vec![b"*".to_vec()]);
|
||||
headers.set_raw(
|
||||
"access-control-allow-headers",
|
||||
vec![b"Content-Type".to_vec()],
|
||||
);
|
||||
resp_json.as_mut().unwrap().headers = headers;
|
||||
resp_json
|
||||
}
|
||||
}
|
||||
/// API Handler/Wrapper for foreign functions
|
||||
|
||||
pub struct ForeignAPIHandler<T: ?Sized, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// Wallet instance
|
||||
pub wallet: Arc<Mutex<Box<T>>>,
|
||||
@@ -413,9 +362,9 @@ where
|
||||
|
||||
impl<T: ?Sized, C, K> ForeignAPIHandler<T, C, K>
|
||||
where
|
||||
T: WalletBackend<C, K>,
|
||||
C: WalletClient,
|
||||
K: Keychain,
|
||||
T: WalletBackend<C, K> + Send + Sync + 'static,
|
||||
C: WalletClient + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
/// create a new api handler
|
||||
pub fn new(wallet: Arc<Mutex<Box<T>>>) -> ForeignAPIHandler<T, C, K> {
|
||||
@@ -428,55 +377,43 @@ where
|
||||
|
||||
fn build_coinbase(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIForeign<T, C, K>,
|
||||
) -> Result<CbData, Error> {
|
||||
let struct_body = req.get::<bodyparser::Struct<BlockFees>>();
|
||||
match struct_body {
|
||||
Ok(Some(block_fees)) => api.build_coinbase(&block_fees),
|
||||
Ok(None) => {
|
||||
error!(LOGGER, "Missing request body: build_coinbase");
|
||||
Err(ErrorKind::GenericError(
|
||||
"Invalid request body: build_coinbase".to_owned(),
|
||||
))?
|
||||
}
|
||||
Err(e) => {
|
||||
error!(LOGGER, "Invalid request body: build_coinbase: {:?}", e);
|
||||
Err(ErrorKind::GenericError(
|
||||
"Invalid request body: build_coinbase".to_owned(),
|
||||
))?
|
||||
}
|
||||
}
|
||||
req: Request<Body>,
|
||||
mut api: APIForeign<T, C, K>,
|
||||
) -> Box<Future<Item = CbData, Error = Error> + Send> {
|
||||
Box::new(parse_body(req).and_then(move |block_fees| api.build_coinbase(&block_fees)))
|
||||
}
|
||||
|
||||
fn receive_tx(&self, req: &mut Request, api: &mut APIForeign<T, C, K>) -> Result<Slate, Error> {
|
||||
let struct_body = req.get::<bodyparser::Struct<Slate>>();
|
||||
if let Ok(Some(mut slate)) = struct_body {
|
||||
api.receive_tx(&mut slate)?;
|
||||
Ok(slate.clone())
|
||||
} else {
|
||||
Err(ErrorKind::GenericError(
|
||||
"Invalid request body: receive_tx".to_owned(),
|
||||
))?
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request(
|
||||
fn receive_tx(
|
||||
&self,
|
||||
req: &mut Request,
|
||||
api: &mut APIForeign<T, C, K>,
|
||||
) -> IronResult<Response> {
|
||||
let url = req.url.clone();
|
||||
let path_elems = url.path();
|
||||
match *path_elems.last().unwrap() {
|
||||
"build_coinbase" => json_response(&self.build_coinbase(req, api)
|
||||
.map_err(|e| IronError::new(Fail::compat(e), status::BadRequest))?),
|
||||
"receive_tx" => json_response(&self.receive_tx(req, api)
|
||||
.map_err(|e| IronError::new(Fail::compat(e), status::BadRequest))?),
|
||||
_ => Err(IronError::new(
|
||||
Fail::compat(ErrorKind::Hyper),
|
||||
status::BadRequest,
|
||||
)),
|
||||
req: Request<Body>,
|
||||
mut api: APIForeign<T, C, K>,
|
||||
) -> Box<Future<Item = Slate, Error = Error> + Send> {
|
||||
Box::new(
|
||||
parse_body(req).and_then(move |mut slate| match api.receive_tx(&mut slate) {
|
||||
Ok(_) => ok(slate.clone()),
|
||||
Err(e) => err(e),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn handle_request(&self, req: Request<Body>) -> WalletResponseFuture {
|
||||
let api = *APIForeign::new(self.wallet.clone());
|
||||
match req.uri()
|
||||
.path()
|
||||
.trim_right_matches("/")
|
||||
.rsplit("/")
|
||||
.next()
|
||||
.unwrap()
|
||||
{
|
||||
"build_coinbase" => Box::new(
|
||||
self.build_coinbase(req, api)
|
||||
.and_then(|res| ok(json_response(&res))),
|
||||
),
|
||||
"receive_tx" => Box::new(
|
||||
self.receive_tx(req, api)
|
||||
.and_then(|res| ok(json_response(&res))),
|
||||
),
|
||||
_ => Box::new(ok(response(StatusCode::BAD_REQUEST, "unknown action"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,32 +423,92 @@ where
|
||||
C: WalletClient + Send + Sync + 'static,
|
||||
K: Keychain + 'static,
|
||||
{
|
||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||
let mut api = APIForeign::new(self.wallet.clone());
|
||||
let resp_json = self.handle_request(req, &mut *api);
|
||||
resp_json
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(self.handle_request(req).and_then(|r| ok(r)).or_else(|e| {
|
||||
error!(LOGGER, "Request Error: {:?}", e);
|
||||
ok(create_error_response(e))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// Utility to serialize a struct into JSON and produce a sensible IronResult
|
||||
// Utility to serialize a struct into JSON and produce a sensible Response
|
||||
// out of it.
|
||||
fn json_response<T>(s: &T) -> IronResult<Response>
|
||||
fn json_response<T>(s: &T) -> Response<Body>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match serde_json::to_string(s) {
|
||||
Ok(json) => Ok(Response::with((status::Ok, json))),
|
||||
Err(_) => Ok(Response::with((status::InternalServerError, ""))),
|
||||
Ok(json) => response(StatusCode::OK, json),
|
||||
Err(_) => response(StatusCode::INTERNAL_SERVER_ERROR, ""),
|
||||
}
|
||||
}
|
||||
|
||||
// pretty-printed version of above
|
||||
fn json_response_pretty<T>(s: &T) -> Result<String, Error>
|
||||
fn json_response_pretty<T>(s: &T) -> Response<Body>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match serde_json::to_string_pretty(s) {
|
||||
Ok(json) => Ok(json),
|
||||
Err(_) => Err(ErrorKind::Format)?,
|
||||
Ok(json) => response(StatusCode::OK, json),
|
||||
Err(_) => response(StatusCode::INTERNAL_SERVER_ERROR, ""),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_error_response(e: Error) -> Response<Body> {
|
||||
Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.header("access-control-allow-origin", "*")
|
||||
.header("access-control-allow-headers", "Content-Type")
|
||||
.body(format!("{}", e.kind()).into())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn create_ok_response(json: &str) -> Response<Body> {
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header("access-control-allow-origin", "*")
|
||||
.body(json.to_string().into())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn response<T: Into<Body>>(status: StatusCode, text: T) -> Response<Body> {
|
||||
Response::builder()
|
||||
.status(status)
|
||||
.header("access-control-allow-origin", "*")
|
||||
.body(text.into())
|
||||
.unwrap()
|
||||
//let mut resp = Response::new(text.into());
|
||||
//*resp.status_mut() = status;
|
||||
//resp
|
||||
}
|
||||
|
||||
fn parse_params(req: &Request<Body>) -> HashMap<String, Vec<String>> {
|
||||
match req.uri().query() {
|
||||
Some(query_string) => form_urlencoded::parse(query_string.as_bytes())
|
||||
.into_owned()
|
||||
.fold(HashMap::new(), |mut hm, (k, v)| {
|
||||
hm.entry(k).or_insert(vec![]).push(v);
|
||||
hm
|
||||
}),
|
||||
None => HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn param_exists(req: &Request<Body>, param: &str) -> bool {
|
||||
parse_params(req).get(param).is_some()
|
||||
}
|
||||
|
||||
fn parse_body<T>(req: Request<Body>) -> Box<Future<Item = T, Error = Error> + Send>
|
||||
where
|
||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||
{
|
||||
Box::new(
|
||||
req.into_body()
|
||||
.concat2()
|
||||
.map_err(|_| ErrorKind::GenericError("Failed to read request".to_owned()).into())
|
||||
.and_then(|body| match serde_json::from_reader(&body.to_vec()[..]) {
|
||||
Ok(obj) => ok(obj),
|
||||
Err(_) => err(ErrorKind::GenericError("Invalid request body".to_owned()).into()),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user