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
//! Crypto-bank HTTP API client primitives.

#[macro_use]
extern crate failure;

extern crate bytes;
extern crate hyper;
extern crate hyper_tls;

#[macro_use]
extern crate err_convert_macro;

use std::collections::BTreeMap;
use std::sync::Arc;

pub use bytes::buf::BufExt;
use hyper::body::Buf;
use hyper::{client::HttpConnector, http::StatusCode, Body, Request as HyperRequest};
use hyper_tls::HttpsConnector;

/// HTTPs Client type.
pub type HttpsClient = Arc<hyper::Client<HttpsConnector<HttpConnector>>>;

/// Exchanges client request.
pub type Request = ::hyper::Request<::hyper::Body>;

/// Binance API request parameters map.
pub type RequestParams = BTreeMap<String, String>;

/// API Client response error.
#[derive(Debug)]
pub struct ErrorResponse {
    pub status: hyper::StatusCode,
    pub body: String,
}

/// HTTP Client module error type.
#[derive(Debug, Fail)]
pub enum Error {
    /// API response error.
    #[fail(display = "http response error: {:?}", _0)]
    Response(ErrorResponse),

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

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

err_converter!(Hyper, ::hyper::Error);
err_converter!(HyperHttp, ::hyper::http::Error);

/// Creates new HTTPs client.
pub fn build_https_client() -> Result<HttpsClient, Error> {
    let https = HttpsConnector::new();
    let client = hyper::Client::builder().build::<_, hyper::Body>(https);
    Ok(Arc::new(client))
}

/// Builds request to public HTTPS API.
pub fn build_request(
    host: &str,
    path: &str,
    params: Option<RequestParams>,
) -> Result<HyperRequest<Body>, Error> {
    let query: String = params
        .unwrap_or_else(|| RequestParams::new())
        .into_iter()
        .map(|(key, value)| format!("{}={}&", key, value))
        .collect();
    HyperRequest::builder()
        .uri(format!("https://{}{}?{}", host, path, query))
        .header("User-Agent", "cxmr.rs")
        .body(Body::empty())
        .map_err(|e| e.into())
}

/// Builds request to unsecured unprotected HTTP service.
pub fn build_unsecured_request(
    host: &str,
    path: &str,
    params: Option<RequestParams>,
) -> Result<HyperRequest<Body>, Error> {
    let query: String = params
        .unwrap_or_else(|| RequestParams::new())
        .into_iter()
        .map(|(key, value)| format!("{}={}&", key, value))
        .collect();
    let url = format!("http://{}{}?{}", host, path, query);
    println!("Request: {}", url);
    HyperRequest::builder()
        .uri(url)
        .header("User-Agent", "cxmr.rs")
        .body(Body::empty())
        .map_err(|e| e.into())
}

/// Sends authenticated HTTPs request.
pub async fn send_request(client: &HttpsClient, request: Request) -> Result<impl Buf, Error> {
    let res = client.request(request).await?;
    let status = res.status();
    let body = hyper::body::aggregate(res).await?;
    if status == StatusCode::OK {
        Ok(body)
    } else {
        let resp = ErrorResponse {
            status: status,
            body: String::from_utf8_lossy(body.bytes()).to_string(),
        };
        Err(Error::Response(resp))
    }
}