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