1use std::convert::TryFrom;
2
3use crate::{
4 cf::Cf, error::Error, headers::Headers, http::Method, ByteStream, FormData, RequestInit, Result,
5};
6
7use serde::de::DeserializeOwned;
8#[cfg(test)]
9use std::borrow::Cow;
10#[cfg(test)]
11use url::form_urlencoded::Parse;
12use url::Url;
13use wasm_bindgen::JsCast;
14use wasm_bindgen_futures::JsFuture;
15use worker_sys::ext::RequestExt;
16
17#[derive(Debug)]
20pub struct Request {
21 method: Method,
22 path: String,
23 headers: Headers,
24 cf: Option<Cf>,
25 edge_request: web_sys::Request,
26 body_used: bool,
27 immutable: bool,
28}
29
30unsafe impl Send for Request {}
31unsafe impl Sync for Request {}
32
33#[cfg(feature = "http")]
34impl<B: http_body::Body<Data = bytes::Bytes> + 'static> TryFrom<http::Request<B>> for Request {
35 type Error = crate::Error;
36 fn try_from(req: http::Request<B>) -> Result<Self> {
37 let web_request: web_sys::Request = crate::http::request::to_wasm(req)?;
38 Ok(Request::from(web_request))
39 }
40}
41
42#[cfg(feature = "http")]
43impl TryFrom<Request> for crate::HttpRequest {
44 type Error = crate::Error;
45 fn try_from(req: Request) -> Result<Self> {
46 crate::http::request::from_wasm(req.edge_request)
47 }
48}
49
50impl From<web_sys::Request> for Request {
51 fn from(req: web_sys::Request) -> Self {
52 Self {
53 method: req.method().into(),
54 path: Url::parse(&req.url())
55 .map(|u| u.path().into())
56 .unwrap_or_else(|_| {
57 let u = req.url();
58 if !u.starts_with('/') {
59 return "/".to_string() + &u;
60 }
61 u
62 }),
63 headers: Headers(req.headers()),
64 cf: req.cf().map(Into::into),
65 edge_request: req,
66 body_used: false,
67 immutable: true,
68 }
69 }
70}
71
72impl TryFrom<Request> for web_sys::Request {
73 type Error = Error;
74 fn try_from(req: Request) -> Result<Self> {
75 req.inner().clone().map_err(Error::from)
76 }
77}
78
79impl TryFrom<&Request> for web_sys::Request {
80 type Error = Error;
81 fn try_from(req: &Request) -> Result<Self> {
82 req.inner().clone().map_err(Error::from)
83 }
84}
85
86impl Request {
87 pub fn new(uri: &str, method: Method) -> Result<Self> {
89 let init = web_sys::RequestInit::new();
90 init.set_method(method.as_ref());
91 web_sys::Request::new_with_str_and_init(uri, &init)
92 .map(|req| {
93 let mut req: Request = req.into();
94 req.immutable = false;
95 req
96 })
97 .map_err(|e| {
98 Error::JsError(
99 e.as_string()
100 .unwrap_or_else(|| "invalid URL or method for Request".to_string()),
101 )
102 })
103 }
104
105 pub fn new_with_init(uri: &str, init: &RequestInit) -> Result<Self> {
107 web_sys::Request::new_with_str_and_init(uri, &init.into())
108 .map(|req| {
109 let mut req: Request = req.into();
110 req.immutable = false;
111 req
112 })
113 .map_err(|e| {
114 Error::JsError(
115 e.as_string()
116 .unwrap_or_else(|| "invalid URL or options for Request".to_string()),
117 )
118 })
119 }
120
121 pub async fn json<B: DeserializeOwned>(&mut self) -> Result<B> {
123 if !self.body_used {
124 self.body_used = true;
125 return JsFuture::from(self.edge_request.json()?)
126 .await
127 .map_err(|e| {
128 Error::JsError(
129 e.as_string()
130 .unwrap_or_else(|| "failed to get JSON for body value".into()),
131 )
132 })
133 .and_then(|val| serde_wasm_bindgen::from_value(val).map_err(Error::from));
134 }
135
136 Err(Error::BodyUsed)
137 }
138
139 pub async fn text(&mut self) -> Result<String> {
141 if !self.body_used {
142 self.body_used = true;
143 return JsFuture::from(self.edge_request.text()?)
144 .await
145 .map(|val| val.as_string().unwrap())
146 .map_err(|e| {
147 Error::JsError(
148 e.as_string()
149 .unwrap_or_else(|| "failed to get text for body value".into()),
150 )
151 });
152 }
153
154 Err(Error::BodyUsed)
155 }
156
157 pub async fn bytes(&mut self) -> Result<Vec<u8>> {
159 if !self.body_used {
160 self.body_used = true;
161 return JsFuture::from(self.edge_request.array_buffer()?)
162 .await
163 .map(|val| js_sys::Uint8Array::new(&val).to_vec())
164 .map_err(|e| {
165 Error::JsError(
166 e.as_string()
167 .unwrap_or_else(|| "failed to read array buffer from request".into()),
168 )
169 });
170 }
171
172 Err(Error::BodyUsed)
173 }
174
175 pub async fn form_data(&mut self) -> Result<FormData> {
177 if !self.body_used {
178 self.body_used = true;
179 return JsFuture::from(self.edge_request.form_data()?)
180 .await
181 .map(|val| val.into())
182 .map_err(|e| {
183 Error::JsError(
184 e.as_string()
185 .unwrap_or_else(|| "failed to get form data from request".into()),
186 )
187 });
188 }
189
190 Err(Error::BodyUsed)
191 }
192
193 pub fn stream(&mut self) -> Result<ByteStream> {
195 if self.body_used {
196 return Err(Error::BodyUsed);
197 }
198
199 self.body_used = true;
200
201 let stream = self
202 .edge_request
203 .body()
204 .ok_or_else(|| Error::RustError("no body for request".into()))?;
205
206 let stream = wasm_streams::ReadableStream::from_raw(stream.dyn_into().unwrap());
207 Ok(ByteStream {
208 inner: stream.into_stream(),
209 })
210 }
211
212 pub fn headers(&self) -> &Headers {
214 &self.headers
215 }
216
217 pub fn headers_mut(&mut self) -> Result<&mut Headers> {
220 if self.immutable {
221 return Err(Error::JsError(
222 "Cannot get a mutable reference to an immutable headers object.".into(),
223 ));
224 }
225 Ok(&mut self.headers)
226 }
227
228 pub fn cf(&self) -> Option<&Cf> {
236 self.cf.as_ref()
237 }
238
239 pub fn method(&self) -> Method {
241 self.method.clone()
242 }
243
244 pub fn path(&self) -> String {
246 self.path.clone()
247 }
248
249 pub fn path_mut(&mut self) -> Result<&mut String> {
252 if self.immutable {
253 return Err(Error::JsError(
254 "Cannot get a mutable reference to an immutable path.".into(),
255 ));
256 }
257 Ok(&mut self.path)
258 }
259
260 pub fn url(&self) -> Result<Url> {
262 let url = self.edge_request.url();
263 url.parse()
264 .map_err(|e| Error::RustError(format!("failed to parse Url from {e}: {url}")))
265 }
266
267 pub fn query<Q: DeserializeOwned>(&self) -> Result<Q> {
269 let url = self.url()?;
270 let pairs = url.query_pairs();
271 let deserializer = serde_urlencoded::Deserializer::new(pairs);
272
273 Q::deserialize(deserializer).map_err(Error::from)
274 }
275
276 #[allow(clippy::should_implement_trait)]
277 pub fn clone(&self) -> Result<Self> {
278 self.edge_request
279 .clone()
280 .map(|req| req.into())
281 .map_err(Error::from)
282 }
283
284 pub fn clone_mut(&self) -> Result<Self> {
285 let mut req: Request = web_sys::Request::new_with_request(&self.edge_request)?.into();
286 req.immutable = false;
287 Ok(req)
288 }
289
290 pub fn inner(&self) -> &web_sys::Request {
291 &self.edge_request
292 }
293}
294
295#[cfg(test)]
296pub struct ParamIter<'a> {
297 inner: Parse<'a>,
298 key: &'a str,
299}
300
301#[cfg(test)]
302impl<'a> Iterator for ParamIter<'a> {
303 type Item = Cow<'a, str>;
304
305 fn next(&mut self) -> Option<Self::Item> {
306 let key = self.key;
307 Some(self.inner.find(|(k, _)| k == key)?.1)
308 }
309}
310
311pub trait FromRequest: std::marker::Sized {
314 fn from_raw(
315 request: web_sys::Request,
316 ) -> std::result::Result<Self, impl Into<Box<dyn std::error::Error>>>;
317}
318
319impl FromRequest for web_sys::Request {
320 fn from_raw(
321 request: web_sys::Request,
322 ) -> std::result::Result<Self, impl Into<Box<dyn std::error::Error>>> {
323 Ok::<web_sys::Request, Error>(request)
324 }
325}
326
327impl FromRequest for Request {
328 fn from_raw(
329 request: web_sys::Request,
330 ) -> std::result::Result<Self, impl Into<Box<dyn std::error::Error>>> {
331 Ok::<Request, Error>(request.into())
332 }
333}
334
335#[cfg(feature = "http")]
336impl FromRequest for crate::HttpRequest {
337 fn from_raw(
338 request: web_sys::Request,
339 ) -> std::result::Result<Self, impl Into<Box<dyn std::error::Error>>> {
340 crate::http::request::from_wasm(request)
341 }
342}
343
344#[cfg(test)]
345mod test {
346 use super::*;
347
348 pub trait UrlExt {
350 fn param<'a>(&'a self, key: &'a str) -> Option<Cow<'a, str>> {
353 self.param_iter(key).next()
354 }
355 fn param_iter<'a>(&'a self, key: &'a str) -> ParamIter<'a>;
358 }
359
360 impl UrlExt for Url {
361 fn param_iter<'a>(&'a self, key: &'a str) -> ParamIter<'a> {
362 ParamIter {
363 inner: self.query_pairs(),
364 key,
365 }
366 }
367 }
368
369 #[test]
370 fn url_param_works() {
371 let url = Url::parse("https://example.com/foo.html?a=foo&b=bar&a=baz").unwrap();
372 assert_eq!(url.param("a").as_deref(), Some("foo"));
373 assert_eq!(url.param("b").as_deref(), Some("bar"));
374 assert_eq!(url.param("c").as_deref(), None);
375 let mut a_values = url.param_iter("a");
376 assert_eq!(a_values.next().as_deref(), Some("foo"));
377 assert_eq!(a_values.next().as_deref(), Some("baz"));
378 assert_eq!(a_values.next(), None);
379 }
380
381 #[test]
382 fn clone_mut_works() {
383 let req = Request::new(
384 "https://example.com/foo.html?a=foo&b=bar&a=baz",
385 crate::Method::Get,
386 )
387 .unwrap();
388 assert!(!req.immutable);
389 let mut_req = req.clone_mut().unwrap();
390 assert!(mut_req.immutable);
391 }
392}