Update hyper/tokio/futures dependencies (#3214)
* Update hyper, tokio, futures versions * Update stratum server * Update API * Update webhooks
This commit is contained in:
+1
-1
@@ -139,5 +139,5 @@ fn unauthorized_response(basic_realm: &HeaderValue) -> ResponseFuture {
|
||||
.header(WWW_AUTHENTICATE, basic_realm)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
Box::new(ok(response))
|
||||
Box::pin(ok(response))
|
||||
}
|
||||
|
||||
+52
-72
@@ -17,23 +17,22 @@
|
||||
use crate::rest::{Error, ErrorKind};
|
||||
use crate::util::to_base64;
|
||||
use failure::{Fail, ResultExt};
|
||||
use futures::future::{err, ok, Either};
|
||||
use http::uri::{InvalidUri, Uri};
|
||||
use hyper::body;
|
||||
use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, USER_AGENT};
|
||||
use hyper::rt::{Future, Stream};
|
||||
use hyper::{Body, Client, Request};
|
||||
use hyper_rustls;
|
||||
use hyper_timeout::TimeoutConnector;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
pub type ClientResponseFuture<T> = Box<dyn Future<Item = T, Error = Error> + Send>;
|
||||
use tokio::runtime::Builder;
|
||||
|
||||
/// Helper function to easily issue a HTTP GET request against a given URL that
|
||||
/// returns a JSON object. Handles request building, JSON deserialization and
|
||||
/// 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>
|
||||
where
|
||||
for<'de> T: Deserialize<'de>,
|
||||
@@ -44,22 +43,18 @@ where
|
||||
/// Helper function to easily issue an async HTTP GET request against a given
|
||||
/// URL that returns a future. Handles request building, JSON deserialization
|
||||
/// 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
|
||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||
{
|
||||
match build_request(url, "GET", api_secret, None) {
|
||||
Ok(req) => Box::new(handle_request_async(req)),
|
||||
Err(e) => Box::new(err(e)),
|
||||
}
|
||||
handle_request_async(build_request(url, "GET", api_secret, None)?).await
|
||||
}
|
||||
|
||||
/// Helper function to easily issue a HTTP GET request
|
||||
/// on a given URL that returns nothing. Handles request
|
||||
/// building and response code checking.
|
||||
pub fn get_no_ret(url: &str, api_secret: Option<String>) -> Result<(), Error> {
|
||||
let req = build_request(url, "GET", api_secret, None)?;
|
||||
send_request(req)?;
|
||||
send_request(build_request(url, "GET", api_secret, None)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -80,20 +75,17 @@ where
|
||||
/// provided JSON object as body on a given URL that returns a future. Handles
|
||||
/// request building, JSON serialization and deserialization, and response code
|
||||
/// checking.
|
||||
pub fn post_async<IN, OUT>(
|
||||
pub async fn post_async<IN, OUT>(
|
||||
url: &str,
|
||||
input: &IN,
|
||||
api_secret: Option<String>,
|
||||
) -> ClientResponseFuture<OUT>
|
||||
) -> Result<OUT, Error>
|
||||
where
|
||||
IN: Serialize,
|
||||
OUT: Send + 'static,
|
||||
for<'de> OUT: Deserialize<'de>,
|
||||
{
|
||||
match create_post_request(url, api_secret, input) {
|
||||
Ok(req) => Box::new(handle_request_async(req)),
|
||||
Err(e) => Box::new(err(e)),
|
||||
}
|
||||
handle_request_async(create_post_request(url, api_secret, input)?).await
|
||||
}
|
||||
|
||||
/// 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
|
||||
IN: Serialize,
|
||||
{
|
||||
let req = create_post_request(url, api_secret, input)?;
|
||||
send_request(req)?;
|
||||
send_request(create_post_request(url, api_secret, input)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -113,18 +104,16 @@ where
|
||||
/// provided JSON object as body on a given URL that returns a future. Handles
|
||||
/// request building, JSON serialization and deserialization, and response code
|
||||
/// checking.
|
||||
pub fn post_no_ret_async<IN>(
|
||||
pub async fn post_no_ret_async<IN>(
|
||||
url: &str,
|
||||
api_secret: Option<String>,
|
||||
input: &IN,
|
||||
) -> ClientResponseFuture<()>
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
IN: Serialize,
|
||||
{
|
||||
match create_post_request(url, api_secret, input) {
|
||||
Ok(req) => Box::new(send_request_async(req).and_then(|_| ok(()))),
|
||||
Err(e) => Box::new(err(e)),
|
||||
}
|
||||
send_request_async(create_post_request(url, api_secret, input)?).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@@ -133,19 +122,15 @@ fn build_request(
|
||||
api_secret: Option<String>,
|
||||
body: Option<String>,
|
||||
) -> 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();
|
||||
if let Some(api_secret) = 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
|
||||
.method(method)
|
||||
.uri(uri)
|
||||
.uri(url)
|
||||
.header(USER_AGENT, "grin-client")
|
||||
.header(ACCEPT, "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
|
||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||
{
|
||||
Box::new(send_request_async(req).and_then(|data| {
|
||||
serde_json::from_str(&data).map_err(|e| {
|
||||
e.context(ErrorKind::ResponseError("Cannot parse response".to_owned()))
|
||||
.into()
|
||||
})
|
||||
}))
|
||||
let data = send_request_async(req).await?;
|
||||
let ser = serde_json::from_str(&data)
|
||||
.map_err(|e| e.context(ErrorKind::ResponseError("Cannot parse response".to_owned())))?;
|
||||
Ok(ser)
|
||||
}
|
||||
|
||||
fn send_request_async(req: Request<Body>) -> Box<dyn Future<Item = String, Error = Error> + Send> {
|
||||
let https = hyper_rustls::HttpsConnector::new(1);
|
||||
async fn send_request_async(req: Request<Body>) -> Result<String, Error> {
|
||||
let https = hyper_rustls::HttpsConnector::new();
|
||||
let mut connector = TimeoutConnector::new(https);
|
||||
connector.set_connect_timeout(Some(Duration::from_secs(20)));
|
||||
connector.set_read_timeout(Some(Duration::from_secs(20)));
|
||||
connector.set_write_timeout(Some(Duration::from_secs(20)));
|
||||
let client = Client::builder().build::<_, hyper::Body>(connector);
|
||||
Box::new(
|
||||
client
|
||||
.request(req)
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Cannot make request: {}", e)).into())
|
||||
.and_then(|resp| {
|
||||
if !resp.status().is_success() {
|
||||
Either::A(err(ErrorKind::RequestError(format!(
|
||||
"Wrong response code: {} with data {:?}",
|
||||
resp.status(),
|
||||
resp.body()
|
||||
))
|
||||
.into()))
|
||||
} else {
|
||||
Either::B(
|
||||
resp.into_body()
|
||||
.map_err(|e| {
|
||||
ErrorKind::RequestError(format!("Cannot read response body: {}", e))
|
||||
.into()
|
||||
})
|
||||
.concat2()
|
||||
.and_then(|ch| ok(String::from_utf8_lossy(&ch.to_vec()).to_string())),
|
||||
)
|
||||
}
|
||||
}),
|
||||
)
|
||||
let client = Client::builder().build::<_, Body>(connector);
|
||||
|
||||
let resp = client
|
||||
.request(req)
|
||||
.await
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Cannot make request: {}", e)))?;
|
||||
|
||||
if !resp.status().is_success() {
|
||||
return Err(ErrorKind::RequestError(format!(
|
||||
"Wrong response code: {} with data {:?}",
|
||||
resp.status(),
|
||||
resp.body()
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
let raw = body::to_bytes(resp)
|
||||
.await
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Cannot read response body: {}", e)))?;
|
||||
|
||||
Ok(String::from_utf8_lossy(&raw).to_string())
|
||||
}
|
||||
|
||||
pub fn send_request(req: Request<Body>) -> Result<String, Error> {
|
||||
let task = send_request_async(req);
|
||||
let mut rt =
|
||||
Runtime::new().context(ErrorKind::Internal("can't create Tokio runtime".to_owned()))?;
|
||||
Ok(rt.block_on(task)?)
|
||||
let mut rt = Builder::new()
|
||||
.basic_scheduler()
|
||||
.enable_all()
|
||||
.build()
|
||||
.map_err(|e| ErrorKind::RequestError(format!("{}", e)))?;
|
||||
rt.block_on(send_request_async(req))
|
||||
}
|
||||
|
||||
+49
-69
@@ -56,8 +56,6 @@ use crate::util::to_base64;
|
||||
use crate::util::RwLock;
|
||||
use crate::web::*;
|
||||
use easy_jsonrpc_mw::{Handler, MaybeReply};
|
||||
use futures::future::ok;
|
||||
use futures::Future;
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use serde::Serialize;
|
||||
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
|
||||
pub struct OwnerAPIHandlerV2 {
|
||||
pub chain: Weak<Chain>,
|
||||
@@ -157,48 +153,40 @@ impl OwnerAPIHandlerV2 {
|
||||
sync_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call_api(
|
||||
&self,
|
||||
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 {
|
||||
impl crate::router::Handler for OwnerAPIHandlerV2 {
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
let api = Owner::new(
|
||||
self.chain.clone(),
|
||||
self.peers.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 {
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(self.handle_post_request(req).and_then(ok).or_else(|e| {
|
||||
error!("Request Error: {:?}", e);
|
||||
ok(create_error_response(e))
|
||||
}))
|
||||
Box::pin(async move {
|
||||
match parse_body(req).await {
|
||||
Ok(val) => {
|
||||
let owner_api = &api as &dyn OwnerRpc;
|
||||
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 {
|
||||
Box::new(ok(create_ok_response("{}")))
|
||||
Box::pin(async { Ok(create_ok_response("{}")) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,48 +210,40 @@ impl ForeignAPIHandlerV2 {
|
||||
sync_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call_api(
|
||||
&self,
|
||||
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 {
|
||||
impl crate::router::Handler for ForeignAPIHandlerV2 {
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
let api = Foreign::new(
|
||||
self.chain.clone(),
|
||||
self.tx_pool.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 {
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(self.handle_post_request(req).and_then(ok).or_else(|e| {
|
||||
error!("Request Error: {:?}", e);
|
||||
ok(create_error_response(e))
|
||||
}))
|
||||
Box::pin(async move {
|
||||
match parse_body(req).await {
|
||||
Ok(val) => {
|
||||
let foreign_api = &api as &dyn ForeignRpc;
|
||||
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 {
|
||||
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
|
||||
/// valid JSON as the content type header will be set to `application/json'
|
||||
fn response<T: Into<Body>>(status: StatusCode, text: T) -> Response<Body> {
|
||||
let mut builder = &mut Response::builder();
|
||||
let mut builder = Response::builder();
|
||||
|
||||
builder = builder
|
||||
.status(status)
|
||||
|
||||
@@ -24,8 +24,6 @@ use crate::util;
|
||||
use crate::util::RwLock;
|
||||
use crate::web::*;
|
||||
use failure::ResultExt;
|
||||
use futures::future::{err, ok};
|
||||
use futures::Future;
|
||||
use hyper::{Body, Request, StatusCode};
|
||||
use std::sync::Weak;
|
||||
|
||||
@@ -102,65 +100,55 @@ pub struct PoolPushHandler {
|
||||
pub tx_pool: Weak<RwLock<pool::TransactionPool>>,
|
||||
}
|
||||
|
||||
impl PoolPushHandler {
|
||||
fn update_pool(&self, req: Request<Body>) -> Box<dyn Future<Item = (), Error = Error> + Send> {
|
||||
let params = QueryParams::from(req.uri().query());
|
||||
async fn update_pool(
|
||||
pool: Weak<RwLock<pool::TransactionPool>>,
|
||||
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 pool_arc = match w(&self.tx_pool) {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Box::new(err(e)),
|
||||
};
|
||||
let wrapper: TxWrapper = parse_body(req).await?;
|
||||
let tx_bin = util::from_hex(wrapper.tx_hex)
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Bad request: {}", e)))?;
|
||||
|
||||
Box::new(
|
||||
parse_body(req)
|
||||
.and_then(move |wrapper: TxWrapper| {
|
||||
util::from_hex(wrapper.tx_hex)
|
||||
.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);
|
||||
// All wallet api interaction explicitly uses protocol version 1 for now.
|
||||
let version = ProtocolVersion(1);
|
||||
let tx: Transaction = ser::deserialize(&mut &tx_bin[..], version)
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Bad request: {}", e)))?;
|
||||
|
||||
ser::deserialize(&mut &tx_bin[..], version)
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Bad request: {}", e)).into())
|
||||
})
|
||||
.and_then(move |tx: Transaction| {
|
||||
let source = pool::TxSource::PushApi;
|
||||
info!(
|
||||
"Pushing transaction {} to pool (inputs: {}, outputs: {}, kernels: {})",
|
||||
tx.hash(),
|
||||
tx.inputs().len(),
|
||||
tx.outputs().len(),
|
||||
tx.kernels().len(),
|
||||
);
|
||||
let source = pool::TxSource::PushApi;
|
||||
info!(
|
||||
"Pushing transaction {} to pool (inputs: {}, outputs: {}, kernels: {})",
|
||||
tx.hash(),
|
||||
tx.inputs().len(),
|
||||
tx.outputs().len(),
|
||||
tx.kernels().len(),
|
||||
);
|
||||
|
||||
// Push to tx pool.
|
||||
let mut tx_pool = pool_arc.write();
|
||||
let header = tx_pool
|
||||
.blockchain
|
||||
.chain_head()
|
||||
.context(ErrorKind::Internal("Failed to get chain head".to_owned()))?;
|
||||
tx_pool
|
||||
.add_to_pool(source, tx, !fluff, &header)
|
||||
.context(ErrorKind::Internal("Failed to update pool".to_owned()))?;
|
||||
Ok(())
|
||||
}),
|
||||
)
|
||||
}
|
||||
// Push to tx pool.
|
||||
let mut tx_pool = pool.write();
|
||||
let header = tx_pool
|
||||
.blockchain
|
||||
.chain_head()
|
||||
.context(ErrorKind::Internal("Failed to get chain head".to_owned()))?;
|
||||
tx_pool
|
||||
.add_to_pool(source, tx, !fluff, &header)
|
||||
.context(ErrorKind::Internal("Failed to update pool".to_owned()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Handler for PoolPushHandler {
|
||||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(
|
||||
self.update_pool(req)
|
||||
.and_then(|_| ok(just_response(StatusCode::OK, "")))
|
||||
.or_else(|e| {
|
||||
ok(just_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("failed: {}", e),
|
||||
))
|
||||
}),
|
||||
)
|
||||
let pool = self.tx_pool.clone();
|
||||
Box::pin(async move {
|
||||
let res = match update_pool(pool, req).await {
|
||||
Ok(_) => just_response(StatusCode::OK, ""),
|
||||
Err(e) => {
|
||||
just_response(StatusCode::INTERNAL_SERVER_ERROR, format!("failed: {}", e))
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+43
-27
@@ -21,19 +21,22 @@
|
||||
use crate::router::{Handler, HandlerObj, ResponseFuture, Router, RouterError};
|
||||
use crate::web::response;
|
||||
use failure::{Backtrace, Context, Fail, ResultExt};
|
||||
use futures::sync::oneshot;
|
||||
use futures::Stream;
|
||||
use hyper::rt::Future;
|
||||
use hyper::{rt, Body, Request, Server, StatusCode};
|
||||
use futures::channel::oneshot;
|
||||
use futures::TryStreamExt;
|
||||
use hyper::server::accept;
|
||||
use hyper::service::make_service_fn;
|
||||
use hyper::{Body, Request, Server, StatusCode};
|
||||
use rustls;
|
||||
use rustls::internal::pemfile;
|
||||
use std::convert::Infallible;
|
||||
use std::fmt::{self, Display};
|
||||
use std::fs::File;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
use tokio_rustls::ServerConfigExt;
|
||||
use tokio_tcp;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
|
||||
/// Errors that can be returned by an ApiEndpoint implementation.
|
||||
#[derive(Debug)]
|
||||
@@ -199,13 +202,23 @@ impl ApiServer {
|
||||
thread::Builder::new()
|
||||
.name("apis".to_string())
|
||||
.spawn(move || {
|
||||
let server = Server::bind(&addr)
|
||||
.serve(router)
|
||||
let server = async move {
|
||||
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
|
||||
//.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())
|
||||
}
|
||||
@@ -225,28 +238,31 @@ impl ApiServer {
|
||||
.into());
|
||||
}
|
||||
|
||||
let tls_conf = conf.build_server_config()?;
|
||||
let acceptor = TlsAcceptor::from(conf.build_server_config()?);
|
||||
|
||||
thread::Builder::new()
|
||||
.name("apis".to_string())
|
||||
.spawn(move || {
|
||||
let listener = tokio_tcp::TcpListener::bind(&addr).expect("failed to bind");
|
||||
let tls = listener
|
||||
.incoming()
|
||||
.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));
|
||||
let server = async move {
|
||||
let mut listener = TcpListener::bind(&addr).await.expect("failed to bind");
|
||||
let listener = listener.incoming().and_then(move |s| acceptor.accept(s));
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
+34
-37
@@ -12,21 +12,23 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use futures::future;
|
||||
use futures::future::{self, Future};
|
||||
use hyper;
|
||||
use hyper::rt::Future;
|
||||
use hyper::service::{NewService, Service};
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Method, Request, Response, StatusCode};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
lazy_static! {
|
||||
static ref WILDCARD_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 {
|
||||
fn get(&self, _req: Request<Body>) -> ResponseFuture {
|
||||
@@ -203,13 +205,16 @@ impl Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Router {
|
||||
type ReqBody = Body;
|
||||
type ResBody = Body;
|
||||
impl Service<Request<Body>> for Router {
|
||||
type Response = Response<Body>;
|
||||
type Error = hyper::Error;
|
||||
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()) {
|
||||
Err(_) => not_found(),
|
||||
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 {
|
||||
fn new(key: u64, value: Option<HandlerObj>) -> Node {
|
||||
Node {
|
||||
@@ -276,7 +269,7 @@ impl Node {
|
||||
pub fn not_found() -> ResponseFuture {
|
||||
let mut response = Response::new(Body::empty());
|
||||
*response.status_mut() = StatusCode::NOT_FOUND;
|
||||
Box::new(future::ok(response))
|
||||
Box::pin(future::ok(response))
|
||||
}
|
||||
|
||||
fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||
@@ -305,19 +298,20 @@ fn collect_node_middleware(handlers: &mut Vec<HandlerObj>, node: &Node) {
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use tokio::prelude::future::ok;
|
||||
use tokio_core::reactor::Core;
|
||||
use futures::executor::block_on;
|
||||
|
||||
struct HandlerImpl(u16);
|
||||
|
||||
impl Handler for HandlerImpl {
|
||||
fn get(&self, _req: Request<Body>) -> ResponseFuture {
|
||||
Box::new(future::ok(
|
||||
Response::builder()
|
||||
.status(self.0)
|
||||
let code = self.0;
|
||||
Box::pin(async move {
|
||||
let res = Response::builder()
|
||||
.status(code)
|
||||
.body(Body::default())
|
||||
.unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,15 +352,18 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let call_handler = |url| {
|
||||
let mut event_loop = Core::new().unwrap();
|
||||
let task = routes
|
||||
.get(url)
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.get(Request::new(Body::default()))
|
||||
.and_then(|resp| ok(resp.status().as_u16()));
|
||||
event_loop.run(task).unwrap()
|
||||
let task = async {
|
||||
let resp = routes
|
||||
.get(url)
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.get(Request::new(Body::default()))
|
||||
.await
|
||||
.unwrap();
|
||||
resp.status().as_u16()
|
||||
};
|
||||
block_on(task)
|
||||
};
|
||||
|
||||
assert_eq!(call_handler("/v1/users"), 101);
|
||||
|
||||
+11
-15
@@ -1,7 +1,8 @@
|
||||
use crate::rest::*;
|
||||
use crate::router::ResponseFuture;
|
||||
use futures::future::{err, ok};
|
||||
use futures::{Future, Stream};
|
||||
use bytes::Buf;
|
||||
use futures::future::ok;
|
||||
use hyper::body;
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
@@ -10,21 +11,16 @@ use std::fmt::Debug;
|
||||
use url::form_urlencoded;
|
||||
|
||||
/// 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
|
||||
for<'de> T: Deserialize<'de> + Send + 'static,
|
||||
{
|
||||
Box::new(
|
||||
req.into_body()
|
||||
.concat2()
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Failed to read request: {}", e)).into())
|
||||
.and_then(|body| match serde_json::from_reader(&body.to_vec()[..]) {
|
||||
Ok(obj) => ok(obj),
|
||||
Err(e) => {
|
||||
err(ErrorKind::RequestError(format!("Invalid request body: {}", e)).into())
|
||||
}
|
||||
}),
|
||||
)
|
||||
let raw = body::to_bytes(req.into_body())
|
||||
.await
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Failed to read request: {}", e)))?;
|
||||
|
||||
serde_json::from_reader(raw.bytes())
|
||||
.map_err(|e| ErrorKind::RequestError(format!("Invalid request body: {}", e)).into())
|
||||
}
|
||||
|
||||
/// 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
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user