mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Auto merge of #16160 - nox:tungstenite, r=jdm
Some enhancements in the net crate See the individual commits. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16160) <!-- Reviewable:end -->
This commit is contained in:
commit
299f9446e6
4 changed files with 50 additions and 32 deletions
|
@ -27,15 +27,19 @@ const DEFAULT_CIPHERS: &'static str = concat!(
|
||||||
"AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
|
"AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn create_http_connector(certificate_file: &str) -> Arc<Pool<Connector>> {
|
pub fn create_ssl_context(certificate_file: &str) -> Arc<SslContext> {
|
||||||
let mut context = SslContext::new(SslMethod::Sslv23).unwrap();
|
let mut context = SslContext::new(SslMethod::Sslv23).unwrap();
|
||||||
context.set_CA_file(&resources_dir_path()
|
context.set_CA_file(&resources_dir_path()
|
||||||
.expect("Need certificate file to make network requests")
|
.expect("Need certificate file to make network requests")
|
||||||
.join(certificate_file)).unwrap();
|
.join(certificate_file)).unwrap();
|
||||||
context.set_cipher_list(DEFAULT_CIPHERS).unwrap();
|
context.set_cipher_list(DEFAULT_CIPHERS).unwrap();
|
||||||
context.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
|
context.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
|
||||||
|
Arc::new(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_http_connector(ssl_context: Arc<SslContext>) -> Arc<Pool<Connector>> {
|
||||||
let connector = HttpsConnector::new(ServoSslClient {
|
let connector = HttpsConnector::new(ServoSslClient {
|
||||||
context: Arc::new(context)
|
context: ssl_context,
|
||||||
});
|
});
|
||||||
|
|
||||||
Arc::new(Pool::with_connector(Default::default(), connector))
|
Arc::new(Pool::with_connector(Default::default(), connector))
|
||||||
|
|
|
@ -11,7 +11,8 @@ use http_loader::{HttpState, determine_request_referrer, http_fetch, set_default
|
||||||
use hyper::Error;
|
use hyper::Error;
|
||||||
use hyper::error::Result as HyperResult;
|
use hyper::error::Result as HyperResult;
|
||||||
use hyper::header::{Accept, AcceptLanguage, ContentLanguage, ContentType};
|
use hyper::header::{Accept, AcceptLanguage, ContentLanguage, ContentType};
|
||||||
use hyper::header::{Header, HeaderFormat, HeaderView, QualityItem, Referer as RefererHeader, q, qitem};
|
use hyper::header::{Header, HeaderFormat, HeaderView, Headers, QualityItem};
|
||||||
|
use hyper::header::{Referer as RefererHeader, q, qitem};
|
||||||
use hyper::method::Method;
|
use hyper::method::Method;
|
||||||
use hyper::mime::{Mime, SubLevel, TopLevel};
|
use hyper::mime::{Mime, SubLevel, TopLevel};
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
@ -282,13 +283,14 @@ pub fn main_fetch(request: Rc<Request>,
|
||||||
// Step 16
|
// Step 16
|
||||||
// TODO Blocking for CSP, mixed content, MIME type
|
// TODO Blocking for CSP, mixed content, MIME type
|
||||||
let blocked_error_response;
|
let blocked_error_response;
|
||||||
let internal_response = if !response.is_network_error() && should_block_nosniff(&request, &response) {
|
let internal_response =
|
||||||
// Defer rebinding result
|
if !response.is_network_error() && should_be_blocked_due_to_nosniff(request.type_, &response.headers) {
|
||||||
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
|
// Defer rebinding result
|
||||||
&blocked_error_response
|
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
|
||||||
} else {
|
&blocked_error_response
|
||||||
internal_response
|
} else {
|
||||||
};
|
internal_response
|
||||||
|
};
|
||||||
|
|
||||||
// Step 17
|
// Step 17
|
||||||
// We check `internal_response` since we did not mutate `response` in the previous step.
|
// We check `internal_response` since we did not mutate `response` in the previous step.
|
||||||
|
@ -525,7 +527,7 @@ fn is_null_body_status(status: &Option<StatusCode>) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?
|
/// https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?
|
||||||
fn should_block_nosniff(request: &Request, response: &Response) -> bool {
|
fn should_be_blocked_due_to_nosniff(request_type: Type, response_headers: &Headers) -> bool {
|
||||||
/// https://fetch.spec.whatwg.org/#x-content-type-options-header
|
/// https://fetch.spec.whatwg.org/#x-content-type-options-header
|
||||||
/// This is needed to parse `X-Content-Type-Options` according to spec,
|
/// This is needed to parse `X-Content-Type-Options` according to spec,
|
||||||
/// which requires that we inspect only the first value.
|
/// which requires that we inspect only the first value.
|
||||||
|
@ -533,7 +535,7 @@ fn should_block_nosniff(request: &Request, response: &Response) -> bool {
|
||||||
/// A [unit-like struct](https://doc.rust-lang.org/book/structs.html#unit-like-structs)
|
/// 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`.
|
/// is sufficient since a valid header implies that we use `nosniff`.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct XContentTypeOptions();
|
struct XContentTypeOptions;
|
||||||
|
|
||||||
impl Header for XContentTypeOptions {
|
impl Header for XContentTypeOptions {
|
||||||
fn header_name() -> &'static str {
|
fn header_name() -> &'static str {
|
||||||
|
@ -545,7 +547,7 @@ fn should_block_nosniff(request: &Request, response: &Response) -> bool {
|
||||||
raw.first()
|
raw.first()
|
||||||
.and_then(|v| str::from_utf8(v).ok())
|
.and_then(|v| str::from_utf8(v).ok())
|
||||||
.and_then(|s| match s.trim().to_lowercase().as_str() {
|
.and_then(|s| match s.trim().to_lowercase().as_str() {
|
||||||
"nosniff" => Some(XContentTypeOptions()),
|
"nosniff" => Some(XContentTypeOptions),
|
||||||
_ => None
|
_ => None
|
||||||
})
|
})
|
||||||
.ok_or(Error::Header)
|
.ok_or(Error::Header)
|
||||||
|
@ -558,16 +560,14 @@ fn should_block_nosniff(request: &Request, response: &Response) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match response.headers.get::<XContentTypeOptions>() {
|
// Steps 1-3.
|
||||||
None => return false, // Step 1
|
if response_headers.get::<XContentTypeOptions>().is_none() {
|
||||||
_ => () // Step 2 & 3 are implemented by the XContentTypeOptions struct
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
// Step 4
|
// Step 4
|
||||||
// Note: an invalid MIME type will produce a `None`.
|
// Note: an invalid MIME type will produce a `None`.
|
||||||
let content_type_header = response.headers.get::<ContentType>();
|
let content_type_header = response_headers.get::<ContentType>();
|
||||||
// Step 5
|
|
||||||
let type_ = request.type_;
|
|
||||||
|
|
||||||
/// https://html.spec.whatwg.org/multipage/#scriptingLanguages
|
/// https://html.spec.whatwg.org/multipage/#scriptingLanguages
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -596,7 +596,7 @@ fn should_block_nosniff(request: &Request, response: &Response) -> bool {
|
||||||
|
|
||||||
let text_css: Mime = mime!(Text / Css);
|
let text_css: Mime = mime!(Text / Css);
|
||||||
// Assumes str::starts_with is equivalent to mime::TopLevel
|
// Assumes str::starts_with is equivalent to mime::TopLevel
|
||||||
return match type_ {
|
return match request_type {
|
||||||
// Step 6
|
// Step 6
|
||||||
Type::Script => {
|
Type::Script => {
|
||||||
match content_type_header {
|
match content_type_header {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use brotli::Decompressor;
|
use brotli::Decompressor;
|
||||||
use connector::{Connector, create_http_connector};
|
use connector::{Connector, create_http_connector, create_ssl_context};
|
||||||
use cookie;
|
use cookie;
|
||||||
use cookie_storage::CookieStorage;
|
use cookie_storage::CookieStorage;
|
||||||
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest};
|
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest};
|
||||||
|
@ -77,11 +77,12 @@ pub struct HttpState {
|
||||||
|
|
||||||
impl HttpState {
|
impl HttpState {
|
||||||
pub fn new(certificate_path: &str) -> HttpState {
|
pub fn new(certificate_path: &str) -> HttpState {
|
||||||
|
let ssl_context = create_ssl_context(certificate_path);
|
||||||
HttpState {
|
HttpState {
|
||||||
hsts_list: Arc::new(RwLock::new(HstsList::new())),
|
hsts_list: Arc::new(RwLock::new(HstsList::new())),
|
||||||
cookie_jar: Arc::new(RwLock::new(CookieStorage::new(150))),
|
cookie_jar: Arc::new(RwLock::new(CookieStorage::new(150))),
|
||||||
auth_cache: Arc::new(RwLock::new(AuthCache::new())),
|
auth_cache: Arc::new(RwLock::new(AuthCache::new())),
|
||||||
connector_pool: create_http_connector(certificate_path),
|
connector_pool: create_http_connector(ssl_context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -629,11 +630,7 @@ pub fn http_fetch(request: Rc<Request>,
|
||||||
// Step 5
|
// Step 5
|
||||||
match response.actual_response().status {
|
match response.actual_response().status {
|
||||||
// Code 301, 302, 303, 307, 308
|
// Code 301, 302, 303, 307, 308
|
||||||
Some(StatusCode::MovedPermanently) |
|
status if status.map_or(false, is_redirect_status) => {
|
||||||
Some(StatusCode::Found) |
|
|
||||||
Some(StatusCode::SeeOther) |
|
|
||||||
Some(StatusCode::TemporaryRedirect) |
|
|
||||||
Some(StatusCode::PermanentRedirect) => {
|
|
||||||
response = match request.redirect_mode.get() {
|
response = match request.redirect_mode.get() {
|
||||||
RedirectMode::Error => Response::network_error(NetworkError::Internal("Redirect mode error".into())),
|
RedirectMode::Error => Response::network_error(NetworkError::Internal("Redirect mode error".into())),
|
||||||
RedirectMode::Manual => {
|
RedirectMode::Manual => {
|
||||||
|
@ -1418,3 +1415,15 @@ fn response_needs_revalidation(_response: &Response) -> bool {
|
||||||
// TODO this function
|
// TODO this function
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://fetch.spec.whatwg.org/#redirect-status
|
||||||
|
fn is_redirect_status(status: StatusCode) -> bool {
|
||||||
|
match status {
|
||||||
|
StatusCode::MovedPermanently |
|
||||||
|
StatusCode::Found |
|
||||||
|
StatusCode::SeeOther |
|
||||||
|
StatusCode::TemporaryRedirect |
|
||||||
|
StatusCode::PermanentRedirect => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
//! A thread that takes a URL and streams back the binary data.
|
//! A thread that takes a URL and streams back the binary data.
|
||||||
use connector::{Connector, create_http_connector};
|
use connector::{Connector, create_http_connector, create_ssl_context};
|
||||||
use cookie;
|
use cookie;
|
||||||
use cookie_rs;
|
use cookie_rs;
|
||||||
use cookie_storage::CookieStorage;
|
use cookie_storage::CookieStorage;
|
||||||
|
@ -21,6 +21,7 @@ use net_traits::{CustomResponseMediator, ResourceId};
|
||||||
use net_traits::{ResourceThreads, WebSocketCommunicate, WebSocketConnectData};
|
use net_traits::{ResourceThreads, WebSocketCommunicate, WebSocketConnectData};
|
||||||
use net_traits::request::{Request, RequestInit};
|
use net_traits::request::{Request, RequestInit};
|
||||||
use net_traits::storage_thread::StorageThreadMsg;
|
use net_traits::storage_thread::StorageThreadMsg;
|
||||||
|
use openssl::ssl::SslContext;
|
||||||
use profile_traits::time::ProfilerChan;
|
use profile_traits::time::ProfilerChan;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
@ -46,6 +47,7 @@ pub struct ResourceGroup {
|
||||||
cookie_jar: Arc<RwLock<CookieStorage>>,
|
cookie_jar: Arc<RwLock<CookieStorage>>,
|
||||||
auth_cache: Arc<RwLock<AuthCache>>,
|
auth_cache: Arc<RwLock<AuthCache>>,
|
||||||
hsts_list: Arc<RwLock<HstsList>>,
|
hsts_list: Arc<RwLock<HstsList>>,
|
||||||
|
ssl_context: Arc<SslContext>,
|
||||||
connector: Arc<Pool<Connector>>,
|
connector: Arc<Pool<Connector>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,17 +106,20 @@ fn create_resource_groups(config_dir: Option<&Path>)
|
||||||
read_json_from_file(&mut hsts_list, config_dir, "hsts_list.json");
|
read_json_from_file(&mut hsts_list, config_dir, "hsts_list.json");
|
||||||
read_json_from_file(&mut cookie_jar, config_dir, "cookie_jar.json");
|
read_json_from_file(&mut cookie_jar, config_dir, "cookie_jar.json");
|
||||||
}
|
}
|
||||||
|
let ssl_context = create_ssl_context("certs");
|
||||||
let resource_group = ResourceGroup {
|
let resource_group = ResourceGroup {
|
||||||
cookie_jar: Arc::new(RwLock::new(cookie_jar)),
|
cookie_jar: Arc::new(RwLock::new(cookie_jar)),
|
||||||
auth_cache: Arc::new(RwLock::new(auth_cache)),
|
auth_cache: Arc::new(RwLock::new(auth_cache)),
|
||||||
hsts_list: Arc::new(RwLock::new(hsts_list.clone())),
|
hsts_list: Arc::new(RwLock::new(hsts_list.clone())),
|
||||||
connector: create_http_connector("certs"),
|
ssl_context: ssl_context.clone(),
|
||||||
|
connector: create_http_connector(ssl_context.clone()),
|
||||||
};
|
};
|
||||||
let private_resource_group = ResourceGroup {
|
let private_resource_group = ResourceGroup {
|
||||||
cookie_jar: Arc::new(RwLock::new(CookieStorage::new(150))),
|
cookie_jar: Arc::new(RwLock::new(CookieStorage::new(150))),
|
||||||
auth_cache: Arc::new(RwLock::new(AuthCache::new())),
|
auth_cache: Arc::new(RwLock::new(AuthCache::new())),
|
||||||
hsts_list: Arc::new(RwLock::new(HstsList::new())),
|
hsts_list: Arc::new(RwLock::new(HstsList::new())),
|
||||||
connector: create_http_connector("certs"),
|
ssl_context: ssl_context.clone(),
|
||||||
|
connector: create_http_connector(ssl_context),
|
||||||
};
|
};
|
||||||
(resource_group, private_resource_group)
|
(resource_group, private_resource_group)
|
||||||
}
|
}
|
||||||
|
@ -324,7 +329,7 @@ impl CoreResourceManager {
|
||||||
cookie_jar: group.cookie_jar.clone(),
|
cookie_jar: group.cookie_jar.clone(),
|
||||||
auth_cache: group.auth_cache.clone(),
|
auth_cache: group.auth_cache.clone(),
|
||||||
// FIXME(#15694): use group.connector.clone() instead.
|
// FIXME(#15694): use group.connector.clone() instead.
|
||||||
connector_pool: create_http_connector("certs"),
|
connector_pool: create_http_connector(group.ssl_context.clone()),
|
||||||
};
|
};
|
||||||
let ua = self.user_agent.clone();
|
let ua = self.user_agent.clone();
|
||||||
let dc = self.devtools_chan.clone();
|
let dc = self.devtools_chan.clone();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue