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::cell::DomRefCell;
15use script_bindings::cformat;
16use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
17use servo_url::ServoUrl;
18use url::Position;
19
20use crate::body::{
21 BodyMixin, BodyType, Extractable, ExtractedBody, body_text_stream,
22 clone_body_stream_for_dom_body, consume_body,
23};
24use crate::dom::bindings::codegen::Bindings::HeadersBinding::HeadersMethods;
25use crate::dom::bindings::codegen::Bindings::ResponseBinding;
26use crate::dom::bindings::codegen::Bindings::ResponseBinding::{
27 ResponseMethods, ResponseType as DOMResponseType,
28};
29use crate::dom::bindings::codegen::Bindings::XMLHttpRequestBinding::BodyInit;
30use crate::dom::bindings::error::{Error, Fallible};
31use crate::dom::bindings::reflector::DomGlobal;
32use crate::dom::bindings::root::{DomRoot, MutNullableDom};
33use crate::dom::bindings::str::{ByteString, USVString, serialize_jsval_to_json_utf8};
34use crate::dom::globalscope::GlobalScope;
35use crate::dom::headers::{Guard, Headers, is_obs_text, is_vchar};
36use crate::dom::promise::Promise;
37use crate::dom::stream::readablestream::ReadableStream;
38use crate::dom::stream::underlyingsourcecontainer::UnderlyingSourceType;
39use crate::script_runtime::StreamConsumer;
40
41#[dom_struct]
42pub(crate) struct Response {
43 reflector_: Reflector,
44 headers_reflector: MutNullableDom<Headers>,
45 #[no_trace]
46 status: DomRefCell<HttpStatus>,
47 response_type: DomRefCell<DOMResponseType>,
48 #[no_trace]
52 url: DomRefCell<Option<ServoUrl>>,
53 #[no_trace]
54 url_list: DomRefCell<Vec<ServoUrl>>,
55 body_stream: MutNullableDom<ReadableStream>,
57 fetch_body_stream: MutNullableDom<ReadableStream>,
60 #[ignore_malloc_size_of = "StreamConsumer"]
61 stream_consumer: DomRefCell<Option<StreamConsumer>>,
62 redirected: Cell<bool>,
64}
65
66impl Response {
67 pub(crate) fn new_inherited(stream: DomRoot<ReadableStream>) -> Response {
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 }
80 }
81
82 pub(crate) fn new(cx: &mut js::context::JSContext, global: &GlobalScope) -> DomRoot<Response> {
84 Self::new_with_proto(cx, global, None)
85 }
86
87 fn new_with_proto(
88 cx: &mut js::context::JSContext,
89 global: &GlobalScope,
90 proto: Option<HandleObject>,
91 ) -> DomRoot<Response> {
92 let stream = ReadableStream::new_with_external_underlying_source(
93 cx,
94 global,
95 UnderlyingSourceType::FetchResponse,
96 )
97 .expect("Failed to create ReadableStream with external underlying source");
98 reflect_dom_object_with_proto(cx, Box::new(Response::new_inherited(stream)), global, proto)
99 }
100
101 pub(crate) fn error_stream(&self, cx: &mut js::context::JSContext, error: Error) {
102 if let Some(body) = self.fetch_body_stream.get() {
103 body.error_native(cx, error);
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, cx: &mut js::context::JSContext) -> Vec<u8> {
138 let headers = self.Headers(cx);
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 cx: &mut js::context::JSContext,
168 global: &GlobalScope,
169 proto: Option<HandleObject>,
170 body_init: Option<BodyInit>,
171 init: &ResponseBinding::ResponseInit,
172 ) -> Fallible<DomRoot<Response>> {
173 let response = Response::new_with_proto(cx, global, proto);
176
177 response.Headers(cx).set_guard(Guard::Response);
180
181 let body_with_type = match body_init {
184 Some(body) => Some(body.extract(cx, global, false)?),
185 None => None,
186 };
187
188 initialize_response(cx, body_with_type, init, response)
190 }
191
192 fn Error(cx: &mut js::context::JSContext, global: &GlobalScope) -> DomRoot<Response> {
194 let response = Response::new(cx, global);
195 *response.response_type.borrow_mut() = DOMResponseType::Error;
196 response.Headers(cx).set_guard(Guard::Immutable);
197 *response.status.borrow_mut() = HttpStatus::new_error();
198 response
199 }
200
201 fn Redirect(
203 cx: &mut js::context::JSContext,
204 global: &GlobalScope,
205 url: USVString,
206 status: u16,
207 ) -> Fallible<DomRoot<Response>> {
208 let base_url = global.api_base_url();
210 let parsed_url = base_url.join(&url.0);
211
212 let url = match parsed_url {
214 Ok(url) => url,
215 Err(_) => return Err(Error::Type(c"ServoUrl could not be parsed".to_owned())),
216 };
217
218 if !is_redirect_status(status) {
220 return Err(Error::Range(c"status is not a redirect status".to_owned()));
221 }
222
223 let response = Response::new(cx, global);
226
227 *response.status.borrow_mut() = HttpStatus::new_raw(status, vec![]);
229
230 let url_bytestring =
232 ByteString::from_str(url.as_str()).unwrap_or(ByteString::new(b"".to_vec()));
233 response
234 .Headers(cx)
235 .Set(ByteString::new(b"Location".to_vec()), url_bytestring)?;
236
237 response.Headers(cx).set_guard(Guard::Immutable);
240
241 Ok(response)
243 }
244
245 fn CreateFromJson(
247 cx: &mut js::context::JSContext,
248 global: &GlobalScope,
249 data: HandleValue,
250 init: &ResponseBinding::ResponseInit,
251 ) -> Fallible<DomRoot<Response>> {
252 let json_str = serialize_jsval_to_json_utf8(cx, data)?;
254
255 let body_init = BodyInit::String(json_str);
259 let mut body = body_init.extract(cx, global, false)?;
260
261 let response = Response::new(cx, global);
264 response.Headers(cx).set_guard(Guard::Response);
265
266 body.content_type = Some("application/json".into());
268 initialize_response(cx, Some(body), init, response)
269 }
270
271 fn Type(&self) -> DOMResponseType {
273 *self.response_type.borrow() }
275
276 fn Url(&self) -> USVString {
278 USVString(String::from(
279 (*self.url.borrow())
280 .as_ref()
281 .map(serialize_without_fragment)
282 .unwrap_or(""),
283 ))
284 }
285
286 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, cx: &mut js::context::JSContext) -> DomRoot<Headers> {
313 self.headers_reflector
314 .or_init(|| Headers::for_response(cx, &self.global()))
315 }
316
317 fn Clone(&self, cx: &mut js::context::JSContext) -> 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(cx, &self.global());
326 new_response
327 .Headers(cx)
328 .copy_from_headers(&self.Headers(cx))?;
329 new_response
330 .Headers(cx)
331 .set_guard(self.Headers(cx).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
344 clone_body_stream_for_dom_body(cx, &self.body_stream, &new_response.body_stream)?;
347 new_response.fetch_body_stream.set(None);
349
350 Ok(new_response)
351 }
352
353 fn BodyUsed(&self) -> bool {
355 self.is_body_used()
356 }
357
358 fn GetBody(&self) -> Option<DomRoot<ReadableStream>> {
360 self.body()
361 }
362
363 fn Text(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
365 consume_body(cx, self, BodyType::Text)
366 }
367
368 fn Blob(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
370 consume_body(cx, self, BodyType::Blob)
371 }
372
373 fn FormData(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
375 consume_body(cx, self, BodyType::FormData)
376 }
377
378 fn Json(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
380 consume_body(cx, self, BodyType::Json)
381 }
382
383 fn ArrayBuffer(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
385 consume_body(cx, self, BodyType::ArrayBuffer)
386 }
387
388 fn Bytes(&self, cx: &mut js::context::JSContext) -> Rc<Promise> {
390 consume_body(cx, self, BodyType::Bytes)
391 }
392
393 fn TextStream(&self, cx: &mut js::context::JSContext) -> Fallible<DomRoot<ReadableStream>> {
395 body_text_stream(cx, self)
396 }
397}
398
399fn initialize_response(
401 cx: &mut js::context::JSContext,
402 body: Option<ExtractedBody>,
403 init: &ResponseBinding::ResponseInit,
404 response: DomRoot<Response>,
405) -> Result<DomRoot<Response>, Error> {
406 if init.status < 200 || init.status > 599 {
408 return Err(Error::Range(cformat!(
409 "init's status member should be in the range 200 to 599, inclusive, but is {}",
410 init.status
411 )));
412 }
413
414 if !is_valid_status_text(&init.statusText) {
417 return Err(Error::Type(
418 c"init's statusText member does not match the reason-phrase token production"
419 .to_owned(),
420 ));
421 }
422
423 *response.status.borrow_mut() =
426 HttpStatus::new_raw(init.status, init.statusText.clone().into());
427
428 if let Some(ref headers_member) = init.headers {
430 response.Headers(cx).fill(Some(headers_member.clone()))?;
431 }
432
433 if let Some(ref body) = body {
435 if is_null_body_status(init.status) {
437 return Err(Error::Type(
438 c"Body is non-null but init's status member is a null body status".to_owned(),
439 ));
440 };
441
442 response.body_stream.set(Some(&*body.stream));
444 response.fetch_body_stream.set(Some(&*body.stream));
445
446 if let Some(content_type_contents) = &body.content_type &&
449 !response
450 .Headers(cx)
451 .Has(ByteString::new(b"Content-Type".to_vec()))
452 .unwrap()
453 {
454 response.Headers(cx).Append(
455 ByteString::new(b"Content-Type".to_vec()),
456 ByteString::new(content_type_contents.as_bytes().to_vec()),
457 )?;
458 };
459 } else {
460 response.body_stream.set(None);
461 response.fetch_body_stream.set(None);
462 }
463
464 Ok(response)
465}
466
467fn serialize_without_fragment(url: &ServoUrl) -> &str {
468 &url[..Position::AfterQuery]
469}
470
471impl Response {
472 pub(crate) fn set_type(
473 &self,
474 cx: &mut js::context::JSContext,
475 new_response_type: DOMResponseType,
476 ) {
477 *self.response_type.borrow_mut() = new_response_type;
478 self.set_response_members_by_type(cx, new_response_type);
479 }
480
481 pub(crate) fn set_headers(
482 &self,
483 cx: &mut js::context::JSContext,
484 option_hyper_headers: Option<Serde<HyperHeaders>>,
485 ) {
486 self.Headers(cx).set_headers(match option_hyper_headers {
487 Some(hyper_headers) => hyper_headers.into_inner(),
488 None => HyperHeaders::new(),
489 });
490 }
491
492 pub(crate) fn set_status(&self, status: &HttpStatus) {
493 self.status.borrow_mut().clone_from(status);
494 }
495
496 pub(crate) fn set_final_url(&self, final_url: ServoUrl) {
497 *self.url.borrow_mut() = Some(final_url);
498 }
499
500 pub(crate) fn set_redirected(&self, is_redirected: bool) {
501 self.redirected.set(is_redirected);
502 }
503
504 fn set_response_members_by_type(
505 &self,
506 cx: &mut js::context::JSContext,
507 response_type: DOMResponseType,
508 ) {
509 match response_type {
510 DOMResponseType::Error => {
511 *self.status.borrow_mut() = HttpStatus::new_error();
512 self.set_headers(cx, None);
513 },
514 DOMResponseType::Opaque => {
515 *self.url_list.borrow_mut() = vec![];
516 *self.status.borrow_mut() = HttpStatus::new_error();
517 self.set_headers(cx, None);
518 self.body_stream.set(None);
519 self.fetch_body_stream.set(None);
520 },
521 DOMResponseType::Opaqueredirect => {
522 *self.status.borrow_mut() = HttpStatus::new_error();
523 self.set_headers(cx, None);
524 self.body_stream.set(None);
525 self.fetch_body_stream.set(None);
526 },
527 DOMResponseType::Default => {},
528 DOMResponseType::Basic => {},
529 DOMResponseType::Cors => {},
530 }
531 }
532
533 pub(crate) fn set_stream_consumer(&self, sc: Option<StreamConsumer>) {
534 *self.stream_consumer.borrow_mut() = sc;
535 }
536
537 pub(crate) fn stream_chunk(&self, cx: &mut js::context::JSContext, chunk: Vec<u8>) {
538 if let Some(stream_consumer) = self.stream_consumer.borrow().as_ref() {
540 stream_consumer.consume_chunk(chunk.as_slice());
541 } else if let Some(body) = self.fetch_body_stream.get() {
542 body.enqueue_native(cx, chunk);
543 }
544 }
545
546 pub(crate) fn finish(&self, cx: &mut js::context::JSContext) {
547 if let Some(body) = self.fetch_body_stream.get() {
548 body.controller_close_native(cx);
549 }
550 let stream_consumer = self.stream_consumer.borrow_mut().take();
551 if let Some(stream_consumer) = stream_consumer {
552 stream_consumer.stream_end();
553 }
554 }
555}