barter_integration/protocol/http/rest/
client.rs1use crate::{
2 error::SocketError,
3 metric::{Field, Metric, Tag},
4 protocol::http::{BuildStrategy, HttpParser, rest::RestRequest},
5};
6use bytes::Bytes;
7use chrono::Utc;
8use std::borrow::Cow;
9
10#[derive(Debug)]
16pub struct RestClient<'a, Strategy, Parser> {
17 pub http_client: reqwest::Client,
19
20 pub base_url: Cow<'a, str>,
22
23 pub strategy: Strategy,
31
32 pub parser: Parser,
35}
36
37impl<Strategy, Parser> RestClient<'_, Strategy, Parser>
38where
39 Strategy: BuildStrategy,
40 Parser: HttpParser,
41{
42 pub async fn execute<Request>(
44 &self,
45 request: Request,
46 ) -> Result<(Request::Response, Metric), Parser::OutputError>
47 where
48 Request: RestRequest,
49 {
50 let request = self.build(request)?;
52
53 let (status, payload, latency) = self.measured_execution::<Request>(request).await?;
55
56 self.parser
58 .parse::<Request::Response>(status, &payload)
59 .map(|response| (response, latency))
60 }
61
62 pub fn build<Request>(&self, request: Request) -> Result<reqwest::Request, SocketError>
64 where
65 Request: RestRequest,
66 {
67 let url = format!("{}{}", self.base_url, request.path());
69
70 let mut builder = self
72 .http_client
73 .request(Request::method(), url)
74 .timeout(Request::timeout());
75
76 if let Some(query_params) = request.query_params() {
78 builder = builder.query(query_params);
79 }
80
81 if let Some(body) = request.body() {
83 builder = builder.json(body);
84 }
85
86 self.strategy.build(request, builder)
88 }
89
90 pub async fn measured_execution<Request>(
94 &self,
95 request: reqwest::Request,
96 ) -> Result<(reqwest::StatusCode, Bytes, Metric), SocketError>
97 where
98 Request: RestRequest,
99 {
100 let mut latency = Metric {
102 name: "http_request_duration",
103 time: Utc::now().timestamp_millis() as u64,
104 tags: vec![
105 Tag::new("http_method", Request::method().as_str()),
106 Tag::new("base_url", self.base_url.as_ref()),
107 Tag::new("path", request.url().path()),
108 ],
109 fields: Vec::with_capacity(1),
110 };
111
112 let start = std::time::Instant::now();
114 let response = self.http_client.execute(request).await?;
115 let duration = start.elapsed().as_millis() as u64;
116
117 latency
119 .tags
120 .push(Tag::new("status_code", response.status().as_str()));
121 latency.fields.push(Field::new("duration", duration));
122
123 let status_code = response.status();
125 let payload = response.bytes().await?;
126
127 Ok((status_code, payload, latency))
128 }
129}
130
131impl<'a, Strategy, Parser> RestClient<'a, Strategy, Parser> {
132 pub fn new<Url: Into<Cow<'a, str>>>(base_url: Url, strategy: Strategy, parser: Parser) -> Self {
134 Self {
135 http_client: reqwest::Client::new(),
136 base_url: base_url.into(),
137 strategy,
138 parser,
139 }
140 }
141}