use crate::error::{Error, Result};
use crate::order_book::{DEFAULT_HTTP_TIMEOUT, MAX_RESPONSE_BYTES};
use crate::transport::{HttpMethod, HttpRequest, HttpResponse, HttpTransport};
#[derive(Debug, Clone)]
pub struct ReqwestTransport {
client: reqwest::Client,
}
impl ReqwestTransport {
pub const fn new(client: reqwest::Client) -> Self {
Self { client }
}
fn default_client() -> reqwest::Client {
reqwest::Client::builder()
.timeout(DEFAULT_HTTP_TIMEOUT)
.build()
.expect("reqwest defaults cannot fail")
}
}
impl Default for ReqwestTransport {
fn default() -> Self {
Self::new(Self::default_client())
}
}
impl HttpTransport for ReqwestTransport {
async fn execute(&self, request: HttpRequest) -> Result<HttpResponse> {
let mut builder = match request.method {
HttpMethod::Get => self.client.get(request.url),
HttpMethod::Post => self.client.post(request.url),
HttpMethod::Put => self.client.put(request.url),
HttpMethod::Delete => self.client.delete(request.url),
};
if let Some(body) = request.json_body {
builder = builder
.header(reqwest::header::CONTENT_TYPE, "application/json")
.body(body);
}
if let Some(token) = request.bearer {
builder = builder.bearer_auth(token);
}
let response = builder.send().await?;
let status = response.status().as_u16();
let body = read_capped_text(response).await?;
Ok(HttpResponse { status, body })
}
}
async fn read_capped_text(response: reqwest::Response) -> Result<String> {
if let Some(declared_len) = response.content_length()
&& declared_len > MAX_RESPONSE_BYTES as u64
{
return Err(Error::ResponseTooLarge {
max: MAX_RESPONSE_BYTES,
});
}
read_capped_body(response).await
}
pub(crate) async fn read_capped_body(mut response: reqwest::Response) -> Result<String> {
let mut body: Vec<u8> = Vec::new();
while let Some(chunk) = response.chunk().await? {
if body.len() + chunk.len() > MAX_RESPONSE_BYTES {
return Err(Error::ResponseTooLarge {
max: MAX_RESPONSE_BYTES,
});
}
body.extend_from_slice(&chunk);
}
Ok(String::from_utf8_lossy(&body).into_owned())
}