1use std::{
16 collections::HashMap,
17 path::PathBuf,
18};
19
20use crate::error::ValidationError;
21
22#[cfg(feature = "with_surf")]
23pub use surf_client::SurfClient;
24
25#[cfg(feature = "with_hyper")]
26pub use hyper_client::HyperClient;
27
28#[cfg(feature = "with_isahc")]
29pub use isahc_client::IsahcClient;
30
31#[async_trait::async_trait]
33pub trait HttpClient
34 where Self: Default + Clone + Sized,
35{
36 type Error;
38
39 fn get(&mut self, url: impl AsRef<str>)
41 -> Result<&mut Self, ValidationError>;
42 fn head(&mut self, url: impl AsRef<str>)
44 -> Result<&mut Self, ValidationError>;
45 fn post(&mut self, url: impl AsRef<str>)
47 -> Result<&mut Self, ValidationError>;
48
49 fn with_header<S: AsRef<str>>(&mut self, name: S, value: S)
53 -> Result<&mut Self, ValidationError>;
54 fn with_body(&mut self, data: impl Into<Vec<u8>>) -> &mut Self;
56 fn with_body_json(&mut self, body: serde_json::Value) -> &mut Self;
58 fn read_body_from_file(&mut self, path: impl Into<PathBuf>) -> &mut Self;
60
61 fn user_agent(&mut self, user_agent_string: impl Into<String>)
63 -> Result<&mut Self, ValidationError>;
64
65 async fn send(&mut self) -> Result<Vec<u8>, Self::Error>;
67
68 async fn send_keep_headers(&mut self)
71 -> Result<(Vec<u8>, HeaderMap), Self::Error>;
72}
73
74pub type HeaderMap = HashMap<String, String>;
76
77#[macro_export]
111macro_rules! default_user_agent {
112 ($client:literal) => {
113 format!("rust-b2-client/{}; {}",
114 option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
115 $client
116 )
117 };
118}
119pub use default_user_agent;
120
121#[cfg(feature = "with_surf")]
122mod surf_client {
123 use std::path::PathBuf;
124 use super::*;
125 use crate::error::Error;
126 use surf::{
127 http::Method,
128 Request,
129 Url,
130 };
131
132 #[derive(Debug, Clone)]
133 pub struct SurfClient {
134 client: surf::Client,
135 req: Option<Request>,
136 body: Option<Body>,
137 user_agent: String,
138 }
139
140 impl Default for SurfClient {
141 fn default() -> Self {
143 Self {
144 client: surf::Client::new(),
145 req: None,
146 body: None,
147 user_agent: default_user_agent!("surf"),
148 }
149 }
150 }
151
152 #[derive(Debug, Clone)]
154 enum Body {
155 Json(serde_json::Value),
156 Bytes(Vec<u8>),
162 File(PathBuf),
163 }
164
165 impl SurfClient {
166 pub fn with_client(mut self, client: surf::Client) -> Self {
168 self.client = client;
169 self
170 }
171
172 async fn send_impl(&mut self, keep_headers: bool)
173 -> Result<(Vec<u8>, Option<HeaderMap>), <Self as HttpClient>::Error> {
174 if let Some(mut req) = self.req.to_owned() {
175 if let Some(body) = &self.body {
176 match body {
177 Body::Json(val) => req.body_json(val)?,
178 Body::Bytes(data) => req.body_bytes(data),
179 Body::File(path) =>
180 req.set_body(surf::Body::from_file(path).await?),
181 }
182 }
183
184 req.insert_header("User-Agent", &self.user_agent);
185
186 let mut res = self.client.send(req).await?;
187 let body = res.body_bytes().await?;
188
189 let headers = if keep_headers {
190 let headers: &surf::http::Headers = res.as_ref();
191 let mut ret = HeaderMap::new();
192
193 for (k, v) in headers.iter() {
194 ret.insert(k.to_string(), v.to_string());
195 }
196
197 Some(ret)
198 } else {
199 None
200 };
201
202 self.req = None;
203
204 Ok((body, headers))
205 } else {
206 Err(Error::NoRequest)
207 }
208 }
209 }
210
211 macro_rules! gen_method_func {
212 ($func:ident, $method:ident) => {
213 fn $func(&mut self, url: impl AsRef<str>)
214 -> Result<&mut Self, ValidationError> {
215 let url = Url::parse(url.as_ref())?;
216 self.req = Some(Request::new(Method::$method, url));
217
218 Ok(self)
219 }
220 }
221 }
222
223 #[async_trait::async_trait]
224 impl HttpClient for SurfClient {
225 type Error = Error<surf::Error>;
227
228 gen_method_func!(get, Get);
229 gen_method_func!(head, Head);
230 gen_method_func!(post, Post);
231
232 fn with_header<S: AsRef<str>>(&mut self, name: S, value: S)
233 -> Result<&mut Self, ValidationError> {
234 use std::str::FromStr as _;
235 use http_types::headers::{HeaderName, HeaderValue};
236
237 if let Some(req) = &mut self.req {
238 let name = HeaderName::from_str(name.as_ref())?;
239 let value = HeaderValue::from_str(value.as_ref())?;
240
241 req.insert_header(name, value);
242 }
243
244 Ok(self)
245 }
246
247 fn with_body(&mut self, data: impl Into<Vec<u8>>) -> &mut Self
248 {
249 self.body = Some(Body::Bytes(data.into()));
250 self
251 }
252
253 fn with_body_json(&mut self, body: serde_json::Value) -> &mut Self {
254 self.body = Some(Body::Json(body));
257 self
258 }
259
260 fn read_body_from_file(&mut self, path: impl Into<PathBuf>)
261 -> &mut Self {
262 self.body = Some(Body::File(path.into()));
263 self
264 }
265
266 fn user_agent(&mut self, user_agent_string: impl Into<String>)
274 -> Result<&mut Self, ValidationError> {
275 let user_agent = user_agent_string.into();
276
277 if user_agent.is_empty() {
278 Err(ValidationError::MissingData(
279 "User-Agent is required".into()
280 ))
281 } else {
282 self.user_agent = user_agent;
283 Ok(self)
284 }
285 }
286
287 async fn send(&mut self) -> Result<Vec<u8>, Self::Error> {
294 self.send_impl(false).await.map(|v| v.0)
295 }
296
297 async fn send_keep_headers(&mut self)
305 -> Result<(Vec<u8>, HeaderMap), Self::Error> {
306 self.send_impl(true).await.map(|(r, m)| (r, m.unwrap()))
307 }
308 }
309}
310
311#[cfg(feature = "with_hyper")]
312mod hyper_client {
313 use std::path::PathBuf;
314 use super::*;
315 use crate::error::Error;
316 use hyper::{
317 client::connect::HttpConnector,
318 header::{HeaderName, HeaderValue},
319 Method
320 };
321 use hyper_tls::HttpsConnector;
322 use url::Url;
323
324
325 #[derive(Debug, Clone)]
326 pub struct HyperClient {
327 client: hyper::Client<HttpsConnector<HttpConnector>>,
328 method: Option<Method>,
329 url: String,
330 headers: Vec<(HeaderName, HeaderValue)>,
331 body: Option<Body>,
332 user_agent: String,
333 }
334
335 impl Default for HyperClient {
336 fn default() -> Self {
338 let https = HttpsConnector::new();
339 let client = hyper::Client::builder()
340 .build::<_, hyper::Body>(https);
341
342 Self {
343 client,
344 method: None,
345 url: String::default(),
346 headers: vec![],
347 body: None,
348 user_agent: default_user_agent!("hyper"),
349 }
350 }
351 }
352
353 #[derive(Debug, Clone)]
354 enum Body {
355 Json(serde_json::Value),
356 Bytes(hyper::body::Bytes),
357 File(PathBuf),
358 }
359
360 macro_rules! gen_method_func {
361 ($func:ident, $method: ident) => {
362 fn $func(&mut self, url: impl AsRef<str>)
363 -> Result<&mut Self, ValidationError> {
364 let _url = Url::parse(url.as_ref())?;
365
366 self.method = Some(Method::$method);
367 self.url = String::from(url.as_ref());
368 Ok(self)
369 }
370 }
371 }
372
373 impl HyperClient {
374 pub fn with_client(
375 mut self,
376 client: hyper::Client<HttpsConnector<HttpConnector>>
377 ) -> Self {
378 self.client = client;
379 self
380 }
381
382 pub fn with_body_bytes(&mut self, bytes: hyper::body::Bytes)
388 -> &mut Self {
389 self.body = Some(Body::Bytes(bytes));
390 self
391 }
392
393 async fn send_impl(&mut self, keep_headers: bool)
394 -> Result<(Vec<u8>, Option<HeaderMap>), <Self as HttpClient>::Error> {
395 if self.method.is_none() {
396 return Err(Error::NoRequest);
397 }
398
399 let mut req = hyper::Request::builder()
400 .method(self.method.as_ref().unwrap())
401 .uri(&self.url);
402
403 for (name, value) in &self.headers {
404 req = req.header(name, value);
405 }
406
407 req = req.header("User-Agent", &self.user_agent);
408
409 let body = match &self.body {
410 Some(body) => match body {
411 Body::Json(val) => hyper::Body::from(val.to_string()),
412 Body::Bytes(data) => hyper::Body::from(data.clone()),
413 Body::File(path) => {
414 use tokio::{
415 fs::File,
416 io::AsyncReadExt as _,
417 };
418
419 let mut file = File::open(path).await?;
422 let mut buf = vec![];
423 file.read_to_end(&mut buf).await?;
424
425 hyper::Body::from(buf)
426 },
427 },
428 None => hyper::Body::empty(),
429 };
430
431 let req = req.body(body).expect(concat!(
432 "Invalid request. Please file an issue on b2-client for ",
433 "improper validation"
434 ));
435
436 let (mut parts, body) = self.client.request(req).await?
437 .into_parts();
438
439 let body = hyper::body::to_bytes(body).await?.to_vec();
440
441 let headers = if keep_headers {
442 let mut headers = HeaderMap::new();
443
444 headers.extend(
445 parts.headers.drain()
446 .filter(|(k, _)| k.is_some())
447 .map(|(k, v)|
448 (
452 k.unwrap().to_string(),
453 v.to_str().unwrap().to_owned()
454 )
455 )
456 );
457
458 Some(headers)
459 } else {
460 None
461 };
462
463 self.method = None;
464 self.url = String::default();
465 self.headers.clear();
466 self.body = None;
467
468 Ok((body, headers))
469 }
470 }
471
472 #[async_trait::async_trait]
473 impl HttpClient for HyperClient {
474 type Error = Error<hyper::Error>;
475
476 gen_method_func!(get, GET);
477 gen_method_func!(head, HEAD);
478 gen_method_func!(post, POST);
479
480 fn with_header<S: AsRef<str>>(&mut self, name: S, value: S)
482 -> Result<&mut Self, ValidationError> {
483 use std::str::FromStr as _;
484 use hyper::header::{HeaderName, HeaderValue};
485
486 let name = HeaderName::from_str(name.as_ref())?;
487 let value = HeaderValue::from_str(value.as_ref())?;
488
489 self.headers.push((name, value));
490 Ok(self)
491 }
492
493 fn with_body<'a>(&mut self, data: impl Into<Vec<u8>>) -> &mut Self {
494 self.body = Some(
495 Body::Bytes(hyper::body::Bytes::from(data.into()))
496 );
497
498 self
499 }
500
501 fn with_body_json(&mut self, body: serde_json::Value) -> &mut Self {
502 self.body = Some(Body::Json(body));
503 self
504 }
505
506 fn read_body_from_file(&mut self, path: impl Into<PathBuf>)
507 -> &mut Self {
508 self.body = Some(Body::File(path.into()));
509 self
510 }
511
512 fn user_agent(&mut self, user_agent_string: impl Into<String>)
513 -> Result<&mut Self, ValidationError> {
514 let user_agent = user_agent_string.into();
515
516 if user_agent.is_empty() {
517 Err(ValidationError::MissingData(
518 "User-Agent is required".into()
519 ))
520 } else {
521 self.user_agent = user_agent;
522 Ok(self)
523 }
524 }
525
526 async fn send(&mut self) -> Result<Vec<u8>, Self::Error> {
533 self.send_impl(false).await.map(|v| v.0)
534 }
535
536 async fn send_keep_headers(&mut self)
537 -> Result<(Vec<u8>, HeaderMap), Self::Error> {
538 self.send_impl(true).await.map(|(r, m)| (r, m.unwrap()))
539 }
540 }
541}
542
543#[cfg(feature = "with_isahc")]
544mod isahc_client {
545 use super::*;
546 use crate::error::Error;
547 use isahc::http::{
548 header::{HeaderName, HeaderValue},
549 method::Method,
550 request::Builder as RequestBuilder,
551 };
552
553 #[derive(Debug)]
554 enum Body {
555 Bytes(Vec<u8>),
556 Json(serde_json::Value),
557 File(PathBuf),
558 }
559
560 #[derive(Debug)]
561 pub struct IsahcClient {
562 client: isahc::HttpClient,
563 req: Option<RequestBuilder>,
564 user_agent: String,
565 body: Option<Body>,
566 headers: Vec<(HeaderName, HeaderValue)>,
567 }
568
569 impl Default for IsahcClient {
570 fn default() -> Self {
572 Self {
573 client: isahc::HttpClient::new().unwrap(),
574 req: None,
575 user_agent: default_user_agent!("isahc"),
576 body: None,
577 headers: Vec::new(),
578 }
579 }
580 }
581
582 impl Clone for IsahcClient {
583 fn clone(&self) -> Self {
588 Self {
589 client: self.client.clone(),
590 req: None,
591 user_agent: self.user_agent.clone(),
592 body: None,
593 headers: Vec::new(),
594 }
595 }
596 }
597
598 impl IsahcClient {
599 async fn send_impl(&mut self, keep_headers: bool)
600 -> Result<(
601 Vec<u8>, Option<HeaderMap>),
602 <Self as super::HttpClient>::Error
603 > {
604 use futures_lite::AsyncReadExt as _;
605
606 if let Some(mut req) = self.req.take() {
607 for (name, value) in &self.headers {
608 req = req.header(name, value);
609 }
610
611 req = req.header("User-Agent", &self.user_agent);
612
613 let body = if let Some(body) = self.body.take() {
614 match body {
615 Body::Bytes(bytes) => Some(bytes),
616 Body::Json(json) => Some(serde_json::to_vec(&json)?),
617 Body::File(path) => {
618 use std::{fs::File, io::Read as _};
620
621 let mut file = File::open(path)?;
622 let mut buf: Vec<u8> = vec![];
623 file.read_to_end(&mut buf)?;
624
625 Some(buf)
626 },
627 }
628 } else {
629 None
630 };
631
632 let (mut parts, body) = match body {
633 Some(body) => self.client.send_async(req.body(body)?)
634 .await?.into_parts(),
635 None => self.client.send_async(
636 req.body(isahc::AsyncBody::empty())?
637 ).await?.into_parts(),
638 };
639
640 let headers = if keep_headers {
641 let mut headers = HeaderMap::new();
642
643 headers.extend(
644 parts.headers.drain()
645 .filter(|(k, _)| k.is_some())
646 .map(|(k, v)|
647 (
651 k.unwrap().to_string(),
652 v.to_str().unwrap().to_owned()
653 )
654 )
655 );
656
657 Some(headers)
658 } else {
659 None
660 };
661
662 let mut buf = Vec::new();
663 body.bytes().read_to_end(&mut buf).await?;
664
665 self.headers.clear();
669
670 Ok((buf, headers))
671 } else {
672 Err(Error::NoRequest)
673 }
674 }
675 }
676
677 macro_rules! gen_method_func {
678 ($func:ident, $method:ident) => {
679 fn $func(&mut self, url: impl AsRef<str>)
680 -> Result<&mut Self, ValidationError> {
681 self.req = Some(
682 RequestBuilder::new()
683 .method(Method::$method)
684 .uri(url.as_ref())
685 );
686
687 Ok(self)
688 }
689 };
690 }
691
692 #[async_trait::async_trait]
693 impl HttpClient for IsahcClient
694 where Self: Clone + Sized,
695 {
696 type Error = Error<isahc::Error>;
697
698 gen_method_func!(get, GET);
699 gen_method_func!(head, HEAD);
700 gen_method_func!(post, POST);
701
702 fn with_header<S: AsRef<str>>(&mut self, name: S, value: S)
703 -> Result<&mut Self, ValidationError> {
704 use std::str::FromStr as _;
705
706 let name = HeaderName::from_str(name.as_ref())?;
707 let value = HeaderValue::from_str(value.as_ref())?;
708
709 self.headers.push((name, value));
710 Ok(self)
711 }
712
713 fn with_body(&mut self, data: impl Into<Vec<u8>>) -> &mut Self {
714 self.body = Some(Body::Bytes(data.into()));
715 self
716 }
717
718 fn with_body_json(&mut self, body: serde_json::Value) -> &mut Self {
719 self.body = Some(Body::Json(body));
720 self
721 }
722
723 fn read_body_from_file(&mut self, path: impl Into<PathBuf>)
724 -> &mut Self {
725 self.body = Some(Body::File(path.into()));
726 self
727 }
728
729 fn user_agent(&mut self, user_agent_string: impl Into<String>)
730 -> Result<&mut Self, ValidationError> {
731 let user_agent = user_agent_string.into();
732
733 if ! user_agent.is_empty() {
734 self.user_agent = user_agent;
735 Ok(self)
736 } else {
737 Err(ValidationError::MissingData(
738 "User-Agent is required".into()
739 ))
740 }
741 }
742
743 async fn send(&mut self) -> Result<Vec<u8>, Self::Error> {
744 self.send_impl(false).await.map(|v| v.0)
745 }
746
747 async fn send_keep_headers(&mut self)
748 -> Result<(Vec<u8>, HeaderMap), Self::Error> {
749 self.send_impl(true).await.map(|v| (v.0, v.1.unwrap()))
750 }
751 }
752}