Skip to main content

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
9mod config;
10mod error;
11mod expect;
12mod request;
13mod request_builder;
14mod response;
15
16pub mod client;
17pub mod command;
18pub mod middleware;
19pub mod protocol;
20pub mod testing;
21
22use std::marker::PhantomData;
23
24pub use http_types as http;
25
26pub use http_types::Method;
27pub use url::Url;
28
29pub use crate::protocol::{HttpRequest, HttpResponse};
30
31pub use self::{config::Config, error::HttpError, request::Request};
32pub use response::Response;
33
34pub use request_builder::RequestBuilder;
35pub use response::ResponseAsync;
36
37use client::Client;
38
39pub type Result<T> = std::result::Result<T, HttpError>;
40
41pub struct Http<Effect, Event> {
42    effect: PhantomData<Effect>,
43    event: PhantomData<Event>,
44}
45
46impl<Effect, Event> Http<Effect, Event>
47where
48    Effect: Send + From<crux_core::Request<HttpRequest>> + 'static,
49    Event: Send + 'static,
50{
51    /// Instruct the Shell to perform a HTTP GET request to the provided `url`.
52    ///
53    /// The request can be configured via associated functions on the returned
54    /// [`RequestBuilder`] and then converted to a [`Command`]
55    /// with [`RequestBuilder::build`].
56    ///
57    /// # Panics
58    ///
59    /// This will panic if a malformed URL is passed.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// # use crux_core::macros::effect;
65    /// # use crux_http::HttpRequest;
66    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<String>>) }
67    /// # #[effect]
68    /// # #[allow(unused)]
69    /// # enum Effect { Http(HttpRequest) }
70    /// # type Http = crux_http::command::Http<Effect, Event>;
71    /// Http::get("https://httpbin.org/get")
72    ///     .expect_string()
73    ///     .build()
74    ///     .then_send(Event::ReceiveResponse);
75    /// ```
76    pub fn get(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
77        command::RequestBuilder::new(Method::Get, url.as_ref().parse().unwrap())
78    }
79
80    /// Instruct the Shell to perform a HTTP HEAD request to the provided `url`.
81    ///
82    /// The request can be configured via associated functions on the returned
83    /// [`RequestBuilder`] and then converted to a [`Command`]
84    /// with [`RequestBuilder::build`].
85    ///
86    /// # Panics
87    ///
88    /// This will panic if a malformed URL is passed.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// # use crux_core::macros::effect;
94    /// # use crux_http::HttpRequest;
95    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
96    /// # #[effect]
97    /// # #[allow(unused)]
98    /// # enum Effect { Http(HttpRequest) }
99    /// # type Http = crux_http::command::Http<Effect, Event>;
100    /// Http::head("https://httpbin.org/get")
101    ///     .build()
102    ///     .then_send(Event::ReceiveResponse);
103    pub fn head(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
104        command::RequestBuilder::new(Method::Head, url.as_ref().parse().unwrap())
105    }
106
107    /// Instruct the Shell to perform a HTTP POST request to the provided `url`.
108    ///
109    /// The request can be configured via associated functions on the returned
110    /// [`RequestBuilder`] and then converted to a [`Command`]
111    /// with [`RequestBuilder::build`].
112    ///
113    /// # Panics
114    ///
115    /// This will panic if a malformed URL is passed.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// # use crux_core::macros::effect;
121    /// # use crux_http::HttpRequest;
122    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
123    /// # #[effect]
124    /// # #[allow(unused)]
125    /// # enum Effect { Http(HttpRequest) }
126    /// # type Http = crux_http::command::Http<Effect, Event>;
127    /// Http::post("https://httpbin.org/post")
128    ///     .body_bytes(b"hello_world".to_owned())
129    ///     .build()
130    ///     .then_send(Event::ReceiveResponse);
131    pub fn post(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
132        command::RequestBuilder::new(Method::Post, url.as_ref().parse().unwrap())
133    }
134
135    /// Instruct the Shell to perform a HTTP PUT request to the provided `url`.
136    ///
137    /// The request can be configured via associated functions on the returned
138    /// [`RequestBuilder`] and then converted to a [`Command`]
139    /// with [`RequestBuilder::build`].
140    ///
141    /// # Panics
142    ///
143    /// This will panic if a malformed URL is passed.
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// # use crux_core::macros::effect;
149    /// # use crux_http::HttpRequest;
150    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
151    /// # #[effect]
152    /// # #[allow(unused)]
153    /// # enum Effect { Http(HttpRequest) }
154    /// # type Http = crux_http::command::Http<Effect, Event>;
155    /// Http::put("https://httpbin.org/put")
156    ///     .body_string("hello_world".to_string())
157    ///     .build()
158    ///     .then_send(Event::ReceiveResponse);
159    pub fn put(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
160        command::RequestBuilder::new(Method::Put, url.as_ref().parse().unwrap())
161    }
162
163    /// Instruct the Shell to perform a HTTP DELETE request to the provided `url`.
164    ///
165    /// The request can be configured via associated functions on the returned
166    /// [`RequestBuilder`] and then converted to a [`Command`]
167    /// with [`RequestBuilder::build`].
168    ///
169    /// # Panics
170    ///
171    /// This will panic if a malformed URL is passed.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// # use crux_core::macros::effect;
177    /// # use crux_http::HttpRequest;
178    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
179    /// # #[effect]
180    /// # #[allow(unused)]
181    /// # enum Effect { Http(HttpRequest) }
182    /// # type Http = crux_http::command::Http<Effect, Event>;
183    /// Http::delete("https://httpbin.org/delete")
184    ///     .build()
185    ///     .then_send(Event::ReceiveResponse);
186    pub fn delete(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
187        command::RequestBuilder::new(Method::Delete, url.as_ref().parse().unwrap())
188    }
189
190    /// Instruct the Shell to perform a HTTP PATCH request to the provided `url`.
191    ///
192    /// The request can be configured via associated functions on the returned
193    /// [`RequestBuilder`] and then converted to a [`Command`]
194    /// with [`RequestBuilder::build`].
195    ///
196    /// # Panics
197    ///
198    /// This will panic if a malformed URL is passed.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// # use crux_core::macros::effect;
204    /// # use crux_http::HttpRequest;
205    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
206    /// # #[effect]
207    /// # #[allow(unused)]
208    /// # enum Effect { Http(HttpRequest) }
209    /// # type Http = crux_http::command::Http<Effect, Event>;
210    /// Http::patch("https://httpbin.org/patch")
211    ///     .body_form(&[("name", "Alice")]).unwrap()
212    ///     .build()
213    ///     .then_send(Event::ReceiveResponse);
214    pub fn patch(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
215        command::RequestBuilder::new(Method::Patch, url.as_ref().parse().unwrap())
216    }
217
218    /// Instruct the Shell to perform a HTTP OPTIONS request to the provided `url`.
219    ///
220    /// The request can be configured via associated functions on the returned
221    /// [`RequestBuilder`] and then converted to a [`Command`]
222    /// with [`RequestBuilder::build`].
223    ///
224    /// # Panics
225    ///
226    /// This will panic if a malformed URL is passed.
227    ///
228    /// # Examples
229    ///
230    /// ```
231    /// # use crux_core::macros::effect;
232    /// # use crux_http::HttpRequest;
233    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
234    /// # #[effect]
235    /// # #[allow(unused)]
236    /// # enum Effect { Http(HttpRequest) }
237    /// # type Http = crux_http::command::Http<Effect, Event>;
238    /// Http::options("https://httpbin.org/get")
239    ///     .build()
240    ///     .then_send(Event::ReceiveResponse);
241    pub fn options(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
242        command::RequestBuilder::new(Method::Options, url.as_ref().parse().unwrap())
243    }
244
245    /// Instruct the Shell to perform a HTTP TRACE request to the provided `url`.
246    ///
247    /// The request can be configured via associated functions on the returned
248    /// [`RequestBuilder`] and then converted to a [`Command`]
249    /// with [`RequestBuilder::build`].
250    ///
251    /// # Panics
252    ///
253    /// This will panic if a malformed URL is passed.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// # use crux_core::macros::effect;
259    /// # use crux_http::HttpRequest;
260    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
261    /// # #[effect]
262    /// # #[allow(unused)]
263    /// # enum Effect { Http(HttpRequest) }
264    /// # type Http = crux_http::command::Http<Effect, Event>;
265    /// Http::trace("https://httpbin.org/get")
266    ///     .build()
267    ///     .then_send(Event::ReceiveResponse);
268    pub fn trace(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
269        command::RequestBuilder::new(Method::Trace, url.as_ref().parse().unwrap())
270    }
271
272    /// Instruct the Shell to perform a HTTP CONNECT request to the provided `url`.
273    ///
274    /// The request can be configured via associated functions on the returned
275    /// [`RequestBuilder`] and then converted to a [`Command`]
276    /// with [`RequestBuilder::build`].
277    ///
278    /// # Panics
279    ///
280    /// This will panic if a malformed URL is passed.
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// # use crux_core::macros::effect;
286    /// # use crux_http::HttpRequest;
287    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
288    /// # #[effect]
289    /// # #[allow(unused)]
290    /// # enum Effect { Http(HttpRequest) }
291    /// # type Http = crux_http::command::Http<Effect, Event>;
292    /// Http::connect("https://httpbin.org/get")
293    ///     .build()
294    ///     .then_send(Event::ReceiveResponse);
295    pub fn connect(url: impl AsRef<str>) -> command::RequestBuilder<Effect, Event> {
296        command::RequestBuilder::new(Method::Connect, url.as_ref().parse().unwrap())
297    }
298
299    /// Instruct the Shell to perform an HTTP request to the provided `url`.
300    ///
301    /// The request can be configured via associated functions on the returned
302    /// [`RequestBuilder`] and then converted to a [`Command`]
303    /// with [`RequestBuilder::build`].
304    ///
305    /// # Panics
306    ///
307    /// This will panic if a malformed URL is passed.
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// # use http_types::Method;
313    /// # use crux_core::macros::effect;
314    /// # use crux_http::HttpRequest;
315    /// # enum Event { ReceiveResponse(crux_http::Result<crux_http::Response<Vec<u8>>>) }
316    /// # #[effect]
317    /// # #[allow(unused)]
318    /// # enum Effect { Http(HttpRequest) }
319    /// # type Http = crux_http::command::Http<Effect, Event>;
320    /// Http::request(Method::Post, "https://httpbin.org/post".parse().unwrap())
321    ///     .body_form(&[("name", "Alice")]).unwrap()
322    ///     .build()
323    ///     .then_send(Event::ReceiveResponse);
324    pub fn request(method: Method, url: Url) -> command::RequestBuilder<Effect, Event> {
325        command::RequestBuilder::new(method, url)
326    }
327}