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