binance/
client.rs

1use error_chain::bail;
2use hex::encode as hex_encode;
3use hmac::{Hmac, Mac};
4use crate::errors::{BinanceContentError, ErrorKind, Result};
5use reqwest::StatusCode;
6use reqwest::blocking::Response;
7use reqwest::header::{HeaderMap, HeaderName, HeaderValue, USER_AGENT, CONTENT_TYPE};
8use sha2::Sha256;
9use serde::de::DeserializeOwned;
10use crate::api::API;
11
12#[derive(Clone)]
13pub struct Client {
14    api_key: String,
15    secret_key: String,
16    host: String,
17    inner_client: reqwest::blocking::Client,
18    verbose: bool,
19}
20
21impl Client {
22    pub fn new(api_key: Option<String>, secret_key: Option<String>, host: String) -> Self {
23        Client {
24            api_key: api_key.unwrap_or_default(),
25            secret_key: secret_key.unwrap_or_default(),
26            host,
27            inner_client: reqwest::blocking::Client::builder()
28                .pool_idle_timeout(None)
29                .build()
30                .unwrap(),
31            verbose: false,
32        }
33    }
34
35    pub fn set_verbose(&mut self, verbose: bool) {
36        self.verbose = verbose;
37    }
38
39    pub fn set_host(&mut self, host: String) {
40        self.host = host;
41    }
42
43    pub fn get_signed<T: DeserializeOwned>(
44        &self, endpoint: API, request: Option<String>,
45    ) -> Result<T> {
46        let url = self.sign_request(endpoint, request);
47        let headers = self.build_headers(true)?;
48        if self.verbose {
49            println!("Request URL: {}", url);
50            println!("Request Headers: {:?}", headers);
51        }
52        let client = &self.inner_client;
53        let response = client.get(url.as_str()).headers(headers).send()?;
54
55        self.handler(response)
56    }
57
58    pub fn post_signed<T: DeserializeOwned>(&self, endpoint: API, request: String) -> Result<T> {
59        let url = self.sign_request(endpoint, Some(request));
60        let client = &self.inner_client;
61
62        let headers = self.build_headers(true)?;
63        if self.verbose {
64            println!("Request URL: {}", url);
65            println!("Request Headers: {:?}", headers);
66        }
67        let response = client.post(url.as_str()).headers(headers).send()?;
68
69        self.handler(response)
70    }
71
72    pub fn delete_signed<T: DeserializeOwned>(
73        &self, endpoint: API, request: Option<String>,
74    ) -> Result<T> {
75        let url = self.sign_request(endpoint, request);
76        let headers = self.build_headers(true)?;
77        if self.verbose {
78            println!("Request URL: {}", url);
79            println!("Request Headers: {:?}", headers);
80        }
81        let client = &self.inner_client;
82        let response = client.delete(url.as_str()).headers(headers).send()?;
83
84        self.handler(response)
85    }
86
87    pub fn get<T: DeserializeOwned>(&self, endpoint: API, request: Option<String>) -> Result<T> {
88        let mut url: String = format!("{}{}", self.host, String::from(endpoint));
89        if let Some(request) = request {
90            if !request.is_empty() {
91                url.push_str(format!("?{}", request).as_str());
92            }
93        }
94
95        let client = &self.inner_client;
96        if self.verbose {
97            println!("Request URL: {}", url);
98        }
99        let response = client.get(url.as_str()).send()?;
100
101        self.handler(response)
102    }
103
104    pub fn post<T: DeserializeOwned>(&self, endpoint: API) -> Result<T> {
105        let url: String = format!("{}{}", self.host, String::from(endpoint));
106
107        let client = &self.inner_client;
108        let response = client
109            .post(url.as_str())
110            .headers(self.build_headers(false)?)
111            .send()?;
112
113        self.handler(response)
114    }
115
116    pub fn put<T: DeserializeOwned>(&self, endpoint: API, listen_key: &str) -> Result<T> {
117        let url: String = format!("{}{}", self.host, String::from(endpoint));
118        let data: String = format!("listenKey={}", listen_key);
119
120        let client = &self.inner_client;
121
122        let headers = self.build_headers(true)?;
123        if self.verbose {
124            println!("Request URL: {}", url);
125            println!("Request Headers: {:?}", headers);
126            println!("Request Body: {}", data);
127        }
128        let response = client
129            .put(url.as_str())
130            .headers(headers)
131            .body(data)
132            .send()?;
133
134        self.handler(response)
135    }
136
137    pub fn delete<T: DeserializeOwned>(&self, endpoint: API, listen_key: &str) -> Result<T> {
138        let url: String = format!("{}{}", self.host, String::from(endpoint));
139        let data: String = format!("listenKey={}", listen_key);
140
141        let client = &self.inner_client;
142        let response = client
143            .delete(url.as_str())
144            .headers(self.build_headers(false)?)
145            .body(data)
146            .send()?;
147
148        self.handler(response)
149    }
150
151    // Request must be signed
152    fn sign_request(&self, endpoint: API, request: Option<String>) -> String {
153        if let Some(request) = request {
154            let mut signed_key =
155                Hmac::<Sha256>::new_from_slice(self.secret_key.as_bytes()).unwrap();
156            signed_key.update(request.as_bytes());
157            let signature = hex_encode(signed_key.finalize().into_bytes());
158            let request_body: String = format!("{}&signature={}", request, signature);
159            format!("{}{}?{}", self.host, String::from(endpoint), request_body)
160        } else {
161            let signed_key = Hmac::<Sha256>::new_from_slice(self.secret_key.as_bytes()).unwrap();
162            let signature = hex_encode(signed_key.finalize().into_bytes());
163            let request_body: String = format!("&signature={}", signature);
164            format!("{}{}?{}", self.host, String::from(endpoint), request_body)
165        }
166    }
167
168    fn build_headers(&self, content_type: bool) -> Result<HeaderMap> {
169        let mut custom_headers = HeaderMap::new();
170
171        custom_headers.insert(USER_AGENT, HeaderValue::from_static("binance-rs"));
172        if content_type {
173            custom_headers.insert(
174                CONTENT_TYPE,
175                HeaderValue::from_static("application/x-www-form-urlencoded"),
176            );
177        }
178        custom_headers.insert(
179            HeaderName::from_static("x-mbx-apikey"),
180            HeaderValue::from_str(self.api_key.as_str())?,
181        );
182
183        Ok(custom_headers)
184    }
185
186    fn handler<T: DeserializeOwned>(&self, response: Response) -> Result<T> {
187        match response.status() {
188            StatusCode::OK => {
189                let headers = response.headers().clone();
190                let response_bytes = response.bytes()?;
191
192                if self.verbose {
193                    println!("Response Headers: {:?}", headers);
194                    let pretty =
195                        serde_json::from_slice::<serde_json::Value>(&response_bytes).unwrap();
196                    println!("Response: {}", pretty);
197                }
198                let json: T = serde_json::from_slice(&response_bytes)?;
199                Ok(json)
200            }
201            StatusCode::INTERNAL_SERVER_ERROR => {
202                bail!("Internal Server Error");
203            }
204            StatusCode::SERVICE_UNAVAILABLE => {
205                bail!("Service Unavailable");
206            }
207            StatusCode::UNAUTHORIZED => {
208                bail!("Unauthorized");
209            }
210            StatusCode::BAD_REQUEST => {
211                let error: BinanceContentError = response.json()?;
212
213                Err(ErrorKind::BinanceError(error).into())
214            }
215            s => {
216                bail!(format!("Received response: {:?}", s));
217            }
218        }
219    }
220}