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>,
58 is_body_empty: Cell<bool>,
59}
60
61impl Response {
62 pub(crate) fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> Response {
63 let stream = ReadableStream::new_with_external_underlying_source(
64 global,
65 UnderlyingSourceType::FetchResponse,
66 can_gc,
67 )
68 .expect("Failed to create ReadableStream with external underlying source");
69 Response {
70 reflector_: Reflector::new(),
71 headers_reflector: Default::default(),
72 status: DomRefCell::new(HttpStatus::default()),
73 response_type: DomRefCell::new(DOMResponseType::Default),
74 url: DomRefCell::new(None),
75 url_list: DomRefCell::new(vec![]),
76 body_stream: MutNullableDom::new(Some(&*stream)),
77 fetch_body_stream: MutNullableDom::new(Some(&*stream)),
78 stream_consumer: DomRefCell::new(None),
79 redirected: Cell::new(false),
80 is_body_empty: Cell::new(true),
81 }
82 }
83
84 pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Response> {
86 Self::new_with_proto(global, None, can_gc)
87 }
88
89 fn new_with_proto(
90 global: &GlobalScope,
91 proto: Option<HandleObject>,
92 can_gc: CanGc,
93 ) -> DomRoot<Response> {
94 reflect_dom_object_with_proto(
95 Box::new(Response::new_inherited(global, can_gc)),
96 global,
97 proto,
98 can_gc,
99 )
100 }
101
102 pub(crate) fn error_stream(&self, error: Error, can_gc: CanGc) {
103 if let Some(body) = self.fetch_body_stream.get() {
104 body.error_native(error, can_gc);
105 }
106 }
107
108 pub(crate) fn is_disturbed(&self) -> bool {
109 let body_stream = self.body_stream.get();
110 body_stream
111 .as_ref()
112 .is_some_and(|stream| stream.is_disturbed())
113 }
114
115 pub(crate) fn is_locked(&self) -> bool {
116 let body_stream = self.body_stream.get();
117 body_stream
118 .as_ref()
119 .is_some_and(|stream| stream.is_locked())
120 }
121}
122
123impl BodyMixin for Response {
124 fn is_body_used(&self) -> bool {
125 self.is_disturbed()
126 }
127
128 fn is_unusable(&self) -> bool {
129 self.body_stream
130 .get()
131 .is_some_and(|stream| stream.is_disturbed() || stream.is_locked())
132 }
133
134 fn body(&self) -> Option<DomRoot<ReadableStream>> {
135 self.body_stream.get()
136 }
137
138 fn get_mime_type(&self, can_gc: CanGc) -> Vec<u8> {
139 let headers = self.Headers(can_gc);
140 headers.extract_mime_type()
141 }
142}
143
144fn is_redirect_status(status: u16) -> bool {
146 status == 301 || status == 302 || status == 303 || status == 307 || status == 308
147}
148
149fn is_valid_status_text(status_text: &ByteString) -> bool {
151 for byte in status_text.iter() {
153 if !(*byte == b'\t' || *byte == b' ' || is_vchar(*byte) || is_obs_text(*byte)) {
154 return false;
155 }
156 }
157 true
158}
159
160fn is_null_body_status(status: u16) -> bool {
162 status == 101 || status == 204 || status == 205 || status == 304
163}
164
165impl ResponseMethods<crate::DomTypeHolder> for Response {
166 fn Constructor(
168 global: &GlobalScope,
169 proto: Option<HandleObject>,
170 can_gc: CanGc,
171 body_init: Option<BodyInit>,
172 init: &ResponseBinding::ResponseInit,
173 ) -> Fallible<DomRoot<Response>> {
174 let response = Response::new_with_proto(global, proto, can_gc);
177 if body_init.is_some() {
178 response.is_body_empty.set(false);
179 }
180
181 response.Headers(can_gc).set_guard(Guard::Response);
184
185 let body_with_type = match body_init {
188 Some(body) => Some(body.extract(global, false, can_gc)?),
189 None => None,
190 };
191
192 initialize_response(global, can_gc, body_with_type, init, response)
194 }
195
196 fn Error(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Response> {
198 let response = Response::new(global, can_gc);
199 *response.response_type.borrow_mut() = DOMResponseType::Error;
200 response.Headers(can_gc).set_guard(Guard::Immutable);
201 *response.status.borrow_mut() = HttpStatus::new_error();
202 response
203 }
204
205 fn Redirect(
207 global: &GlobalScope,
208 url: USVString,
209 status: u16,
210 can_gc: CanGc,
211 ) -> Fallible<DomRoot<Response>> {
212 let base_url = global.api_base_url();
214 let parsed_url = base_url.join(&url.0);
215
216 let url = match parsed_url {
218 Ok(url) => url,
219 Err(_) => return Err(Error::Type(c"ServoUrl could not be parsed".to_owned())),
220 };
221
222 if !is_redirect_status(status) {
224 return Err(Error::Range(c"status is not a redirect status".to_owned()));
225 }
226
227 let response = Response::new(global, can_gc);
230
231 *response.status.borrow_mut() = HttpStatus::new_raw(status, vec![]);
233
234 let url_bytestring =
236 ByteString::from_str(url.as_str()).unwrap_or(ByteString::new(b"".to_vec()));
237 response
238 .Headers(can_gc)
239 .Set(ByteString::new(b"Location".to_vec()), url_bytestring)?;
240
241 response.Headers(can_gc).set_guard(Guard::Immutable);
244
245 Ok(response)
247 }
248
249 fn CreateFromJson(
251 cx: JSContext,
252 global: &GlobalScope,
253 data: HandleValue,
254 init: &ResponseBinding::ResponseInit,
255 can_gc: CanGc,
256 ) -> Fallible<DomRoot<Response>> {
257 let json_str = serialize_jsval_to_json_utf8(cx, data)?;
259
260 let body_init = BodyInit::String(json_str);
264 let mut body = body_init.extract(global, false, can_gc)?;
265
266 let response = Response::new(global, can_gc);
269 response.Headers(can_gc).set_guard(Guard::Response);
270
271 body.content_type = Some("application/json".into());
273 initialize_response(global, can_gc, Some(body), init, response)
274 }
275
276 fn Type(&self) -> DOMResponseType {
278 *self.response_type.borrow() }
280
281 fn Url(&self) -> USVString {
283 USVString(String::from(
284 (*self.url.borrow())
285 .as_ref()
286 .map(serialize_without_fragment)
287 .unwrap_or(""),
288 ))
289 }
290
291 fn Redirected(&self) -> bool {
293 self.redirected.get()
294 }
295
296 fn Status(&self) -> u16 {
298 self.status.borrow().raw_code()
299 }
300
301 fn Ok(&self) -> bool {
303 self.status.borrow().is_success()
304 }
305
306 fn StatusText(&self) -> ByteString {
308 ByteString::new(self.status.borrow().message().to_vec())
309 }
310
311 fn Headers(&self, can_gc: CanGc) -> DomRoot<Headers> {
313 self.headers_reflector
314 .or_init(|| Headers::for_response(&self.global(), can_gc))
315 }
316
317 fn Clone(&self, can_gc: CanGc) -> Fallible<DomRoot<Response>> {
319 if self.is_unusable() {
321 return Err(Error::Type(c"cannot clone a disturbed response".to_owned()));
322 }
323
324 let new_response = Response::new(&self.global(), can_gc);
326 new_response
327 .Headers(can_gc)
328 .copy_from_headers(self.Headers(can_gc))?;
329 new_response
330 .Headers(can_gc)
331 .set_guard(self.Headers(can_gc).get_guard());
332
333 *new_response.response_type.borrow_mut() = *self.response_type.borrow();
334 new_response
335 .status
336 .borrow_mut()
337 .clone_from(&self.status.borrow());
338 new_response.url.borrow_mut().clone_from(&self.url.borrow());
339 new_response
340 .url_list
341 .borrow_mut()
342 .clone_from(&self.url_list.borrow());
343 new_response.is_body_empty.set(self.is_body_empty.get());
344
345 clone_body_stream_for_dom_body(&self.body_stream, &new_response.body_stream, can_gc)?;
348 new_response.fetch_body_stream.set(None);
350
351 Ok(new_response)
352 }
353
354 fn BodyUsed(&self) -> bool {
356 !self.is_body_empty.get() && self.is_body_used()
357 }
358
359 fn GetBody(&self) -> Option<DomRoot<ReadableStream>> {
361 self.body()
362 }
363
364 fn Text(&self, can_gc: CanGc) -> Rc<Promise> {
366 consume_body(self, BodyType::Text, can_gc)
367 }
368
369 fn Blob(&self, can_gc: CanGc) -> Rc<Promise> {
371 consume_body(self, BodyType::Blob, can_gc)
372 }
373
374 fn FormData(&self, can_gc: CanGc) -> Rc<Promise> {
376 consume_body(self, BodyType::FormData, can_gc)
377 }
378
379 fn Json(&self, can_gc: CanGc) -> Rc<Promise> {
381 consume_body(self, BodyType::Json, can_gc)
382 }
383
384 fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
386 consume_body(self, BodyType::ArrayBuffer, can_gc)
387 }
388
389 fn Bytes(&self, can_gc: CanGc) -> std::rc::Rc<Promise> {
391 consume_body(self, BodyType::Bytes, can_gc)
392 }
393}
394
395fn initialize_response(
397 global: &GlobalScope,
398 can_gc: CanGc,
399 body: Option<ExtractedBody>,
400 init: &ResponseBinding::ResponseInit,
401 response: DomRoot<Response>,
402) -> Result<DomRoot<Response>, Error> {
403 if init.status < 200 || init.status > 599 {
405 return Err(Error::Range(cformat!(
406 "init's status member should be in the range 200 to 599, inclusive, but is {}",
407 init.status
408 )));
409 }
410
411 if !is_valid_status_text(&init.statusText) {
414 return Err(Error::Type(
415 c"init's statusText member does not match the reason-phrase token production"
416 .to_owned(),
417 ));
418 }
419
420 *response.status.borrow_mut() =
423 HttpStatus::new_raw(init.status, init.statusText.clone().into());
424
425 if let Some(ref headers_member) = init.headers {
427 response
428 .Headers(can_gc)
429 .fill(Some(headers_member.clone()))?;
430 }
431
432 if let Some(ref body) = body {
434 if is_null_body_status(init.status) {
436 return Err(Error::Type(
437 c"Body is non-null but init's status member is a null body status".to_owned(),
438 ));
439 };
440
441 response.body_stream.set(Some(&*body.stream));
443 response.fetch_body_stream.set(Some(&*body.stream));
444 response.is_body_empty.set(false);
445
446 if let Some(content_type_contents) = &body.content_type {
449 if !response
450 .Headers(can_gc)
451 .Has(ByteString::new(b"Content-Type".to_vec()))
452 .unwrap()
453 {
454 response.Headers(can_gc).Append(
455 ByteString::new(b"Content-Type".to_vec()),
456 ByteString::new(content_type_contents.as_bytes().to_vec()),
457 )?;
458 }
459 };
460 } else {
461 let stream = ReadableStream::new_from_bytes(global, Vec::with_capacity(0), can_gc)?;
465 response.body_stream.set(Some(&*stream));
466 response.fetch_body_stream.set(Some(&*stream));
467 }
468
469 Ok(response)
470}
471
472fn serialize_without_fragment(url: &ServoUrl) -> &str {
473 &url[..Position::AfterQuery]
474}
475
476impl Response {
477 pub(crate) fn set_type(&self, new_response_type: DOMResponseType, can_gc: CanGc) {
478 *self.response_type.borrow_mut() = new_response_type;
479 self.set_response_members_by_type(new_response_type, can_gc);
480 }
481
482 pub(crate) fn set_headers(
483 &self,
484 option_hyper_headers: Option<Serde<HyperHeaders>>,
485 can_gc: CanGc,
486 ) {
487 self.Headers(can_gc)
488 .set_headers(match option_hyper_headers {
489 Some(hyper_headers) => hyper_headers.into_inner(),
490 None => HyperHeaders::new(),
491 });
492 }
493
494 pub(crate) fn set_status(&self, status: &HttpStatus) {
495 self.status.borrow_mut().clone_from(status);
496 }
497
498 pub(crate) fn set_final_url(&self, final_url: ServoUrl) {
499 *self.url.borrow_mut() = Some(final_url);
500 }
501
502 pub(crate) fn set_redirected(&self, is_redirected: bool) {
503 self.redirected.set(is_redirected);
504 }
505
506 fn set_response_members_by_type(&self, response_type: DOMResponseType, can_gc: CanGc) {
507 match response_type {
508 DOMResponseType::Error => {
509 *self.status.borrow_mut() = HttpStatus::new_error();
510 self.set_headers(None, can_gc);
511 },
512 DOMResponseType::Opaque => {
513 *self.url_list.borrow_mut() = vec![];
514 *self.status.borrow_mut() = HttpStatus::new_error();
515 self.set_headers(None, can_gc);
516 self.body_stream.set(None);
517 self.fetch_body_stream.set(None);
518 },
519 DOMResponseType::Opaqueredirect => {
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::Default => {},
526 DOMResponseType::Basic => {},
527 DOMResponseType::Cors => {},
528 }
529 }
530
531 pub(crate) fn set_stream_consumer(&self, sc: Option<StreamConsumer>) {
532 *self.stream_consumer.borrow_mut() = sc;
533 }
534
535 pub(crate) fn stream_chunk(&self, chunk: Vec<u8>, can_gc: CanGc) {
536 self.is_body_empty.set(false);
537 if let Some(stream_consumer) = self.stream_consumer.borrow().as_ref() {
539 stream_consumer.consume_chunk(chunk.as_slice());
540 } else if let Some(body) = self.fetch_body_stream.get() {
541 body.enqueue_native(chunk, can_gc);
542 }
543 }
544
545 pub(crate) fn finish(&self, can_gc: CanGc) {
546 if let Some(body) = self.fetch_body_stream.get() {
547 body.controller_close_native(can_gc);
548 }
549 let stream_consumer = self.stream_consumer.borrow_mut().take();
550 if let Some(stream_consumer) = stream_consumer {
551 stream_consumer.stream_end();
552 }
553 }
554}