//! 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>>; /// Exchanges client request. pub type Request = ::hyper::Request<::hyper::Body>; /// Binance API request parameters map. pub type RequestParams = BTreeMap; /// 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 { 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, ) -> Result, 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, ) -> Result, 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 { 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)) } }