1use std::sync::atomic::AtomicBool;
8
9use http::HeaderMap;
10use hyper_serde::Serde;
11use malloc_size_of_derive::MallocSizeOf;
12use parking_lot::Mutex;
13use serde::{Deserialize, Serialize};
14use servo_arc::Arc;
15use servo_url::ServoUrl;
16
17use crate::fetch::headers::extract_mime_type_as_mime;
18use crate::http_status::HttpStatus;
19use crate::resource_fetch_timing::{ResourceFetchTimingContainer, ResourceTimingType};
20use crate::{
21 FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
22 TlsSecurityInfo,
23};
24
25#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
27pub enum ResponseType {
28 Basic,
29 Cors,
30 Default,
31 Error(NetworkError),
32 Opaque,
33 OpaqueRedirect,
34}
35
36#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
38pub enum TerminationReason {
39 EndUserAbort,
40 Fatal,
41 Timeout,
42}
43
44#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
47pub enum ResponseBody {
48 Empty, Receiving(Vec<u8>),
50 Done(Vec<u8>),
51}
52
53impl ResponseBody {
54 pub fn is_done(&self) -> bool {
55 match *self {
56 ResponseBody::Done(..) => true,
57 ResponseBody::Empty | ResponseBody::Receiving(..) => false,
58 }
59 }
60}
61
62#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
64pub enum RedirectTaint {
65 #[default]
66 SameOrigin,
67 SameSite,
68 CrossSite,
69}
70
71#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
73pub enum CacheState {
74 None,
75 Local,
76 Validated,
77 Partial,
78}
79
80#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
81pub struct ResponseInit {
82 pub url: ServoUrl,
83 #[serde(
84 deserialize_with = "::hyper_serde::deserialize",
85 serialize_with = "::hyper_serde::serialize"
86 )]
87 pub headers: HeaderMap,
88 pub status_code: u16,
89 pub referrer: Option<ServoUrl>,
90 pub location_url: Option<Result<ServoUrl, String>>,
91}
92
93#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
95pub struct Response {
96 pub response_type: ResponseType,
97 pub termination_reason: Option<TerminationReason>,
98 url: Option<ServoUrl>,
99 pub url_list: Vec<ServoUrl>,
100 pub status: HttpStatus,
101 #[serde(
102 deserialize_with = "::hyper_serde::deserialize",
103 serialize_with = "::hyper_serde::serialize"
104 )]
105 pub headers: HeaderMap,
106 #[conditional_malloc_size_of]
107 pub body: Arc<Mutex<ResponseBody>>,
108 pub cache_state: CacheState,
109 pub tls_security_info: Option<TlsSecurityInfo>,
110 pub referrer: Option<ServoUrl>,
111 pub redirect_taint: RedirectTaint,
113 pub referrer_policy: ReferrerPolicy,
114 pub cors_exposed_header_name_list: Vec<String>,
116 pub location_url: Option<Result<ServoUrl, String>>,
118 pub internal_response: Option<Box<Response>>,
121 pub return_internal: bool,
123 #[conditional_malloc_size_of]
125 pub aborted: Arc<AtomicBool>,
126 pub resource_timing: ResourceFetchTimingContainer,
128
129 pub range_requested: bool,
131
132 pub request_includes_credentials: bool,
135}
136
137impl Response {
138 pub fn new(url: ServoUrl, resource_timing: ResourceFetchTiming) -> Response {
139 Response {
140 response_type: ResponseType::Default,
141 termination_reason: None,
142 url: Some(url),
143 url_list: vec![],
144 status: HttpStatus::default(),
145 headers: HeaderMap::new(),
146 body: Arc::new(Mutex::new(ResponseBody::Empty)),
147 cache_state: CacheState::None,
148 tls_security_info: None,
149 referrer: None,
150 referrer_policy: ReferrerPolicy::EmptyString,
151 cors_exposed_header_name_list: vec![],
152 location_url: None,
153 internal_response: None,
154 return_internal: true,
155 aborted: Arc::new(AtomicBool::new(false)),
156 resource_timing: resource_timing.into(),
157 range_requested: false,
158 request_includes_credentials: true,
159 redirect_taint: Default::default(),
160 }
161 }
162
163 pub fn from_init(init: ResponseInit, resource_timing_type: ResourceTimingType) -> Response {
164 let mut res = Response::new(init.url, ResourceFetchTiming::new(resource_timing_type));
165 res.location_url = init.location_url;
166 res.headers = init.headers;
167 res.referrer = init.referrer;
168 res.status = HttpStatus::new_raw(init.status_code, vec![]);
169 res
170 }
171
172 pub fn network_error(e: NetworkError) -> Response {
173 Response {
174 response_type: ResponseType::Error(e),
175 termination_reason: None,
176 url: None,
177 url_list: vec![],
178 status: HttpStatus::new_error(),
179 headers: HeaderMap::new(),
180 body: Arc::new(Mutex::new(ResponseBody::Empty)),
181 cache_state: CacheState::None,
182 tls_security_info: None,
183 referrer: None,
184 referrer_policy: ReferrerPolicy::EmptyString,
185 cors_exposed_header_name_list: vec![],
186 location_url: None,
187 internal_response: None,
188 return_internal: true,
189 aborted: Arc::new(AtomicBool::new(false)),
190 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Error).into(),
191 range_requested: false,
192 request_includes_credentials: true,
193 redirect_taint: Default::default(),
194 }
195 }
196
197 pub fn url(&self) -> Option<&ServoUrl> {
198 self.url.as_ref()
199 }
200
201 pub fn is_network_error(&self) -> bool {
202 matches!(self.response_type, ResponseType::Error(..))
203 }
204
205 pub fn get_network_error(&self) -> Option<&NetworkError> {
206 match self.response_type {
207 ResponseType::Error(ref e) => Some(e),
208 _ => None,
209 }
210 }
211
212 pub fn set_network_error(&mut self, network_error: NetworkError) {
213 self.response_type = ResponseType::Error(network_error);
214 }
215
216 pub fn actual_response(&self) -> &Response {
217 match &self.internal_response {
218 Some(internal_response) if self.return_internal => internal_response,
219 _ => self,
220 }
221 }
222
223 #[expect(
224 clippy::unnecessary_unwrap,
225 reason = "match doesn't work, the borrow checker is overly conservative about &mut here"
226 )]
227 pub fn actual_response_mut(&mut self) -> &mut Response {
228 if self.return_internal && self.internal_response.is_some() {
229 self.internal_response.as_mut().unwrap()
230 } else {
231 self
232 }
233 }
234
235 pub fn to_actual(self) -> Response {
236 match self.internal_response {
237 Some(internal_response) if self.return_internal => *internal_response,
238 _ => self,
239 }
240 }
241
242 pub fn get_resource_timing(&self) -> &ResourceFetchTimingContainer {
243 &self.resource_timing
244 }
245
246 #[rustfmt::skip]
249 pub fn to_filtered(self, filter_type: ResponseType) -> Response {
250 match filter_type {
251 ResponseType::Default |
252 ResponseType::Error(..) => panic!(),
253 _ => (),
254 }
255
256 let old_response = self.to_actual();
257
258 if let ResponseType::Error(e) = old_response.response_type {
259 return Response::network_error(e);
260 }
261
262 let old_headers = old_response.headers.clone();
263 let exposed_headers = old_response.cors_exposed_header_name_list.clone();
264 let mut response = old_response.clone();
265 response.internal_response = Some(Box::new(old_response));
266 response.response_type = filter_type;
267
268 match response.response_type {
269 ResponseType::Default |
270 ResponseType::Error(..) => unreachable!(),
271
272 ResponseType::Basic => {
273 let headers = old_headers.iter().filter(|(name, _)| {
274 !matches!(&*name.as_str().to_ascii_lowercase(), "set-cookie" | "set-cookie2")
275 }).map(|(n, v)| (n.clone(), v.clone())).collect();
276 response.headers = headers;
277 },
278
279 ResponseType::Cors => {
280 let headers = old_headers.iter().filter(|(name, _)| {
281 match &*name.as_str().to_ascii_lowercase() {
282 "cache-control" | "content-language" | "content-length" | "content-type" |
283 "expires" | "last-modified" | "pragma" => true,
284 "set-cookie" | "set-cookie2" => false,
285 header => {
286 exposed_headers.iter().any(|h| *header == h.as_str().to_ascii_lowercase())
287 }
288 }
289 }).map(|(n, v)| (n.clone(), v.clone())).collect();
290 response.headers = headers;
291 },
292
293 ResponseType::Opaque => {
294 response.url_list = vec![];
295 response.url = None;
296 response.headers = HeaderMap::new();
297 response.status = HttpStatus::new_error();
298 response.body = Arc::new(Mutex::new(ResponseBody::Empty));
299 response.cache_state = CacheState::None;
300 },
301
302 ResponseType::OpaqueRedirect => {
303 response.headers = HeaderMap::new();
304 response.status = HttpStatus::new_error();
305 response.body = Arc::new(Mutex::new(ResponseBody::Empty));
306 response.cache_state = CacheState::None;
307 },
308 }
309
310 response
311 }
312
313 pub fn metadata(&self) -> Result<FetchMetadata, NetworkError> {
314 fn init_metadata(response: &Response, url: &ServoUrl) -> Metadata {
315 let mut metadata = Metadata::default(url.clone());
316 metadata.set_content_type(extract_mime_type_as_mime(&response.headers).as_ref());
317 metadata.location_url.clone_from(&response.location_url);
318 metadata.headers = Some(Serde(response.headers.clone()));
319 metadata.status.clone_from(&response.status);
320 metadata.referrer.clone_from(&response.referrer);
321 metadata.referrer_policy = response.referrer_policy;
322 metadata.redirected = response.actual_response().url_list.len() > 1;
323 metadata
324 .tls_security_info
325 .clone_from(&response.tls_security_info);
326 metadata
327 }
328
329 if let Some(error) = self.get_network_error() {
330 return Err(error.clone());
331 }
332
333 let metadata = self.url.as_ref().map(|url| init_metadata(self, url));
334
335 if let Some(ref response) = self.internal_response {
336 match response.url {
337 Some(ref url) => {
338 let unsafe_metadata = init_metadata(response, url);
339
340 match self.response_type {
341 ResponseType::Basic => Ok(FetchMetadata::Filtered {
342 filtered: FilteredMetadata::Basic(metadata.unwrap()),
343 unsafe_: unsafe_metadata,
344 }),
345 ResponseType::Cors => Ok(FetchMetadata::Filtered {
346 filtered: FilteredMetadata::Cors(metadata.unwrap()),
347 unsafe_: unsafe_metadata,
348 }),
349 ResponseType::Default => unreachable!(),
350 ResponseType::Error(ref network_err) => Err(network_err.clone()),
351 ResponseType::Opaque => Ok(FetchMetadata::Filtered {
352 filtered: FilteredMetadata::Opaque,
353 unsafe_: unsafe_metadata,
354 }),
355 ResponseType::OpaqueRedirect => Ok(FetchMetadata::Filtered {
356 filtered: FilteredMetadata::OpaqueRedirect(url.clone()),
357 unsafe_: unsafe_metadata,
358 }),
359 }
360 },
361 None => Err(NetworkError::ResourceLoadError(
362 "No url found in unsafe response".to_owned(),
363 )),
364 }
365 } else {
366 assert_eq!(self.response_type, ResponseType::Default);
367 Ok(FetchMetadata::Unfiltered(metadata.unwrap()))
368 }
369 }
370}