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