crux_http/
lib.rs

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