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}