Skip to main content

server_fn/request/
mod.rs

1use bytes::Bytes;
2use futures::{Sink, Stream};
3use http::Method;
4use std::{borrow::Cow, future::Future};
5
6/// Request types for Actix.
7#[cfg(feature = "actix-no-default")]
8pub mod actix;
9/// Request types for Axum.
10#[cfg(feature = "axum-no-default")]
11pub mod axum;
12/// Request types for the browser.
13#[cfg(feature = "browser")]
14pub mod browser;
15#[cfg(feature = "generic")]
16pub mod generic;
17/// Request types for [`reqwest`].
18#[cfg(feature = "reqwest")]
19pub mod reqwest;
20
21/// Represents a request as made by the client.
22pub trait ClientReq<E>
23where
24    Self: Sized,
25{
26    /// The type used for URL-encoded form data in this client.
27    type FormData;
28
29    /// Attempts to construct a new request with query parameters.
30    fn try_new_req_query(
31        path: &str,
32        content_type: &str,
33        accepts: &str,
34        query: &str,
35        method: Method,
36    ) -> Result<Self, E>;
37
38    /// Attempts to construct a new request with a text body.
39    fn try_new_req_text(
40        path: &str,
41        content_type: &str,
42        accepts: &str,
43        body: String,
44        method: Method,
45    ) -> Result<Self, E>;
46
47    /// Attempts to construct a new request with a binary body.
48    fn try_new_req_bytes(
49        path: &str,
50        content_type: &str,
51        accepts: &str,
52        body: Bytes,
53        method: Method,
54    ) -> Result<Self, E>;
55
56    /// Attempts to construct a new request with form data as the body.
57    fn try_new_req_form_data(
58        path: &str,
59        accepts: &str,
60        content_type: &str,
61        body: Self::FormData,
62        method: Method,
63    ) -> Result<Self, E>;
64
65    /// Attempts to construct a new request with a multipart body.
66    fn try_new_req_multipart(
67        path: &str,
68        accepts: &str,
69        body: Self::FormData,
70        method: Method,
71    ) -> Result<Self, E>;
72
73    /// Attempts to construct a new request with a streaming body.
74    fn try_new_req_streaming(
75        path: &str,
76        accepts: &str,
77        content_type: &str,
78        body: impl Stream<Item = Bytes> + Send + 'static,
79        method: Method,
80    ) -> Result<Self, E>;
81
82    /// Attempts to construct a new `GET` request.
83    fn try_new_get(
84        path: &str,
85        content_type: &str,
86        accepts: &str,
87        query: &str,
88    ) -> Result<Self, E> {
89        Self::try_new_req_query(path, content_type, accepts, query, Method::GET)
90    }
91
92    /// Attempts to construct a new `DELETE` request.
93    /// **Note**: Browser support for `DELETE` requests without JS/WASM may be poor.
94    /// Consider using a `POST` request if functionality without JS/WASM is required.
95    fn try_new_delete(
96        path: &str,
97        content_type: &str,
98        accepts: &str,
99        query: &str,
100    ) -> Result<Self, E> {
101        Self::try_new_req_query(
102            path,
103            content_type,
104            accepts,
105            query,
106            Method::DELETE,
107        )
108    }
109
110    /// Attempts to construct a new `POST` request with a text body.
111    fn try_new_post(
112        path: &str,
113        content_type: &str,
114        accepts: &str,
115        body: String,
116    ) -> Result<Self, E> {
117        Self::try_new_req_text(path, content_type, accepts, body, Method::POST)
118    }
119
120    /// Attempts to construct a new `PATCH` request with a text body.
121    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.
122    /// Consider using a `POST` request if functionality without JS/WASM is required.
123    fn try_new_patch(
124        path: &str,
125        content_type: &str,
126        accepts: &str,
127        body: String,
128    ) -> Result<Self, E> {
129        Self::try_new_req_text(path, content_type, accepts, body, Method::PATCH)
130    }
131
132    /// Attempts to construct a new `PUT` request with a text body.
133    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.
134    /// Consider using a `POST` request if functionality without JS/WASM is required.
135    fn try_new_put(
136        path: &str,
137        content_type: &str,
138        accepts: &str,
139        body: String,
140    ) -> Result<Self, E> {
141        Self::try_new_req_text(path, content_type, accepts, body, Method::PUT)
142    }
143
144    /// Attempts to construct a new `POST` request with a binary body.
145    fn try_new_post_bytes(
146        path: &str,
147        content_type: &str,
148        accepts: &str,
149        body: Bytes,
150    ) -> Result<Self, E> {
151        Self::try_new_req_bytes(path, content_type, accepts, body, Method::POST)
152    }
153
154    /// Attempts to construct a new `PATCH` request with a binary body.
155    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.
156    /// Consider using a `POST` request if functionality without JS/WASM is required.
157    fn try_new_patch_bytes(
158        path: &str,
159        content_type: &str,
160        accepts: &str,
161        body: Bytes,
162    ) -> Result<Self, E> {
163        Self::try_new_req_bytes(
164            path,
165            content_type,
166            accepts,
167            body,
168            Method::PATCH,
169        )
170    }
171
172    /// Attempts to construct a new `PUT` request with a binary body.
173    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.
174    /// Consider using a `POST` request if functionality without JS/WASM is required.
175    fn try_new_put_bytes(
176        path: &str,
177        content_type: &str,
178        accepts: &str,
179        body: Bytes,
180    ) -> Result<Self, E> {
181        Self::try_new_req_bytes(path, content_type, accepts, body, Method::PUT)
182    }
183
184    /// Attempts to construct a new `POST` request with form data as the body.
185    fn try_new_post_form_data(
186        path: &str,
187        accepts: &str,
188        content_type: &str,
189        body: Self::FormData,
190    ) -> Result<Self, E> {
191        Self::try_new_req_form_data(
192            path,
193            accepts,
194            content_type,
195            body,
196            Method::POST,
197        )
198    }
199
200    /// Attempts to construct a new `PATCH` request with form data as the body.
201    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.
202    /// Consider using a `POST` request if functionality without JS/WASM is required.
203    fn try_new_patch_form_data(
204        path: &str,
205        accepts: &str,
206        content_type: &str,
207        body: Self::FormData,
208    ) -> Result<Self, E> {
209        Self::try_new_req_form_data(
210            path,
211            accepts,
212            content_type,
213            body,
214            Method::PATCH,
215        )
216    }
217
218    /// Attempts to construct a new `PUT` request with form data as the body.
219    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.
220    /// Consider using a `POST` request if functionality without JS/WASM is required.
221    fn try_new_put_form_data(
222        path: &str,
223        accepts: &str,
224        content_type: &str,
225        body: Self::FormData,
226    ) -> Result<Self, E> {
227        Self::try_new_req_form_data(
228            path,
229            accepts,
230            content_type,
231            body,
232            Method::PUT,
233        )
234    }
235
236    /// Attempts to construct a new `POST` request with a multipart body.
237    fn try_new_post_multipart(
238        path: &str,
239        accepts: &str,
240        body: Self::FormData,
241    ) -> Result<Self, E> {
242        Self::try_new_req_multipart(path, accepts, body, Method::POST)
243    }
244
245    /// Attempts to construct a new `PATCH` request with a multipart body.
246    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.
247    /// Consider using a `POST` request if functionality without JS/WASM is required.
248    fn try_new_patch_multipart(
249        path: &str,
250        accepts: &str,
251        body: Self::FormData,
252    ) -> Result<Self, E> {
253        Self::try_new_req_multipart(path, accepts, body, Method::PATCH)
254    }
255
256    /// Attempts to construct a new `PUT` request with a multipart body.
257    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.
258    /// Consider using a `POST` request if functionality without JS/WASM is required.
259    fn try_new_put_multipart(
260        path: &str,
261        accepts: &str,
262        body: Self::FormData,
263    ) -> Result<Self, E> {
264        Self::try_new_req_multipart(path, accepts, body, Method::PUT)
265    }
266
267    /// Attempts to construct a new `POST` request with a streaming body.
268    fn try_new_post_streaming(
269        path: &str,
270        accepts: &str,
271        content_type: &str,
272        body: impl Stream<Item = Bytes> + Send + 'static,
273    ) -> Result<Self, E> {
274        Self::try_new_req_streaming(
275            path,
276            accepts,
277            content_type,
278            body,
279            Method::POST,
280        )
281    }
282
283    /// Attempts to construct a new `PATCH` request with a streaming body.
284    /// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.
285    /// Consider using a `POST` request if functionality without JS/WASM is required.
286    fn try_new_patch_streaming(
287        path: &str,
288        accepts: &str,
289        content_type: &str,
290        body: impl Stream<Item = Bytes> + Send + 'static,
291    ) -> Result<Self, E> {
292        Self::try_new_req_streaming(
293            path,
294            accepts,
295            content_type,
296            body,
297            Method::PATCH,
298        )
299    }
300
301    /// Attempts to construct a new `PUT` request with a streaming body.
302    /// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.
303    /// Consider using a `POST` request if functionality without JS/WASM is required.
304    fn try_new_put_streaming(
305        path: &str,
306        accepts: &str,
307        content_type: &str,
308        body: impl Stream<Item = Bytes> + Send + 'static,
309    ) -> Result<Self, E> {
310        Self::try_new_req_streaming(
311            path,
312            accepts,
313            content_type,
314            body,
315            Method::PUT,
316        )
317    }
318}
319
320/// Represents the request as received by the server.
321pub trait Req<Error, InputStreamError = Error, OutputStreamError = Error>
322where
323    Self: Sized,
324{
325    /// The response type for websockets.
326    type WebsocketResponse: Send;
327
328    /// Returns the query string of the request’s URL, starting after the `?`.
329    fn as_query(&self) -> Option<&str>;
330
331    /// Returns the `Content-Type` header, if any.
332    fn to_content_type(&self) -> Option<Cow<'_, str>>;
333
334    /// Returns the `Accepts` header, if any.
335    fn accepts(&self) -> Option<Cow<'_, str>>;
336
337    /// Returns the `Referer` header, if any.
338    fn referer(&self) -> Option<Cow<'_, str>>;
339
340    /// Attempts to extract the body of the request into [`Bytes`].
341    fn try_into_bytes(
342        self,
343    ) -> impl Future<Output = Result<Bytes, Error>> + Send;
344
345    /// Attempts to convert the body of the request into a string.
346    fn try_into_string(
347        self,
348    ) -> impl Future<Output = Result<String, Error>> + Send;
349
350    /// Attempts to convert the body of the request into a stream of bytes.
351    fn try_into_stream(
352        self,
353    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, Error>;
354
355    /// Attempts to convert the body of the request into a websocket handle.
356    #[allow(clippy::type_complexity)]
357    fn try_into_websocket(
358        self,
359    ) -> impl Future<
360        Output = Result<
361            (
362                impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,
363                impl Sink<Bytes> + Send + 'static,
364                Self::WebsocketResponse,
365            ),
366            Error,
367        >,
368    > + Send;
369}
370
371/// A mocked request type that can be used in place of the actual server request,
372/// when compiling for the browser.
373pub struct BrowserMockReq;
374
375impl<Error, InputStreamError, OutputStreamError>
376    Req<Error, InputStreamError, OutputStreamError> for BrowserMockReq
377where
378    Error: Send + 'static,
379    InputStreamError: Send + 'static,
380    OutputStreamError: Send + 'static,
381{
382    type WebsocketResponse = crate::response::BrowserMockRes;
383
384    fn as_query(&self) -> Option<&str> {
385        unreachable!()
386    }
387
388    fn to_content_type(&self) -> Option<Cow<'_, str>> {
389        unreachable!()
390    }
391
392    fn accepts(&self) -> Option<Cow<'_, str>> {
393        unreachable!()
394    }
395
396    fn referer(&self) -> Option<Cow<'_, str>> {
397        unreachable!()
398    }
399    async fn try_into_bytes(self) -> Result<Bytes, Error> {
400        unreachable!()
401    }
402
403    async fn try_into_string(self) -> Result<String, Error> {
404        unreachable!()
405    }
406
407    fn try_into_stream(
408        self,
409    ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send, Error> {
410        Ok(futures::stream::once(async { unreachable!() }))
411    }
412
413    async fn try_into_websocket(
414        self,
415    ) -> Result<
416        (
417            impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static,
418            impl Sink<Bytes> + Send + 'static,
419            Self::WebsocketResponse,
420        ),
421        Error,
422    > {
423        #[allow(unreachable_code)]
424        Err::<
425            (
426                futures::stream::Once<std::future::Ready<Result<Bytes, Bytes>>>,
427                futures::sink::Drain<Bytes>,
428                Self::WebsocketResponse,
429            ),
430            _,
431        >(unreachable!())
432    }
433}