Create HttpStatus to safely deal with HTTP responses status. (#33581)

Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
webbeef 2024-09-29 11:23:48 -07:00 committed by GitHub
parent 013473f1d5
commit 58f34ad7a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 344 additions and 403 deletions

View file

@ -21,6 +21,7 @@ use ipc_channel::router::ROUTER;
use js::jsapi::JSAutoRealm;
use js::rust::HandleObject;
use mime::{self, Mime};
use net_traits::http_status::HttpStatus;
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, PendingImageId,
PendingImageResponse, UsePlaceholder,
@ -240,20 +241,24 @@ impl FetchResponseListener for ImageContext {
}
}
let status_code = metadata
let status = metadata
.as_ref()
.and_then(|m| m.status.as_ref().map(|&(code, _)| code))
.unwrap_or(0);
.map(|m| m.status.clone())
.unwrap_or_else(HttpStatus::new_error);
self.status = match status_code {
0 => Err(NetworkError::Internal(
"No http status code received".to_owned(),
)),
200..=299 => Ok(()), // HTTP ok status codes
_ => Err(NetworkError::Internal(format!(
"HTTP error code {}",
status_code
))),
self.status = {
if status.is_error() {
Err(NetworkError::Internal(
"No http status code received".to_owned(),
))
} else if status.is_success() {
Ok(())
} else {
Err(NetworkError::Internal(format!(
"HTTP error code {}",
status.code()
)))
}
};
}

View file

@ -16,6 +16,7 @@ use euclid::default::Size2D;
use headers::{ContentLength, ContentRange, HeaderMapExt};
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix};
use http::header::{self, HeaderMap, HeaderValue};
use http::StatusCode;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsapi::JSAutoRealm;
@ -2731,13 +2732,14 @@ impl FetchResponseListener for HTMLMediaElementFetchListener {
}
}
let (status_is_ok, is_seekable) = self
.metadata
.as_ref()
.and_then(|m| m.status.as_ref())
.map_or((true, false), |s| {
(s.0 >= 200 && s.0 < 300, s.0 == 206 || s.0 == 416)
});
let (status_is_ok, is_seekable) = self.metadata.as_ref().map_or((true, false), |s| {
let status = &s.status;
(
status.is_success(),
*status == StatusCode::PARTIAL_CONTENT ||
*status == StatusCode::RANGE_NOT_SATISFIABLE,
)
});
if is_seekable {
// The server supports range requests,

View file

@ -21,6 +21,7 @@ use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsval::UndefinedValue;
use js::rust::{transform_str_to_source_text, CompileOptionsWrapper, HandleObject, Stencil};
use net_traits::http_status::HttpStatus;
use net_traits::request::{
CorsSettings, CredentialsMode, Destination, ParserMetadata, RequestBuilder,
};
@ -360,24 +361,25 @@ impl FetchResponseListener for ClassicContext {
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
});
let status_code = self
let status = self
.metadata
.as_ref()
.and_then(|m| match m.status {
Some((c, _)) => Some(c),
_ => None,
})
.unwrap_or(0);
.map(|m| m.status.clone())
.unwrap_or_else(HttpStatus::new_error);
self.status = match status_code {
0 => Err(NetworkError::Internal(
"No http status code received".to_owned(),
)),
200..=299 => Ok(()), // HTTP ok status codes
_ => Err(NetworkError::Internal(format!(
"HTTP error code {}",
status_code
))),
self.status = {
if status.is_error() {
Err(NetworkError::Internal(
"No http status code received".to_owned(),
))
} else if status.is_success() {
Ok(())
} else {
Err(NetworkError::Internal(format!(
"HTTP error code {}",
status.code()
)))
}
};
}

View file

@ -340,8 +340,7 @@ impl FetchResponseListener for PosterFrameFetchContext {
let status_is_ok = metadata
.as_ref()
.and_then(|m| m.status.as_ref())
.map_or(true, |s| s.0 >= 200 && s.0 < 300);
.map_or(true, |m| m.status.in_range(200..300));
if !status_is_ok {
self.cancelled = true;

View file

@ -8,10 +8,10 @@ use std::str::FromStr;
use dom_struct::dom_struct;
use http::header::HeaderMap as HyperHeaders;
use http::StatusCode;
use hyper_serde::Serde;
use js::jsapi::JSObject;
use js::rust::HandleObject;
use net_traits::http_status::HttpStatus;
use servo_url::ServoUrl;
use url::Position;
@ -37,11 +37,8 @@ use crate::script_runtime::{CanGc, JSContext as SafeJSContext, StreamConsumer};
pub struct Response {
reflector_: Reflector,
headers_reflector: MutNullableDom<Headers>,
/// `None` can be considered a StatusCode of `0`.
#[ignore_malloc_size_of = "Defined in hyper"]
#[no_trace]
status: DomRefCell<Option<StatusCode>>,
raw_status: DomRefCell<Option<(u16, Vec<u8>)>>,
status: DomRefCell<HttpStatus>,
response_type: DomRefCell<DOMResponseType>,
#[no_trace]
url: DomRefCell<Option<ServoUrl>>,
@ -64,8 +61,7 @@ impl Response {
Response {
reflector_: Reflector::new(),
headers_reflector: Default::default(),
status: DomRefCell::new(Some(StatusCode::OK)),
raw_status: DomRefCell::new(Some((200, b"".to_vec()))),
status: DomRefCell::new(HttpStatus::default()),
response_type: DomRefCell::new(DOMResponseType::Default),
url: DomRefCell::new(None),
url_list: DomRefCell::new(vec![]),
@ -119,11 +115,8 @@ impl Response {
let r = Response::new_with_proto(global, proto, can_gc);
// Step 3
*r.status.borrow_mut() = Some(StatusCode::from_u16(init.status).unwrap());
// Step 4
*r.raw_status.borrow_mut() = Some((init.status, init.statusText.clone().into()));
// Step 3 & 4
*r.status.borrow_mut() = HttpStatus::new_raw(init.status, init.statusText.clone().into());
// Step 5
if let Some(ref headers_member) = init.headers {
@ -177,7 +170,7 @@ impl Response {
let r = Response::new(global);
*r.response_type.borrow_mut() = DOMResponseType::Error;
r.Headers().set_guard(Guard::Immutable);
*r.raw_status.borrow_mut() = Some((0, b"".to_vec()));
*r.status.borrow_mut() = HttpStatus::new_error();
r
}
@ -207,8 +200,7 @@ impl Response {
let r = Response::new(global);
// Step 5
*r.status.borrow_mut() = Some(StatusCode::from_u16(status).unwrap());
*r.raw_status.borrow_mut() = Some((status, b"".to_vec()));
*r.status.borrow_mut() = HttpStatus::new_raw(status, vec![]);
// Step 6
let url_bytestring =
@ -298,29 +290,17 @@ impl ResponseMethods for Response {
// https://fetch.spec.whatwg.org/#dom-response-status
fn Status(&self) -> u16 {
match *self.raw_status.borrow() {
Some((s, _)) => s,
None => 0,
}
self.status.borrow().raw_code()
}
// https://fetch.spec.whatwg.org/#dom-response-ok
fn Ok(&self) -> bool {
match *self.status.borrow() {
Some(s) => {
let status_num = s.as_u16();
(200..=299).contains(&status_num)
},
None => false,
}
self.status.borrow().is_success()
}
// https://fetch.spec.whatwg.org/#dom-response-statustext
fn StatusText(&self) -> ByteString {
match *self.raw_status.borrow() {
Some((_, ref st)) => ByteString::new(st.clone()),
None => ByteString::new(b"".to_vec()),
}
ByteString::new(self.status.borrow().message().to_vec())
}
// https://fetch.spec.whatwg.org/#dom-response-headers
@ -345,11 +325,10 @@ impl ResponseMethods for Response {
// Instead of storing a net_traits::Response internally, we
// only store the relevant fields, and only clone them here
*new_response.response_type.borrow_mut() = *self.response_type.borrow();
*new_response.status.borrow_mut() = *self.status.borrow();
new_response
.raw_status
.status
.borrow_mut()
.clone_from(&self.raw_status.borrow());
.clone_from(&self.status.borrow());
new_response.url.borrow_mut().clone_from(&self.url.borrow());
new_response
.url_list
@ -420,8 +399,8 @@ impl Response {
});
}
pub fn set_raw_status(&self, status: Option<(u16, Vec<u8>)>) {
*self.raw_status.borrow_mut() = status;
pub fn set_status(&self, status: &HttpStatus) {
self.status.borrow_mut().clone_from(status);
}
pub fn set_final_url(&self, final_url: ServoUrl) {
@ -435,20 +414,17 @@ impl Response {
fn set_response_members_by_type(&self, response_type: DOMResponseType) {
match response_type {
DOMResponseType::Error => {
*self.status.borrow_mut() = None;
self.set_raw_status(None);
*self.status.borrow_mut() = HttpStatus::new_error();
self.set_headers(None);
},
DOMResponseType::Opaque => {
*self.url_list.borrow_mut() = vec![];
*self.status.borrow_mut() = None;
self.set_raw_status(None);
*self.status.borrow_mut() = HttpStatus::new_error();
self.set_headers(None);
self.body_stream.set(None);
},
DOMResponseType::Opaqueredirect => {
*self.status.borrow_mut() = None;
self.set_raw_status(None);
*self.status.borrow_mut() = HttpStatus::new_error();
self.set_headers(None);
self.body_stream.set(None);
},

View file

@ -26,6 +26,7 @@ use js::rust::wrappers::JS_ParseJSON;
use js::rust::HandleObject;
use js::typedarray::{ArrayBuffer, ArrayBufferU8};
use mime::{self, Mime, Name};
use net_traits::http_status::HttpStatus;
use net_traits::request::{CredentialsMode, Destination, Referrer, RequestBuilder, RequestMode};
use net_traits::CoreResourceMsg::Fetch;
use net_traits::{
@ -101,7 +102,7 @@ struct XHRContext {
#[derive(Clone)]
pub enum XHRProgress {
/// Notify that headers have been received
HeadersReceived(GenerationId, Option<HeaderMap>, Option<(u16, Vec<u8>)>),
HeadersReceived(GenerationId, Option<HeaderMap>, HttpStatus),
/// Partial progress (after receiving headers), containing portion of the response
Loading(GenerationId, Vec<u8>),
/// Loading is done
@ -129,8 +130,8 @@ pub struct XMLHttpRequest {
with_credentials: Cell<bool>,
upload: Dom<XMLHttpRequestUpload>,
response_url: DomRefCell<String>,
status: Cell<u16>,
status_text: DomRefCell<ByteString>,
#[no_trace]
status: DomRefCell<HttpStatus>,
response: DomRefCell<Vec<u8>>,
response_type: Cell<XMLHttpRequestResponseType>,
response_xml: MutNullableDom<Document>,
@ -189,8 +190,7 @@ impl XMLHttpRequest {
with_credentials: Cell::new(false),
upload: Dom::from_ref(&*XMLHttpRequestUpload::new(global)),
response_url: DomRefCell::new(String::new()),
status: Cell::new(0),
status_text: DomRefCell::new(ByteString::new(vec![])),
status: DomRefCell::new(HttpStatus::new_error()),
response: DomRefCell::new(vec![]),
response_type: Cell::new(XMLHttpRequestResponseType::_empty),
response_xml: Default::default(),
@ -442,8 +442,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
*self.request_headers.borrow_mut() = HeaderMap::new();
self.send_flag.set(false);
self.upload_listener.set(false);
*self.status_text.borrow_mut() = ByteString::new(vec![]);
self.status.set(0);
*self.status.borrow_mut() = HttpStatus::new_error();
// Step 13
if self.ready_state.get() != XMLHttpRequestState::Opened {
@ -835,12 +834,12 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
/// <https://xhr.spec.whatwg.org/#the-status-attribute>
fn Status(&self) -> u16 {
self.status.get()
self.status.borrow().raw_code()
}
/// <https://xhr.spec.whatwg.org/#the-statustext-attribute>
fn StatusText(&self) -> ByteString {
self.status_text.borrow().clone()
ByteString::new(self.status.borrow().message().to_vec())
}
/// <https://xhr.spec.whatwg.org/#the-getresponseheader()-method>
@ -1132,9 +1131,8 @@ impl XMLHttpRequest {
// Part of step 13, send() (processing response)
// XXXManishearth handle errors, if any (substep 1)
// Substep 2
if let Some((code, reason)) = status {
self.status.set(code);
*self.status_text.borrow_mut() = ByteString::new(reason);
if !status.is_error() {
*self.status.borrow_mut() = status.clone();
}
if let Some(h) = headers.as_ref() {
*self.response_headers.borrow_mut() = h.clone();