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 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}