cxmr_http_client/
lib.rs

1//! Crypto-bank HTTP API client primitives.
2
3#[macro_use]
4extern crate failure;
5
6extern crate bytes;
7extern crate hyper;
8extern crate hyper_tls;
9
10#[macro_use]
11extern crate err_convert_macro;
12
13use std::collections::BTreeMap;
14use std::sync::Arc;
15
16pub use bytes::buf::BufExt;
17use hyper::body::Buf;
18use hyper::{client::HttpConnector, http::StatusCode, Body, Request as HyperRequest};
19use hyper_tls::HttpsConnector;
20
21/// HTTPs Client type.
22pub type HttpsClient = Arc<hyper::Client<HttpsConnector<HttpConnector>>>;
23
24/// Exchanges client request.
25pub type Request = ::hyper::Request<::hyper::Body>;
26
27/// Binance API request parameters map.
28pub type RequestParams = BTreeMap<String, String>;
29
30/// API Client response error.
31#[derive(Debug)]
32pub struct ErrorResponse {
33    pub status: hyper::StatusCode,
34    pub body: String,
35}
36
37/// HTTP Client module error type.
38#[derive(Debug, Fail)]
39pub enum Error {
40    /// API response error.
41    #[fail(display = "http response error: {:?}", _0)]
42    Response(ErrorResponse),
43
44    /// Hyper error.
45    #[fail(display = "hyper error: {:?}", _0)]
46    Hyper(::hyper::Error),
47
48    /// Hyper HTTP error.
49    #[fail(display = "hyper http error: {:?}", _0)]
50    HyperHttp(::hyper::http::Error),
51}
52
53err_converter!(Hyper, ::hyper::Error);
54err_converter!(HyperHttp, ::hyper::http::Error);
55
56/// Creates new HTTPs client.
57pub fn build_https_client() -> Result<HttpsClient, Error> {
58    let https = HttpsConnector::new();
59    let client = hyper::Client::builder().build::<_, hyper::Body>(https);
60    Ok(Arc::new(client))
61}
62
63/// Builds request to public HTTPS API.
64pub fn build_request(
65    host: &str,
66    path: &str,
67    params: Option<RequestParams>,
68) -> Result<HyperRequest<Body>, Error> {
69    let query: String = params
70        .unwrap_or_else(|| RequestParams::new())
71        .into_iter()
72        .map(|(key, value)| format!("{}={}&", key, value))
73        .collect();
74    HyperRequest::builder()
75        .uri(format!("https://{}{}?{}", host, path, query))
76        .header("User-Agent", "cxmr.rs")
77        .body(Body::empty())
78        .map_err(|e| e.into())
79}
80
81/// Builds request to unsecured unprotected HTTP service.
82pub fn build_unsecured_request(
83    host: &str,
84    path: &str,
85    params: Option<RequestParams>,
86) -> Result<HyperRequest<Body>, Error> {
87    let query: String = params
88        .unwrap_or_else(|| RequestParams::new())
89        .into_iter()
90        .map(|(key, value)| format!("{}={}&", key, value))
91        .collect();
92    let url = format!("http://{}{}?{}", host, path, query);
93    println!("Request: {}", url);
94    HyperRequest::builder()
95        .uri(url)
96        .header("User-Agent", "cxmr.rs")
97        .body(Body::empty())
98        .map_err(|e| e.into())
99}
100
101/// Sends authenticated HTTPs request.
102pub async fn send_request(client: &HttpsClient, request: Request) -> Result<impl Buf, Error> {
103    let res = client.request(request).await?;
104    let status = res.status();
105    let body = hyper::body::aggregate(res).await?;
106    if status == StatusCode::OK {
107        Ok(body)
108    } else {
109        let resp = ErrorResponse {
110            status: status,
111            body: String::from_utf8_lossy(body.bytes()).to_string(),
112        };
113        Err(Error::Response(resp))
114    }
115}