mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-20 19:11:08 +03:00
Update hyper/tokio/futures dependencies (#3214)
* Update hyper, tokio, futures versions * Update stratum server * Update API * Update webhooks
This commit is contained in:
parent
2d4a2c30ce
commit
6bca34c6a8
13 changed files with 838 additions and 772 deletions
935
Cargo.lock
generated
935
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -13,24 +13,23 @@ edition = "2018"
|
||||||
easy-jsonrpc-mw = "0.5.3"
|
easy-jsonrpc-mw = "0.5.3"
|
||||||
failure = "0.1.1"
|
failure = "0.1.1"
|
||||||
failure_derive = "0.1.1"
|
failure_derive = "0.1.1"
|
||||||
hyper = "0.12"
|
hyper = "0.13"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
ring = "0.13"
|
ring = "0.16"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_derive = "1"
|
serde_derive = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
tokio = "0.1.7"
|
tokio = { version = "0.2", features = ["full"] }
|
||||||
tokio-core = "0.1.17"
|
tokio-rustls = "0.12"
|
||||||
tokio-tcp = "0.1"
|
|
||||||
tokio-rustls = "0.7"
|
|
||||||
http = "0.1.5"
|
http = "0.1.5"
|
||||||
hyper-rustls = "0.14"
|
hyper-rustls = "0.19"
|
||||||
hyper-timeout = "0.2"
|
hyper-timeout = "0.3"
|
||||||
futures = "0.1.21"
|
futures = "0.3"
|
||||||
rustls = "0.13"
|
rustls = "0.16"
|
||||||
url = "1.7.0"
|
url = "1.7.0"
|
||||||
|
bytes = "0.5"
|
||||||
|
|
||||||
grin_core = { path = "../core", version = "3.1.0-beta.1" }
|
grin_core = { path = "../core", version = "3.1.0-beta.1" }
|
||||||
grin_chain = { path = "../chain", version = "3.1.0-beta.1" }
|
grin_chain = { path = "../chain", version = "3.1.0-beta.1" }
|
||||||
|
|
|
@ -139,5 +139,5 @@ fn unauthorized_response(basic_realm: &HeaderValue) -> ResponseFuture {
|
||||||
.header(WWW_AUTHENTICATE, basic_realm)
|
.header(WWW_AUTHENTICATE, basic_realm)
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Box::new(ok(response))
|
Box::pin(ok(response))
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,23 +17,22 @@
|
||||||
use crate::rest::{Error, ErrorKind};
|
use crate::rest::{Error, ErrorKind};
|
||||||
use crate::util::to_base64;
|
use crate::util::to_base64;
|
||||||
use failure::{Fail, ResultExt};
|
use failure::{Fail, ResultExt};
|
||||||
use futures::future::{err, ok, Either};
|
use hyper::body;
|
||||||
use http::uri::{InvalidUri, Uri};
|
|
||||||
use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT};
|
use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT};
|
||||||
use hyper::rt::{Future, Stream};
|
|
||||||
use hyper::{Body, Client, Request};
|
use hyper::{Body, Client, Request};
|
||||||
use hyper_rustls;
|
use hyper_rustls;
|
||||||
use hyper_timeout::TimeoutConnector;
|
use hyper_timeout::TimeoutConnector;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Builder;
|
||||||
|
|
||||||
pub type ClientResponseFuture<T> = Box<dyn Future<Item = T, Error = Error> + Send>;
|
|
||||||
|
|
||||||
/// Helper function to easily issue a HTTP GET request against a given URL that
|
/// Helper function to easily issue a HTTP GET request against a given URL that
|
||||||
/// returns a JSON object. Handles request building, JSON deserialization and
|
/// returns a JSON object. Handles request building, JSON deserialization and
|
||||||
/// response code checking.
|
/// response code checking.
|
||||||
|
/// This function spawns a new Tokio runtime, which means it is pretty inefficient for multiple
|
||||||
|
/// requests. In those situations you are probably better off creating a runtime once and spawning
|
||||||
|
/// `get_async` tasks on it
|
||||||
pub fn get<T>(url: &str, api_secret: Option<String>) -> Result<T, Error>
|
pub fn get<T>(url: &str, api_secret: Option<String>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de>,
|
for<'de> T: Deserialize<'de>,
|
||||||
|
@ -44,22 +43,18 @@ where
|
||||||
/// Helper function to easily issue an async HTTP GET request against a given
|
/// Helper function to easily issue an async HTTP GET request against a given
|
||||||
/// URL that returns a future. Handles request building, JSON deserialization
|
/// URL that returns a future. Handles request building, JSON deserialization
|
||||||
/// and response code checking.
|
/// and response code checking.
|
||||||
pub fn get_async<T>(url: &str, api_secret: Option<String>) -> ClientResponseFuture<T>
|
pub async fn get_async<T>(url: &str, api_secret: Option<String>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||||
{
|
{
|
||||||
match build_request(url, "GET", api_secret, None) {
|
handle_request_async(build_request(url, "GET", api_secret, None)?).await
|
||||||
Ok(req) => Box::new(handle_request_async(req)),
|
|
||||||
Err(e) => Box::new(err(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to easily issue a HTTP GET request
|
/// Helper function to easily issue a HTTP GET request
|
||||||
/// on a given URL that returns nothing. Handles request
|
/// on a given URL that returns nothing. Handles request
|
||||||
/// building and response code checking.
|
/// building and response code checking.
|
||||||
pub fn get_no_ret(url: &str, api_secret: Option<String>) -> Result<(), Error> {
|
pub fn get_no_ret(url: &str, api_secret: Option<String>) -> Result<(), Error> {
|
||||||
let req = build_request(url, "GET", api_secret, None)?;
|
send_request(build_request(url, "GET", api_secret, None)?)?;
|
||||||
send_request(req)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,20 +75,17 @@ where
|
||||||
/// provided JSON object as body on a given URL that returns a future. Handles
|
/// provided JSON object as body on a given URL that returns a future. Handles
|
||||||
/// request building, JSON serialization and deserialization, and response code
|
/// request building, JSON serialization and deserialization, and response code
|
||||||
/// checking.
|
/// checking.
|
||||||
pub fn post_async<IN, OUT>(
|
pub async fn post_async<IN, OUT>(
|
||||||
url: &str,
|
url: &str,
|
||||||
input: &IN,
|
input: &IN,
|
||||||
api_secret: Option<String>,
|
api_secret: Option<String>,
|
||||||
) -> ClientResponseFuture<OUT>
|
) -> Result<OUT, Error>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
OUT: Send + 'static,
|
OUT: Send + 'static,
|
||||||
for<'de> OUT: Deserialize<'de>,
|
for<'de> OUT: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
match create_post_request(url, api_secret, input) {
|
handle_request_async(create_post_request(url, api_secret, input)?).await
|
||||||
Ok(req) => Box::new(handle_request_async(req)),
|
|
||||||
Err(e) => Box::new(err(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to easily issue a HTTP POST request with the provided JSON
|
/// Helper function to easily issue a HTTP POST request with the provided JSON
|
||||||
|
@ -104,8 +96,7 @@ pub fn post_no_ret<IN>(url: &str, api_secret: Option<String>, input: &IN) -> Res
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
{
|
{
|
||||||
let req = create_post_request(url, api_secret, input)?;
|
send_request(create_post_request(url, api_secret, input)?)?;
|
||||||
send_request(req)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,18 +104,16 @@ where
|
||||||
/// provided JSON object as body on a given URL that returns a future. Handles
|
/// provided JSON object as body on a given URL that returns a future. Handles
|
||||||
/// request building, JSON serialization and deserialization, and response code
|
/// request building, JSON serialization and deserialization, and response code
|
||||||
/// checking.
|
/// checking.
|
||||||
pub fn post_no_ret_async<IN>(
|
pub async fn post_no_ret_async<IN>(
|
||||||
url: &str,
|
url: &str,
|
||||||
api_secret: Option<String>,
|
api_secret: Option<String>,
|
||||||
input: &IN,
|
input: &IN,
|
||||||
) -> ClientResponseFuture<()>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
IN: Serialize,
|
IN: Serialize,
|
||||||
{
|
{
|
||||||
match create_post_request(url, api_secret, input) {
|
send_request_async(create_post_request(url, api_secret, input)?).await?;
|
||||||
Ok(req) => Box::new(send_request_async(req).and_then(|_| ok(()))),
|
Ok(())
|
||||||
Err(e) => Box::new(err(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_request(
|
fn build_request(
|
||||||
|
@ -133,19 +122,15 @@ fn build_request(
|
||||||
api_secret: Option<String>,
|
api_secret: Option<String>,
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
) -> Result<Request<Body>, Error> {
|
) -> Result<Request<Body>, Error> {
|
||||||
let uri = url.parse::<Uri>().map_err::<Error, _>(|e: InvalidUri| {
|
|
||||||
e.context(ErrorKind::Argument(format!("Invalid url {}", url)))
|
|
||||||
.into()
|
|
||||||
})?;
|
|
||||||
let mut builder = Request::builder();
|
let mut builder = Request::builder();
|
||||||
if let Some(api_secret) = api_secret {
|
if let Some(api_secret) = api_secret {
|
||||||
let basic_auth = format!("Basic {}", to_base64(&format!("grin:{}", api_secret)));
|
let basic_auth = format!("Basic {}", to_base64(&format!("grin:{}", api_secret)));
|
||||||
builder.header(AUTHORIZATION, basic_auth);
|
builder = builder.header(AUTHORIZATION, basic_auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.method(method)
|
.method(method)
|
||||||
.uri(uri)
|
.uri(url)
|
||||||
.header(USER_AGENT, "grin-client")
|
.header(USER_AGENT, "grin-client")
|
||||||
.header(ACCEPT, "application/json")
|
.header(ACCEPT, "application/json")
|
||||||
.header(CONTENT_TYPE, "application/json")
|
.header(CONTENT_TYPE, "application/json")
|
||||||
|
@ -183,55 +168,50 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request_async<T>(req: Request<Body>) -> ClientResponseFuture<T>
|
async fn handle_request_async<T>(req: Request<Body>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||||
{
|
{
|
||||||
Box::new(send_request_async(req).and_then(|data| {
|
let data = send_request_async(req).await?;
|
||||||
serde_json::from_str(&data).map_err(|e| {
|
let ser = serde_json::from_str(&data)
|
||||||
e.context(ErrorKind::ResponseError("Cannot parse response".to_owned()))
|
.map_err(|e| e.context(ErrorKind::ResponseError("Cannot parse response".to_owned())))?;
|
||||||
.into()
|
Ok(ser)
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_request_async(req: Request<Body>) -> Box<dyn Future<Item = String, Error = Error> + Send> {
|
async fn send_request_async(req: Request<Body>) -> Result<String, Error> {
|
||||||
let https = hyper_rustls::HttpsConnector::new(1);
|
let https = hyper_rustls::HttpsConnector::new();
|
||||||
let mut connector = TimeoutConnector::new(https);
|
let mut connector = TimeoutConnector::new(https);
|
||||||
connector.set_connect_timeout(Some(Duration::from_secs(20)));
|
connector.set_connect_timeout(Some(Duration::from_secs(20)));
|
||||||
connector.set_read_timeout(Some(Duration::from_secs(20)));
|
connector.set_read_timeout(Some(Duration::from_secs(20)));
|
||||||
connector.set_write_timeout(Some(Duration::from_secs(20)));
|
connector.set_write_timeout(Some(Duration::from_secs(20)));
|
||||||
let client = Client::builder().build::<_, hyper::Body>(connector);
|
let client = Client::builder().build::<_, Body>(connector);
|
||||||
Box::new(
|
|
||||||
client
|
let resp = client
|
||||||
.request(req)
|
.request(req)
|
||||||
.map_err(|e| ErrorKind::RequestError(format!("Cannot make request: {}", e)).into())
|
.await
|
||||||
.and_then(|resp| {
|
.map_err(|e| ErrorKind::RequestError(format!("Cannot make request: {}", e)))?;
|
||||||
if !resp.status().is_success() {
|
|
||||||
Either::A(err(ErrorKind::RequestError(format!(
|
if !resp.status().is_success() {
|
||||||
"Wrong response code: {} with data {:?}",
|
return Err(ErrorKind::RequestError(format!(
|
||||||
resp.status(),
|
"Wrong response code: {} with data {:?}",
|
||||||
resp.body()
|
resp.status(),
|
||||||
))
|
resp.body()
|
||||||
.into()))
|
))
|
||||||
} else {
|
.into());
|
||||||
Either::B(
|
}
|
||||||
resp.into_body()
|
|
||||||
.map_err(|e| {
|
let raw = body::to_bytes(resp)
|
||||||
ErrorKind::RequestError(format!("Cannot read response body: {}", e))
|
.await
|
||||||
.into()
|
.map_err(|e| ErrorKind::RequestError(format!("Cannot read response body: {}", e)))?;
|
||||||
})
|
|
||||||
.concat2()
|
Ok(String::from_utf8_lossy(&raw).to_string())
|
||||||
.and_then(|ch| ok(String::from_utf8_lossy(&ch.to_vec()).to_string())),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_request(req: Request<Body>) -> Result<String, Error> {
|
pub fn send_request(req: Request<Body>) -> Result<String, Error> {
|
||||||
let task = send_request_async(req);
|
let mut rt = Builder::new()
|
||||||
let mut rt =
|
.basic_scheduler()
|
||||||
Runtime::new().context(ErrorKind::Internal("can't create Tokio runtime".to_owned()))?;
|
.enable_all()
|
||||||
Ok(rt.block_on(task)?)
|
.build()
|
||||||
|
.map_err(|e| ErrorKind::RequestError(format!("{}", e)))?;
|
||||||
|
rt.block_on(send_request_async(req))
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,8 +56,6 @@ use crate::util::to_base64;
|
||||||
use crate::util::RwLock;
|
use crate::util::RwLock;
|
||||||
use crate::web::*;
|
use crate::web::*;
|
||||||
use easy_jsonrpc_mw::{Handler, MaybeReply};
|
use easy_jsonrpc_mw::{Handler, MaybeReply};
|
||||||
use futures::future::ok;
|
|
||||||
use futures::Future;
|
|
||||||
use hyper::{Body, Request, Response, StatusCode};
|
use hyper::{Body, Request, Response, StatusCode};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
@ -139,8 +137,6 @@ pub fn node_apis(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeResponseFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>;
|
|
||||||
|
|
||||||
/// V2 API Handler/Wrapper for owner functions
|
/// V2 API Handler/Wrapper for owner functions
|
||||||
pub struct OwnerAPIHandlerV2 {
|
pub struct OwnerAPIHandlerV2 {
|
||||||
pub chain: Weak<Chain>,
|
pub chain: Weak<Chain>,
|
||||||
|
@ -157,48 +153,40 @@ impl OwnerAPIHandlerV2 {
|
||||||
sync_state,
|
sync_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn call_api(
|
impl crate::router::Handler for OwnerAPIHandlerV2 {
|
||||||
&self,
|
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||||
req: Request<Body>,
|
|
||||||
api: Owner,
|
|
||||||
) -> Box<dyn Future<Item = serde_json::Value, Error = Error> + Send> {
|
|
||||||
Box::new(parse_body(req).and_then(move |val: serde_json::Value| {
|
|
||||||
let owner_api = &api as &dyn OwnerRpc;
|
|
||||||
match owner_api.handle_request(val) {
|
|
||||||
MaybeReply::Reply(r) => ok(r),
|
|
||||||
MaybeReply::DontReply => {
|
|
||||||
// Since it's http, we need to return something. We return [] because jsonrpc
|
|
||||||
// clients will parse it as an empty batch response.
|
|
||||||
ok(serde_json::json!([]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_post_request(&self, req: Request<Body>) -> NodeResponseFuture {
|
|
||||||
let api = Owner::new(
|
let api = Owner::new(
|
||||||
self.chain.clone(),
|
self.chain.clone(),
|
||||||
self.peers.clone(),
|
self.peers.clone(),
|
||||||
self.sync_state.clone(),
|
self.sync_state.clone(),
|
||||||
);
|
);
|
||||||
Box::new(
|
|
||||||
self.call_api(req, api)
|
|
||||||
.and_then(|resp| ok(json_response_pretty(&resp))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::router::Handler for OwnerAPIHandlerV2 {
|
Box::pin(async move {
|
||||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
match parse_body(req).await {
|
||||||
Box::new(self.handle_post_request(req).and_then(ok).or_else(|e| {
|
Ok(val) => {
|
||||||
error!("Request Error: {:?}", e);
|
let owner_api = &api as &dyn OwnerRpc;
|
||||||
ok(create_error_response(e))
|
let res = match owner_api.handle_request(val) {
|
||||||
}))
|
MaybeReply::Reply(r) => r,
|
||||||
|
MaybeReply::DontReply => {
|
||||||
|
// Since it's http, we need to return something. We return [] because jsonrpc
|
||||||
|
// clients will parse it as an empty batch response.
|
||||||
|
serde_json::json!([])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(json_response_pretty(&res))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Request Error: {:?}", e);
|
||||||
|
Ok(create_error_response(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
||||||
Box::new(ok(create_ok_response("{}")))
|
Box::pin(async { Ok(create_ok_response("{}")) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,48 +210,40 @@ impl ForeignAPIHandlerV2 {
|
||||||
sync_state,
|
sync_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn call_api(
|
impl crate::router::Handler for ForeignAPIHandlerV2 {
|
||||||
&self,
|
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||||
req: Request<Body>,
|
|
||||||
api: Foreign,
|
|
||||||
) -> Box<dyn Future<Item = serde_json::Value, Error = Error> + Send> {
|
|
||||||
Box::new(parse_body(req).and_then(move |val: serde_json::Value| {
|
|
||||||
let foreign_api = &api as &dyn ForeignRpc;
|
|
||||||
match foreign_api.handle_request(val) {
|
|
||||||
MaybeReply::Reply(r) => ok(r),
|
|
||||||
MaybeReply::DontReply => {
|
|
||||||
// Since it's http, we need to return something. We return [] because jsonrpc
|
|
||||||
// clients will parse it as an empty batch response.
|
|
||||||
ok(serde_json::json!([]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_post_request(&self, req: Request<Body>) -> NodeResponseFuture {
|
|
||||||
let api = Foreign::new(
|
let api = Foreign::new(
|
||||||
self.chain.clone(),
|
self.chain.clone(),
|
||||||
self.tx_pool.clone(),
|
self.tx_pool.clone(),
|
||||||
self.sync_state.clone(),
|
self.sync_state.clone(),
|
||||||
);
|
);
|
||||||
Box::new(
|
|
||||||
self.call_api(req, api)
|
|
||||||
.and_then(|resp| ok(json_response_pretty(&resp))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::router::Handler for ForeignAPIHandlerV2 {
|
Box::pin(async move {
|
||||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
match parse_body(req).await {
|
||||||
Box::new(self.handle_post_request(req).and_then(ok).or_else(|e| {
|
Ok(val) => {
|
||||||
error!("Request Error: {:?}", e);
|
let foreign_api = &api as &dyn ForeignRpc;
|
||||||
ok(create_error_response(e))
|
let res = match foreign_api.handle_request(val) {
|
||||||
}))
|
MaybeReply::Reply(r) => r,
|
||||||
|
MaybeReply::DontReply => {
|
||||||
|
// Since it's http, we need to return something. We return [] because jsonrpc
|
||||||
|
// clients will parse it as an empty batch response.
|
||||||
|
serde_json::json!([])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(json_response_pretty(&res))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Request Error: {:?}", e);
|
||||||
|
Ok(create_error_response(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
fn options(&self, _req: Request<Body>) -> ResponseFuture {
|
||||||
Box::new(ok(create_ok_response("{}")))
|
Box::pin(async { Ok(create_ok_response("{}")) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +288,7 @@ fn create_ok_response(json: &str) -> Response<Body> {
|
||||||
/// Whenever the status code is `StatusCode::OK` the text parameter should be
|
/// Whenever the status code is `StatusCode::OK` the text parameter should be
|
||||||
/// valid JSON as the content type header will be set to `application/json'
|
/// valid JSON as the content type header will be set to `application/json'
|
||||||
fn response<T: Into<Body>>(status: StatusCode, text: T) -> Response<Body> {
|
fn response<T: Into<Body>>(status: StatusCode, text: T) -> Response<Body> {
|
||||||
let mut builder = &mut Response::builder();
|
let mut builder = Response::builder();
|
||||||
|
|
||||||
builder = builder
|
builder = builder
|
||||||
.status(status)
|
.status(status)
|
||||||
|
|
|
@ -24,8 +24,6 @@ use crate::util;
|
||||||
use crate::util::RwLock;
|
use crate::util::RwLock;
|
||||||
use crate::web::*;
|
use crate::web::*;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
use futures::future::{err, ok};
|
|
||||||
use futures::Future;
|
|
||||||
use hyper::{Body, Request, StatusCode};
|
use hyper::{Body, Request, StatusCode};
|
||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
|
|
||||||
|
@ -102,65 +100,55 @@ pub struct PoolPushHandler {
|
||||||
pub tx_pool: Weak<RwLock<pool::TransactionPool>>,
|
pub tx_pool: Weak<RwLock<pool::TransactionPool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PoolPushHandler {
|
async fn update_pool(
|
||||||
fn update_pool(&self, req: Request<Body>) -> Box<dyn Future<Item = (), Error = Error> + Send> {
|
pool: Weak<RwLock<pool::TransactionPool>>,
|
||||||
let params = QueryParams::from(req.uri().query());
|
req: Request<Body>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let pool = w(&pool)?;
|
||||||
|
let params = QueryParams::from(req.uri().query());
|
||||||
|
let fluff = params.get("fluff").is_some();
|
||||||
|
|
||||||
let fluff = params.get("fluff").is_some();
|
let wrapper: TxWrapper = parse_body(req).await?;
|
||||||
let pool_arc = match w(&self.tx_pool) {
|
let tx_bin = util::from_hex(wrapper.tx_hex)
|
||||||
Ok(p) => p,
|
.map_err(|e| ErrorKind::RequestError(format!("Bad request: {}", e)))?;
|
||||||
Err(e) => return Box::new(err(e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
Box::new(
|
// All wallet api interaction explicitly uses protocol version 1 for now.
|
||||||
parse_body(req)
|
let version = ProtocolVersion(1);
|
||||||
.and_then(move |wrapper: TxWrapper| {
|
let tx: Transaction = ser::deserialize(&mut &tx_bin[..], version)
|
||||||
util::from_hex(wrapper.tx_hex)
|
.map_err(|e| ErrorKind::RequestError(format!("Bad request: {}", e)))?;
|
||||||
.map_err(|e| ErrorKind::RequestError(format!("Bad request: {}", e)).into())
|
|
||||||
})
|
|
||||||
.and_then(move |tx_bin| {
|
|
||||||
// All wallet api interaction explicitly uses protocol version 1 for now.
|
|
||||||
let version = ProtocolVersion(1);
|
|
||||||
|
|
||||||
ser::deserialize(&mut &tx_bin[..], version)
|
let source = pool::TxSource::PushApi;
|
||||||
.map_err(|e| ErrorKind::RequestError(format!("Bad request: {}", e)).into())
|
info!(
|
||||||
})
|
"Pushing transaction {} to pool (inputs: {}, outputs: {}, kernels: {})",
|
||||||
.and_then(move |tx: Transaction| {
|
tx.hash(),
|
||||||
let source = pool::TxSource::PushApi;
|
tx.inputs().len(),
|
||||||
info!(
|
tx.outputs().len(),
|
||||||
"Pushing transaction {} to pool (inputs: {}, outputs: {}, kernels: {})",
|
tx.kernels().len(),
|
||||||
tx.hash(),
|
);
|
||||||
tx.inputs().len(),
|
|
||||||
tx.outputs().len(),
|
|
||||||
tx.kernels().len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Push to tx pool.
|
// Push to tx pool.
|
||||||
let mut tx_pool = pool_arc.write();
|
let mut tx_pool = pool.write();
|
||||||
let header = tx_pool
|
let header = tx_pool
|
||||||
.blockchain
|
.blockchain
|
||||||
.chain_head()
|
.chain_head()
|
||||||
.context(ErrorKind::Internal("Failed to get chain head".to_owned()))?;
|
.context(ErrorKind::Internal("Failed to get chain head".to_owned()))?;
|
||||||
tx_pool
|
tx_pool
|
||||||
.add_to_pool(source, tx, !fluff, &header)
|
.add_to_pool(source, tx, !fluff, &header)
|
||||||
.context(ErrorKind::Internal("Failed to update pool".to_owned()))?;
|
.context(ErrorKind::Internal("Failed to update pool".to_owned()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler for PoolPushHandler {
|
impl Handler for PoolPushHandler {
|
||||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||||
Box::new(
|
let pool = self.tx_pool.clone();
|
||||||
self.update_pool(req)
|
Box::pin(async move {
|
||||||
.and_then(|_| ok(just_response(StatusCode::OK, "")))
|
let res = match update_pool(pool, req).await {
|
||||||
.or_else(|e| {
|
Ok(_) => just_response(StatusCode::OK, ""),
|
||||||
ok(just_response(
|
Err(e) => {
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
just_response(StatusCode::INTERNAL_SERVER_ERROR, format!("failed: {}", e))
|
||||||
format!("failed: {}", e),
|
}
|
||||||
))
|
};
|
||||||
}),
|
Ok(res)
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,19 +21,22 @@
|
||||||
use crate::router::{Handler, HandlerObj, ResponseFuture, Router, RouterError};
|
use crate::router::{Handler, HandlerObj, ResponseFuture, Router, RouterError};
|
||||||
use crate::web::response;
|
use crate::web::response;
|
||||||
use failure::{Backtrace, Context, Fail, ResultExt};
|
use failure::{Backtrace, Context, Fail, ResultExt};
|
||||||
use futures::sync::oneshot;
|
use futures::channel::oneshot;
|
||||||
use futures::Stream;
|
use futures::TryStreamExt;
|
||||||
use hyper::rt::Future;
|
use hyper::server::accept;
|
||||||
use hyper::{rt, Body, Request, Server, StatusCode};
|
use hyper::service::make_service_fn;
|
||||||
|
use hyper::{Body, Request, Server, StatusCode};
|
||||||
use rustls;
|
use rustls;
|
||||||
use rustls::internal::pemfile;
|
use rustls::internal::pemfile;
|
||||||
|
use std::convert::Infallible;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{io, thread};
|
use std::{io, thread};
|
||||||
use tokio_rustls::ServerConfigExt;
|
use tokio::net::TcpListener;
|
||||||
use tokio_tcp;
|
use tokio::runtime::Runtime;
|
||||||
|
use tokio_rustls::TlsAcceptor;
|
||||||
|
|
||||||
/// Errors that can be returned by an ApiEndpoint implementation.
|
/// Errors that can be returned by an ApiEndpoint implementation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -199,13 +202,23 @@ impl ApiServer {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("apis".to_string())
|
.name("apis".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let server = Server::bind(&addr)
|
let server = async move {
|
||||||
.serve(router)
|
let server = Server::bind(&addr).serve(make_service_fn(move |_| {
|
||||||
|
let router = router.clone();
|
||||||
|
async move { Ok::<_, Infallible>(router) }
|
||||||
|
}));
|
||||||
// TODO graceful shutdown is unstable, investigate
|
// TODO graceful shutdown is unstable, investigate
|
||||||
//.with_graceful_shutdown(rx)
|
//.with_graceful_shutdown(rx)
|
||||||
.map_err(|e| eprintln!("HTTP API server error: {}", e));
|
|
||||||
|
|
||||||
rt::run(server);
|
server.await
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rt = Runtime::new()
|
||||||
|
.map_err(|e| eprintln!("HTTP API server error: {}", e))
|
||||||
|
.unwrap();
|
||||||
|
if let Err(e) = rt.block_on(server) {
|
||||||
|
eprintln!("HTTP API server error: {}", e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
.map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
||||||
}
|
}
|
||||||
|
@ -225,28 +238,31 @@ impl ApiServer {
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let tls_conf = conf.build_server_config()?;
|
let acceptor = TlsAcceptor::from(conf.build_server_config()?);
|
||||||
|
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("apis".to_string())
|
.name("apis".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let listener = tokio_tcp::TcpListener::bind(&addr).expect("failed to bind");
|
let server = async move {
|
||||||
let tls = listener
|
let mut listener = TcpListener::bind(&addr).await.expect("failed to bind");
|
||||||
.incoming()
|
let listener = listener.incoming().and_then(move |s| acceptor.accept(s));
|
||||||
.and_then(move |s| tls_conf.accept_async(s))
|
|
||||||
.then(|r| match r {
|
|
||||||
Ok(x) => Ok::<_, io::Error>(Some(x)),
|
|
||||||
Err(e) => {
|
|
||||||
error!("accept_async failed: {}", e);
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter_map(|x| x);
|
|
||||||
let server = Server::builder(tls)
|
|
||||||
.serve(router)
|
|
||||||
.map_err(|e| eprintln!("HTTP API server error: {}", e));
|
|
||||||
|
|
||||||
rt::run(server);
|
let server = Server::builder(accept::from_stream(listener)).serve(
|
||||||
|
make_service_fn(move |_| {
|
||||||
|
let router = router.clone();
|
||||||
|
async move { Ok::<_, Infallible>(router) }
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.await
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rt = Runtime::new()
|
||||||
|
.map_err(|e| eprintln!("HTTP API server error: {}", e))
|
||||||
|
.unwrap();
|
||||||
|
if let Err(e) = rt.block_on(server) {
|
||||||
|
eprintln!("HTTP API server error: {}", e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
.map_err(|_| ErrorKind::Internal("failed to spawn API thread".to_string()).into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,21 +12,23 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use futures::future;
|
use futures::future::{self, Future};
|
||||||
use hyper;
|
use hyper;
|
||||||
use hyper::rt::Future;
|
use hyper::service::Service;
|
||||||
use hyper::service::{NewService, Service};
|
|
||||||
use hyper::{Body, Method, Request, Response, StatusCode};
|
use hyper::{Body, Method, Request, Response, StatusCode};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref WILDCARD_HASH: u64 = calculate_hash(&"*");
|
static ref WILDCARD_HASH: u64 = calculate_hash(&"*");
|
||||||
static ref WILDCARD_STOP_HASH: u64 = calculate_hash(&"**");
|
static ref WILDCARD_STOP_HASH: u64 = calculate_hash(&"**");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ResponseFuture = Box<dyn Future<Item = Response<Body>, Error = hyper::Error> + Send>;
|
pub type ResponseFuture =
|
||||||
|
Pin<Box<dyn Future<Output = Result<Response<Body>, hyper::Error>> + Send>>;
|
||||||
|
|
||||||
pub trait Handler {
|
pub trait Handler {
|
||||||
fn get(&self, _req: Request<Body>) -> ResponseFuture {
|
fn get(&self, _req: Request<Body>) -> ResponseFuture {
|
||||||
|
@ -203,13 +205,16 @@ impl Router {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service for Router {
|
impl Service<Request<Body>> for Router {
|
||||||
type ReqBody = Body;
|
type Response = Response<Body>;
|
||||||
type ResBody = Body;
|
|
||||||
type Error = hyper::Error;
|
type Error = hyper::Error;
|
||||||
type Future = ResponseFuture;
|
type Future = ResponseFuture;
|
||||||
|
|
||||||
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||||
match self.get(req.uri().path()) {
|
match self.get(req.uri().path()) {
|
||||||
Err(_) => not_found(),
|
Err(_) => not_found(),
|
||||||
Ok(mut handlers) => match handlers.next() {
|
Ok(mut handlers) => match handlers.next() {
|
||||||
|
@ -220,18 +225,6 @@ impl Service for Router {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NewService for Router {
|
|
||||||
type ReqBody = Body;
|
|
||||||
type ResBody = Body;
|
|
||||||
type Error = hyper::Error;
|
|
||||||
type InitError = hyper::Error;
|
|
||||||
type Service = Router;
|
|
||||||
type Future = Box<dyn Future<Item = Self::Service, Error = Self::InitError> + Send>;
|
|
||||||
fn new_service(&self) -> Self::Future {
|
|
||||||
Box::new(future::ok(self.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
fn new(key: u64, value: Option<HandlerObj>) -> Node {
|
fn new(key: u64, value: Option<HandlerObj>) -> Node {
|
||||||
Node {
|
Node {
|
||||||
|
@ -276,7 +269,7 @@ impl Node {
|
||||||
pub fn not_found() -> ResponseFuture {
|
pub fn not_found() -> ResponseFuture {
|
||||||
let mut response = Response::new(Body::empty());
|
let mut response = Response::new(Body::empty());
|
||||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
*response.status_mut() = StatusCode::NOT_FOUND;
|
||||||
Box::new(future::ok(response))
|
Box::pin(future::ok(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||||
|
@ -305,19 +298,20 @@ fn collect_node_middleware(handlers: &mut Vec<HandlerObj>, node: &Node) {
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use tokio::prelude::future::ok;
|
use futures::executor::block_on;
|
||||||
use tokio_core::reactor::Core;
|
|
||||||
|
|
||||||
struct HandlerImpl(u16);
|
struct HandlerImpl(u16);
|
||||||
|
|
||||||
impl Handler for HandlerImpl {
|
impl Handler for HandlerImpl {
|
||||||
fn get(&self, _req: Request<Body>) -> ResponseFuture {
|
fn get(&self, _req: Request<Body>) -> ResponseFuture {
|
||||||
Box::new(future::ok(
|
let code = self.0;
|
||||||
Response::builder()
|
Box::pin(async move {
|
||||||
.status(self.0)
|
let res = Response::builder()
|
||||||
|
.status(code)
|
||||||
.body(Body::default())
|
.body(Body::default())
|
||||||
.unwrap(),
|
.unwrap();
|
||||||
))
|
Ok(res)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,15 +352,18 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let call_handler = |url| {
|
let call_handler = |url| {
|
||||||
let mut event_loop = Core::new().unwrap();
|
let task = async {
|
||||||
let task = routes
|
let resp = routes
|
||||||
.get(url)
|
.get(url)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(Request::new(Body::default()))
|
.get(Request::new(Body::default()))
|
||||||
.and_then(|resp| ok(resp.status().as_u16()));
|
.await
|
||||||
event_loop.run(task).unwrap()
|
.unwrap();
|
||||||
|
resp.status().as_u16()
|
||||||
|
};
|
||||||
|
block_on(task)
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(call_handler("/v1/users"), 101);
|
assert_eq!(call_handler("/v1/users"), 101);
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::rest::*;
|
use crate::rest::*;
|
||||||
use crate::router::ResponseFuture;
|
use crate::router::ResponseFuture;
|
||||||
use futures::future::{err, ok};
|
use bytes::Buf;
|
||||||
use futures::{Future, Stream};
|
use futures::future::ok;
|
||||||
|
use hyper::body;
|
||||||
use hyper::{Body, Request, Response, StatusCode};
|
use hyper::{Body, Request, Response, StatusCode};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
@ -10,21 +11,16 @@ use std::fmt::Debug;
|
||||||
use url::form_urlencoded;
|
use url::form_urlencoded;
|
||||||
|
|
||||||
/// Parse request body
|
/// Parse request body
|
||||||
pub fn parse_body<T>(req: Request<Body>) -> Box<dyn Future<Item = T, Error = Error> + Send>
|
pub async fn parse_body<T>(req: Request<Body>) -> Result<T, Error>
|
||||||
where
|
where
|
||||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||||
{
|
{
|
||||||
Box::new(
|
let raw = body::to_bytes(req.into_body())
|
||||||
req.into_body()
|
.await
|
||||||
.concat2()
|
.map_err(|e| ErrorKind::RequestError(format!("Failed to read request: {}", e)))?;
|
||||||
.map_err(|e| ErrorKind::RequestError(format!("Failed to read request: {}", e)).into())
|
|
||||||
.and_then(|body| match serde_json::from_reader(&body.to_vec()[..]) {
|
serde_json::from_reader(raw.bytes())
|
||||||
Ok(obj) => ok(obj),
|
.map_err(|e| ErrorKind::RequestError(format!("Invalid request body: {}", e)).into())
|
||||||
Err(e) => {
|
|
||||||
err(ErrorKind::RequestError(format!("Invalid request body: {}", e)).into())
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Result to ResponseFuture
|
/// Convert Result to ResponseFuture
|
||||||
|
@ -83,7 +79,7 @@ pub fn just_response<T: Into<Body> + Debug>(status: StatusCode, text: T) -> Resp
|
||||||
|
|
||||||
/// Text response as future
|
/// Text response as future
|
||||||
pub fn response<T: Into<Body> + Debug>(status: StatusCode, text: T) -> ResponseFuture {
|
pub fn response<T: Into<Body> + Debug>(status: StatusCode, text: T) -> ResponseFuture {
|
||||||
Box::new(ok(just_response(status, text)))
|
Box::pin(ok(just_response(status, text)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct QueryParams {
|
pub struct QueryParams {
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
hard_tabs = true
|
hard_tabs = true
|
||||||
|
edition = "2018"
|
||||||
|
|
|
@ -10,10 +10,10 @@ workspace = ".."
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hyper = "0.12"
|
hyper = "0.13"
|
||||||
hyper-rustls = "0.14"
|
hyper-rustls = "0.19"
|
||||||
fs2 = "0.4"
|
fs2 = "0.4"
|
||||||
futures = "0.1"
|
futures = "0.3"
|
||||||
http = "0.1"
|
http = "0.1"
|
||||||
lmdb-zero = "0.4.4"
|
lmdb-zero = "0.4.4"
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
|
@ -22,7 +22,8 @@ log = "0.4"
|
||||||
serde_derive = "1"
|
serde_derive = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
chrono = "0.4.4"
|
chrono = "0.4.4"
|
||||||
tokio = "0.1.11"
|
tokio = {version = "0.2", features = ["full"] }
|
||||||
|
tokio-util = { version = "0.2", features = ["codec"] }
|
||||||
walkdir = "2.2.9"
|
walkdir = "2.2.9"
|
||||||
|
|
||||||
grin_api = { path = "../api", version = "3.1.0-beta.1" }
|
grin_api = { path = "../api", version = "3.1.0-beta.1" }
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::common::types::{ServerConfig, WebHooksConfig};
|
||||||
use crate::core::core;
|
use crate::core::core;
|
||||||
use crate::core::core::hash::Hashed;
|
use crate::core::core::hash::Hashed;
|
||||||
use crate::p2p::types::PeerAddr;
|
use crate::p2p::types::PeerAddr;
|
||||||
use futures::future::Future;
|
use futures::TryFutureExt;
|
||||||
use hyper::client::HttpConnector;
|
use hyper::client::HttpConnector;
|
||||||
use hyper::header::HeaderValue;
|
use hyper::header::HeaderValue;
|
||||||
use hyper::Client;
|
use hyper::Client;
|
||||||
|
@ -33,7 +33,7 @@ use hyper_rustls::HttpsConnector;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::{json, to_string};
|
use serde_json::{json, to_string};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::{Builder, Runtime};
|
||||||
|
|
||||||
/// Returns the list of event hooks that will be initialized for network events
|
/// Returns the list of event hooks that will be initialized for network events
|
||||||
pub fn init_net_hooks(config: &ServerConfig) -> Vec<Box<dyn NetEvents + Send + Sync>> {
|
pub fn init_net_hooks(config: &ServerConfig) -> Vec<Box<dyn NetEvents + Send + Sync>> {
|
||||||
|
@ -153,7 +153,7 @@ fn parse_url(value: &Option<String>) -> Option<hyper::Uri> {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(_) => panic!("Invalid url : {}", url),
|
Err(_) => panic!("Invalid url : {}", url),
|
||||||
};
|
};
|
||||||
let scheme = uri.scheme_part().map(|s| s.as_str());
|
let scheme = uri.scheme().map(|s| s.as_str());
|
||||||
if (scheme != Some("http")) && (scheme != Some("https")) {
|
if (scheme != Some("http")) && (scheme != Some("https")) {
|
||||||
panic!(
|
panic!(
|
||||||
"Invalid url scheme {}, expected one of ['http', https']",
|
"Invalid url scheme {}, expected one of ['http', https']",
|
||||||
|
@ -199,7 +199,7 @@ impl WebHook {
|
||||||
nthreads, timeout
|
nthreads, timeout
|
||||||
);
|
);
|
||||||
|
|
||||||
let https = HttpsConnector::new(nthreads as usize);
|
let https = HttpsConnector::new();
|
||||||
let client = Client::builder()
|
let client = Client::builder()
|
||||||
.keep_alive_timeout(keep_alive)
|
.keep_alive_timeout(keep_alive)
|
||||||
.build::<_, hyper::Body>(https);
|
.build::<_, hyper::Body>(https);
|
||||||
|
@ -210,7 +210,12 @@ impl WebHook {
|
||||||
header_received_url,
|
header_received_url,
|
||||||
block_accepted_url,
|
block_accepted_url,
|
||||||
client,
|
client,
|
||||||
runtime: Runtime::new().unwrap(),
|
runtime: Builder::new()
|
||||||
|
.threaded_scheduler()
|
||||||
|
.enable_all()
|
||||||
|
.core_threads(nthreads as usize)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,16 +240,11 @@ impl WebHook {
|
||||||
HeaderValue::from_static("application/json"),
|
HeaderValue::from_static("application/json"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let future = self
|
let future = self.client.request(req).map_err(move |_res| {
|
||||||
.client
|
warn!("Error sending POST request to {}", url);
|
||||||
.request(req)
|
});
|
||||||
.map(|_res| {})
|
|
||||||
.map_err(move |_res| {
|
|
||||||
warn!("Error sending POST request to {}", url);
|
|
||||||
});
|
|
||||||
|
|
||||||
let handle = self.runtime.executor();
|
self.runtime.spawn(future);
|
||||||
handle.spawn(future);
|
|
||||||
}
|
}
|
||||||
fn make_request<T: Serialize>(&self, payload: &T, uri: &Option<hyper::Uri>) -> bool {
|
fn make_request<T: Serialize>(&self, payload: &T, uri: &Option<hyper::Uri>) -> bool {
|
||||||
if let Some(url) = uri {
|
if let Some(url) = uri {
|
||||||
|
|
|
@ -14,11 +14,12 @@
|
||||||
|
|
||||||
//! Mining Stratum Server
|
//! Mining Stratum Server
|
||||||
|
|
||||||
use futures::future::Future;
|
use futures::channel::mpsc;
|
||||||
use futures::stream::Stream;
|
use futures::pin_mut;
|
||||||
use tokio::io::AsyncRead;
|
use futures::{SinkExt, StreamExt, TryStreamExt};
|
||||||
use tokio::io::{lines, write_all};
|
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
use tokio_util::codec::{Framed, LinesCodec};
|
||||||
|
|
||||||
use crate::util::RwLock;
|
use crate::util::RwLock;
|
||||||
use chrono::prelude::Utc;
|
use chrono::prelude::Utc;
|
||||||
|
@ -26,7 +27,6 @@ use serde;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::BufReader;
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
@ -44,8 +44,6 @@ use crate::mining::mine_block;
|
||||||
use crate::pool;
|
use crate::pool;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
use futures::sync::mpsc;
|
|
||||||
|
|
||||||
type Tx = mpsc::UnboundedSender<String>;
|
type Tx = mpsc::UnboundedSender<String>;
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
@ -600,53 +598,68 @@ impl Handler {
|
||||||
// Worker Factory Thread Function
|
// Worker Factory Thread Function
|
||||||
fn accept_connections(listen_addr: SocketAddr, handler: Arc<Handler>) {
|
fn accept_connections(listen_addr: SocketAddr, handler: Arc<Handler>) {
|
||||||
info!("Start tokio stratum server");
|
info!("Start tokio stratum server");
|
||||||
let listener = TcpListener::bind(&listen_addr).expect(&format!(
|
let task = async move {
|
||||||
"Stratum: Failed to bind to listen address {}",
|
let mut listener = TcpListener::bind(&listen_addr).await.expect(&format!(
|
||||||
listen_addr
|
"Stratum: Failed to bind to listen address {}",
|
||||||
));
|
listen_addr
|
||||||
let server = listener
|
));
|
||||||
.incoming()
|
let server = listener
|
||||||
.for_each(move |socket| {
|
.incoming()
|
||||||
// Spawn a task to process the connection
|
.filter_map(|s| async { s.map_err(|e| error!("accept error = {:?}", e)).ok() })
|
||||||
let (tx, rx) = mpsc::unbounded();
|
.for_each(move |socket| {
|
||||||
|
let handler = handler.clone();
|
||||||
|
async move {
|
||||||
|
// Spawn a task to process the connection
|
||||||
|
let (tx, mut rx) = mpsc::unbounded();
|
||||||
|
|
||||||
let worker_id = handler.workers.add_worker(tx);
|
let worker_id = handler.workers.add_worker(tx);
|
||||||
info!("Worker {} connected", worker_id);
|
info!("Worker {} connected", worker_id);
|
||||||
|
|
||||||
let (reader, writer) = socket.split();
|
let framed = Framed::new(socket, LinesCodec::new());
|
||||||
let reader = BufReader::new(reader);
|
let (mut writer, mut reader) = framed.split();
|
||||||
let h = handler.clone();
|
|
||||||
let workers = h.workers.clone();
|
|
||||||
let input = lines(reader)
|
|
||||||
.for_each(move |line| {
|
|
||||||
let request = serde_json::from_str(&line)?;
|
|
||||||
let resp = h.handle_rpc_requests(request, worker_id);
|
|
||||||
workers.send_to(worker_id, resp);
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.map_err(|e| error!("error {}", e));
|
|
||||||
|
|
||||||
let output = rx.fold(writer, |writer, s| {
|
let h = handler.clone();
|
||||||
let s2 = s + "\n";
|
let read = async move {
|
||||||
write_all(writer, s2.into_bytes())
|
while let Some(line) = reader
|
||||||
.map(|(writer, _)| writer)
|
.try_next()
|
||||||
.map_err(|e| error!("cannot send {}", e))
|
.await
|
||||||
|
.map_err(|e| error!("error reading line: {}", e))?
|
||||||
|
{
|
||||||
|
let request = serde_json::from_str(&line)
|
||||||
|
.map_err(|e| error!("error serializing line: {}", e))?;
|
||||||
|
let resp = h.handle_rpc_requests(request, worker_id);
|
||||||
|
h.workers.send_to(worker_id, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result::<_, ()>::Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
let write = async move {
|
||||||
|
while let Some(line) = rx.next().await {
|
||||||
|
let line = line + "\n";
|
||||||
|
writer
|
||||||
|
.send(line)
|
||||||
|
.await
|
||||||
|
.map_err(|e| error!("error writing line: {}", e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result::<_, ()>::Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
let task = async move {
|
||||||
|
pin_mut!(read, write);
|
||||||
|
futures::future::select(read, write).await;
|
||||||
|
handler.workers.remove_worker(worker_id);
|
||||||
|
info!("Worker {} disconnected", worker_id);
|
||||||
|
};
|
||||||
|
tokio::spawn(task);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
server.await
|
||||||
|
};
|
||||||
|
|
||||||
let workers = handler.workers.clone();
|
let mut rt = Runtime::new().unwrap();
|
||||||
let both = output.map(|_| ()).select(input);
|
rt.block_on(task);
|
||||||
tokio::spawn(both.then(move |_| {
|
|
||||||
workers.remove_worker(worker_id);
|
|
||||||
info!("Worker {} disconnected", worker_id);
|
|
||||||
Ok(())
|
|
||||||
}));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
error!("accept error = {:?}", err);
|
|
||||||
});
|
|
||||||
tokio::run(server.map(|_| ()).map_err(|_| ()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue