mirror of
https://github.com/servo/servo.git
synced 2025-06-08 08:33:26 +00:00
Update hyper to 0.12
This commit is contained in:
parent
95bfaa0a77
commit
024b40b39d
122 changed files with 3835 additions and 3448 deletions
|
@ -7,16 +7,15 @@ use data_loader::decode;
|
|||
use devtools_traits::DevtoolsControlMsg;
|
||||
use fetch::cors_cache::CorsCache;
|
||||
use filemanager_thread::FileManager;
|
||||
use headers_core::HeaderMapExt;
|
||||
use headers_ext::{AccessControlExposeHeaders, ContentType, Range};
|
||||
use http::header::{self, HeaderMap, HeaderName, HeaderValue};
|
||||
use http_loader::{HttpState, determine_request_referrer, http_fetch};
|
||||
use http_loader::{set_default_accept, set_default_accept_language};
|
||||
use hyper::{Error, Result as HyperResult};
|
||||
use hyper::header;
|
||||
use hyper::header::{Accept, AcceptLanguage, AccessControlExposeHeaders, ContentLanguage, ContentType};
|
||||
use hyper::header::{Header, HeaderFormat, HeaderView, Headers, Referer as RefererHeader};
|
||||
use hyper::method::Method;
|
||||
use hyper::mime::{Mime, SubLevel, TopLevel};
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::Method;
|
||||
use hyper::StatusCode;
|
||||
use ipc_channel::ipc::IpcReceiver;
|
||||
use mime::{self, Mime};
|
||||
use mime_guess::guess_mime_type;
|
||||
use net_traits::{FetchTaskTarget, NetworkError, ReferrerPolicy};
|
||||
use net_traits::request::{CredentialsMode, Destination, Referrer, Request, RequestMode};
|
||||
|
@ -25,16 +24,20 @@ use net_traits::response::{Response, ResponseBody, ResponseType};
|
|||
use servo_channel::{channel, Sender, Receiver};
|
||||
use servo_url::ServoUrl;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufRead, Seek, SeekFrom};
|
||||
use std::mem;
|
||||
use std::ops::Bound;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use subresource_integrity::is_response_integrity_valid;
|
||||
|
||||
lazy_static! {
|
||||
static ref X_CONTENT_TYPE_OPTIONS: HeaderName = HeaderName::from_static("x-content-type-options");
|
||||
}
|
||||
|
||||
const FILE_CHUNK_SIZE: usize = 32768; //32 KB
|
||||
|
||||
pub type Target<'a> = &'a mut (FetchTaskTarget + Send);
|
||||
|
@ -173,11 +176,11 @@ pub fn main_fetch(request: &mut Request,
|
|||
Referrer::Client => {
|
||||
// FIXME(#14507): We should never get this value here; it should
|
||||
// already have been handled in the script thread.
|
||||
request.headers.remove::<RefererHeader>();
|
||||
request.headers.remove(header::REFERER);
|
||||
None
|
||||
},
|
||||
Referrer::ReferrerUrl(url) => {
|
||||
request.headers.remove::<RefererHeader>();
|
||||
request.headers.remove(header::REFERER);
|
||||
let current_url = request.current_url().clone();
|
||||
determine_request_referrer(&mut request.headers,
|
||||
request.referrer_policy.unwrap(),
|
||||
|
@ -238,7 +241,7 @@ pub fn main_fetch(request: &mut Request,
|
|||
} else if request.use_cors_preflight ||
|
||||
(request.unsafe_request &&
|
||||
(!is_cors_safelisted_method(&request.method) ||
|
||||
request.headers.iter().any(|h| !is_cors_safelisted_request_header(&h)))) {
|
||||
request.headers.iter().any(|(name, value)| !is_cors_safelisted_request_header(&name, &value)))) {
|
||||
// Substep 1.
|
||||
request.response_tainting = ResponseTainting::CorsTainting;
|
||||
// Substep 2.
|
||||
|
@ -269,18 +272,19 @@ pub fn main_fetch(request: &mut Request,
|
|||
// Substep 1.
|
||||
if request.response_tainting == ResponseTainting::CorsTainting {
|
||||
// Subsubstep 1.
|
||||
let header_names = response.headers.get::<AccessControlExposeHeaders>();
|
||||
let header_names: Option<Vec<HeaderName>> = response.headers.typed_get::<AccessControlExposeHeaders>()
|
||||
.map(|v| v.iter().collect());
|
||||
match header_names {
|
||||
// Subsubstep 2.
|
||||
Some(list) if request.credentials_mode != CredentialsMode::Include => {
|
||||
Some(ref list) if request.credentials_mode != CredentialsMode::Include => {
|
||||
if list.len() == 1 && list[0] == "*" {
|
||||
response.cors_exposed_header_name_list =
|
||||
response.headers.iter().map(|h| h.name().to_owned()).collect();
|
||||
response.headers.iter().map(|(name, _)| name.as_str().to_owned()).collect();
|
||||
}
|
||||
},
|
||||
// Subsubstep 3.
|
||||
Some(list) => {
|
||||
response.cors_exposed_header_name_list = list.iter().map(|h| (**h).clone()).collect();
|
||||
response.cors_exposed_header_name_list = list.iter().map(|h| h.as_str().to_owned()).collect();
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -341,7 +345,7 @@ pub fn main_fetch(request: &mut Request,
|
|||
let not_network_error = !response_is_network_error && !internal_response.is_network_error();
|
||||
if not_network_error && (is_null_body_status(&internal_response.status) ||
|
||||
match request.method {
|
||||
Method::Head | Method::Connect => true,
|
||||
Method::HEAD | Method::CONNECT => true,
|
||||
_ => false }) {
|
||||
// when Fetch is used only asynchronously, we will need to make sure
|
||||
// that nothing tries to write to the body at this point
|
||||
|
@ -463,7 +467,7 @@ fn scheme_fetch(request: &mut Request,
|
|||
match url.scheme() {
|
||||
"about" if url.path() == "blank" => {
|
||||
let mut response = Response::new(url);
|
||||
response.headers.set(ContentType(mime!(Text / Html; Charset = Utf8)));
|
||||
response.headers.typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
|
||||
*response.body.lock().unwrap() = ResponseBody::Done(vec![]);
|
||||
response
|
||||
},
|
||||
|
@ -477,7 +481,7 @@ fn scheme_fetch(request: &mut Request,
|
|||
Ok((mime, bytes)) => {
|
||||
let mut response = Response::new(url);
|
||||
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
|
||||
response.headers.set(ContentType(mime));
|
||||
response.headers.typed_insert(ContentType::from(mime));
|
||||
response
|
||||
},
|
||||
Err(_) => Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
|
||||
|
@ -485,7 +489,7 @@ fn scheme_fetch(request: &mut Request,
|
|||
},
|
||||
|
||||
"file" => {
|
||||
if request.method == Method::Get {
|
||||
if request.method == Method::GET {
|
||||
match url.to_file_path() {
|
||||
Ok(file_path) => {
|
||||
match File::open(file_path.clone()) {
|
||||
|
@ -493,7 +497,7 @@ fn scheme_fetch(request: &mut Request,
|
|||
let mime = guess_mime_type(file_path);
|
||||
|
||||
let mut response = Response::new(url);
|
||||
response.headers.set(ContentType(mime));
|
||||
response.headers.typed_insert(ContentType::from(mime));
|
||||
|
||||
let (done_sender, done_receiver) = channel();
|
||||
*done_chan = Some((done_sender.clone(), done_receiver));
|
||||
|
@ -503,22 +507,22 @@ fn scheme_fetch(request: &mut Request,
|
|||
|
||||
let cancellation_listener = context.cancellation_listener.clone();
|
||||
|
||||
let range = request.headers.get::<header::Range>();
|
||||
let (start, end) = if let Some(&header::Range::Bytes(ref range)) = range {
|
||||
match range.first().unwrap() {
|
||||
&header::ByteRangeSpec::AllFrom(start) => (start, None),
|
||||
&header::ByteRangeSpec::FromTo(start, end) => {
|
||||
let (start, end) = if let Some(ref range) = request.headers.typed_get::<Range>() {
|
||||
match range.iter().collect::<Vec<(Bound<u64>, Bound<u64>)>>().first() {
|
||||
Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None),
|
||||
Some(&(Bound::Included(start), Bound::Included(end))) => {
|
||||
// `end` should be less or equal to `start`.
|
||||
(start, Some(u64::max(start, end)))
|
||||
},
|
||||
&header::ByteRangeSpec::Last(offset) => {
|
||||
Some(&(Bound::Unbounded, Bound::Included(offset))) => {
|
||||
if let Ok(metadata) = file.metadata() {
|
||||
// `offset` cannot be bigger than the file size.
|
||||
(metadata.len() - u64::min(metadata.len(), offset), None)
|
||||
} else {
|
||||
(0, None)
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (0, None)
|
||||
}
|
||||
} else {
|
||||
(0, None)
|
||||
|
@ -591,7 +595,7 @@ fn scheme_fetch(request: &mut Request,
|
|||
"blob" => {
|
||||
println!("Loading blob {}", url.as_str());
|
||||
// Step 2.
|
||||
if request.method != Method::Get {
|
||||
if request.method != Method::GET {
|
||||
return Response::network_error(NetworkError::Internal("Unexpected method for blob".into()));
|
||||
}
|
||||
|
||||
|
@ -619,33 +623,33 @@ fn scheme_fetch(request: &mut Request,
|
|||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#cors-safelisted-request-header>
|
||||
pub fn is_cors_safelisted_request_header(h: &HeaderView) -> bool {
|
||||
if h.is::<ContentType>() {
|
||||
match h.value() {
|
||||
Some(&ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) |
|
||||
Some(&ContentType(Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _))) |
|
||||
Some(&ContentType(Mime(TopLevel::Multipart, SubLevel::FormData, _))) => true,
|
||||
_ => false
|
||||
|
||||
pub fn is_cors_safelisted_request_header(name: &HeaderName, value: &HeaderValue) -> bool {
|
||||
if name == header::CONTENT_TYPE {
|
||||
if let Some(m) = value.to_str().ok().and_then(|s| s.parse::<Mime>().ok()) {
|
||||
m.type_() == mime::TEXT && m.subtype() == mime::PLAIN ||
|
||||
m.type_() == mime::APPLICATION && m.subtype() == mime::WWW_FORM_URLENCODED ||
|
||||
m.type_() == mime::MULTIPART && m.subtype() == mime::FORM_DATA
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
h.is::<Accept>() || h.is::<AcceptLanguage>() || h.is::<ContentLanguage>()
|
||||
name == header::ACCEPT || name == header::ACCEPT_LANGUAGE || name == header::CONTENT_LANGUAGE
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#cors-safelisted-method>
|
||||
pub fn is_cors_safelisted_method(m: &Method) -> bool {
|
||||
match *m {
|
||||
Method::Get | Method::Head | Method::Post => true,
|
||||
Method::GET | Method::HEAD | Method::POST => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_null_body_status(status: &Option<StatusCode>) -> bool {
|
||||
fn is_null_body_status(status: &Option<(StatusCode, String)>) -> bool {
|
||||
match *status {
|
||||
Some(status) => match status {
|
||||
StatusCode::SwitchingProtocols | StatusCode::NoContent |
|
||||
StatusCode::ResetContent | StatusCode::NotModified => true,
|
||||
Some((status, _)) => match status {
|
||||
StatusCode::SWITCHING_PROTOCOLS | StatusCode::NO_CONTENT |
|
||||
StatusCode::RESET_CONTENT | StatusCode::NOT_MODIFIED => true,
|
||||
_ => false
|
||||
},
|
||||
_ => false
|
||||
|
@ -653,84 +657,56 @@ fn is_null_body_status(status: &Option<StatusCode>) -> bool {
|
|||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?>
|
||||
pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_headers: &Headers) -> bool {
|
||||
/// <https://fetch.spec.whatwg.org/#x-content-type-options-header>
|
||||
/// This is needed to parse `X-Content-Type-Options` according to spec,
|
||||
/// which requires that we inspect only the first value.
|
||||
///
|
||||
/// A [unit-like struct](https://doc.rust-lang.org/book/structs.html#unit-like-structs)
|
||||
/// is sufficient since a valid header implies that we use `nosniff`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct XContentTypeOptions;
|
||||
|
||||
impl Header for XContentTypeOptions {
|
||||
fn header_name() -> &'static str {
|
||||
"X-Content-Type-Options"
|
||||
}
|
||||
|
||||
/// https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff%3F #2
|
||||
fn parse_header(raw: &[Vec<u8>]) -> HyperResult<Self> {
|
||||
raw.first()
|
||||
.and_then(|v| str::from_utf8(v).ok())
|
||||
.and_then(|s| if s.trim().eq_ignore_ascii_case("nosniff") {
|
||||
Some(XContentTypeOptions)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.ok_or(Error::Header)
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderFormat for XContentTypeOptions {
|
||||
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("nosniff")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_headers: &HeaderMap) -> bool {
|
||||
// Steps 1-3.
|
||||
if response_headers.get::<XContentTypeOptions>().is_none() {
|
||||
// TODO(eijebong): Replace this once typed headers allow custom ones...
|
||||
if response_headers.get("x-content-type-options")
|
||||
.map_or(true, |val| val.to_str().unwrap_or("").to_lowercase() != "nosniff")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 4
|
||||
// Note: an invalid MIME type will produce a `None`.
|
||||
let content_type_header = response_headers.get::<ContentType>();
|
||||
let content_type_header = response_headers.typed_get::<ContentType>();
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#scriptingLanguages>
|
||||
#[inline]
|
||||
fn is_javascript_mime_type(mime_type: &Mime) -> bool {
|
||||
let javascript_mime_types: [Mime; 16] = [
|
||||
mime!(Application / ("ecmascript")),
|
||||
mime!(Application / ("javascript")),
|
||||
mime!(Application / ("x-ecmascript")),
|
||||
mime!(Application / ("x-javascript")),
|
||||
mime!(Text / ("ecmascript")),
|
||||
mime!(Text / ("javascript")),
|
||||
mime!(Text / ("javascript1.0")),
|
||||
mime!(Text / ("javascript1.1")),
|
||||
mime!(Text / ("javascript1.2")),
|
||||
mime!(Text / ("javascript1.3")),
|
||||
mime!(Text / ("javascript1.4")),
|
||||
mime!(Text / ("javascript1.5")),
|
||||
mime!(Text / ("jscript")),
|
||||
mime!(Text / ("livescript")),
|
||||
mime!(Text / ("x-ecmascript")),
|
||||
mime!(Text / ("x-javascript")),
|
||||
"application/ecmascript".parse().unwrap(),
|
||||
"application/javascript".parse().unwrap(),
|
||||
"application/x-ecmascript".parse().unwrap(),
|
||||
"application/x-javascript".parse().unwrap(),
|
||||
"text/ecmascript".parse().unwrap(),
|
||||
"text/javascript".parse().unwrap(),
|
||||
"text/javascript1.0".parse().unwrap(),
|
||||
"text/javascript1.1".parse().unwrap(),
|
||||
"text/javascript1.2".parse().unwrap(),
|
||||
"text/javascript1.3".parse().unwrap(),
|
||||
"text/javascript1.4".parse().unwrap(),
|
||||
"text/javascript1.5".parse().unwrap(),
|
||||
"text/jscript".parse().unwrap(),
|
||||
"text/livescript".parse().unwrap(),
|
||||
"text/x-ecmascript".parse().unwrap(),
|
||||
"text/x-javascript".parse().unwrap(),
|
||||
];
|
||||
|
||||
javascript_mime_types.iter()
|
||||
.any(|mime| mime.0 == mime_type.0 && mime.1 == mime_type.1)
|
||||
.any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
|
||||
}
|
||||
|
||||
// Assumes str::starts_with is equivalent to mime::TopLevel
|
||||
match content_type_header {
|
||||
// Step 6
|
||||
Some(&ContentType(ref mime_type)) if destination.is_script_like()
|
||||
=> !is_javascript_mime_type(mime_type),
|
||||
Some(ref ct) if destination.is_script_like()
|
||||
=> !is_javascript_mime_type(&ct.clone().into()),
|
||||
|
||||
// Step 7
|
||||
Some(&ContentType(Mime(ref tl, ref sl, _))) if destination == Destination::Style
|
||||
=> *tl != TopLevel::Text && *sl != SubLevel::Css,
|
||||
Some(ref ct) if destination == Destination::Style
|
||||
=> {
|
||||
let m: mime::Mime = ct.clone().into();
|
||||
m.type_() != mime::TEXT && m.subtype() != mime::CSS
|
||||
},
|
||||
|
||||
None if destination == Destination::Style || destination.is_script_like() => true,
|
||||
// Step 8
|
||||
|
@ -739,23 +715,23 @@ pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_heade
|
|||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-mime-type?>
|
||||
fn should_be_blocked_due_to_mime_type(destination: Destination, response_headers: &Headers) -> bool {
|
||||
fn should_be_blocked_due_to_mime_type(destination: Destination, response_headers: &HeaderMap) -> bool {
|
||||
// Step 1
|
||||
let mime_type = match response_headers.get::<ContentType>() {
|
||||
Some(header) => header,
|
||||
let mime_type: mime::Mime = match response_headers.typed_get::<ContentType>() {
|
||||
Some(header) => header.into(),
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// Step 2-3
|
||||
destination.is_script_like() && match *mime_type {
|
||||
ContentType(Mime(TopLevel::Audio, _, _)) |
|
||||
ContentType(Mime(TopLevel::Video, _, _)) |
|
||||
ContentType(Mime(TopLevel::Image, _, _)) => true,
|
||||
ContentType(Mime(TopLevel::Text, SubLevel::Ext(ref ext), _)) => ext == "csv",
|
||||
|
||||
// Step 4
|
||||
_ => false,
|
||||
}
|
||||
destination.is_script_like() &&
|
||||
match mime_type.type_() {
|
||||
mime::AUDIO |
|
||||
mime::VIDEO |
|
||||
mime::IMAGE => true,
|
||||
mime::TEXT if mime_type.subtype() == mime::CSV => true,
|
||||
// Step 4
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#block-bad-port>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue