xpx_chain_sdk/api/service/transport/client/
mod.rs

1/*
2 * Copyright 2018 ProximaX Limited. All rights reserved.
3 * Use of this source code is governed by the Apache 2.0
4 * license that can be found in the LICENSE file.
5 */
6
7//! Client implementation and builder.
8
9use std::{
10    fmt,
11    task::{Context, Poll},
12};
13
14use bytes::Bytes;
15use futures_util::future;
16use hyper::http::uri::InvalidUri;
17use hyper::Uri;
18use tower::Service;
19
20pub use api_node::ApiNode;
21
22use crate::api;
23use crate::api::routes::{
24    account_routes_api::AccountRoutes, chain_routes_api::ChainRoutes,
25    metadata_v2_routes_api::MetadataV2Routes, mosaic_routes_api::MosaicRoutes,
26    namespace_routes_api::NamespaceRoutes, network_routes_api::NetworkRoutes,
27    node_routes_api::NodeRoutes, resolver_routes_api::ResolverRoutes,
28    transaction_routes_api::TransactionRoutes,
29};
30use crate::api::transport::service::connection::{Request, Response, TimeoutConnector};
31
32use super::service::Connection;
33
34mod api_node;
35
36const DEFAULT_BUFFER_SIZE: usize = 1024;
37
38/// A default batteries included `transport` client.
39///
40/// This provides a fully featured http client based on [`hyper::Client`]
41/// and `tower` services.
42///
43/// # Multiplexing requests
44///
45/// Sending a request on a client requires a `&mut self` and thus can only send
46/// one request in flight. This is intentional and is required to follow the `Service`
47/// contract from the `tower` library which this client implementation is built on
48/// top of.
49///
50/// `tower` itself has a concept of `poll_ready` which is the main mechanism to apply
51/// back pressure. `poll_ready` takes a `&mut self` and when it returns `Poll::Ready`
52/// we know the `Service` is able to accept only one request before we must `poll_ready`
53/// again. Due to this fact any `async fn` that wants to poll for readiness and submit
54/// the request must have a `&mut self` reference.
55///
56/// To work around this and to ease the use of the client, `Channel` provides a
57/// `Clone` implementation that is _cheap_. This is because at the very top level
58/// the client is backed by a `tower_buffer::Buffer` which runs the connection
59/// in a background task and provides a `mpsc` client interface. Due to this
60/// cloning the `Channel` type is cheap and encouraged.
61#[derive(Clone)]
62pub struct Client {
63    inner: Connection,
64}
65
66impl Client {
67    /// Create an [`ApiNode`] builder that can create [`Channel`]s.
68    pub fn builder(uri: Uri) -> ApiNode {
69        ApiNode::from(uri)
70    }
71
72    /// Create an `Endpoint` from a static string.
73    ///
74    /// ```
75    /// # use xpx_chain_sdk::api::Client;
76    /// Client::from_static("http://api-1.testnet2.xpxsirius.io:3000");
77    /// ```
78    pub fn from_static(s: &'static str) -> ApiNode {
79        let uri = Uri::from_static(s);
80        Self::builder(uri)
81    }
82
83    /// Create an `Endpoint` from shared bytes.
84    ///
85    /// ```
86    /// # use xpx_chain_sdk::api::Client;
87    /// Client::from_shared("http://api-1.testnet2.xpxsirius.io:3000");
88    /// ```
89    pub fn from_shared(s: impl Into<Bytes>) -> Result<ApiNode, InvalidUri> {
90        let uri = Uri::from_maybe_shared(s.into())?;
91        Ok(Self::builder(uri))
92    }
93
94    pub(crate) fn new(connector: TimeoutConnector, endpoint: ApiNode) -> Self {
95        let svc = Connection::lazy(connector, endpoint);
96
97        Client { inner: svc }
98    }
99
100    pub(crate) async fn connect(
101        connector: TimeoutConnector,
102        endpoint: ApiNode,
103    ) -> Result<Self, super::Error> {
104        let svc = Connection::connect(connector, endpoint)
105            .await
106            .map_err(super::Error::from_source)?;
107
108        Ok(Client { inner: svc })
109    }
110}
111
112impl Client {
113    fn __client(&self) -> Connection {
114        self.inner.clone()
115    }
116
117    /// Get Account ApiClient routes.
118    pub fn account_api(&self) -> Box<AccountRoutes> {
119        Box::new(AccountRoutes::new(self.__client()))
120    }
121
122    /// Get Chain ApiClient routes.
123    pub fn chain_api(&self) -> Box<ChainRoutes> {
124        Box::new(ChainRoutes::new(self.__client()))
125    }
126
127    // /// Get Exchange ApiClient routes.
128    // pub fn exchange_api(&self) -> Box<ExchangeRoutes> {
129    //     Box::new(ExchangeRoutes::new(
130    //         self.__client(),
131    //         self.network_type(),
132    //         *self.resolver_api(),
133    //     ))
134    // }
135
136    /// Get Node ApiClient routes.
137    pub fn node_api(&self) -> Box<NodeRoutes> {
138        Box::new(NodeRoutes::new(self.__client()))
139    }
140
141    /// Get Network ApiClient routes.
142    pub fn network_api(&self) -> Box<NetworkRoutes> {
143        Box::new(NetworkRoutes::new(self.__client()))
144    }
145
146    /// Get Mosaic ApiClient routes.
147    pub fn mosaic_api(&self) -> Box<MosaicRoutes> {
148        Box::new(MosaicRoutes::new(self.__client()))
149    }
150
151    /// Get Namespace ApiClient routes.
152    pub fn namespace_api(&self) -> Box<NamespaceRoutes> {
153        Box::new(NamespaceRoutes::new(self.__client()))
154    }
155
156    /// Get Transaction ApiClient routes.
157    pub fn transaction_api(&self) -> Box<TransactionRoutes> {
158        Box::new(TransactionRoutes::new(self.__client()))
159    }
160
161    /// Get Resolver ApiClient routes.
162    pub fn resolver_api(&self) -> Box<ResolverRoutes> {
163        Box::new(ResolverRoutes::new(self.__client(), *self.namespace_api(), *self.mosaic_api()))
164    }
165
166    // /// Get Metadata ApiClient routes.
167    // pub fn metadata_api(&self) -> Box<MetadataRoutes> {
168    //     Box::new(MetadataRoutes::new(self.__client()))
169    // }
170
171    /// Get MetadataV2 ApiClient routes.
172    pub fn metadata_v2_api(&self) -> Box<MetadataV2Routes> {
173        Box::new(MetadataV2Routes::new(self.__client()))
174    }
175}
176
177impl Service<Request> for Client {
178    type Response = Response;
179    type Error = api::error::Error;
180    type Future = future::BoxFuture<'static, Result<Self::Response, Self::Error>>;
181
182    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
183        self.inner.poll_ready(cx).map_err(Into::into)
184    }
185
186    fn call(&mut self, request: Request) -> Self::Future {
187        let fut = self.inner.call(request);
188        Box::pin(async move { fut.await.map_err(Into::into) })
189    }
190}
191
192impl fmt::Debug for Client {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        f.debug_struct("Channel").finish()
195    }
196}