Skip to main content

gpui/
http_client.rs

1use futures::future::BoxFuture;
2use http::StatusCode;
3
4/// A simple HTTP response.
5pub struct HttpResponse {
6    /// The HTTP status code.
7    pub status: StatusCode,
8    /// The response body bytes.
9    pub body: Vec<u8>,
10}
11
12/// A trait for making HTTP requests.
13pub trait HttpClient: 'static + Send + Sync {
14    /// Perform a GET request and return the full response.
15    fn get(
16        &self,
17        url: &str,
18        follow_redirects: bool,
19    ) -> BoxFuture<'static, anyhow::Result<HttpResponse>>;
20}
21
22/// An HTTP client that always returns an error.
23pub 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
35/// An HTTP client that blocks all requests.
36pub struct BlockedHttpClient;
37
38impl BlockedHttpClient {
39    /// Create a new `BlockedHttpClient`.
40    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/// A fake HTTP client for testing.
68#[cfg(any(test, feature = "test-support"))]
69pub struct FakeHttpClient {
70    status: StatusCode,
71}
72
73#[cfg(any(test, feature = "test-support"))]
74impl FakeHttpClient {
75    /// Create a fake client that returns 404 responses.
76    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    /// Create a fake client that returns 200 responses.
83    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}