1use futures::future::BoxFuture;
2use http::StatusCode;
3
4pub struct HttpResponse {
6 pub status: StatusCode,
8 pub body: Vec<u8>,
10}
11
12pub trait HttpClient: 'static + Send + Sync {
14 fn get(
16 &self,
17 url: &str,
18 follow_redirects: bool,
19 ) -> BoxFuture<'static, anyhow::Result<HttpResponse>>;
20}
21
22pub struct NullHttpClient;
24
25impl HttpClient for NullHttpClient {
26 fn get(
27 &self,
28 _url: &str,
29 _follow_redirects: bool,
30 ) -> BoxFuture<'static, anyhow::Result<HttpResponse>> {
31 Box::pin(async { anyhow::bail!("No HttpClient available") })
32 }
33}
34
35pub struct BlockedHttpClient;
37
38impl BlockedHttpClient {
39 pub fn new() -> Self {
41 Self
42 }
43}
44
45impl Default for BlockedHttpClient {
46 fn default() -> Self {
47 Self
48 }
49}
50
51impl HttpClient for BlockedHttpClient {
52 fn get(
53 &self,
54 _url: &str,
55 _follow_redirects: bool,
56 ) -> BoxFuture<'static, anyhow::Result<HttpResponse>> {
57 Box::pin(async {
58 Err(std::io::Error::new(
59 std::io::ErrorKind::PermissionDenied,
60 "BlockedHttpClient disallowed request",
61 )
62 .into())
63 })
64 }
65}
66
67#[cfg(any(test, feature = "test-support"))]
69pub struct FakeHttpClient {
70 status: StatusCode,
71}
72
73#[cfg(any(test, feature = "test-support"))]
74impl FakeHttpClient {
75 pub fn with_404_response() -> std::sync::Arc<dyn HttpClient> {
77 std::sync::Arc::new(Self {
78 status: StatusCode::NOT_FOUND,
79 })
80 }
81
82 pub fn with_200_response() -> std::sync::Arc<dyn HttpClient> {
84 std::sync::Arc::new(Self {
85 status: StatusCode::OK,
86 })
87 }
88}
89
90#[cfg(any(test, feature = "test-support"))]
91impl HttpClient for FakeHttpClient {
92 fn get(
93 &self,
94 _url: &str,
95 _follow_redirects: bool,
96 ) -> BoxFuture<'static, anyhow::Result<HttpResponse>> {
97 let status = self.status;
98 Box::pin(async move {
99 Ok(HttpResponse {
100 status,
101 body: Vec::new(),
102 })
103 })
104 }
105}