crux_http/
lib.rs

1#![deny(clippy::pedantic)]
2//! A HTTP client for use with Crux
3//!
4//! `crux_http` allows Crux apps to make HTTP requests by asking the Shell to perform them.
5//!
6//! This is still work in progress and large parts of HTTP are not yet supported.
7// #![warn(missing_docs)]
8
9use crux_core::capability::CapabilityContext;
10use http_types::Method;
11use url::Url;
12
13mod config;
14mod error;
15mod expect;
16mod request;
17mod request_builder;
18mod response;
19
20pub mod client;
21pub mod command;
22pub mod middleware;
23pub mod protocol;
24pub mod testing;
25
26pub use http_types as http;
27
28pub use self::{
29    config::Config,
30    error::HttpError,
31    request::Request,
32    request_builder::RequestBuilder,
33    response::{Response, ResponseAsync},
34};
35
36use client::Client;
37
38pub type Result<T> = std::result::Result<T, HttpError>;
39
40/// The Http capability API.
41pub struct Http<Ev> {
42    context: CapabilityContext<protocol::HttpRequest, Ev>,
43    client: Client,
44}
45
46impl<Ev> crux_core::Capability<Ev> for Http<Ev> {
47    type Operation = protocol::HttpRequest;
48
49    type MappedSelf<MappedEv> = Http<MappedEv>;
50
51    fn map_event<F, NewEv>(&self, f: F) -> Self::MappedSelf<NewEv>
52    where
53        F: Fn(NewEv) -> Ev + Send + Sync + 'static,
54        Ev: 'static,
55        NewEv: 'static + Send,
56    {
57        Http::new(self.context.map_event(f))
58    }
59}
60
61impl<Ev> Clone for Http<Ev> {
62    fn clone(&self) -> Self {
63        Self {
64            context: self.context.clone(),
65            client: self.client.clone(),
66        }
67    }
68}
69
70impl<Ev> Http<Ev>
71where
72    Ev: 'static,
73{
74    #[must_use]
75    pub fn new(context: CapabilityContext<protocol::HttpRequest, Ev>) -> Self {
76        Self {
77            client: Client::new(context.clone()),
78            context,
79        }
80    }
81
82    /// Instruct the Shell to perform a HTTP GET request to the provided `url`.
83    ///
84    /// The request can be configured via associated functions on `RequestBuilder`
85    /// and then sent with `RequestBuilder::send`
86    ///
87    /// When finished, the response will be wrapped in an event and dispatched to
88    /// the app's `update` function.
89    ///
90    /// # Panics
91    ///
92    /// This will panic if a malformed URL is passed.
93    ///
94    /// # Examples
95    ///
96    /// ```no_run
97    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
98    /// # struct Capabilities { http: crux_http::Http<Event> }
99    /// # fn update(caps: &Capabilities) {
100    /// caps.http.get("https://httpbin.org/get").send(Event::ReceiveResponse)
101    /// # }
102    /// ```
103    pub fn get(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
104        RequestBuilder::new(Method::Get, url.as_ref().parse().unwrap(), self.clone())
105    }
106
107    /// Instruct the Shell to perform a HTTP HEAD request to the provided `url`.
108    ///
109    /// The request can be configured via associated functions on `RequestBuilder`
110    /// and then sent with `RequestBuilder::send`
111    ///
112    /// When finished, the response will be wrapped in an event and dispatched to
113    /// the app's `update` function.
114    ///
115    /// # Panics
116    ///
117    /// This will panic if a malformed URL is passed.
118    ///
119    /// # Examples
120    ///
121    /// ```no_run
122    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
123    /// # struct Capabilities { http: crux_http::Http<Event> }
124    /// # fn update(caps: &Capabilities) {
125    /// caps.http.head("https://httpbin.org/get").send(Event::ReceiveResponse)
126    /// # }
127    /// ```
128    pub fn head(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
129        RequestBuilder::new(Method::Head, url.as_ref().parse().unwrap(), self.clone())
130    }
131
132    /// Instruct the Shell to perform a HTTP POST request to the provided `url`.
133    ///
134    /// The request can be configured via associated functions on `RequestBuilder`
135    /// and then sent with `RequestBuilder::send`
136    ///
137    /// When finished, the response will be wrapped in an event and dispatched to
138    /// the app's `update` function.
139    ///
140    /// # Panics
141    ///
142    /// This will panic if a malformed URL is passed.
143    ///
144    /// # Examples
145    ///
146    /// ```no_run
147    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
148    /// # struct Capabilities { http: crux_http::Http<Event> }
149    /// # fn update(caps: &Capabilities) {
150    /// caps.http.post("https://httpbin.org/post").send(Event::ReceiveResponse)
151    /// # }
152    /// ```
153    pub fn post(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
154        RequestBuilder::new(Method::Post, url.as_ref().parse().unwrap(), self.clone())
155    }
156
157    /// Instruct the Shell to perform a HTTP PUT request to the provided `url`.
158    ///
159    /// The request can be configured via associated functions on `RequestBuilder`
160    /// and then sent with `RequestBuilder::send`
161    ///
162    /// When finished, the response will be wrapped in an event and dispatched to
163    /// the app's `update` function.
164    ///
165    /// # Panics
166    ///
167    /// This will panic if a malformed URL is passed.
168    ///
169    /// # Examples
170    ///
171    /// ```no_run
172    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
173    /// # struct Capabilities { http: crux_http::Http<Event> }
174    /// # fn update(caps: &Capabilities) {
175    /// caps.http.put("https://httpbin.org/post").send(Event::ReceiveResponse)
176    /// # }
177    /// ```
178    pub fn put(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
179        RequestBuilder::new(Method::Put, url.as_ref().parse().unwrap(), self.clone())
180    }
181
182    /// Instruct the Shell to perform a HTTP DELETE request to the provided `url`.
183    ///
184    /// The request can be configured via associated functions on `RequestBuilder`
185    /// and then sent with `RequestBuilder::send`
186    ///
187    /// When finished, the response will be wrapped in an event and dispatched to
188    /// the app's `update` function.
189    ///
190    /// # Panics
191    ///
192    /// This will panic if a malformed URL is passed.
193    ///
194    /// # Examples
195    ///
196    /// ```no_run
197    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
198    /// # struct Capabilities { http: crux_http::Http<Event> }
199    /// # fn update(caps: &Capabilities) {
200    /// caps.http.delete("https://httpbin.org/post").send(Event::ReceiveResponse)
201    /// # }
202    /// ```
203    pub fn delete(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
204        RequestBuilder::new(Method::Delete, url.as_ref().parse().unwrap(), self.clone())
205    }
206
207    /// Instruct the Shell to perform a HTTP CONNECT request to the provided `url`.
208    ///
209    /// The request can be configured via associated functions on `RequestBuilder`
210    /// and then sent with `RequestBuilder::send`
211    ///
212    /// When finished, the response will be wrapped in an event and dispatched to
213    /// the app's `update` function.
214    ///
215    /// # Panics
216    ///
217    /// This will panic if a malformed URL is passed.
218    ///
219    /// # Examples
220    ///
221    /// ```no_run
222    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
223    /// # struct Capabilities { http: crux_http::Http<Event> }
224    /// # fn update(caps: &Capabilities) {
225    /// caps.http.connect("https://httpbin.org/get").send(Event::ReceiveResponse)
226    /// # }
227    /// ```
228    pub fn connect(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
229        RequestBuilder::new(Method::Connect, url.as_ref().parse().unwrap(), self.clone())
230    }
231
232    /// Instruct the Shell to perform a HTTP OPTIONS request to the provided `url`.
233    ///
234    /// The request can be configured via associated functions on `RequestBuilder`
235    /// and then sent with `RequestBuilder::send`
236    ///
237    /// When finished, the response will be wrapped in an event and dispatched to
238    /// the app's `update` function.
239    ///
240    /// # Panics
241    ///
242    /// This will panic if a malformed URL is passed.
243    ///
244    /// # Examples
245    ///
246    /// ```no_run
247    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
248    /// # struct Capabilities { http: crux_http::Http<Event> }
249    /// # fn update(caps: &Capabilities) {
250    /// caps.http.options("https://httpbin.org/get").send(Event::ReceiveResponse)
251    /// # }
252    /// ```
253    pub fn options(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
254        RequestBuilder::new(Method::Options, url.as_ref().parse().unwrap(), self.clone())
255    }
256
257    /// Instruct the Shell to perform a HTTP TRACE request to the provided `url`.
258    ///
259    /// The request can be configured via associated functions on `RequestBuilder`
260    /// and then sent with `RequestBuilder::send`
261    ///
262    /// When finished, the response will be wrapped in an event and dispatched to
263    /// the app's `update` function.
264    ///
265    /// # Panics
266    ///
267    /// This will panic if a malformed URL is passed.
268    ///
269    /// # Examples
270    ///
271    /// ```no_run
272    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
273    /// # struct Capabilities { http: crux_http::Http<Event> }
274    /// # fn update(caps: &Capabilities) {
275    /// caps.http.trace("https://httpbin.org/get").send(Event::ReceiveResponse)
276    /// # }
277    /// ```
278    pub fn trace(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
279        RequestBuilder::new(Method::Trace, url.as_ref().parse().unwrap(), self.clone())
280    }
281
282    /// Instruct the Shell to perform a HTTP PATCH request to the provided `url`.
283    ///
284    /// The request can be configured via associated functions on `RequestBuilder`
285    /// and then sent with `RequestBuilder::send`
286    ///
287    /// When finished, the response will be wrapped in an event and dispatched to
288    /// the app's `update` function.
289    ///
290    /// # Panics
291    ///
292    /// This will panic if a malformed URL is passed.
293    pub fn patch(&self, url: impl AsRef<str>) -> RequestBuilder<Ev> {
294        RequestBuilder::new(Method::Patch, url.as_ref().parse().unwrap(), self.clone())
295    }
296
297    /// Instruct the Shell to perform an HTTP request with the provided `method` and `url`.
298    ///
299    /// The request can be configured via associated functions on `RequestBuilder`
300    /// and then sent with `RequestBuilder::send`
301    ///
302    /// When finished, the response will be wrapped in an event and dispatched to
303    /// the app's `update` function.
304    pub fn request(&self, method: http_types::Method, url: Url) -> RequestBuilder<Ev> {
305        RequestBuilder::new(method, url, self.clone())
306    }
307}