1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Crypto-bank daemon server.
#![feature(try_trait)]

#[macro_use]
extern crate log;
#[macro_use]
extern crate failure;
extern crate hyper;
extern crate yew_router_min;

extern crate cxmr_balances;
extern crate cxmr_broker;
extern crate cxmr_exchanges;
#[macro_use]
extern crate err_convert_macro;
extern crate cxmr_util_servers;

mod methods;

use std::task::{Context, Poll};

use hyper::{service::Service, Body, Request, Response};
use yew_router_min::Switch;

use cxmr_broker::SharedBroker;
use cxmr_util_servers::helpers::*;

/// RPC server module error.
#[derive(Debug, Fail)]
pub enum Error {
    /// Option is `None` error.
    #[fail(display = "option none")]
    OptionNone,

    /// Not found error.
    #[fail(display = "not found error")]
    NotFound,

    /// Mutex poison error.
    #[fail(display = "mutex poison error")]
    MutexPoison,

    /// Hyper error.
    #[fail(display = "hyper error: {:?}", _0)]
    Hyper(#[cause] hyper::Error),

    /// Hyper HTTP error.
    #[fail(display = "hyper http error: {:?}", _0)]
    Http(#[cause] hyper::http::Error),
}

err_converter!(Http, hyper::http::Error);
err_converter!(Hyper, hyper::Error);
err_converter_no_args!(OptionNone, std::option::NoneError);

impl<E> From<std::sync::PoisonError<E>> for Error {
    fn from(_: std::sync::PoisonError<E>) -> Error {
        Error::MutexPoison
    }
}

/// Application route path element.
#[derive(Switch, Debug, PartialEq)]
pub enum App {
    /// Broker routes.
    #[to = "/v1/broker{*:rest}"]
    Broker(Broker),
    /// Prometheus routes.
    #[to = "/v1/prometheus{*:rest}"]
    Prometheus(Prometheus),
}

/// Prometheus routes.
#[derive(Switch, Debug, PartialEq)]
pub enum Prometheus {
    /// Prices route.
    #[to = "/prices"]
    Prices,
    /// Balances route.
    #[to = "/balances"]
    Balances,
}

/// Broker routes.
#[derive(Switch, Debug, PartialEq)]
pub enum Broker {
    /// Orders route.
    #[to = "/orders"]
    Orders,
    /// Orderbooks route.
    #[to = "/orderbooks"]
    Orderbooks,
    /// Balances route.
    #[to = "/balances"]
    Balances,
}

/// Router service alias type.
pub type RouterService = cxmr_util_servers::RouterService<Router>;

/// Routes HTTP request.
#[derive(Clone)]
pub struct Router {
    broker: SharedBroker,
}

impl Service<Request<Body>> for Router {
    type Response = Response<Body>;
    type Error = failure::Error;
    type Future = std::pin::Pin<
        Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>,
    >;

    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Ok(()).into()
    }

    fn call(&mut self, req: Request<Body>) -> Self::Future {
        Box::pin(router(req, self.broker.clone()))
    }
}

impl From<SharedBroker> for Router {
    fn from(broker: SharedBroker) -> Router {
        Router { broker }
    }
}

async fn router(
    req: Request<Body>,
    broker: SharedBroker,
) -> Result<Response<Body>, failure::Error> {
    match router_impl(req, broker).await {
        Ok(res) => Ok(res),
        Err(Error::NotFound) => Ok(not_found()?),
        Err(err) => {
            error!("RPC Server error: {:?}", err);
            Ok(server_error()?)
        }
    }
}

async fn router_impl(req: Request<Body>, broker: SharedBroker) -> Result<Response<Body>, Error> {
    let route = App::from_path(req.uri().path().into());
    match route {
        Some(App::Broker(Broker::Orders)) => methods::serve_orders(broker).await,
        Some(App::Broker(Broker::Orderbooks)) => methods::serve_orderbooks(broker).await,
        Some(App::Broker(Broker::Balances)) => methods::serve_balances(broker).await,
        Some(App::Prometheus(Prometheus::Prices)) => methods::serve_prometheus_prices(broker).await,
        Some(App::Prometheus(Prometheus::Balances)) => {
            methods::serve_prometheus_balances(broker).await
        }
        None => Err(Error::NotFound),
    }
}