1use std::cell::Cell;
6use std::rc::Rc;
7use std::str::FromStr;
8
9use dom_struct::dom_struct;
10use http::header::HeaderMap as HyperHeaders;
11use hyper_serde::Serde;
12use js::rust::{HandleObject, HandleValue};
13use net_traits::http_status::HttpStatus;
14use script_bindings::cformat;
15use servo_url::ServoUrl;
16use url::Position;
17
18use crate::body::{
19 BodyMixin, BodyType, Extractable, ExtractedBody, clone_body_stream_for_dom_body, consume_body,
20};
21use crate::dom::bindings::cell::DomRefCell;
22use crate::dom::bindings::codegen::Bindings::HeadersBinding::HeadersMethods;
23use crate::dom::bindings::codegen::Bindings::ResponseBinding;
24use crate::dom::bindings::codegen::Bindings::ResponseBinding::{
25 ResponseMethods, ResponseType as DOMResponseType,
26};
27use crate::dom::bindings::codegen::Bindings::XMLHttpRequestBinding::BodyInit;
28use crate::dom::bindings::error::{Error, Fallible};
29use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
30use crate::dom::bindings::root::{DomRoot, MutNullableDom};
31use crate::dom::bindings::str::{ByteString, USVString, serialize_jsval_to_json_utf8};
32use crate::dom::globalscope::GlobalScope;
33use crate::dom::headers::{Guard, Headers, is_obs_text, is_vchar};
34use crate::dom::promise::Promise;
35use crate::dom::stream::readablestream::ReadableStream;
36use crate::dom::stream::underlyingsourcecontainer::UnderlyingSourceType;
37use crate::script_runtime::{CanGc, JSContext, StreamConsumer};
38
39#[dom_struct]
40pub(crate) struct Response {
41 reflector_: Reflector,
42 headers_reflector: MutNullableDom<Headers>,
43 #[no_trace]
44 status: DomRefCell<HttpStatus>,
45 response_type: DomRefCell<DOMResponseType>,
46 #[no_trace]
47 url: DomRefCell<Option<ServoUrl>>,
48 #[no_trace]
49 url_list: DomRefCell<Vec<ServoUrl>>,
50 body_stream: MutNullableDom<ReadableStream>,
52 fetch_body_stream: MutNullableDom<ReadableStream>,
55 #[ignore_malloc_size_of = "StreamConsumer"]
56 stream_consumer: DomRefCell<Option<StreamConsumer>>,
57 redirected: Cell<bool>,
59 is_body_empty: Cell<bool>,
60}
61
62impl Response {
63 pub(crate) fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> Response {
64 let stream = ReadableStream::new_with_external_underlying_source(
65 global,
66 UnderlyingSourceType::FetchResponse,
67 can_gc,
68 )
69 .expect("Failed to create ReadableStream with external underlying source");
70 Response {
71 reflector_: Reflector::new(),
72 headers_reflector: Default::default(),
73 status: DomRefCell::new(HttpStatus::default()),
74 response_type: DomRefCell::new(DOMResponseType::Default),
75 url: DomRefCell::new(None),
76 url_list: DomRefCell::new(vec![]),
77 body_stream: MutNullableDom::new(Some(&*stream)),
78 fetch_body_stream: MutNullableDom::new(Some(&*stream)),
79 stream_consumer: DomRefCell::new(None),
80 redirected: Cell::new(false),
81 is_body_empty: Cell::new(true),
82 }
83 }
84
85 pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Response> {
87 Self::new_with_proto(global, None, can_gc)
88 }
89
90 fn new_with_proto(
91 global: &GlobalScope,
92 proto: Option<HandleObject>,
93 can_gc: CanGc,
94 ) -> DomRoot<Response> {
95 reflect_dom_object_with_proto(
96 Box::new(Response::new_inherited(global, can_gc)),
97 global,
98 proto,
99 can_gc,
100 )
101 }
102
103 pub(crate) fn error_stream(&self, error: Error, can_gc: CanGc) {
104 if let Some(body) = self.fetch_body_stream.get() {
105 body.error_native(error, can_gc);
106 }
107 }
108
109 pub(crate) fn is_disturbed(&self) -> bool {
110 let body_stream = self.body_stream.get();
111 body_stream
112 .as_ref()
113 .is_some_and(|stream| stream.is_disturbed())
114 }
115
116 pub(crate) fn is_locked(&self) -> bool {
117 let body_stream = self.body_stream.get();
118 body_stream
119 .as_ref()
120 .is_some_and(|stream| stream.is_locked())
121 }
122}
123
124impl BodyMixin for Response {
125 fn is_body_used(&self) -> bool {
126 self.is_disturbed()
127 }
128
129 fn is_unusable(&self) -> bool {
130 self.body_stream
131 .get()
132 .is_some_and(|stream| stream.is_disturbed() || stream.is_locked())
133 }
134
135 fn body(&self) -> Option<DomRoot<ReadableStream>> {
136 self.body_stream.get()
137 }
138
139 fn get_mime_type(&self, can_gc: CanGc) -> Vec<u8> {
140 let headers = self.Headers(can_gc);
141 headers.extract_mime_type()
142 }
143}
144
145fn is_redirect_status(status: u16) -> bool {
147 status == 301 || status == 302 || status == 303 || status == 307 || status == 308
148}
149
150fn is_valid_status_text(status_text: &ByteString) -> bool {
152 for byte in status_text.iter() {
154 if !(*byte == b'\t' || *byte == b' ' || is_vchar(*byte) || is_obs_text(*byte)) {
155 return false;
156 }
157 }
158 true
159}
160
161fn is_null_body_status(status: u16) -> bool {
163 status == 101 || status == 204 || status == 205 || status == 304
164}
165
166impl ResponseMethods<crate::DomTypeHolder> for Response {
167 fn Constructor(
169 global: &GlobalScope,
170 proto: Option<HandleObject>,
171 can_gc: CanGc,
172 body_init: Option<BodyInit>,
173 init: &ResponseBinding::ResponseInit,
174 ) -> Fallible<DomRoot<Response>> {
175 let response = Response::new_with_proto(global, proto, can_gc);
178 if body_init.is_some() {
179 response.is_body_empty.set(false);
180 }
181
182 response.Headers(can_gc).set_guard(Guard::Response);
185
186 let body_with_type = match body_init {
189 Some(body) => Some(body.extract(global, false, can_gc)?),
190 None => None,
191 };
192
193 initialize_response(global, can_gc, body_with_type, init, response)
195 }
196
197 fn Error(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Response> {
199 let response = Response::new(global, can_gc);
200 *response.response_type.borrow_mut() = DOMResponseType::Error;
201 response.Headers(can_gc).set_guard(Guard::Immutable);
202 *response.status.borrow_mut() = HttpStatus::new_error();
203 response
204 }
205
206 fn Redirect(
208 global: &GlobalScope,
209 url: USVString,
210 status: u16,
211 can_gc: CanGc,
212 ) -> Fallible<DomRoot<Response>> {
213 let base_url = global.api_base_url();
215 let parsed_url = base_url.join(&url.0);
216
217 let url = match parsed_url {
219 Ok(url) => url,
220 Err(_) => return Err(Error::Type(c"ServoUrl could not be parsed".to_owned())),
221 };
222
223 if !is_redirect_status(status) {
225 return Err(Error::Range(c"status is not a redirect status".to_owned()));
226 }
227
228 let response = Response::new(global, can_gc);
231
232 *response.status.borrow_mut() = HttpStatus::new_raw(status, vec![]);
234
235 let url_bytestring =
237 ByteString::from_str(url.as_str()).unwrap_or(ByteString::new(b"".to_vec()));
238 response
239 .Headers(can_gc)
240 .Set(ByteString::new(b"Location".to_vec()), url_bytestring)?;
241
242 response.Headers(can_gc).set_guard(Guard::Immutable);
245
246 Ok(response)
248 }
249
250 fn CreateFromJson(
252 cx: JSContext,
253 global: &GlobalScope,
254 data: HandleValue,
255 init: &ResponseBinding::ResponseInit,
256 can_gc: CanGc,
257 ) -> Fallible<DomRoot<Response>> {
258 let json_str = serialize_jsval_to_json_utf8(cx, data)?;
260
261 let body_init = BodyInit::String(json_str);
265 let mut body = body_init.extract(global, false, can_gc)?;
266
267 let response = Response::new(global, can_gc);
270 response.Headers(can_gc).set_guard(Guard::Response);
271
272 body.content_type = Some("application/json".into());
274 initialize_response(global, can_gc, Some(body), init, response)
275 }
276
277 fn Type(&self) -> DOMResponseType {
279 *self.response_type.borrow() }
281
282 fn Url(&self) -> USVString {
284 USVString(String::from(
285 (*self.url.borrow())
286 .as_ref()
287 .map(serialize_without_fragment)
288 .unwrap_or(""),
289 ))
290 }
291
292 fn Redirected(&self) -> bool {
299 self.redirected.get()
300 }
301
302 fn Status(&self) -> u16 {
304 self.status.borrow().raw_code()
305 }
306
307 fn Ok(&self) -> bool {
309 self.status.borrow().is_success()
310 }
311
312 fn StatusText(&self) -> ByteString {
314 ByteString::new(self.status.borrow().message().to_vec())
315 }
316
317 fn Headers(&self, can_gc: CanGc) -> DomRoot<Headers> {
319 self.headers_reflector
320 .or_init(|| Headers::for_response(&self.global(), can_gc))
321 }
322
323 fn Clone(&self, can_gc: CanGc) -> Fallible<DomRoot<Response>> {
325 if self.is_unusable() {
327 return Err(Error::Type(c"cannot clone a disturbed response".to_owned()));
328 }
329
330 let new_response = Response::new(&self.global(), can_gc);
332 new_response
333 .Headers(can_gc)
334 .copy_from_headers(self.Headers(can_gc))?;
335 new_response
336 .Headers(can_gc)
337 .set_guard(self.Headers(can_gc).get_guard());
338
339 *new_response.response_type.borrow_mut() = *self.response_type.borrow();
340 new_response
341 .status
342 .borrow_mut()
343 .clone_from(&self.status.borrow());
344 new_response.url.borrow_mut().clone_from(&self.url.borrow());
345 new_response
346 .url_list
347 .borrow_mut()
348 .clone_from(&self.url_list.borrow());
349 new_response.is_body_empty.set(self.is_body_empty.get());
350
351 clone_body_stream_for_dom_body(&self.body_stream, &new_response.body_stream, can_gc)?;
354 new_response.fetch_body_stream.set(None);
356
357 Ok(new_response)
358 }
359
360 fn BodyUsed(&self) -> bool {
362 !self.is_body_empty.get() && self.is_body_used()
363 }
364
365 fn GetBody(&self) -> Option<DomRoot<ReadableStream>> {
367 self.body()
368 }
369
370 fn Text(&self, can_gc: CanGc) -> Rc<Promise> {
372 consume_body(self, BodyType::Text, can_gc)
373 }
374
375 fn Blob(&self, can_gc: CanGc) -> Rc<Promise> {
377 consume_body(self, BodyType::Blob, can_gc)
378 }
379
380 fn FormData(&self, can_gc: CanGc) -> Rc<Promise> {
382 consume_body(self, BodyType::FormData, can_gc)
383 }
384
385 fn Json(&self, can_gc: CanGc) -> Rc<Promise> {
387 consume_body(self, BodyType::Json, can_gc)
388 }
389
390 fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
392 consume_body(self, BodyType::ArrayBuffer, can_gc)
393 }
394
395 fn Bytes(&self, can_gc: CanGc) -> std::rc::Rc<Promise> {
397 consume_body(self, BodyType::Bytes, can_gc)
398 }
399}
400
401fn initialize_response(
403 global: &GlobalScope,
404 can_gc: CanGc,
405 body: Option<ExtractedBody>,
406 init: &ResponseBinding::ResponseInit,
407 response: DomRoot<Response>,
408) -> Result<DomRoot<Response>, Error> {
409 if init.status < 200 || init.status > 599 {
411 return Err(Error::Range(cformat!(
412 "init's status member should be in the range 200 to 599, inclusive, but is {}",
413 init.status
414 )));
415 }
416
417 if !is_valid_status_text(&init.statusText) {
420 return Err(Error::Type(
421 c"init's statusText member does not match the reason-phrase token production"
422 .to_owned(),
423 ));
424 }
425
426 *response.status.borrow_mut() =
429 HttpStatus::new_raw(init.status, init.statusText.clone().into());
430
431 if let Some(ref headers_member) = init.headers {
433 response
434 .Headers(can_gc)
435 .fill(Some(headers_member.clone()))?;
436 }
437
438 if let Some(ref body) = body {
440 if is_null_body_status(init.status) {
442 return Err(Error::Type(
443 c"Body is non-null but init's status member is a null body status".to_owned(),
444 ));
445 };
446
447 response.body_stream.set(Some(&*body.stream));
449 response.fetch_body_stream.set(Some(&*body.stream));
450 response.is_body_empty.set(false);
451
452 if let Some(content_type_contents) = &body.content_type {
455 if !response
456 .Headers(can_gc)
457 .Has(ByteString::new(b"Content-Type".to_vec()))
458 .unwrap()
459 {
460 response.Headers(can_gc).Append(
461 ByteString::new(b"Content-Type".to_vec()),
462 ByteString::new(content_type_contents.as_bytes().to_vec()),
463 )?;
464 }
465 };
466 } else {
467 let stream = ReadableStream::new_from_bytes(global, Vec::with_capacity(0), can_gc)?;
471 response.body_stream.set(Some(&*stream));
472 response.fetch_body_stream.set(Some(&*stream));
473 }
474
475 Ok(response)
476}
477
478fn serialize_without_fragment(url: &ServoUrl) -> &str {
479 &url[..Position::AfterQuery]
480}
481
482impl Response {
483 pub(crate) fn set_type(&self, new_response_type: DOMResponseType, can_gc: CanGc) {
484 *self.response_type.borrow_mut() = new_response_type;
485 self.set_response_members_by_type(new_response_type, can_gc);
486 }
487
488 pub(crate) fn set_headers(
489 &self,
490 option_hyper_headers: Option<Serde<HyperHeaders>>,
491 can_gc: CanGc,
492 ) {
493 self.Headers(can_gc)
494 .set_headers(match option_hyper_headers {
495 Some(hyper_headers) => hyper_headers.into_inner(),
496 None => HyperHeaders::new(),
497 });
498 }
499
500 pub(crate) fn set_status(&self, status: &HttpStatus) {
501 self.status.borrow_mut().clone_from(status);
502 }
503
504 pub(crate) fn set_final_url(&self, final_url: ServoUrl) {
505 *self.url.borrow_mut() = Some(final_url);
506 }
507
508 pub(crate) fn set_redirected(&self, is_redirected: bool) {
509 self.redirected.set(is_redirected);
510 }
511
512 fn set_response_members_by_type(&self, response_type: DOMResponseType, can_gc: CanGc) {
513 match response_type {
514 DOMResponseType::Error => {
515 *self.status.borrow_mut() = HttpStatus::new_error();
516 self.set_headers(None, can_gc);
517 },
518 DOMResponseType::Opaque => {
519 *self.url_list.borrow_mut() = vec![];
520 *self.status.borrow_mut() = HttpStatus::new_error();
521 self.set_headers(None, can_gc);
522 self.body_stream.set(None);
523 self.fetch_body_stream.set(None);
524 },
525 DOMResponseType::Opaqueredirect => {
526 *self.status.borrow_mut() = HttpStatus::new_error();
527 self.set_headers(None, can_gc);
528 self.body_stream.set(None);
529 self.fetch_body_stream.set(None);
530 },
531 DOMResponseType::Default => {},
532 DOMResponseType::Basic => {},
533 DOMResponseType::Cors => {},
534 }
535 }
536
537 pub(crate) fn set_stream_consumer(&self, sc: Option<StreamConsumer>) {
538 *self.stream_consumer.borrow_mut() = sc;
539 }
540
541 pub(crate) fn stream_chunk(&self, chunk: Vec<u8>, can_gc: CanGc) {
542 self.is_body_empty.set(false);
543 if let Some(stream_consumer) = self.stream_consumer.borrow().as_ref() {
545 stream_consumer.consume_chunk(chunk.as_slice());
546 } else if let Some(body) = self.fetch_body_stream.get() {
547 body.enqueue_native(chunk, can_gc);
548 }
549 }
550
551 pub(crate) fn finish(&self, can_gc: CanGc) {
552 if let Some(body) = self.fetch_body_stream.get() {
553 body.controller_close_native(can_gc);
554 }
555 let stream_consumer = self.stream_consumer.borrow_mut().take();
556 if let Some(stream_consumer) = stream_consumer {
557 stream_consumer.stream_end();
558 }
559 }
560}