script/dom/
request.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::rc::Rc;
6use std::str::FromStr;
7
8use cssparser::match_ignore_ascii_case;
9use dom_struct::dom_struct;
10use http::Method as HttpMethod;
11use http::header::{HeaderName, HeaderValue};
12use http::method::InvalidMethod;
13use js::rust::HandleObject;
14use net_traits::ReferrerPolicy as MsgReferrerPolicy;
15use net_traits::fetch::headers::is_forbidden_method;
16use net_traits::request::{
17    CacheMode as NetTraitsRequestCache, CredentialsMode as NetTraitsRequestCredentials,
18    Destination as NetTraitsRequestDestination, Origin, RedirectMode as NetTraitsRequestRedirect,
19    Referrer as NetTraitsRequestReferrer, Request as NetTraitsRequest, RequestBuilder,
20    RequestMode as NetTraitsRequestMode, TraversableForUserPrompts,
21};
22use servo_url::ServoUrl;
23
24use crate::body::{BodyMixin, BodyType, Extractable, clone_body_stream_for_dom_body, consume_body};
25use crate::conversions::Convert;
26use crate::dom::abortsignal::AbortSignal;
27use crate::dom::bindings::cell::DomRefCell;
28use crate::dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods};
29use crate::dom::bindings::codegen::Bindings::RequestBinding::{
30    ReferrerPolicy, RequestCache, RequestCredentials, RequestDestination, RequestInfo, RequestInit,
31    RequestMethods, RequestMode, RequestRedirect,
32};
33use crate::dom::bindings::error::{Error, Fallible};
34use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
35use crate::dom::bindings::root::{DomRoot, MutNullableDom};
36use crate::dom::bindings::str::{ByteString, DOMString, USVString};
37use crate::dom::bindings::trace::RootedTraceableBox;
38use crate::dom::globalscope::GlobalScope;
39use crate::dom::headers::{Guard, Headers};
40use crate::dom::promise::Promise;
41use crate::dom::stream::readablestream::ReadableStream;
42use crate::fetch::RequestWithGlobalScope;
43use crate::script_runtime::CanGc;
44
45#[dom_struct]
46pub(crate) struct Request {
47    reflector_: Reflector,
48    #[no_trace]
49    /// <https://fetch.spec.whatwg.org/#concept-request-request>
50    request: DomRefCell<NetTraitsRequest>,
51    /// <https://fetch.spec.whatwg.org/#concept-request-body>
52    body_stream: MutNullableDom<ReadableStream>,
53    /// <https://fetch.spec.whatwg.org/#request-headers>
54    headers: MutNullableDom<Headers>,
55    /// <https://fetch.spec.whatwg.org/#request-signal>
56    signal: MutNullableDom<AbortSignal>,
57}
58
59impl Request {
60    fn new_inherited(global: &GlobalScope, url: ServoUrl) -> Request {
61        Request {
62            reflector_: Reflector::new(),
63            request: DomRefCell::new(net_request_from_global(global, url)),
64            body_stream: MutNullableDom::new(None),
65            headers: Default::default(),
66            signal: MutNullableDom::new(None),
67        }
68    }
69
70    fn new(
71        global: &GlobalScope,
72        proto: Option<HandleObject>,
73        url: ServoUrl,
74        can_gc: CanGc,
75    ) -> DomRoot<Request> {
76        reflect_dom_object_with_proto(
77            Box::new(Request::new_inherited(global, url)),
78            global,
79            proto,
80            can_gc,
81        )
82    }
83
84    fn from_net_request(
85        global: &GlobalScope,
86        proto: Option<HandleObject>,
87        net_request: NetTraitsRequest,
88        can_gc: CanGc,
89    ) -> DomRoot<Request> {
90        let r = Request::new(global, proto, net_request.current_url(), can_gc);
91        *r.request.borrow_mut() = net_request;
92        r
93    }
94
95    // https://fetch.spec.whatwg.org/#dom-request
96    pub(crate) fn constructor(
97        global: &GlobalScope,
98        proto: Option<HandleObject>,
99        can_gc: CanGc,
100        mut input: RequestInfo,
101        init: &RequestInit,
102    ) -> Fallible<DomRoot<Request>> {
103        // Step 1. Let request be null.
104        let temporary_request: NetTraitsRequest;
105
106        // Step 2. Let fallbackMode be null.
107        let mut fallback_mode: Option<NetTraitsRequestMode> = None;
108
109        // Step 3. Let baseURL be this’s relevant settings object’s API base URL.
110        let base_url = global.api_base_url();
111
112        // Step 4. Let signal be null.
113        let mut signal: Option<DomRoot<AbortSignal>> = None;
114
115        // Required later for step 41.1
116        let mut input_body_is_unusable = false;
117
118        match input {
119            // Step 5. If input is a string, then:
120            RequestInfo::USVString(USVString(ref usv_string)) => {
121                // Step 5.1. Let parsedURL be the result of parsing input with baseURL.
122                let parsed_url = base_url.join(usv_string);
123                // Step 5.2. If parsedURL is failure, then throw a TypeError.
124                if parsed_url.is_err() {
125                    return Err(Error::Type("Url could not be parsed".to_string()));
126                }
127                // Step 5.3. If parsedURL includes credentials, then throw a TypeError.
128                let url = parsed_url.unwrap();
129                if includes_credentials(&url) {
130                    return Err(Error::Type("Url includes credentials".to_string()));
131                }
132                // Step 5.4. Set request to a new request whose URL is parsedURL.
133                temporary_request = net_request_from_global(global, url);
134                // Step 5.5. Set fallbackMode to "cors".
135                fallback_mode = Some(NetTraitsRequestMode::CorsMode);
136            },
137            // Step 6. Otherwise:
138            // Step 6.1. Assert: input is a Request object.
139            RequestInfo::Request(ref input_request) => {
140                // Preparation for step 41.1
141                input_body_is_unusable = input_request.is_unusable();
142                // Step 6.2. Set request to input’s request.
143                temporary_request = input_request.request.borrow().clone();
144                // Step 6.3. Set signal to input’s signal.
145                signal = Some(input_request.Signal());
146            },
147        }
148
149        // Step 7. Let origin be this’s relevant settings object’s origin.
150        let origin = global.origin().immutable();
151
152        // Step 8. Let traversableForUserPrompts be "client".
153        let mut traversable_for_user_prompts = TraversableForUserPrompts::Client;
154
155        // Step 9. If request’s traversable for user prompts is an environment settings object
156        // and its origin is same origin with origin, then set traversableForUserPrompts
157        // to request’s traversable for user prompts.
158        // TODO: `environment settings object` is not implemented in Servo yet.
159
160        // Step 10. If init["window"] exists and is non-null, then throw a TypeError.
161        if !init.window.handle().is_null_or_undefined() {
162            return Err(Error::Type("Window is present and is not null".to_string()));
163        }
164
165        // Step 11. If init["window"] exists, then set traversableForUserPrompts to "no-traversable".
166        if !init.window.handle().is_undefined() {
167            traversable_for_user_prompts = TraversableForUserPrompts::NoTraversable;
168        }
169
170        // Step 12. Set request to a new request with the following properties:
171        let mut request: NetTraitsRequest;
172        request = net_request_from_global(global, temporary_request.current_url());
173        request.method = temporary_request.method;
174        request.headers = temporary_request.headers.clone();
175        request.unsafe_request = true;
176        request.traversable_for_user_prompts = traversable_for_user_prompts;
177        // TODO: `entry settings object` is not implemented in Servo yet.
178        request.origin = Origin::Client;
179        request.referrer = temporary_request.referrer;
180        request.referrer_policy = temporary_request.referrer_policy;
181        request.mode = temporary_request.mode;
182        request.credentials_mode = temporary_request.credentials_mode;
183        request.cache_mode = temporary_request.cache_mode;
184        request.redirect_mode = temporary_request.redirect_mode;
185        request.integrity_metadata = temporary_request.integrity_metadata;
186
187        // Step 13. If init is not empty, then:
188        if init.body.is_some() ||
189            init.cache.is_some() ||
190            init.credentials.is_some() ||
191            init.integrity.is_some() ||
192            init.headers.is_some() ||
193            init.keepalive.is_some() ||
194            init.method.is_some() ||
195            init.mode.is_some() ||
196            init.redirect.is_some() ||
197            init.referrer.is_some() ||
198            init.referrerPolicy.is_some() ||
199            !init.window.handle().is_undefined()
200        {
201            // Step 13.1. If request’s mode is "navigate", then set it to "same-origin".
202            if request.mode == NetTraitsRequestMode::Navigate {
203                request.mode = NetTraitsRequestMode::SameOrigin;
204            }
205            // Step 13.2. Unset request’s reload-navigation flag.
206            // TODO
207            // Step 13.3. Unset request’s history-navigation flag.
208            // TODO
209            // Step 13.4. Set request’s origin to "client".
210            // TODO
211            // Step 13.5. Set request’s referrer to "client".
212            request.referrer = global.get_referrer();
213            // Step 13.6. Set request’s referrer policy to the empty string.
214            request.referrer_policy = MsgReferrerPolicy::EmptyString;
215            // Step 13.7. Set request’s URL to request’s current URL.
216            // TODO
217            // Step 13.8. Set request’s URL list to « request’s URL ».
218            // TODO
219        }
220
221        // Step 14. If init["referrer"] exists, then:
222        if let Some(init_referrer) = init.referrer.as_ref() {
223            // Step 14.1. Let referrer be init["referrer"].
224            let referrer = &init_referrer.0;
225            // Step 14.2. If referrer is the empty string, then set request’s referrer to "no-referrer".
226            if referrer.is_empty() {
227                request.referrer = NetTraitsRequestReferrer::NoReferrer;
228            // Step 14.3. Otherwise:
229            } else {
230                // Step 14.3.1. Let parsedReferrer be the result of parsing referrer with baseURL.
231                let parsed_referrer = base_url.join(referrer);
232                // Step 14.3.2. If parsedReferrer is failure, then throw a TypeError.
233                if parsed_referrer.is_err() {
234                    return Err(Error::Type("Failed to parse referrer url".to_string()));
235                }
236                // Step 14.3.3. If one of the following is true
237                // parsedReferrer’s scheme is "about" and path is the string "client"
238                // parsedReferrer’s origin is not same origin with origin
239                if let Ok(parsed_referrer) = parsed_referrer {
240                    if (parsed_referrer.cannot_be_a_base() &&
241                        parsed_referrer.scheme() == "about" &&
242                        parsed_referrer.path() == "client") ||
243                        parsed_referrer.origin() != *origin
244                    {
245                        // then set request’s referrer to "client".
246                        request.referrer = global.get_referrer();
247                    } else {
248                        // Step 14.3.4. Otherwise, set request’s referrer to parsedReferrer.
249                        request.referrer = NetTraitsRequestReferrer::ReferrerUrl(parsed_referrer);
250                    }
251                }
252            }
253        }
254
255        // Step 15. If init["referrerPolicy"] exists, then set request’s referrer policy to it.
256        if let Some(init_referrerpolicy) = init.referrerPolicy.as_ref() {
257            let init_referrer_policy = (*init_referrerpolicy).convert();
258            request.referrer_policy = init_referrer_policy;
259        }
260
261        // Step 16. Let mode be init["mode"] if it exists, and fallbackMode otherwise.
262        let mode = init.mode.as_ref().map(|m| (*m).convert()).or(fallback_mode);
263
264        // Step 17. If mode is "navigate", then throw a TypeError.
265        if let Some(NetTraitsRequestMode::Navigate) = mode {
266            return Err(Error::Type("Request mode is Navigate".to_string()));
267        }
268
269        // Step 18. If mode is non-null, set request’s mode to mode.
270        if let Some(m) = mode {
271            request.mode = m;
272        }
273
274        // Step 19. If init["credentials"] exists, then set request’s credentials mode to it.
275        if let Some(init_credentials) = init.credentials.as_ref() {
276            let credentials = (*init_credentials).convert();
277            request.credentials_mode = credentials;
278        }
279
280        // Step 20. If init["cache"] exists, then set request’s cache mode to it.
281        if let Some(init_cache) = init.cache.as_ref() {
282            let cache = (*init_cache).convert();
283            request.cache_mode = cache;
284        }
285
286        // Step 21. If request’s cache mode is "only-if-cached" and request’s mode
287        // is not "same-origin", then throw a TypeError.
288        if request.cache_mode == NetTraitsRequestCache::OnlyIfCached &&
289            request.mode != NetTraitsRequestMode::SameOrigin
290        {
291            return Err(Error::Type(
292                "Cache is 'only-if-cached' and mode is not 'same-origin'".to_string(),
293            ));
294        }
295
296        // Step 22. If init["redirect"] exists, then set request’s redirect mode to it.
297        if let Some(init_redirect) = init.redirect.as_ref() {
298            let redirect = (*init_redirect).convert();
299            request.redirect_mode = redirect;
300        }
301
302        // Step 23. If init["integrity"] exists, then set request’s integrity metadata to it.
303        if let Some(init_integrity) = init.integrity.as_ref() {
304            let integrity = init_integrity.clone().to_string();
305            request.integrity_metadata = integrity;
306        }
307
308        // Step 24. If init["keepalive"] exists, then set request’s keepalive to it.
309        if let Some(init_keepalive) = init.keepalive {
310            request.keep_alive = init_keepalive;
311        }
312
313        // Step 25. If init["method"] exists, then:
314        // Step 25.1. Let method be init["method"].
315        if let Some(init_method) = init.method.as_ref() {
316            // Step 25.2. If method is not a method or method is a forbidden method, then throw a TypeError.
317            if !is_method(init_method) {
318                return Err(Error::Type("Method is not a method".to_string()));
319            }
320            if is_forbidden_method(init_method) {
321                return Err(Error::Type("Method is forbidden".to_string()));
322            }
323            // Step 25.3. Normalize method.
324            let method = match init_method.as_str() {
325                Some(s) => normalize_method(s)
326                    .map_err(|e| Error::Type(format!("Method is not valid: {:?}", e)))?,
327                None => return Err(Error::Type("Method is not a valid UTF8".to_string())),
328            };
329            // Step 25.4. Set request’s method to method.
330            request.method = method;
331        }
332
333        // Step 26. If init["signal"] exists, then set signal to it.
334        if let Some(init_signal) = init.signal.as_ref() {
335            signal = init_signal.clone();
336        }
337        // Step 27. If init["priority"] exists, then:
338        // TODO
339        // Step 27.1. If request’s internal priority is not null,
340        // then update request’s internal priority in an implementation-defined manner.
341        // TODO
342        // Step 27.2. Otherwise, set request’s priority to init["priority"].
343        // TODO
344
345        // Step 28. Set this’s request to request.
346        let r = Request::from_net_request(global, proto, request, can_gc);
347
348        // Step 29. Let signals be « signal » if signal is non-null; otherwise « ».
349        let signals = signal.map_or(vec![], |s| vec![s]);
350        // Step 30. Set this’s signal to the result of creating a dependent
351        // abort signal from signals, using AbortSignal and this’s relevant realm.
352        r.signal
353            .set(Some(&AbortSignal::create_dependent_abort_signal(
354                signals, global, can_gc,
355            )));
356
357        // Step 31. Set this’s headers to a new Headers object with this’s relevant realm,
358        // whose header list is request’s header list and guard is "request".
359        //
360        // "or_init" looks unclear here, but it always enters the block since r
361        // hasn't had any other way to initialize its headers
362        r.headers
363            .or_init(|| Headers::for_request(&r.global(), can_gc));
364
365        // Step 33. If init is not empty, then:
366        //
367        // but spec says this should only be when non-empty init?
368        let headers_copy = init
369            .headers
370            .as_ref()
371            .map(|possible_header| match possible_header {
372                HeadersInit::ByteStringSequenceSequence(init_sequence) => {
373                    HeadersInit::ByteStringSequenceSequence(init_sequence.clone())
374                },
375                HeadersInit::ByteStringByteStringRecord(init_map) => {
376                    HeadersInit::ByteStringByteStringRecord(init_map.clone())
377                },
378            });
379
380        // Step 33.3
381        // We cannot empty `r.Headers().header_list` because
382        // we would undo the Step 25 above.  One alternative is to set
383        // `headers_copy` as a deep copy of `r.Headers()`. However,
384        // `r.Headers()` is a `DomRoot<T>`, and therefore it is difficult
385        // to obtain a mutable reference to `r.Headers()`. Without the
386        // mutable reference, we cannot mutate `r.Headers()` to be the
387        // deep copied headers in Step 25.
388
389        // Step 32. If this’s request’s mode is "no-cors", then:
390        if r.request.borrow().mode == NetTraitsRequestMode::NoCors {
391            let borrowed_request = r.request.borrow();
392            // Step 32.1. If this’s request’s method is not a CORS-safelisted method, then throw a TypeError.
393            if !is_cors_safelisted_method(&borrowed_request.method) {
394                return Err(Error::Type(
395                    "The mode is 'no-cors' but the method is not a cors-safelisted method"
396                        .to_string(),
397                ));
398            }
399            // Step 32.2. Set this’s headers’s guard to "request-no-cors".
400            r.Headers(can_gc).set_guard(Guard::RequestNoCors);
401        }
402
403        match headers_copy {
404            None => {
405                // Step 33.4. If headers is a Headers object, then for each header of its header list, append header to this’s headers.
406                //
407                // This is equivalent to the specification's concept of
408                // "associated headers list". If an init headers is not given,
409                // but an input with headers is given, set request's
410                // headers as the input's Headers.
411                if let RequestInfo::Request(ref input_request) = input {
412                    r.Headers(can_gc)
413                        .copy_from_headers(input_request.Headers(can_gc))?;
414                }
415            },
416            // Step 33.5. Otherwise, fill this’s headers with headers.
417            Some(headers_copy) => r.Headers(can_gc).fill(Some(headers_copy))?,
418        }
419
420        // Step 33.5 depending on how we got here
421        // Copy the headers list onto the headers of net_traits::Request
422        r.request.borrow_mut().headers = r.Headers(can_gc).get_headers_list();
423
424        // Step 34. Let inputBody be input’s request’s body if input is a Request object; otherwise null.
425        let input_body = if let RequestInfo::Request(ref mut input_request) = input {
426            let mut input_request_request = input_request.request.borrow_mut();
427            r.body_stream.set(input_request.body().as_deref());
428            input_request_request.body.take()
429        } else {
430            None
431        };
432
433        // Step 35. If either init["body"] exists and is non-null or inputBody is non-null,
434        // and request’s method is `GET` or `HEAD`, then throw a TypeError.
435        if init.body.as_ref().is_some_and(|body| body.is_some()) || input_body.is_some() {
436            let req = r.request.borrow();
437            let req_method = &req.method;
438            match *req_method {
439                HttpMethod::GET => {
440                    return Err(Error::Type(
441                        "Init's body is non-null, and request method is GET".to_string(),
442                    ));
443                },
444                HttpMethod::HEAD => {
445                    return Err(Error::Type(
446                        "Init's body is non-null, and request method is HEAD".to_string(),
447                    ));
448                },
449                _ => {},
450            }
451        }
452
453        // Step 36. Let initBody be null.
454        let mut init_body = None;
455        // Step 37. If init["body"] exists and is non-null, then:
456        if let Some(Some(ref input_init_body)) = init.body {
457            // Step 37.1. Let bodyWithType be the result of extracting init["body"], with keepalive set to request’s keepalive.
458            let mut body_with_type =
459                input_init_body.extract(global, r.request.borrow().keep_alive, can_gc)?;
460
461            // Step 37.3. Let type be bodyWithType’s type.
462            if let Some(contents) = body_with_type.content_type.take() {
463                let ct_header_name = b"Content-Type";
464                // Step 37.4. If type is non-null and this’s headers’s header list
465                // does not contain `Content-Type`, then append (`Content-Type`, type) to this’s headers.
466                if !r
467                    .Headers(can_gc)
468                    .Has(ByteString::new(ct_header_name.to_vec()))
469                    .unwrap()
470                {
471                    let ct_header_val = contents.as_bytes();
472                    r.Headers(can_gc).Append(
473                        ByteString::new(ct_header_name.to_vec()),
474                        ByteString::new(ct_header_val.to_vec()),
475                    )?;
476
477                    // In Servo r.Headers's header list isn't a pointer to
478                    // the same actual list as r.request's, and so we need to
479                    // append to both lists to keep them in sync.
480                    if let Ok(v) = HeaderValue::from_bytes(&ct_header_val) {
481                        r.request
482                            .borrow_mut()
483                            .headers
484                            .insert(HeaderName::from_bytes(ct_header_name).unwrap(), v);
485                    }
486                }
487            }
488
489            // Step 37.2. Set initBody to bodyWithType’s body.
490            let (net_body, stream) = body_with_type.into_net_request_body();
491            r.body_stream.set(Some(&*stream));
492            init_body = Some(net_body);
493        }
494
495        // Step 38. Let inputOrInitBody be initBody if it is non-null; otherwise inputBody.
496        // Step 40. Let finalBody be inputOrInitBody.
497        // Step 41.2. Set finalBody to the result of creating a proxy for inputBody.
498        //
499        // There are multiple reassignments to similar values. In the end, all end up as
500        // final_body. Therefore, final_body is equivalent to inputOrInitBody
501        let final_body = init_body.or(input_body);
502
503        // Step 39. If inputOrInitBody is non-null and inputOrInitBody’s source is null, then:
504        if final_body
505            .as_ref()
506            .is_some_and(|body| body.source_is_null())
507        {
508            // Step 39.1. If initBody is non-null and init["duplex"] does not exist, then throw a TypeError.
509            // TODO
510            // Step 39.2. If this’s request’s mode is neither "same-origin" nor "cors", then throw a TypeError.
511            let request_mode = &r.request.borrow().mode;
512            if *request_mode != NetTraitsRequestMode::CorsMode &&
513                *request_mode != NetTraitsRequestMode::SameOrigin
514            {
515                return Err(Error::Type(
516                    "Request mode must be Cors or SameOrigin".to_string(),
517                ));
518            }
519            // Step 39.3. Set this’s request’s use-CORS-preflight flag.
520            // TODO
521        }
522
523        // Step 41. If initBody is null and inputBody is non-null, then:
524        // Step 41.1. If inputBody is unusable, then throw a TypeError.
525        //
526        // We only perform this check on input_body. However, we already
527        // processed the input body. Therefore, we check it all the way
528        // above and throw the error at the last possible moment
529        if input_body_is_unusable {
530            return Err(Error::Type("Input body is unusable".to_string()));
531        }
532
533        // Step 42. Set this’s request’s body to finalBody.
534        r.request.borrow_mut().body = final_body;
535
536        Ok(r)
537    }
538
539    /// <https://fetch.spec.whatwg.org/#concept-request-clone>
540    fn clone_from(r: &Request, can_gc: CanGc) -> Fallible<DomRoot<Request>> {
541        let req = r.request.borrow();
542        let url = req.url();
543        let headers_guard = r.Headers(can_gc).get_guard();
544
545        // Step 1. Let newRequest be a copy of request, except for its body.
546        let mut new_req_inner = req.clone();
547        let body = new_req_inner.body.take();
548
549        let r_clone = Request::new(&r.global(), None, url, can_gc);
550        *r_clone.request.borrow_mut() = new_req_inner;
551
552        // Step 2. If request’s body is non-null, set newRequest’s body
553        // to the result of cloning request’s body.
554        if let Some(body) = body {
555            r_clone.request.borrow_mut().body = Some(body);
556        }
557
558        r_clone
559            .Headers(can_gc)
560            .copy_from_headers(r.Headers(can_gc))?;
561        r_clone.Headers(can_gc).set_guard(headers_guard);
562
563        clone_body_stream_for_dom_body(&r.body_stream, &r_clone.body_stream, can_gc)?;
564
565        // Step 3. Return newRequest.
566        Ok(r_clone)
567    }
568
569    pub(crate) fn get_request(&self) -> NetTraitsRequest {
570        self.request.borrow().clone()
571    }
572}
573
574fn net_request_from_global(global: &GlobalScope, url: ServoUrl) -> NetTraitsRequest {
575    RequestBuilder::new(global.webview_id(), url, global.get_referrer())
576        .with_global_scope(global)
577        .build()
578}
579
580/// <https://fetch.spec.whatwg.org/#concept-method-normalize>
581fn normalize_method(m: &str) -> Result<HttpMethod, InvalidMethod> {
582    match_ignore_ascii_case! { m,
583        "delete" => return Ok(HttpMethod::DELETE),
584        "get" => return Ok(HttpMethod::GET),
585        "head" => return Ok(HttpMethod::HEAD),
586        "options" => return Ok(HttpMethod::OPTIONS),
587        "post" => return Ok(HttpMethod::POST),
588        "put" => return Ok(HttpMethod::PUT),
589        _ => (),
590    }
591    debug!("Method: {:?}", m);
592    HttpMethod::from_str(m)
593}
594
595/// <https://fetch.spec.whatwg.org/#concept-method>
596fn is_method(m: &ByteString) -> bool {
597    m.as_str().is_some()
598}
599
600/// <https://fetch.spec.whatwg.org/#cors-safelisted-method>
601fn is_cors_safelisted_method(m: &HttpMethod) -> bool {
602    m == HttpMethod::GET || m == HttpMethod::HEAD || m == HttpMethod::POST
603}
604
605/// <https://url.spec.whatwg.org/#include-credentials>
606fn includes_credentials(input: &ServoUrl) -> bool {
607    !input.username().is_empty() || input.password().is_some()
608}
609
610impl RequestMethods<crate::DomTypeHolder> for Request {
611    /// <https://fetch.spec.whatwg.org/#dom-request>
612    fn Constructor(
613        global: &GlobalScope,
614        proto: Option<HandleObject>,
615        can_gc: CanGc,
616        input: RequestInfo,
617        init: RootedTraceableBox<RequestInit>,
618    ) -> Fallible<DomRoot<Request>> {
619        Self::constructor(global, proto, can_gc, input, &init)
620    }
621
622    /// <https://fetch.spec.whatwg.org/#dom-request-method>
623    fn Method(&self) -> ByteString {
624        let r = self.request.borrow();
625        ByteString::new(r.method.as_ref().as_bytes().into())
626    }
627
628    /// <https://fetch.spec.whatwg.org/#dom-request-url>
629    fn Url(&self) -> USVString {
630        let r = self.request.borrow();
631        USVString(r.url_list.first().map_or("", |u| u.as_str()).into())
632    }
633
634    /// <https://fetch.spec.whatwg.org/#dom-request-headers>
635    fn Headers(&self, can_gc: CanGc) -> DomRoot<Headers> {
636        self.headers
637            .or_init(|| Headers::new(&self.global(), can_gc))
638    }
639
640    /// <https://fetch.spec.whatwg.org/#dom-request-destination>
641    fn Destination(&self) -> RequestDestination {
642        self.request.borrow().destination.convert()
643    }
644
645    /// <https://fetch.spec.whatwg.org/#dom-request-referrer>
646    fn Referrer(&self) -> USVString {
647        let r = self.request.borrow();
648        USVString(match r.referrer {
649            NetTraitsRequestReferrer::NoReferrer => String::from(""),
650            NetTraitsRequestReferrer::Client(_) => String::from("about:client"),
651            NetTraitsRequestReferrer::ReferrerUrl(ref u) => {
652                let u_c = u.clone();
653                u_c.into_string()
654            },
655        })
656    }
657
658    /// <https://fetch.spec.whatwg.org/#dom-request-referrerpolicy>
659    fn ReferrerPolicy(&self) -> ReferrerPolicy {
660        self.request.borrow().referrer_policy.convert()
661    }
662
663    /// <https://fetch.spec.whatwg.org/#dom-request-mode>
664    fn Mode(&self) -> RequestMode {
665        self.request.borrow().mode.clone().convert()
666    }
667
668    /// <https://fetch.spec.whatwg.org/#dom-request-credentials>
669    fn Credentials(&self) -> RequestCredentials {
670        let r = self.request.borrow().clone();
671        r.credentials_mode.convert()
672    }
673
674    /// <https://fetch.spec.whatwg.org/#dom-request-cache>
675    fn Cache(&self) -> RequestCache {
676        let r = self.request.borrow().clone();
677        r.cache_mode.convert()
678    }
679
680    /// <https://fetch.spec.whatwg.org/#dom-request-redirect>
681    fn Redirect(&self) -> RequestRedirect {
682        let r = self.request.borrow().clone();
683        r.redirect_mode.convert()
684    }
685
686    /// <https://fetch.spec.whatwg.org/#dom-request-integrity>
687    fn Integrity(&self) -> DOMString {
688        let r = self.request.borrow();
689        DOMString::from_string(r.integrity_metadata.clone())
690    }
691
692    /// <https://fetch.spec.whatwg.org/#dom-request-keepalive>
693    fn Keepalive(&self) -> bool {
694        self.request.borrow().keep_alive
695    }
696
697    /// <https://fetch.spec.whatwg.org/#dom-body-body>
698    fn GetBody(&self) -> Option<DomRoot<ReadableStream>> {
699        self.body()
700    }
701
702    /// <https://fetch.spec.whatwg.org/#dom-body-bodyused>
703    fn BodyUsed(&self) -> bool {
704        self.is_body_used()
705    }
706
707    /// <https://fetch.spec.whatwg.org/#dom-request-signal>
708    fn Signal(&self) -> DomRoot<AbortSignal> {
709        self.signal
710            .get()
711            .expect("Should always be initialized in constructor and clone")
712    }
713
714    /// <https://fetch.spec.whatwg.org/#dom-request-clone>
715    fn Clone(&self, can_gc: CanGc) -> Fallible<DomRoot<Request>> {
716        // Step 1. If this is unusable, then throw a TypeError.
717        if self.is_unusable() {
718            return Err(Error::Type("Request is unusable".to_string()));
719        }
720
721        // Step 2. Let clonedRequest be the result of cloning this’s request.
722        let cloned_request = Request::clone_from(self, can_gc)?;
723        // Step 3. Assert: this’s signal is non-null.
724        let signal = self.signal.get().expect("Should always be initialized");
725        // Step 4. Let clonedSignal be the result of creating a dependent
726        // abort signal from « this’s signal », using AbortSignal and this’s relevant realm.
727        let cloned_signal =
728            AbortSignal::create_dependent_abort_signal(vec![signal], &self.global(), can_gc);
729        // Step 5. Let clonedRequestObject be the result of creating a Request object,
730        // given clonedRequest, this’s headers’s guard, clonedSignal and this’s relevant realm.
731        //
732        // These steps already happen in `clone_from`
733        cloned_request.signal.set(Some(&cloned_signal));
734        // Step 6. Return clonedRequestObject.
735        Ok(cloned_request)
736    }
737
738    /// <https://fetch.spec.whatwg.org/#dom-body-text>
739    fn Text(&self, can_gc: CanGc) -> Rc<Promise> {
740        consume_body(self, BodyType::Text, can_gc)
741    }
742
743    /// <https://fetch.spec.whatwg.org/#dom-body-blob>
744    fn Blob(&self, can_gc: CanGc) -> Rc<Promise> {
745        consume_body(self, BodyType::Blob, can_gc)
746    }
747
748    /// <https://fetch.spec.whatwg.org/#dom-body-formdata>
749    fn FormData(&self, can_gc: CanGc) -> Rc<Promise> {
750        consume_body(self, BodyType::FormData, can_gc)
751    }
752
753    /// <https://fetch.spec.whatwg.org/#dom-body-json>
754    fn Json(&self, can_gc: CanGc) -> Rc<Promise> {
755        consume_body(self, BodyType::Json, can_gc)
756    }
757
758    /// <https://fetch.spec.whatwg.org/#dom-body-arraybuffer>
759    fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
760        consume_body(self, BodyType::ArrayBuffer, can_gc)
761    }
762
763    /// <https://fetch.spec.whatwg.org/#dom-body-bytes>
764    fn Bytes(&self, can_gc: CanGc) -> std::rc::Rc<Promise> {
765        consume_body(self, BodyType::Bytes, can_gc)
766    }
767}
768
769impl BodyMixin for Request {
770    fn is_body_used(&self) -> bool {
771        let body_stream = self.body_stream.get();
772        body_stream
773            .as_ref()
774            .is_some_and(|stream| stream.is_disturbed())
775    }
776
777    fn is_unusable(&self) -> bool {
778        let body_stream = self.body_stream.get();
779        body_stream
780            .as_ref()
781            .is_some_and(|stream| stream.is_disturbed() || stream.is_locked())
782    }
783
784    fn body(&self) -> Option<DomRoot<ReadableStream>> {
785        self.body_stream.get()
786    }
787
788    fn get_mime_type(&self, can_gc: CanGc) -> Vec<u8> {
789        let headers = self.Headers(can_gc);
790        headers.extract_mime_type()
791    }
792}
793
794impl Convert<NetTraitsRequestCache> for RequestCache {
795    fn convert(self) -> NetTraitsRequestCache {
796        match self {
797            RequestCache::Default => NetTraitsRequestCache::Default,
798            RequestCache::No_store => NetTraitsRequestCache::NoStore,
799            RequestCache::Reload => NetTraitsRequestCache::Reload,
800            RequestCache::No_cache => NetTraitsRequestCache::NoCache,
801            RequestCache::Force_cache => NetTraitsRequestCache::ForceCache,
802            RequestCache::Only_if_cached => NetTraitsRequestCache::OnlyIfCached,
803        }
804    }
805}
806
807impl Convert<RequestCache> for NetTraitsRequestCache {
808    fn convert(self) -> RequestCache {
809        match self {
810            NetTraitsRequestCache::Default => RequestCache::Default,
811            NetTraitsRequestCache::NoStore => RequestCache::No_store,
812            NetTraitsRequestCache::Reload => RequestCache::Reload,
813            NetTraitsRequestCache::NoCache => RequestCache::No_cache,
814            NetTraitsRequestCache::ForceCache => RequestCache::Force_cache,
815            NetTraitsRequestCache::OnlyIfCached => RequestCache::Only_if_cached,
816        }
817    }
818}
819
820impl Convert<NetTraitsRequestCredentials> for RequestCredentials {
821    fn convert(self) -> NetTraitsRequestCredentials {
822        match self {
823            RequestCredentials::Omit => NetTraitsRequestCredentials::Omit,
824            RequestCredentials::Same_origin => NetTraitsRequestCredentials::CredentialsSameOrigin,
825            RequestCredentials::Include => NetTraitsRequestCredentials::Include,
826        }
827    }
828}
829
830impl Convert<RequestCredentials> for NetTraitsRequestCredentials {
831    fn convert(self) -> RequestCredentials {
832        match self {
833            NetTraitsRequestCredentials::Omit => RequestCredentials::Omit,
834            NetTraitsRequestCredentials::CredentialsSameOrigin => RequestCredentials::Same_origin,
835            NetTraitsRequestCredentials::Include => RequestCredentials::Include,
836        }
837    }
838}
839
840impl Convert<NetTraitsRequestDestination> for RequestDestination {
841    fn convert(self) -> NetTraitsRequestDestination {
842        match self {
843            RequestDestination::_empty => NetTraitsRequestDestination::None,
844            RequestDestination::Audio => NetTraitsRequestDestination::Audio,
845            RequestDestination::Document => NetTraitsRequestDestination::Document,
846            RequestDestination::Embed => NetTraitsRequestDestination::Embed,
847            RequestDestination::Font => NetTraitsRequestDestination::Font,
848            RequestDestination::Frame => NetTraitsRequestDestination::Frame,
849            RequestDestination::Iframe => NetTraitsRequestDestination::IFrame,
850            RequestDestination::Image => NetTraitsRequestDestination::Image,
851            RequestDestination::Manifest => NetTraitsRequestDestination::Manifest,
852            RequestDestination::Json => NetTraitsRequestDestination::Json,
853            RequestDestination::Object => NetTraitsRequestDestination::Object,
854            RequestDestination::Report => NetTraitsRequestDestination::Report,
855            RequestDestination::Script => NetTraitsRequestDestination::Script,
856            RequestDestination::Sharedworker => NetTraitsRequestDestination::SharedWorker,
857            RequestDestination::Style => NetTraitsRequestDestination::Style,
858            RequestDestination::Track => NetTraitsRequestDestination::Track,
859            RequestDestination::Video => NetTraitsRequestDestination::Video,
860            RequestDestination::Worker => NetTraitsRequestDestination::Worker,
861            RequestDestination::Xslt => NetTraitsRequestDestination::Xslt,
862        }
863    }
864}
865
866impl Convert<RequestDestination> for NetTraitsRequestDestination {
867    fn convert(self) -> RequestDestination {
868        match self {
869            NetTraitsRequestDestination::None => RequestDestination::_empty,
870            NetTraitsRequestDestination::Audio => RequestDestination::Audio,
871            NetTraitsRequestDestination::Document => RequestDestination::Document,
872            NetTraitsRequestDestination::Embed => RequestDestination::Embed,
873            NetTraitsRequestDestination::Font => RequestDestination::Font,
874            NetTraitsRequestDestination::Frame => RequestDestination::Frame,
875            NetTraitsRequestDestination::IFrame => RequestDestination::Iframe,
876            NetTraitsRequestDestination::Image => RequestDestination::Image,
877            NetTraitsRequestDestination::Manifest => RequestDestination::Manifest,
878            NetTraitsRequestDestination::Json => RequestDestination::Json,
879            NetTraitsRequestDestination::Object => RequestDestination::Object,
880            NetTraitsRequestDestination::Report => RequestDestination::Report,
881            NetTraitsRequestDestination::Script => RequestDestination::Script,
882            NetTraitsRequestDestination::ServiceWorker |
883            NetTraitsRequestDestination::AudioWorklet |
884            NetTraitsRequestDestination::PaintWorklet => {
885                panic!("ServiceWorker request destination should not be exposed to DOM")
886            },
887            NetTraitsRequestDestination::SharedWorker => RequestDestination::Sharedworker,
888            NetTraitsRequestDestination::Style => RequestDestination::Style,
889            NetTraitsRequestDestination::Track => RequestDestination::Track,
890            NetTraitsRequestDestination::Video => RequestDestination::Video,
891            NetTraitsRequestDestination::Worker => RequestDestination::Worker,
892            NetTraitsRequestDestination::Xslt => RequestDestination::Xslt,
893            NetTraitsRequestDestination::WebIdentity => RequestDestination::_empty,
894        }
895    }
896}
897
898impl Convert<NetTraitsRequestMode> for RequestMode {
899    fn convert(self) -> NetTraitsRequestMode {
900        match self {
901            RequestMode::Navigate => NetTraitsRequestMode::Navigate,
902            RequestMode::Same_origin => NetTraitsRequestMode::SameOrigin,
903            RequestMode::No_cors => NetTraitsRequestMode::NoCors,
904            RequestMode::Cors => NetTraitsRequestMode::CorsMode,
905        }
906    }
907}
908
909impl Convert<RequestMode> for NetTraitsRequestMode {
910    fn convert(self) -> RequestMode {
911        match self {
912            NetTraitsRequestMode::Navigate => RequestMode::Navigate,
913            NetTraitsRequestMode::SameOrigin => RequestMode::Same_origin,
914            NetTraitsRequestMode::NoCors => RequestMode::No_cors,
915            NetTraitsRequestMode::CorsMode => RequestMode::Cors,
916            NetTraitsRequestMode::WebSocket { .. } => {
917                unreachable!("Websocket request mode should never be exposed to Dom")
918            },
919        }
920    }
921}
922
923impl Convert<MsgReferrerPolicy> for ReferrerPolicy {
924    fn convert(self) -> MsgReferrerPolicy {
925        match self {
926            ReferrerPolicy::_empty => MsgReferrerPolicy::EmptyString,
927            ReferrerPolicy::No_referrer => MsgReferrerPolicy::NoReferrer,
928            ReferrerPolicy::No_referrer_when_downgrade => {
929                MsgReferrerPolicy::NoReferrerWhenDowngrade
930            },
931            ReferrerPolicy::Origin => MsgReferrerPolicy::Origin,
932            ReferrerPolicy::Origin_when_cross_origin => MsgReferrerPolicy::OriginWhenCrossOrigin,
933            ReferrerPolicy::Unsafe_url => MsgReferrerPolicy::UnsafeUrl,
934            ReferrerPolicy::Same_origin => MsgReferrerPolicy::SameOrigin,
935            ReferrerPolicy::Strict_origin => MsgReferrerPolicy::StrictOrigin,
936            ReferrerPolicy::Strict_origin_when_cross_origin => {
937                MsgReferrerPolicy::StrictOriginWhenCrossOrigin
938            },
939        }
940    }
941}
942
943impl Convert<ReferrerPolicy> for MsgReferrerPolicy {
944    fn convert(self) -> ReferrerPolicy {
945        match self {
946            MsgReferrerPolicy::EmptyString => ReferrerPolicy::_empty,
947            MsgReferrerPolicy::NoReferrer => ReferrerPolicy::No_referrer,
948            MsgReferrerPolicy::NoReferrerWhenDowngrade => {
949                ReferrerPolicy::No_referrer_when_downgrade
950            },
951            MsgReferrerPolicy::Origin => ReferrerPolicy::Origin,
952            MsgReferrerPolicy::OriginWhenCrossOrigin => ReferrerPolicy::Origin_when_cross_origin,
953            MsgReferrerPolicy::UnsafeUrl => ReferrerPolicy::Unsafe_url,
954            MsgReferrerPolicy::SameOrigin => ReferrerPolicy::Same_origin,
955            MsgReferrerPolicy::StrictOrigin => ReferrerPolicy::Strict_origin,
956            MsgReferrerPolicy::StrictOriginWhenCrossOrigin => {
957                ReferrerPolicy::Strict_origin_when_cross_origin
958            },
959        }
960    }
961}
962
963impl Convert<NetTraitsRequestRedirect> for RequestRedirect {
964    fn convert(self) -> NetTraitsRequestRedirect {
965        match self {
966            RequestRedirect::Follow => NetTraitsRequestRedirect::Follow,
967            RequestRedirect::Error => NetTraitsRequestRedirect::Error,
968            RequestRedirect::Manual => NetTraitsRequestRedirect::Manual,
969        }
970    }
971}
972
973impl Convert<RequestRedirect> for NetTraitsRequestRedirect {
974    fn convert(self) -> RequestRedirect {
975        match self {
976            NetTraitsRequestRedirect::Follow => RequestRedirect::Follow,
977            NetTraitsRequestRedirect::Error => RequestRedirect::Error,
978            NetTraitsRequestRedirect::Manual => RequestRedirect::Manual,
979        }
980    }
981}