Skip to content

Commit 6edd78e

Browse files
committed
Fix some Content-Type tests
1 parent 6621612 commit 6edd78e

File tree

19 files changed

+126
-262
lines changed

19 files changed

+126
-262
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/net/fetch/methods.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use crate::data_loader::decode;
66
use crate::fetch::cors_cache::CorsCache;
7-
use crate::fetch::headers::determine_nosniff;
87
use crate::filemanager_thread::{FileManager, FILE_CHUNK_SIZE};
98
use crate::http_loader::{determine_requests_referrer, http_fetch, HttpState};
109
use crate::http_loader::{set_default_accept, set_default_accept_language};
@@ -20,6 +19,7 @@ use ipc_channel::ipc::{self, IpcReceiver};
2019
use mime::{self, Mime};
2120
use net_traits::blob_url_store::{parse_blob_url, BlobURLStoreError};
2221
use net_traits::filemanager_thread::{FileTokenCheck, RelativePos};
22+
use net_traits::header::{determine_nosniff, extract_mime_type_as_mime};
2323
use net_traits::request::{
2424
is_cors_safelisted_method, is_cors_safelisted_request_header, Origin, ResponseTainting, Window,
2525
};
@@ -842,7 +842,7 @@ pub fn should_be_blocked_due_to_nosniff(
842842

843843
// Step 2
844844
// Note: an invalid MIME type will produce a `None`.
845-
let content_type_header = response_headers.typed_get::<ContentType>();
845+
let mime_type = extract_mime_type_as_mime(response_headers);
846846

847847
/// <https://html.spec.whatwg.org/multipage/#scriptingLanguages>
848848
#[inline]
@@ -871,15 +871,12 @@ pub fn should_be_blocked_due_to_nosniff(
871871
.any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
872872
}
873873

874-
match content_type_header {
874+
match mime_type {
875875
// Step 4
876-
Some(ref ct) if destination.is_script_like() => {
877-
!is_javascript_mime_type(&ct.clone().into())
878-
},
876+
Some(ref essence) if destination.is_script_like() => !is_javascript_mime_type(&essence),
879877

880878
// Step 5
881-
Some(ref ct) if destination == Destination::Style => {
882-
let m: mime::Mime = ct.clone().into();
879+
Some(ref m) if destination == Destination::Style => {
883880
m.type_() != mime::TEXT && m.subtype() != mime::CSS
884881
},
885882

@@ -895,12 +892,13 @@ fn should_be_blocked_due_to_mime_type(
895892
response_headers: &HeaderMap,
896893
) -> bool {
897894
// Step 1
898-
let mime_type: mime::Mime = match response_headers.typed_get::<ContentType>() {
899-
Some(header) => header.into(),
895+
let mime_type = match extract_mime_type_as_mime(response_headers) {
896+
Some(essence) => essence,
897+
// Step 2
900898
None => return false,
901899
};
902900

903-
// Step 2-3
901+
// Step 3-5
904902
destination.is_script_like() &&
905903
match mime_type.type_() {
906904
mime::AUDIO | mime::VIDEO | mime::IMAGE => true,

components/net/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ mod websocket_loader;
3737
/// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/)
3838
pub mod fetch {
3939
pub mod cors_cache;
40-
pub mod headers;
4140
pub mod methods;
4241
}
4342

components/net_traits/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ doctest = false
1515
[dependencies]
1616
content-security-policy = { version = "0.4.0", features = ["serde"] }
1717
cookie = "0.11"
18+
data-url = "0.1.0"
1819
embedder_traits = { path = "../embedder_traits" }
1920
headers = "0.2"
2021
http = "0.1"
Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

5+
use data_url::mime::Mime as DataUrlMime;
56
use headers::HeaderMap;
67
use std::iter::Peekable;
7-
use std::str::Chars;
8+
use std::str::{Chars, FromStr};
89

910
/// <https://fetch.spec.whatwg.org/#http-tab-or-space>
1011
const HTTP_TAB_OR_SPACE: &[char] = &['\u{0009}', '\u{0020}'];
@@ -171,3 +172,89 @@ fn get_value_from_header_list(name: &str, headers: &HeaderMap) -> Option<String>
171172
// Step 2
172173
return Some(values.collect::<Vec<&str>>().join(", "));
173174
}
175+
176+
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
177+
// This function uses data_url::Mime to parse the MIME Type because
178+
// mime::Mime does not provide a parser following the Fetch spec
179+
// see https://github.com/hyperium/mime/issues/106
180+
fn extract_mime_type_as_dataurl_mime(headers: &HeaderMap) -> Option<DataUrlMime> {
181+
let mut charset: Option<String> = None;
182+
let mut essence: String = "".to_string();
183+
let mut mime_type: Option<DataUrlMime> = None;
184+
185+
// Step 4
186+
let headers_values = get_header_value_as_list("content-type", headers);
187+
188+
// Step 5
189+
if headers_values.is_none() {
190+
return None;
191+
}
192+
193+
// Step 6
194+
for header_value in headers_values.unwrap().iter() {
195+
// Step 6.1
196+
match DataUrlMime::from_str(header_value) {
197+
// Step 6.2
198+
Err(_) => continue,
199+
Ok(temp_mime) => {
200+
let temp_essence = format!("{}/{}", temp_mime.type_, temp_mime.subtype);
201+
202+
// Step 6.2
203+
if temp_essence == "*/*" {
204+
continue;
205+
}
206+
207+
let temp_charset = &temp_mime.get_parameter("charset");
208+
209+
// Step 6.3
210+
mime_type = Some(DataUrlMime {
211+
type_: temp_mime.type_.to_string(),
212+
subtype: temp_mime.subtype.to_string(),
213+
parameters: temp_mime.parameters.clone(),
214+
});
215+
216+
// Step 6.4
217+
if temp_essence != essence {
218+
charset = temp_charset.map(|c| c.to_string());
219+
essence = temp_essence.to_owned();
220+
} else {
221+
// Step 6.5
222+
if temp_charset.is_none() && charset.is_some() {
223+
let DataUrlMime {
224+
type_: t,
225+
subtype: st,
226+
parameters: p,
227+
} = mime_type.unwrap();
228+
let mut params = p;
229+
params.push(("charset".to_string(), charset.clone().unwrap()));
230+
mime_type = Some(DataUrlMime {
231+
type_: t.to_string(),
232+
subtype: st.to_string(),
233+
parameters: params,
234+
})
235+
}
236+
}
237+
},
238+
}
239+
}
240+
241+
// Step 7, 8
242+
return mime_type;
243+
}
244+
245+
pub fn extract_mime_type(headers: &HeaderMap) -> Option<Vec<u8>> {
246+
return extract_mime_type_as_dataurl_mime(headers).map(|m| format!("{}", m).into_bytes());
247+
}
248+
249+
pub fn extract_mime_type_as_mime(headers: &HeaderMap) -> Option<mime::Mime> {
250+
return extract_mime_type_as_dataurl_mime(headers)
251+
.map(|m: DataUrlMime| {
252+
// Try to transform a data-url::mime::Mime into a mime::Mime
253+
let mut mime_as_str = format!("{}/{}", m.type_, m.subtype);
254+
for p in m.parameters {
255+
mime_as_str.push_str(format!("; {}={}", p.0, p.1).as_str());
256+
}
257+
return mime_as_str.parse().ok();
258+
})
259+
.flatten();
260+
}

components/net_traits/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use webrender_api::{ImageData, ImageDescriptor, ImageKey};
3737

3838
pub mod blob_url_store;
3939
pub mod filemanager_thread;
40+
pub mod header;
4041
pub mod image_cache;
4142
pub mod pub_domains;
4243
pub mod quality;

components/net_traits/response.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
//! The [Response](https://fetch.spec.whatwg.org/#responses) object
66
//! resulting from a [fetch operation](https://fetch.spec.whatwg.org/#concept-fetch)
7+
use crate::header::extract_mime_type_as_mime;
78
use crate::{FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy};
89
use crate::{ResourceFetchTiming, ResourceTimingType};
9-
use headers::{ContentType, HeaderMapExt};
1010
use http::{HeaderMap, StatusCode};
1111
use hyper_serde::Serde;
1212
use servo_arc::Arc;
@@ -297,13 +297,7 @@ impl Response {
297297
pub fn metadata(&self) -> Result<FetchMetadata, NetworkError> {
298298
fn init_metadata(response: &Response, url: &ServoUrl) -> Metadata {
299299
let mut metadata = Metadata::default(url.clone());
300-
metadata.set_content_type(
301-
response
302-
.headers
303-
.typed_get::<ContentType>()
304-
.map(|v| v.into())
305-
.as_ref(),
306-
);
300+
metadata.set_content_type(extract_mime_type_as_mime(&response.headers).as_ref());
307301
metadata.location_url = response.location_url.clone();
308302
metadata.headers = Some(Serde(response.headers.clone()));
309303
metadata.status = response.raw_status.clone();

components/script/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ content-security-policy = { version = "0.4.0", features = ["serde"] }
4343
cookie = "0.11"
4444
crossbeam-channel = "0.4"
4545
cssparser = "0.28"
46-
data-url = "0.1.0"
4746
deny_public_fields = { path = "../deny_public_fields" }
4847
devtools_traits = { path = "../devtools_traits" }
4948
dom_struct = { path = "../dom_struct" }
@@ -77,6 +76,7 @@ mime = "0.3.13"
7776
mime_guess = "2.0.0"
7877
mitochondria = "1.1.2"
7978
msg = { path = "../msg" }
79+
net = { path = "../net" }
8080
net_traits = { path = "../net_traits" }
8181
num-traits = "0.2"
8282
parking_lot = "0.10"

components/script/dom/headers.rs

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
1010
use crate::dom::bindings::root::DomRoot;
1111
use crate::dom::bindings::str::{is_token, ByteString};
1212
use crate::dom::globalscope::GlobalScope;
13-
use data_url::mime::Mime as DataUrlMime;
1413
use dom_struct::dom_struct;
1514
use http::header::{HeaderMap as HyperHeaders, HeaderName, HeaderValue};
15+
use net_traits::header::extract_mime_type;
1616
use net_traits::request::is_cors_safelisted_request_header;
1717
use std::cell::Cell;
1818
use std::str::{self, FromStr};
@@ -467,72 +467,3 @@ pub fn is_obs_text(x: u8) -> bool {
467467
_ => false,
468468
}
469469
}
470-
471-
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
472-
// This function uses data_url::Mime to parse the MIME Type because
473-
// mime::Mime does not provide a parser following the Fetch spec
474-
// see https://github.com/hyperium/mime/issues/106
475-
pub fn extract_mime_type(headers: &HyperHeaders) -> Option<Vec<u8>> {
476-
let mut charset: Option<String> = None;
477-
let mut essence: String = "".to_string();
478-
let mut mime_type: Option<DataUrlMime> = None;
479-
480-
// Step 4
481-
let headers_values = headers.get_all(http::header::CONTENT_TYPE).iter();
482-
483-
// Step 5
484-
if headers_values.size_hint() == (0, Some(0)) {
485-
return None;
486-
}
487-
488-
// Step 6
489-
for header_value in headers_values {
490-
// Step 6.1
491-
match DataUrlMime::from_str(header_value.to_str().unwrap_or("")) {
492-
// Step 6.2
493-
Err(_) => continue,
494-
Ok(temp_mime) => {
495-
let temp_essence = format!("{}/{}", temp_mime.type_, temp_mime.subtype);
496-
497-
// Step 6.2
498-
if temp_essence == "*/*" {
499-
continue;
500-
}
501-
502-
let temp_charset = &temp_mime.get_parameter("charset");
503-
504-
// Step 6.3
505-
mime_type = Some(DataUrlMime {
506-
type_: temp_mime.type_.to_string(),
507-
subtype: temp_mime.subtype.to_string(),
508-
parameters: temp_mime.parameters.clone(),
509-
});
510-
511-
// Step 6.4
512-
if temp_essence != essence {
513-
charset = temp_charset.map(|c| c.to_string());
514-
essence = temp_essence.to_owned();
515-
} else {
516-
// Step 6.5
517-
if temp_charset.is_none() && charset.is_some() {
518-
let DataUrlMime {
519-
type_: t,
520-
subtype: st,
521-
parameters: p,
522-
} = mime_type.unwrap();
523-
let mut params = p;
524-
params.push(("charset".to_string(), charset.clone().unwrap()));
525-
mime_type = Some(DataUrlMime {
526-
type_: t.to_string(),
527-
subtype: st.to_string(),
528-
parameters: params,
529-
})
530-
}
531-
}
532-
},
533-
}
534-
}
535-
536-
// Step 7, 8
537-
return mime_type.map(|m| format!("{}", m).into_bytes());
538-
}

components/script/dom/xmlhttprequest.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::dom::document::{Document, HasBrowsingContext, IsHTMLDocument};
2222
use crate::dom::event::{Event, EventBubbles, EventCancelable};
2323
use crate::dom::eventtarget::EventTarget;
2424
use crate::dom::globalscope::GlobalScope;
25-
use crate::dom::headers::{extract_mime_type, is_forbidden_header_name};
25+
use crate::dom::headers::is_forbidden_header_name;
2626
use crate::dom::node::Node;
2727
use crate::dom::performanceresourcetiming::InitiatorType;
2828
use crate::dom::progressevent::ProgressEvent;
@@ -55,6 +55,7 @@ use js::jsval::{JSVal, NullValue, UndefinedValue};
5555
use js::rust::wrappers::JS_ParseJSON;
5656
use js::typedarray::{ArrayBuffer, CreateWith};
5757
use mime::{self, Mime, Name};
58+
use net_traits::header::extract_mime_type_as_mime;
5859
use net_traits::request::{CredentialsMode, Destination, Referrer, RequestBuilder, RequestMode};
5960
use net_traits::trim_http_whitespace;
6061
use net_traits::CoreResourceMsg::Fetch;
@@ -1592,9 +1593,8 @@ impl XMLHttpRequest {
15921593
if self.override_charset.borrow().is_some() {
15931594
self.override_charset.borrow().clone()
15941595
} else {
1595-
match self.response_headers.borrow().typed_get::<ContentType>() {
1596-
Some(ct) => {
1597-
let mime: Mime = ct.into();
1596+
match extract_mime_type_as_mime(&self.response_headers.borrow()) {
1597+
Some(mime) => {
15981598
let value = mime.get_param(mime::CHARSET);
15991599
value.and_then(|value| Encoding::for_label(value.as_ref().as_bytes()))
16001600
},
@@ -1605,15 +1605,7 @@ impl XMLHttpRequest {
16051605

16061606
/// <https://xhr.spec.whatwg.org/#response-mime-type>
16071607
fn response_mime_type(&self) -> Option<Mime> {
1608-
return extract_mime_type(&self.response_headers.borrow())
1609-
.map(|mime_as_bytes| {
1610-
String::from_utf8(mime_as_bytes)
1611-
.unwrap_or_default()
1612-
.parse()
1613-
.ok()
1614-
})
1615-
.flatten()
1616-
.or(Some(mime::TEXT_XML));
1608+
return extract_mime_type_as_mime(&self.response_headers.borrow()).or(Some(mime::TEXT_XML));
16171609
}
16181610

16191611
/// <https://xhr.spec.whatwg.org/#final-mime-type>

0 commit comments

Comments
 (0)