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