mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Update all network-related dependencies to the latest versions (#34630)
* Update all network-related dependencies to the latest versions: * rustls * hyper * http * headers * tungstenite * async-tungstenite Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Fix panics with 1xx responses in WPT tests. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Use reported response length when calculating available ranges. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Remove unreachable match arm. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Clean up commented fragments in blob and file handlers. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Remove unreachable match arm. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Fix clippy warning. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Cleanup. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Fix up unit tests for dependency upgrades. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Update aws-lc-sys to fix Windows builds. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * net: Use ring instead of aws-lc-sys. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * embedding: Require embedder to initialize a rustls CryptoProvider. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Disable aws-lc-rs pending OhOS build fixes. Signed-off-by: Josh Matthews <josh@joshmatthews.net> --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
270df6e263
commit
76e0a1872b
25 changed files with 1342 additions and 1050 deletions
|
@ -33,9 +33,11 @@ futures-util = { version = "0.3.30", default-features = false }
|
|||
generic-array = "0.14"
|
||||
headers = { workspace = true }
|
||||
http = { workspace = true }
|
||||
hyper = { workspace = true, features = ["client", "http1", "http2", "tcp", "stream"] }
|
||||
http-body-util = { workspace = true }
|
||||
hyper = { workspace = true, features = ["client", "http1", "http2"] }
|
||||
hyper-rustls = { workspace = true }
|
||||
hyper_serde = { workspace = true }
|
||||
hyper-util = { workspace = true, features = ["client", "client-legacy", "tokio"] }
|
||||
imsz = { workspace = true }
|
||||
ipc-channel = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
@ -49,6 +51,7 @@ profile_traits = { workspace = true }
|
|||
rayon = { workspace = true }
|
||||
rustls = { workspace = true }
|
||||
rustls-pemfile = { workspace = true }
|
||||
rustls-pki-types = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
servo_allocator = { path = "../allocator" }
|
||||
|
@ -62,6 +65,7 @@ tokio = { workspace = true, features = ["sync", "macros", "rt-multi-thread"] }
|
|||
tokio-util = { version = "0.7.12", default-features = false, features = ["codec", "io"] }
|
||||
tokio-rustls = { workspace = true }
|
||||
tokio-stream = "0.1"
|
||||
tower-service = { workspace = true }
|
||||
tungstenite = { workspace = true }
|
||||
url = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
|
@ -72,9 +76,9 @@ webpki-roots = { workspace = true }
|
|||
[dev-dependencies]
|
||||
flate2 = "1"
|
||||
futures = { version = "0.3", features = ["compat"] }
|
||||
tokio-test = "0.4"
|
||||
tokio-stream = { version = "0.1", features = ["net"] }
|
||||
hyper = { workspace = true, features = ["full"] }
|
||||
hyper-util = { workspace = true, features = ["server-graceful"] }
|
||||
rustls = { workspace = true, features = ["ring"] }
|
||||
|
||||
[[test]]
|
||||
name = "main"
|
||||
|
|
|
@ -9,14 +9,17 @@ use std::sync::{Arc, Mutex};
|
|||
use futures::task::{Context, Poll};
|
||||
use futures::Future;
|
||||
use http::uri::{Authority, Uri as Destination};
|
||||
use hyper::client::HttpConnector as HyperHttpConnector;
|
||||
use http_body_util::combinators::BoxBody;
|
||||
use hyper::body::Bytes;
|
||||
use hyper::rt::Executor;
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Client};
|
||||
use hyper_rustls::HttpsConnector as HyperRustlsHttpsConnector;
|
||||
use hyper_util::client::legacy::connect::HttpConnector as HyperHttpConnector;
|
||||
use hyper_util::client::legacy::Client;
|
||||
use log::warn;
|
||||
use rustls::client::WebPkiVerifier;
|
||||
use rustls::{Certificate, ClientConfig, OwnedTrustAnchor, RootCertStore, ServerName};
|
||||
use rustls::client::WebPkiServerVerifier;
|
||||
use rustls::{ClientConfig, RootCertStore};
|
||||
use rustls_pki_types::{CertificateDer, ServerName, UnixTime};
|
||||
use tower_service::Service;
|
||||
|
||||
use crate::async_runtime::HANDLE;
|
||||
use crate::hosts::replace_host;
|
||||
|
@ -80,10 +83,10 @@ pub type TlsConfig = ClientConfig;
|
|||
struct CertificateErrorOverrideManagerInternal {
|
||||
/// A mapping of certificates and their hosts, which have seen certificate errors.
|
||||
/// This is used to later create an override in this [CertificateErrorOverrideManager].
|
||||
certificates_failing_to_verify: HashMap<ServerName, Certificate>,
|
||||
certificates_failing_to_verify: HashMap<ServerName<'static>, CertificateDer<'static>>,
|
||||
/// A list of certificates that should be accepted despite encountering verification
|
||||
/// errors.
|
||||
overrides: Vec<Certificate>,
|
||||
overrides: Vec<CertificateDer<'static>>,
|
||||
}
|
||||
|
||||
/// This data structure is used to track certificate verification errors and overrides.
|
||||
|
@ -100,7 +103,7 @@ impl CertificateErrorOverrideManager {
|
|||
|
||||
/// Add a certificate to this manager's list of certificates for which to ignore
|
||||
/// validation errors.
|
||||
pub fn add_override(&self, certificate: &Certificate) {
|
||||
pub fn add_override(&self, certificate: &CertificateDer<'static>) {
|
||||
self.0.lock().unwrap().overrides.push(certificate.clone());
|
||||
}
|
||||
|
||||
|
@ -110,9 +113,9 @@ impl CertificateErrorOverrideManager {
|
|||
pub(crate) fn remove_certificate_failing_verification(
|
||||
&self,
|
||||
host: &str,
|
||||
) -> Option<Certificate> {
|
||||
) -> Option<CertificateDer<'static>> {
|
||||
let server_name = match ServerName::try_from(host) {
|
||||
Ok(name) => name,
|
||||
Ok(name) => name.to_owned(),
|
||||
Err(error) => {
|
||||
warn!("Could not convert host string into RustTLS ServerName: {error:?}");
|
||||
return None;
|
||||
|
@ -149,11 +152,12 @@ pub fn create_tls_config(
|
|||
override_manager,
|
||||
);
|
||||
rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(Arc::new(verifier))
|
||||
.with_no_client_auth()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TokioExecutor {}
|
||||
|
||||
impl<F> Executor<F> for TokioExecutor
|
||||
|
@ -165,8 +169,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CertificateVerificationOverrideVerifier {
|
||||
webpki_verifier: WebPkiVerifier,
|
||||
webpki_verifier: Arc<WebPkiServerVerifier>,
|
||||
ignore_certificate_errors: bool,
|
||||
override_manager: CertificateErrorOverrideManager,
|
||||
}
|
||||
|
@ -178,18 +183,8 @@ impl CertificateVerificationOverrideVerifier {
|
|||
override_manager: CertificateErrorOverrideManager,
|
||||
) -> Self {
|
||||
let root_cert_store = match ca_certficates {
|
||||
CACertificates::Default => {
|
||||
let mut root_cert_store = rustls::RootCertStore::empty();
|
||||
root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(
|
||||
|trust_anchor| {
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
trust_anchor.subject,
|
||||
trust_anchor.spki,
|
||||
trust_anchor.name_constraints,
|
||||
)
|
||||
},
|
||||
));
|
||||
root_cert_store
|
||||
CACertificates::Default => rustls::RootCertStore {
|
||||
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
|
||||
},
|
||||
CACertificates::Override(root_cert_store) => root_cert_store,
|
||||
};
|
||||
|
@ -197,28 +192,52 @@ impl CertificateVerificationOverrideVerifier {
|
|||
Self {
|
||||
// See https://github.com/rustls/rustls/blame/v/0.21.6/rustls/src/client/builder.rs#L141
|
||||
// This is the default verifier for Rustls that we are wrapping.
|
||||
webpki_verifier: WebPkiVerifier::new(root_cert_store, None),
|
||||
webpki_verifier: WebPkiServerVerifier::builder(root_cert_store.into())
|
||||
.build()
|
||||
.unwrap(),
|
||||
ignore_certificate_errors,
|
||||
override_manager,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl rustls::client::ServerCertVerifier for CertificateVerificationOverrideVerifier {
|
||||
impl rustls::client::danger::ServerCertVerifier for CertificateVerificationOverrideVerifier {
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &rustls::DigitallySignedStruct,
|
||||
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||
self.webpki_verifier
|
||||
.verify_tls12_signature(message, cert, dss)
|
||||
}
|
||||
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &rustls::DigitallySignedStruct,
|
||||
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||
self.webpki_verifier
|
||||
.verify_tls13_signature(message, cert, dss)
|
||||
}
|
||||
|
||||
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||
self.webpki_verifier.supported_verify_schemes()
|
||||
}
|
||||
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
end_entity: &Certificate,
|
||||
intermediates: &[Certificate],
|
||||
server_name: &ServerName,
|
||||
scts: &mut dyn Iterator<Item = &[u8]>,
|
||||
end_entity: &CertificateDer<'_>,
|
||||
intermediates: &[CertificateDer<'_>],
|
||||
server_name: &ServerName<'_>,
|
||||
ocsp_response: &[u8],
|
||||
now: std::time::SystemTime,
|
||||
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
|
||||
now: UnixTime,
|
||||
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||
let error = match self.webpki_verifier.verify_server_cert(
|
||||
end_entity,
|
||||
intermediates,
|
||||
server_name,
|
||||
scts,
|
||||
ocsp_response,
|
||||
now,
|
||||
) {
|
||||
|
@ -228,13 +247,13 @@ impl rustls::client::ServerCertVerifier for CertificateVerificationOverrideVerif
|
|||
|
||||
if self.ignore_certificate_errors {
|
||||
warn!("Ignoring certficate error: {error:?}");
|
||||
return Ok(rustls::client::ServerCertVerified::assertion());
|
||||
return Ok(rustls::client::danger::ServerCertVerified::assertion());
|
||||
}
|
||||
|
||||
// If there's an override for this certificate, just accept it.
|
||||
for cert_with_exception in &*self.override_manager.0.lock().unwrap().overrides {
|
||||
if *end_entity == *cert_with_exception {
|
||||
return Ok(rustls::client::ServerCertVerified::assertion());
|
||||
return Ok(rustls::client::danger::ServerCertVerified::assertion());
|
||||
}
|
||||
}
|
||||
self.override_manager
|
||||
|
@ -242,12 +261,14 @@ impl rustls::client::ServerCertVerifier for CertificateVerificationOverrideVerif
|
|||
.lock()
|
||||
.unwrap()
|
||||
.certificates_failing_to_verify
|
||||
.insert(server_name.clone(), end_entity.clone());
|
||||
.insert(server_name.to_owned(), end_entity.clone().into_owned());
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_http_client(tls_config: TlsConfig) -> Client<Connector, Body> {
|
||||
pub type BoxedBody = BoxBody<Bytes, hyper::Error>;
|
||||
|
||||
pub fn create_http_client(tls_config: TlsConfig) -> Client<Connector, BoxedBody> {
|
||||
let connector = hyper_rustls::HttpsConnectorBuilder::new()
|
||||
.with_tls_config(tls_config)
|
||||
.https_or_http()
|
||||
|
@ -255,8 +276,7 @@ pub fn create_http_client(tls_config: TlsConfig) -> Client<Connector, Body> {
|
|||
.enable_http2()
|
||||
.wrap_connector(ServoHttpConnector::new());
|
||||
|
||||
Client::builder()
|
||||
Client::builder(TokioExecutor {})
|
||||
.http1_title_case_headers(true)
|
||||
.executor(TokioExecutor {})
|
||||
.build(connector)
|
||||
}
|
||||
|
|
|
@ -28,12 +28,16 @@ use futures::task::{Context, Poll};
|
|||
use futures::{Future, Stream};
|
||||
use futures_util::StreamExt;
|
||||
use headers::{ContentLength, HeaderMapExt};
|
||||
use http_body_util::BodyExt;
|
||||
use hyper::body::Body;
|
||||
use hyper::header::{HeaderValue, CONTENT_ENCODING, TRANSFER_ENCODING};
|
||||
use hyper::{Body, Response};
|
||||
use hyper::Response;
|
||||
use servo_config::pref;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead};
|
||||
use tokio_util::io::StreamReader;
|
||||
|
||||
use crate::connector::BoxedBody;
|
||||
|
||||
pub const DECODER_BUFFER_SIZE: usize = 8192;
|
||||
|
||||
/// A response decompressor over a non-blocking stream of bytes.
|
||||
|
@ -81,7 +85,7 @@ impl Decoder {
|
|||
/// This decoder will emit the underlying bytes as-is.
|
||||
#[inline]
|
||||
fn plain_text(
|
||||
body: Body,
|
||||
body: BoxedBody,
|
||||
is_secure_scheme: bool,
|
||||
content_length: Option<ContentLength>,
|
||||
) -> Decoder {
|
||||
|
@ -95,7 +99,7 @@ impl Decoder {
|
|||
/// This decoder will buffer and decompress bytes that are encoded in the expected format.
|
||||
#[inline]
|
||||
fn pending(
|
||||
body: Body,
|
||||
body: BoxedBody,
|
||||
type_: DecoderType,
|
||||
is_secure_scheme: bool,
|
||||
content_length: Option<ContentLength>,
|
||||
|
@ -114,7 +118,7 @@ impl Decoder {
|
|||
/// how to decode the content body of the response.
|
||||
///
|
||||
/// Uses the correct variant by inspecting the Content-Encoding header.
|
||||
pub fn detect(response: Response<Body>, is_secure_scheme: bool) -> Response<Decoder> {
|
||||
pub fn detect(response: Response<BoxedBody>, is_secure_scheme: bool) -> Response<Decoder> {
|
||||
let values = response
|
||||
.headers()
|
||||
.get_all(CONTENT_ENCODING)
|
||||
|
@ -225,7 +229,7 @@ impl Future for Pending {
|
|||
}
|
||||
|
||||
struct BodyStream {
|
||||
body: Body,
|
||||
body: BoxedBody,
|
||||
is_secure_scheme: bool,
|
||||
content_length: Option<ContentLength>,
|
||||
total_read: u64,
|
||||
|
@ -234,14 +238,16 @@ struct BodyStream {
|
|||
impl BodyStream {
|
||||
fn empty() -> Self {
|
||||
BodyStream {
|
||||
body: Body::empty(),
|
||||
body: http_body_util::Empty::new()
|
||||
.map_err(|_| unreachable!())
|
||||
.boxed(),
|
||||
is_secure_scheme: false,
|
||||
content_length: None,
|
||||
total_read: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn new(body: Body, is_secure_scheme: bool, content_length: Option<ContentLength>) -> Self {
|
||||
fn new(body: BoxedBody, is_secure_scheme: bool, content_length: Option<ContentLength>) -> Self {
|
||||
BodyStream {
|
||||
body,
|
||||
is_secure_scheme,
|
||||
|
@ -255,8 +261,11 @@ impl Stream for BodyStream {
|
|||
type Item = Result<Bytes, io::Error>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||
match futures_core::ready!(Pin::new(&mut self.body).poll_next(cx)) {
|
||||
match futures_core::ready!(Pin::new(&mut self.body).poll_frame(cx)) {
|
||||
Some(Ok(bytes)) => {
|
||||
let Ok(bytes) = bytes.into_data() else {
|
||||
return Poll::Ready(None);
|
||||
};
|
||||
self.total_read += bytes.len() as u64;
|
||||
Poll::Ready(Some(Ok(bytes)))
|
||||
},
|
||||
|
|
|
@ -31,7 +31,7 @@ use net_traits::{
|
|||
FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
|
||||
ResourceTimeValue, ResourceTimingType,
|
||||
};
|
||||
use rustls::Certificate;
|
||||
use rustls_pki_types::CertificateDer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use servo_url::ServoUrl;
|
||||
|
@ -675,7 +675,7 @@ fn handle_allowcert_request(request: &mut Request, context: &FetchContext) -> io
|
|||
context
|
||||
.state
|
||||
.override_manager
|
||||
.add_override(&Certificate(cert_bytes));
|
||||
.add_override(&CertificateDer::from_slice(&cert_bytes).into_owned());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering};
|
|||
use std::sync::{Arc, Mutex, RwLock, Weak};
|
||||
|
||||
use embedder_traits::{EmbedderMsg, EmbedderProxy, FilterPattern};
|
||||
use headers::{ContentLength, ContentType, HeaderMap, HeaderMapExt};
|
||||
use headers::{ContentLength, ContentType, HeaderMap, HeaderMapExt, Range};
|
||||
use http::header::{self, HeaderValue};
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use log::warn;
|
||||
|
@ -30,6 +30,7 @@ use url::Url;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::fetch::methods::{CancellationListener, Data, RangeRequestBounds};
|
||||
use crate::protocols::get_range_request_bounds;
|
||||
use crate::resource_thread::CoreResourceThreadPool;
|
||||
|
||||
pub const FILE_CHUNK_SIZE: usize = 32768; //32 KB
|
||||
|
@ -132,7 +133,7 @@ impl FileManager {
|
|||
file_token: &FileTokenCheck,
|
||||
origin: FileOrigin,
|
||||
response: &mut Response,
|
||||
range: RangeRequestBounds,
|
||||
range: Option<Range>,
|
||||
) -> Result<(), BlobURLStoreError> {
|
||||
self.fetch_blob_buf(
|
||||
done_sender,
|
||||
|
@ -140,7 +141,7 @@ impl FileManager {
|
|||
&id,
|
||||
file_token,
|
||||
&origin,
|
||||
range,
|
||||
BlobBounds::Unresolved(range),
|
||||
response,
|
||||
)
|
||||
}
|
||||
|
@ -285,13 +286,17 @@ impl FileManager {
|
|||
id: &Uuid,
|
||||
file_token: &FileTokenCheck,
|
||||
origin_in: &FileOrigin,
|
||||
range: RangeRequestBounds,
|
||||
bounds: BlobBounds,
|
||||
response: &mut Response,
|
||||
) -> Result<(), BlobURLStoreError> {
|
||||
let file_impl = self.store.get_impl(id, file_token, origin_in)?;
|
||||
match file_impl {
|
||||
FileImpl::Memory(buf) => {
|
||||
let range = range
|
||||
let bounds = match bounds {
|
||||
BlobBounds::Unresolved(range) => get_range_request_bounds(range, buf.size),
|
||||
BlobBounds::Resolved(bounds) => bounds,
|
||||
};
|
||||
let range = bounds
|
||||
.get_final(Some(buf.size))
|
||||
.map_err(|_| BlobURLStoreError::InvalidRange)?;
|
||||
|
||||
|
@ -323,7 +328,11 @@ impl FileManager {
|
|||
let file = File::open(&metadata.path)
|
||||
.map_err(|e| BlobURLStoreError::External(e.to_string()))?;
|
||||
|
||||
let range = range
|
||||
let bounds = match bounds {
|
||||
BlobBounds::Unresolved(range) => get_range_request_bounds(range, metadata.size),
|
||||
BlobBounds::Resolved(bounds) => bounds,
|
||||
};
|
||||
let range = bounds
|
||||
.get_final(Some(metadata.size))
|
||||
.map_err(|_| BlobURLStoreError::InvalidRange)?;
|
||||
|
||||
|
@ -362,15 +371,16 @@ impl FileManager {
|
|||
FileImpl::Sliced(parent_id, inner_rel_pos) => {
|
||||
// Next time we don't need to check validity since
|
||||
// we have already done that for requesting URL if necessary.
|
||||
let bounds = RangeRequestBounds::Final(
|
||||
RelativePos::full_range().slice_inner(&inner_rel_pos),
|
||||
);
|
||||
self.fetch_blob_buf(
|
||||
done_sender,
|
||||
cancellation_listener,
|
||||
&parent_id,
|
||||
file_token,
|
||||
origin_in,
|
||||
RangeRequestBounds::Final(
|
||||
RelativePos::full_range().slice_inner(&inner_rel_pos),
|
||||
),
|
||||
BlobBounds::Resolved(bounds),
|
||||
response,
|
||||
)
|
||||
},
|
||||
|
@ -378,6 +388,11 @@ impl FileManager {
|
|||
}
|
||||
}
|
||||
|
||||
enum BlobBounds {
|
||||
Unresolved(Option<Range>),
|
||||
Resolved(RangeRequestBounds),
|
||||
}
|
||||
|
||||
/// File manager's data store. It maintains a thread-safe mapping
|
||||
/// from FileID to FileStoreEntry which might have different backend implementation.
|
||||
/// Access to the content is encapsulated as methods of this struct.
|
||||
|
|
|
@ -345,7 +345,7 @@ fn create_resource_with_bytes_from_resource(
|
|||
fn handle_range_request(
|
||||
request: &Request,
|
||||
candidates: &[&CachedResource],
|
||||
range_spec: Vec<(Bound<u64>, Bound<u64>)>,
|
||||
range_spec: &Range,
|
||||
done_chan: &mut DoneChannel,
|
||||
) -> Option<CachedResponse> {
|
||||
let mut complete_cached_resources = candidates
|
||||
|
@ -354,10 +354,7 @@ fn handle_range_request(
|
|||
let partial_cached_resources = candidates
|
||||
.iter()
|
||||
.filter(|resource| resource.status == StatusCode::PARTIAL_CONTENT);
|
||||
match (
|
||||
range_spec.first().unwrap(),
|
||||
complete_cached_resources.next(),
|
||||
) {
|
||||
if let Some(complete_resource) = complete_cached_resources.next() {
|
||||
// TODO: take the full range spec into account.
|
||||
// If we have a complete resource, take the request range from the body.
|
||||
// When there isn't a complete resource available, we loop over cached partials,
|
||||
|
@ -366,172 +363,145 @@ fn handle_range_request(
|
|||
// see <https://tools.ietf.org/html/rfc7233#section-4.3>.
|
||||
// TODO: add support for complete and partial resources,
|
||||
// whose body is in the ResponseBody::Receiving state.
|
||||
(&(Bound::Included(beginning), Bound::Included(end)), Some(complete_resource)) => {
|
||||
if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() {
|
||||
if end == u64::MAX {
|
||||
// Prevent overflow on the addition below.
|
||||
return None;
|
||||
}
|
||||
let b = beginning as usize;
|
||||
let e = end as usize + 1;
|
||||
let requested = body.get(b..e);
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, complete_resource);
|
||||
let cached_headers = new_resource.metadata.headers.lock().unwrap();
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &cached_headers, done_chan);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
let body_len = match *complete_resource.body.lock().unwrap() {
|
||||
ResponseBody::Done(ref body) => body.len(),
|
||||
_ => 0,
|
||||
};
|
||||
let bound = range_spec
|
||||
.satisfiable_ranges(body_len.try_into().unwrap())
|
||||
.next()
|
||||
.unwrap();
|
||||
match bound {
|
||||
(Bound::Included(beginning), Bound::Included(end)) => {
|
||||
if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() {
|
||||
if end == u64::MAX {
|
||||
// Prevent overflow on the addition below.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(&(Bound::Included(beginning), Bound::Included(end)), None) => {
|
||||
for partial_resource in partial_cached_resources {
|
||||
let headers = partial_resource.metadata.headers.lock().unwrap();
|
||||
let content_range = headers.typed_get::<ContentRange>();
|
||||
let (res_beginning, res_end) = match content_range {
|
||||
Some(range) => {
|
||||
if let Some(bytes_range) = range.bytes_range() {
|
||||
bytes_range
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
if res_beginning <= beginning && res_end >= end {
|
||||
let resource_body = &*partial_resource.body.lock().unwrap();
|
||||
let requested = match resource_body {
|
||||
ResponseBody::Done(body) => {
|
||||
let b = beginning as usize - res_beginning as usize;
|
||||
let e = end as usize - res_beginning as usize + 1;
|
||||
body.get(b..e)
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
let b = beginning as usize;
|
||||
let e = end as usize + 1;
|
||||
let requested = body.get(b..e);
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, partial_resource);
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &headers, done_chan);
|
||||
create_resource_with_bytes_from_resource(bytes, complete_resource);
|
||||
let cached_headers = new_resource.metadata.headers.lock().unwrap();
|
||||
let cached_response = create_cached_response(
|
||||
request,
|
||||
&new_resource,
|
||||
&cached_headers,
|
||||
done_chan,
|
||||
);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(&(Bound::Included(beginning), Bound::Unbounded), Some(complete_resource)) => {
|
||||
if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() {
|
||||
let b = beginning as usize;
|
||||
let requested = body.get(b..);
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, complete_resource);
|
||||
let cached_headers = new_resource.metadata.headers.lock().unwrap();
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &cached_headers, done_chan);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(&(Bound::Included(beginning), Bound::Unbounded), None) => {
|
||||
for partial_resource in partial_cached_resources {
|
||||
let headers = partial_resource.metadata.headers.lock().unwrap();
|
||||
let content_range = headers.typed_get::<ContentRange>();
|
||||
let (res_beginning, res_end, total) = if let Some(range) = content_range {
|
||||
match (range.bytes_range(), range.bytes_len()) {
|
||||
(Some(bytes_range), Some(total)) => (bytes_range.0, bytes_range.1, total),
|
||||
_ => continue,
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if total == 0 {
|
||||
// Prevent overflow in the below operations from occuring.
|
||||
continue;
|
||||
};
|
||||
if res_beginning < beginning && res_end == total - 1 {
|
||||
let resource_body = &*partial_resource.body.lock().unwrap();
|
||||
let requested = match resource_body {
|
||||
ResponseBody::Done(body) => {
|
||||
let from_byte = beginning as usize - res_beginning as usize;
|
||||
body.get(from_byte..)
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
},
|
||||
(Bound::Included(beginning), Bound::Unbounded) => {
|
||||
if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() {
|
||||
let b = beginning as usize;
|
||||
let requested = body.get(b..);
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, partial_resource);
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &headers, done_chan);
|
||||
create_resource_with_bytes_from_resource(bytes, complete_resource);
|
||||
let cached_headers = new_resource.metadata.headers.lock().unwrap();
|
||||
let cached_response = create_cached_response(
|
||||
request,
|
||||
&new_resource,
|
||||
&cached_headers,
|
||||
done_chan,
|
||||
);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(&(Bound::Unbounded, Bound::Included(offset)), Some(complete_resource)) => {
|
||||
if let ResponseBody::Done(ref body) = *complete_resource.body.lock().unwrap() {
|
||||
let from_byte = body.len() - offset as usize;
|
||||
let requested = body.get(from_byte..);
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, complete_resource);
|
||||
let cached_headers = new_resource.metadata.headers.lock().unwrap();
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &cached_headers, done_chan);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(&(Bound::Unbounded, Bound::Included(offset)), None) => {
|
||||
for partial_resource in partial_cached_resources {
|
||||
let headers = partial_resource.metadata.headers.lock().unwrap();
|
||||
let content_range = headers.typed_get::<ContentRange>();
|
||||
let (res_beginning, res_end, total) = if let Some(range) = content_range {
|
||||
match (range.bytes_range(), range.bytes_len()) {
|
||||
(Some(bytes_range), Some(total)) => (bytes_range.0, bytes_range.1, total),
|
||||
_ => continue,
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if total < res_beginning || total < res_end || offset == 0 || offset == u64::MAX {
|
||||
// Prevent overflow in the below operations from occuring.
|
||||
continue;
|
||||
}
|
||||
if (total - res_beginning) > (offset - 1) && (total - res_end) < offset + 1 {
|
||||
let resource_body = &*partial_resource.body.lock().unwrap();
|
||||
let requested = match resource_body {
|
||||
ResponseBody::Done(body) => {
|
||||
let from_byte = body.len() - offset as usize;
|
||||
body.get(from_byte..)
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
for partial_resource in partial_cached_resources {
|
||||
let headers = partial_resource.metadata.headers.lock().unwrap();
|
||||
let content_range = headers.typed_get::<ContentRange>();
|
||||
|
||||
let Some(body_len) = content_range.as_ref().and_then(|range| range.bytes_len()) else {
|
||||
continue;
|
||||
};
|
||||
match range_spec.satisfiable_ranges(body_len - 1).next().unwrap() {
|
||||
(Bound::Included(beginning), Bound::Included(end)) => {
|
||||
let (res_beginning, res_end) = match content_range {
|
||||
Some(range) => {
|
||||
if let Some(bytes_range) = range.bytes_range() {
|
||||
bytes_range
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, partial_resource);
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &headers, done_chan);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
if res_beginning <= beginning && res_end >= end {
|
||||
let resource_body = &*partial_resource.body.lock().unwrap();
|
||||
let requested = match resource_body {
|
||||
ResponseBody::Done(body) => {
|
||||
let b = beginning as usize - res_beginning as usize;
|
||||
let e = end as usize - res_beginning as usize + 1;
|
||||
body.get(b..e)
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, partial_resource);
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &headers, done_chan);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
(Bound::Included(beginning), Bound::Unbounded) => {
|
||||
let (res_beginning, res_end, total) = if let Some(range) = content_range {
|
||||
match (range.bytes_range(), range.bytes_len()) {
|
||||
(Some(bytes_range), Some(total)) => {
|
||||
(bytes_range.0, bytes_range.1, total)
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if total == 0 {
|
||||
// Prevent overflow in the below operations from occuring.
|
||||
continue;
|
||||
};
|
||||
if res_beginning <= beginning && res_end == total - 1 {
|
||||
let resource_body = &*partial_resource.body.lock().unwrap();
|
||||
let requested = match resource_body {
|
||||
ResponseBody::Done(body) => {
|
||||
let from_byte = beginning as usize - res_beginning as usize;
|
||||
body.get(from_byte..)
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(bytes) = requested {
|
||||
let new_resource =
|
||||
create_resource_with_bytes_from_resource(bytes, partial_resource);
|
||||
let cached_response =
|
||||
create_cached_response(request, &new_resource, &headers, done_chan);
|
||||
if let Some(cached_response) = cached_response {
|
||||
return Some(cached_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ => continue,
|
||||
}
|
||||
},
|
||||
// All the cases with Bound::Excluded should be unreachable anyway
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -607,12 +577,7 @@ impl HttpCache {
|
|||
}
|
||||
// Support for range requests
|
||||
if let Some(range_spec) = request.headers.typed_get::<Range>() {
|
||||
return handle_range_request(
|
||||
request,
|
||||
candidates.as_slice(),
|
||||
range_spec.iter().collect(),
|
||||
done_chan,
|
||||
);
|
||||
return handle_range_request(request, candidates.as_slice(), &range_spec, done_chan);
|
||||
}
|
||||
while let Some(cached_resource) = candidates.pop() {
|
||||
// Not a Range request.
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use core::convert::Infallible;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::iter::FromIterator;
|
||||
use std::sync::{Arc as StdArc, Condvar, Mutex, RwLock};
|
||||
|
@ -19,7 +18,7 @@ use devtools_traits::{
|
|||
use embedder_traits::{
|
||||
EmbedderMsg, EmbedderProxy, PromptCredentialsInput, PromptDefinition, PromptOrigin,
|
||||
};
|
||||
use futures::{future, StreamExt, TryFutureExt, TryStreamExt};
|
||||
use futures::{future, TryFutureExt, TryStreamExt};
|
||||
use headers::authorization::Basic;
|
||||
use headers::{
|
||||
AccessControlAllowCredentials, AccessControlAllowHeaders, AccessControlAllowMethods,
|
||||
|
@ -32,10 +31,14 @@ use http::header::{
|
|||
CONTENT_TYPE,
|
||||
};
|
||||
use http::{HeaderMap, Method, Request as HyperRequest, StatusCode};
|
||||
use http_body_util::combinators::BoxBody;
|
||||
use http_body_util::{BodyExt, Full};
|
||||
use hyper::body::{Bytes, Frame};
|
||||
use hyper::ext::ReasonPhrase;
|
||||
use hyper::header::{HeaderName, TRANSFER_ENCODING};
|
||||
use hyper::{Body, Client, Response as HyperResponse};
|
||||
use hyper::Response as HyperResponse;
|
||||
use hyper_serde::Serde;
|
||||
use hyper_util::client::legacy::Client;
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use log::{debug, error, info, log_enabled, warn};
|
||||
|
@ -101,7 +104,7 @@ pub struct HttpState {
|
|||
pub http_cache_state: HttpCacheState,
|
||||
pub auth_cache: RwLock<AuthCache>,
|
||||
pub history_states: RwLock<HashMap<HistoryStateId, Vec<u8>>>,
|
||||
pub client: Client<Connector, Body>,
|
||||
pub client: Client<Connector, crate::connector::BoxedBody>,
|
||||
pub override_manager: CertificateErrorOverrideManager,
|
||||
pub embedder_proxy: Mutex<EmbedderProxy>,
|
||||
}
|
||||
|
@ -440,7 +443,7 @@ enum BodyChunk {
|
|||
enum BodyStream {
|
||||
/// A receiver that can be used in Body::wrap_stream,
|
||||
/// for streaming the request over the network.
|
||||
Chunked(TokioReceiver<Vec<u8>>),
|
||||
Chunked(TokioReceiver<Result<Frame<Bytes>, hyper::Error>>),
|
||||
/// A body whose bytes are buffered
|
||||
/// and sent in one chunk over the network.
|
||||
Buffered(UnboundedReceiver<BodyChunk>),
|
||||
|
@ -450,7 +453,7 @@ enum BodyStream {
|
|||
/// used to enqueue chunks.
|
||||
enum BodySink {
|
||||
/// A Tokio sender used to feed chunks to the network stream.
|
||||
Chunked(TokioSender<Vec<u8>>),
|
||||
Chunked(TokioSender<Result<Frame<Bytes>, hyper::Error>>),
|
||||
/// A Crossbeam sender used to send chunks to the fetch worker,
|
||||
/// where they will be buffered
|
||||
/// in order to ensure they are not streamed them over the network.
|
||||
|
@ -463,7 +466,7 @@ impl BodySink {
|
|||
BodySink::Chunked(ref sender) => {
|
||||
let sender = sender.clone();
|
||||
HANDLE.lock().unwrap().as_mut().unwrap().spawn(async move {
|
||||
let _ = sender.send(bytes).await;
|
||||
let _ = sender.send(Ok(Frame::data(bytes.into()))).await;
|
||||
});
|
||||
},
|
||||
BodySink::Buffered(ref sender) => {
|
||||
|
@ -484,7 +487,7 @@ impl BodySink {
|
|||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn obtain_response(
|
||||
client: &Client<Connector, Body>,
|
||||
client: &Client<Connector, crate::connector::BoxedBody>,
|
||||
url: &ServoUrl,
|
||||
method: &Method,
|
||||
request_headers: &mut HeaderMap,
|
||||
|
@ -584,7 +587,7 @@ async fn obtain_response(
|
|||
let body = match stream {
|
||||
BodyStream::Chunked(receiver) => {
|
||||
let stream = ReceiverStream::new(receiver);
|
||||
Body::wrap_stream(stream.map(Ok::<_, Infallible>))
|
||||
BoxBody::new(http_body_util::StreamBody::new(stream))
|
||||
},
|
||||
BodyStream::Buffered(mut receiver) => {
|
||||
// Accumulate bytes received over IPC into a vector.
|
||||
|
@ -598,7 +601,7 @@ async fn obtain_response(
|
|||
None => warn!("Failed to read all chunks from request body."),
|
||||
}
|
||||
}
|
||||
body.into()
|
||||
Full::new(body.into()).map_err(|_| unreachable!()).boxed()
|
||||
},
|
||||
};
|
||||
HyperRequest::builder()
|
||||
|
@ -609,7 +612,11 @@ async fn obtain_response(
|
|||
HyperRequest::builder()
|
||||
.method(method)
|
||||
.uri(encoded_url)
|
||||
.body(Body::empty())
|
||||
.body(
|
||||
http_body_util::Empty::new()
|
||||
.map_err(|_| unreachable!())
|
||||
.boxed(),
|
||||
)
|
||||
};
|
||||
|
||||
context
|
||||
|
@ -695,7 +702,10 @@ async fn obtain_response(
|
|||
None
|
||||
};
|
||||
|
||||
future::ready(Ok((Decoder::detect(res, is_secure_scheme), msg)))
|
||||
future::ready(Ok((
|
||||
Decoder::detect(res.map(|r| r.boxed()), is_secure_scheme),
|
||||
msg,
|
||||
)))
|
||||
})
|
||||
.map_err(move |error| {
|
||||
NetworkError::from_hyper_error(
|
||||
|
|
|
@ -16,9 +16,7 @@ use net_traits::{NetworkError, ResourceFetchTiming};
|
|||
use tokio::sync::mpsc::unbounded_channel;
|
||||
|
||||
use crate::fetch::methods::{Data, DoneChannel, FetchContext};
|
||||
use crate::protocols::{
|
||||
get_range_request_bounds, partial_content, range_not_satisfiable_error, ProtocolHandler,
|
||||
};
|
||||
use crate::protocols::{partial_content, range_not_satisfiable_error, ProtocolHandler};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BlobProtocolHander {}
|
||||
|
@ -42,9 +40,6 @@ impl ProtocolHandler for BlobProtocolHander {
|
|||
|
||||
let range_header = request.headers.typed_get::<Range>();
|
||||
let is_range_request = range_header.is_some();
|
||||
// We will get a final version of this range once we have
|
||||
// the length of the data backing the blob.
|
||||
let range = get_range_request_bounds(range_header);
|
||||
|
||||
let (id, origin) = match parse_blob_url(&url) {
|
||||
Ok((id, origin)) => (id, origin),
|
||||
|
@ -73,7 +68,7 @@ impl ProtocolHandler for BlobProtocolHander {
|
|||
&context.file_token,
|
||||
origin,
|
||||
&mut response,
|
||||
range,
|
||||
range_header,
|
||||
) {
|
||||
let _ = done_sender.send(Data::Done);
|
||||
let err = match err {
|
||||
|
|
|
@ -58,7 +58,9 @@ impl ProtocolHandler for FileProtocolHander {
|
|||
|
||||
let range_header = request.headers.typed_get::<Range>();
|
||||
let is_range_request = range_header.is_some();
|
||||
let Ok(range) = get_range_request_bounds(range_header).get_final(file_size) else {
|
||||
let Ok(range) = get_range_request_bounds(range_header, file_size.unwrap_or(0))
|
||||
.get_final(file_size)
|
||||
else {
|
||||
range_not_satisfiable_error(&mut response);
|
||||
return Box::pin(ready(response));
|
||||
};
|
||||
|
|
|
@ -106,19 +106,15 @@ pub fn range_not_satisfiable_error(response: &mut Response) {
|
|||
}
|
||||
|
||||
/// Get the range bounds if the `Range` header is present.
|
||||
pub fn get_range_request_bounds(range: Option<Range>) -> RangeRequestBounds {
|
||||
pub fn get_range_request_bounds(range: Option<Range>, len: u64) -> RangeRequestBounds {
|
||||
if let Some(ref range) = range {
|
||||
let (start, end) = 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))) => {
|
||||
let (start, end) = match range.satisfiable_ranges(len).next() {
|
||||
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(i64::max(start as i64, end as i64)))
|
||||
},
|
||||
Some(&(Bound::Unbounded, Bound::Included(offset))) => {
|
||||
Some((Bound::Unbounded, Bound::Included(offset))) => {
|
||||
return RangeRequestBounds::Pending(offset);
|
||||
},
|
||||
_ => (0, None),
|
||||
|
|
|
@ -61,8 +61,8 @@ fn load_root_cert_store_from_file(file_path: String) -> io::Result<RootCertStore
|
|||
let mut root_cert_store = RootCertStore::empty();
|
||||
|
||||
let mut pem = BufReader::new(File::open(file_path)?);
|
||||
let certs = rustls_pemfile::certs(&mut pem)?;
|
||||
root_cert_store.add_parsable_certificates(&certs);
|
||||
let certs: Result<Vec<_>, _> = rustls_pemfile::certs(&mut pem).collect();
|
||||
root_cert_store.add_parsable_certificates(certs?);
|
||||
Ok(root_cert_store)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ use headers::{
|
|||
};
|
||||
use http::header::{self, HeaderMap, HeaderName, HeaderValue};
|
||||
use http::{Method, StatusCode};
|
||||
use hyper::{Body, Request as HyperRequest, Response as HyperResponse};
|
||||
use http_body_util::combinators::BoxBody;
|
||||
use hyper::body::{Bytes, Incoming};
|
||||
use hyper::{Request as HyperRequest, Response as HyperResponse};
|
||||
use mime::{self, Mime};
|
||||
use net::fetch::cors_cache::CorsCache;
|
||||
use net::fetch::methods::{self, CancellationListener, FetchContext};
|
||||
|
@ -41,13 +43,12 @@ use net_traits::{
|
|||
};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use servo_url::ServoUrl;
|
||||
use tokio_test::block_on;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::http_loader::{expect_devtools_http_request, expect_devtools_http_response};
|
||||
use crate::{
|
||||
create_embedder_proxy, create_http_state, fetch, fetch_with_context, fetch_with_cors_cache,
|
||||
make_server, make_ssl_server, new_fetch_context, DEFAULT_USER_AGENT,
|
||||
make_body, make_server, make_ssl_server, new_fetch_context, DEFAULT_USER_AGENT,
|
||||
};
|
||||
|
||||
// TODO write a struct that impls Handler for storing test values
|
||||
|
@ -55,9 +56,11 @@ use crate::{
|
|||
#[test]
|
||||
fn test_fetch_response_is_not_network_error() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
|
@ -89,9 +92,11 @@ fn test_fetch_on_bad_port_is_network_error() {
|
|||
#[test]
|
||||
fn test_fetch_response_body_matches_const_message() {
|
||||
static MESSAGE: &'static [u8] = b"Hello World!";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
|
@ -191,7 +196,7 @@ fn test_fetch_blob() {
|
|||
expected: bytes.to_vec(),
|
||||
};
|
||||
|
||||
block_on(methods::fetch(&mut request, &mut target, &context));
|
||||
crate::HANDLE.block_on(methods::fetch(&mut request, &mut target, &context));
|
||||
|
||||
let fetch_response = receiver.recv().unwrap();
|
||||
assert!(!fetch_response.is_network_error());
|
||||
|
@ -285,38 +290,41 @@ fn test_fetch_bogus_scheme() {
|
|||
fn test_cors_preflight_fetch() {
|
||||
static ACK: &'static [u8] = b"ACK";
|
||||
let state = Arc::new(AtomicUsize::new(0));
|
||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0
|
||||
{
|
||||
assert!(request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.get(header::REFERER)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.contains("a.html"));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowCredentials);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
||||
} else {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
*response.body_mut() = ACK.to_vec().into();
|
||||
}
|
||||
};
|
||||
let handler =
|
||||
move |request: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
if request.method() == Method::OPTIONS &&
|
||||
state.clone().fetch_add(1, Ordering::SeqCst) == 0
|
||||
{
|
||||
assert!(request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.get(header::REFERER)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.contains("a.html"));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowCredentials);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
||||
} else {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
*response.body_mut() = make_body(ACK.to_vec());
|
||||
}
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let target_url = url.clone().join("a.html").unwrap();
|
||||
|
@ -340,34 +348,37 @@ fn test_cors_preflight_cache_fetch() {
|
|||
let state = Arc::new(AtomicUsize::new(0));
|
||||
let counter = state.clone();
|
||||
let mut cache = CorsCache::default();
|
||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0
|
||||
{
|
||||
assert!(request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowCredentials);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlMaxAge::from(Duration::new(6000, 0)));
|
||||
} else {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
*response.body_mut() = ACK.to_vec().into();
|
||||
}
|
||||
};
|
||||
let handler =
|
||||
move |request: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
if request.method() == Method::OPTIONS &&
|
||||
state.clone().fetch_add(1, Ordering::SeqCst) == 0
|
||||
{
|
||||
assert!(request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowCredentials);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlMaxAge::from(Duration::new(6000, 0)));
|
||||
} else {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
*response.body_mut() = make_body(ACK.to_vec());
|
||||
}
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build();
|
||||
|
@ -403,31 +414,34 @@ fn test_cors_preflight_cache_fetch() {
|
|||
fn test_cors_preflight_fetch_network_error() {
|
||||
static ACK: &'static [u8] = b"ACK";
|
||||
let state = Arc::new(AtomicUsize::new(0));
|
||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
if request.method() == Method::OPTIONS && state.clone().fetch_add(1, Ordering::SeqCst) == 0
|
||||
{
|
||||
assert!(request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowCredentials);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
||||
} else {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
*response.body_mut() = ACK.to_vec().into();
|
||||
}
|
||||
};
|
||||
let handler =
|
||||
move |request: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
if request.method() == Method::OPTIONS &&
|
||||
state.clone().fetch_add(1, Ordering::SeqCst) == 0
|
||||
{
|
||||
assert!(request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_METHOD));
|
||||
assert!(!request
|
||||
.headers()
|
||||
.contains_key(header::ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowCredentials);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowMethods::from_iter(vec![Method::GET]));
|
||||
} else {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
*response.body_mut() = make_body(ACK.to_vec());
|
||||
}
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url, Referrer::NoReferrer).build();
|
||||
|
@ -443,18 +457,20 @@ fn test_cors_preflight_fetch_network_error() {
|
|||
#[test]
|
||||
fn test_fetch_response_is_basic_filtered() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::SET_COOKIE, HeaderValue::from_static(""));
|
||||
// this header is obsoleted, so hyper doesn't implement it, but it's still covered by the spec
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static("set-cookie2"),
|
||||
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||
);
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::SET_COOKIE, HeaderValue::from_static(""));
|
||||
// this header is obsoleted, so hyper doesn't implement it, but it's still covered by the spec
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static("set-cookie2"),
|
||||
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||
);
|
||||
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
|
@ -476,47 +492,49 @@ fn test_fetch_response_is_basic_filtered() {
|
|||
#[test]
|
||||
fn test_fetch_response_is_cors_filtered() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
// this is mandatory for the Cors Check to pass
|
||||
// TODO test using different url encodings with this value ie. punycode
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
// this is mandatory for the Cors Check to pass
|
||||
// TODO test using different url encodings with this value ie. punycode
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowOrigin::ANY);
|
||||
|
||||
// these are the headers that should be kept after filtering
|
||||
response.headers_mut().typed_insert(CacheControl::new());
|
||||
response.headers_mut().insert(
|
||||
header::CONTENT_LANGUAGE,
|
||||
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||
);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(ContentType::from(mime::TEXT_HTML));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(Expires::from(SystemTime::now() + Duration::new(86400, 0)));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(LastModified::from(SystemTime::now()));
|
||||
response.headers_mut().typed_insert(Pragma::no_cache());
|
||||
// these are the headers that should be kept after filtering
|
||||
response.headers_mut().typed_insert(CacheControl::new());
|
||||
response.headers_mut().insert(
|
||||
header::CONTENT_LANGUAGE,
|
||||
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||
);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(ContentType::from(mime::TEXT_HTML));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(Expires::from(SystemTime::now() + Duration::new(86400, 0)));
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(LastModified::from(SystemTime::now()));
|
||||
response.headers_mut().typed_insert(Pragma::no_cache());
|
||||
|
||||
// these headers should not be kept after filtering, even though they are given a pass
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::SET_COOKIE, HeaderValue::from_static(""));
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static("set-cookie2"),
|
||||
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||
);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowHeaders::from_iter(vec![
|
||||
HeaderName::from_static("set-cookie"),
|
||||
// these headers should not be kept after filtering, even though they are given a pass
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::SET_COOKIE, HeaderValue::from_static(""));
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static("set-cookie2"),
|
||||
]));
|
||||
HeaderValue::from_bytes(&vec![]).unwrap(),
|
||||
);
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(AccessControlAllowHeaders::from_iter(vec![
|
||||
HeaderName::from_static("set-cookie"),
|
||||
HeaderName::from_static("set-cookie2"),
|
||||
]));
|
||||
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
// an origin mis-match will stop it from defaulting to a basic filtered response
|
||||
|
@ -546,9 +564,11 @@ fn test_fetch_response_is_cors_filtered() {
|
|||
#[test]
|
||||
fn test_fetch_response_is_opaque_filtered() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
// an origin mis-match will fall through to an Opaque filtered response
|
||||
|
@ -577,24 +597,26 @@ fn test_fetch_response_is_opaque_filtered() {
|
|||
#[test]
|
||||
fn test_fetch_response_is_opaque_redirect_filtered() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
let handler =
|
||||
move |request: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
|
||||
if redirects == 1 {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
} else {
|
||||
*response.status_mut() = StatusCode::FOUND;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("1"));
|
||||
}
|
||||
};
|
||||
if redirects == 1 {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
} else {
|
||||
*response.status_mut() = StatusCode::FOUND;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("1"));
|
||||
}
|
||||
};
|
||||
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
|
@ -626,9 +648,11 @@ fn test_fetch_with_local_urls_only() {
|
|||
// If flag `local_urls_only` is set, fetching a non-local URL must result in network error.
|
||||
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, server_url) = make_server(handler);
|
||||
|
||||
let do_fetch = |url: ServoUrl| {
|
||||
|
@ -661,9 +685,11 @@ fn test_fetch_with_local_urls_only() {
|
|||
#[test]
|
||||
fn test_fetch_with_hsts() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
|
||||
let (server, url) = make_ssl_server(handler);
|
||||
|
||||
|
@ -710,14 +736,16 @@ fn test_fetch_with_hsts() {
|
|||
|
||||
#[test]
|
||||
fn test_load_adds_host_to_hsts_list_when_url_is_https() {
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(StrictTransportSecurity::excluding_subdomains(
|
||||
Duration::from_secs(31536000),
|
||||
));
|
||||
*response.body_mut() = b"Yay!".to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
response
|
||||
.headers_mut()
|
||||
.typed_insert(StrictTransportSecurity::excluding_subdomains(
|
||||
Duration::from_secs(31536000),
|
||||
));
|
||||
*response.body_mut() = make_body(b"Yay!".to_vec());
|
||||
};
|
||||
|
||||
let (server, mut url) = make_ssl_server(handler);
|
||||
url.as_mut_url().set_scheme("https").unwrap();
|
||||
|
@ -772,9 +800,11 @@ fn test_load_adds_host_to_hsts_list_when_url_is_https() {
|
|||
|
||||
#[test]
|
||||
fn test_fetch_self_signed() {
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = b"Yay!".to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(b"Yay!".to_vec());
|
||||
};
|
||||
|
||||
let (server, mut url) = make_ssl_server(handler);
|
||||
url.as_mut_url().set_scheme("https").unwrap();
|
||||
|
@ -834,9 +864,11 @@ fn test_fetch_self_signed() {
|
|||
#[test]
|
||||
fn test_fetch_with_sri_network_error() {
|
||||
static MESSAGE: &'static [u8] = b"alert('Hello, Network Error');";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
|
@ -858,9 +890,11 @@ fn test_fetch_with_sri_network_error() {
|
|||
#[test]
|
||||
fn test_fetch_with_sri_sucess() {
|
||||
static MESSAGE: &'static [u8] = b"alert('Hello, world.');";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
|
@ -888,18 +922,20 @@ fn test_fetch_blocked_nosniff() {
|
|||
const HEADER: &'static str = "x-content-type-options";
|
||||
const VALUE: &'static [u8] = b"nosniff";
|
||||
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
let mime_header = ContentType::from(mime.clone());
|
||||
response.headers_mut().typed_insert(mime_header);
|
||||
assert!(response.headers().contains_key(header::CONTENT_TYPE));
|
||||
// Add the nosniff header
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static(HEADER),
|
||||
HeaderValue::from_bytes(VALUE).unwrap(),
|
||||
);
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
let mime_header = ContentType::from(mime.clone());
|
||||
response.headers_mut().typed_insert(mime_header);
|
||||
assert!(response.headers().contains_key(header::CONTENT_TYPE));
|
||||
// Add the nosniff header
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static(HEADER),
|
||||
HeaderValue::from_bytes(VALUE).unwrap(),
|
||||
);
|
||||
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
|
@ -926,25 +962,27 @@ fn test_fetch_blocked_nosniff() {
|
|||
}
|
||||
|
||||
fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response {
|
||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
let handler =
|
||||
move |request: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
|
||||
if redirects >= redirect_cap {
|
||||
*response.body_mut() = message.to_vec().into();
|
||||
} else {
|
||||
*response.status_mut() = StatusCode::FOUND;
|
||||
let url = format!("{redirects}", redirects = redirects + 1);
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_str(&url).unwrap());
|
||||
}
|
||||
};
|
||||
if redirects >= redirect_cap {
|
||||
*response.body_mut() = make_body(message.to_vec());
|
||||
} else {
|
||||
*response.status_mut() = StatusCode::FOUND;
|
||||
let url = format!("{redirects}", redirects = redirects + 1);
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_str(&url).unwrap());
|
||||
}
|
||||
};
|
||||
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
|
@ -997,44 +1035,46 @@ fn test_fetch_redirect_updates_method_runner(
|
|||
method: Method,
|
||||
) {
|
||||
let handler_method = method.clone();
|
||||
let handler_tx = Arc::new(Mutex::new(tx));
|
||||
let handler_tx = Arc::new(tx);
|
||||
|
||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
let handler =
|
||||
move |request: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
|
||||
let mut test_pass = true;
|
||||
let mut test_pass = true;
|
||||
|
||||
if redirects == 0 {
|
||||
*response.status_mut() = StatusCode::TEMPORARY_REDIRECT;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("1"));
|
||||
} else if redirects == 1 {
|
||||
// this makes sure that the request method does't change from the wrong status code
|
||||
if handler_method != Method::GET && request.method() == Method::GET {
|
||||
if redirects == 0 {
|
||||
*response.status_mut() = StatusCode::TEMPORARY_REDIRECT;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("1"));
|
||||
} else if redirects == 1 {
|
||||
// this makes sure that the request method does't change from the wrong status code
|
||||
if handler_method != Method::GET && request.method() == Method::GET {
|
||||
test_pass = false;
|
||||
}
|
||||
*response.status_mut() = status_code;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("2"));
|
||||
} else if request.method() != Method::GET {
|
||||
test_pass = false;
|
||||
}
|
||||
*response.status_mut() = status_code;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("2"));
|
||||
} else if request.method() != Method::GET {
|
||||
test_pass = false;
|
||||
}
|
||||
|
||||
// the first time this handler is reached, nothing is being tested, so don't send anything
|
||||
if redirects > 0 {
|
||||
handler_tx.lock().unwrap().send(test_pass).unwrap();
|
||||
}
|
||||
};
|
||||
// the first time this handler is reached, nothing is being tested, so don't send anything
|
||||
if redirects > 0 {
|
||||
handler_tx.send(test_pass).unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
let (server, url) = make_server(handler);
|
||||
let (server, url) = crate::make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
.origin(url.origin())
|
||||
|
@ -1114,9 +1154,11 @@ fn response_is_done(response: &Response) -> bool {
|
|||
#[test]
|
||||
fn test_fetch_async_returns_complete_response() {
|
||||
static MESSAGE: &'static [u8] = b"this message should be retrieved in full";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
|
@ -1131,9 +1173,11 @@ fn test_fetch_async_returns_complete_response() {
|
|||
#[test]
|
||||
fn test_opaque_filtered_fetch_async_returns_complete_response() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
// an origin mis-match will fall through to an Opaque filtered response
|
||||
|
@ -1149,24 +1193,26 @@ fn test_opaque_filtered_fetch_async_returns_complete_response() {
|
|||
#[test]
|
||||
fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() {
|
||||
static MESSAGE: &'static [u8] = b"";
|
||||
let handler = move |request: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
let handler =
|
||||
move |request: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
let redirects = request
|
||||
.uri()
|
||||
.path()
|
||||
.split("/")
|
||||
.collect::<String>()
|
||||
.parse::<u32>()
|
||||
.unwrap_or(0);
|
||||
|
||||
if redirects == 1 {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
} else {
|
||||
*response.status_mut() = StatusCode::FOUND;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("1"));
|
||||
}
|
||||
};
|
||||
if redirects == 1 {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
} else {
|
||||
*response.status_mut() = StatusCode::FOUND;
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(header::LOCATION, HeaderValue::from_static("1"));
|
||||
}
|
||||
};
|
||||
|
||||
let (server, url) = make_server(handler);
|
||||
let mut request = RequestBuilder::new(url.clone(), Referrer::NoReferrer)
|
||||
|
@ -1186,9 +1232,11 @@ fn test_opaque_redirect_filtered_fetch_async_returns_complete_response() {
|
|||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_fetch_with_devtools() {
|
||||
static MESSAGE: &'static [u8] = b"Yay!";
|
||||
let handler = move |_: HyperRequest<Body>, response: &mut HyperResponse<Body>| {
|
||||
*response.body_mut() = MESSAGE.to_vec().into();
|
||||
};
|
||||
let handler =
|
||||
move |_: HyperRequest<Incoming>,
|
||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||
*response.body_mut() = make_body(MESSAGE.to_vec());
|
||||
};
|
||||
|
||||
let (server, url) = make_server(handler);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,11 +30,13 @@ use crossbeam_channel::{unbounded, Sender};
|
|||
use devtools_traits::DevtoolsControlMsg;
|
||||
use embedder_traits::{EmbedderProxy, EmbedderReceiver, EventLoopWaker};
|
||||
use futures::future::ready;
|
||||
use futures::StreamExt;
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::server::Server as HyperServer;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Request as HyperRequest, Response as HyperResponse};
|
||||
use http_body_util::combinators::BoxBody;
|
||||
use http_body_util::{BodyExt, Empty, Full};
|
||||
use hyper::body::{Bytes, Incoming};
|
||||
use hyper::server::conn::http1;
|
||||
use hyper::service::service_fn;
|
||||
use hyper::{Request as HyperRequest, Response as HyperResponse};
|
||||
use hyper_util::rt::tokio::TokioIo;
|
||||
use net::connector::{create_http_client, create_tls_config};
|
||||
use net::fetch::cors_cache::CorsCache;
|
||||
use net::fetch::methods::{self, CancellationListener, FetchContext};
|
||||
|
@ -46,30 +48,26 @@ use net_traits::filemanager_thread::FileTokenCheck;
|
|||
use net_traits::request::Request;
|
||||
use net_traits::response::Response;
|
||||
use net_traits::{FetchTaskTarget, ResourceFetchTiming, ResourceTimingType};
|
||||
use rustls::{self, Certificate, PrivateKey};
|
||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||
use rustls_pki_types::{CertificateDer, PrivateKeyDer};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use servo_url::ServoUrl;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::runtime::{Builder, Runtime};
|
||||
use tokio_rustls::{self, TlsAcceptor};
|
||||
use tokio_stream::wrappers::TcpListenerStream;
|
||||
use tokio_test::block_on;
|
||||
|
||||
pub static HANDLE: LazyLock<Mutex<Runtime>> = LazyLock::new(|| {
|
||||
Mutex::new(
|
||||
Builder::new_multi_thread()
|
||||
.enable_io()
|
||||
.worker_threads(10)
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
pub static HANDLE: LazyLock<Runtime> = LazyLock::new(|| {
|
||||
Builder::new_multi_thread()
|
||||
.enable_io()
|
||||
.worker_threads(10)
|
||||
.build()
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
const DEFAULT_USER_AGENT: &'static str = "Such Browser. Very Layout. Wow.";
|
||||
|
||||
struct FetchResponseCollector {
|
||||
sender: Sender<Response>,
|
||||
sender: Option<tokio::sync::oneshot::Sender<Response>>,
|
||||
}
|
||||
|
||||
fn create_embedder_proxy() -> EmbedderProxy {
|
||||
|
@ -149,6 +147,8 @@ fn receive_credential_prompt_msgs(
|
|||
}
|
||||
|
||||
fn create_http_state(fc: Option<EmbedderProxy>) -> HttpState {
|
||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||
|
||||
let override_manager = net::connector::CertificateErrorOverrideManager::new();
|
||||
HttpState {
|
||||
hsts_list: RwLock::new(net::hsts::HstsList::default()),
|
||||
|
@ -197,7 +197,7 @@ impl FetchTaskTarget for FetchResponseCollector {
|
|||
fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {}
|
||||
/// Fired when the response is fully fetched
|
||||
fn process_response_eof(&mut self, _: &Request, response: &Response) {
|
||||
let _ = self.sender.send(response.clone());
|
||||
let _ = self.sender.take().unwrap().send(response.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,18 +206,22 @@ fn fetch(request: &mut Request, dc: Option<Sender<DevtoolsControlMsg>>) -> Respo
|
|||
}
|
||||
|
||||
fn fetch_with_context(request: &mut Request, mut context: &mut FetchContext) -> Response {
|
||||
let (sender, receiver) = unbounded();
|
||||
let mut target = FetchResponseCollector { sender: sender };
|
||||
block_on(async move {
|
||||
let (sender, receiver) = tokio::sync::oneshot::channel();
|
||||
let mut target = FetchResponseCollector {
|
||||
sender: Some(sender),
|
||||
};
|
||||
HANDLE.block_on(async move {
|
||||
methods::fetch(request, &mut target, &mut context).await;
|
||||
receiver.recv().unwrap()
|
||||
receiver.await.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Response {
|
||||
let (sender, receiver) = unbounded();
|
||||
let mut target = FetchResponseCollector { sender: sender };
|
||||
block_on(async move {
|
||||
let (sender, receiver) = tokio::sync::oneshot::channel();
|
||||
let mut target = FetchResponseCollector {
|
||||
sender: Some(sender),
|
||||
};
|
||||
HANDLE.block_on(async move {
|
||||
methods::fetch_with_cors_cache(
|
||||
request,
|
||||
cache,
|
||||
|
@ -225,13 +229,13 @@ fn fetch_with_cors_cache(request: &mut Request, cache: &mut CorsCache) -> Respon
|
|||
&mut new_fetch_context(None, None, None),
|
||||
)
|
||||
.await;
|
||||
receiver.recv().unwrap()
|
||||
receiver.await.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) struct Server {
|
||||
pub close_channel: tokio::sync::oneshot::Sender<()>,
|
||||
pub certificates: Option<Vec<Certificate>>,
|
||||
pub certificates: Option<Vec<CertificateDer<'static>>>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
|
@ -242,34 +246,59 @@ impl Server {
|
|||
|
||||
fn make_server<H>(handler: H) -> (Server, ServoUrl)
|
||||
where
|
||||
H: Fn(HyperRequest<Body>, &mut HyperResponse<Body>) + Send + Sync + 'static,
|
||||
H: Fn(HyperRequest<Incoming>, &mut HyperResponse<BoxBody<Bytes, hyper::Error>>)
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
{
|
||||
let handler = Arc::new(handler);
|
||||
|
||||
let listener = StdTcpListener::bind("0.0.0.0:0").unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let listener = HANDLE.block_on(async move { TcpListener::from_std(listener).unwrap() });
|
||||
|
||||
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
|
||||
let url = ServoUrl::parse(&url_string).unwrap();
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<()>();
|
||||
|
||||
let graceful = hyper_util::server::graceful::GracefulShutdown::new();
|
||||
|
||||
let (tx, mut rx) = tokio::sync::oneshot::channel::<()>();
|
||||
let server = async move {
|
||||
HyperServer::from_tcp(listener)
|
||||
.unwrap()
|
||||
.serve(make_service_fn(move |_| {
|
||||
let handler = handler.clone();
|
||||
ready(Ok::<_, Infallible>(service_fn(
|
||||
move |req: HyperRequest<Body>| {
|
||||
let mut response = HyperResponse::new(Vec::<u8>::new().into());
|
||||
handler(req, &mut response);
|
||||
ready(Ok::<_, Infallible>(response))
|
||||
},
|
||||
)))
|
||||
}))
|
||||
.with_graceful_shutdown(async move {
|
||||
rx.await.ok();
|
||||
})
|
||||
.await
|
||||
.expect("Could not start server");
|
||||
loop {
|
||||
let stream = tokio::select! {
|
||||
stream = listener.accept() => stream.unwrap().0,
|
||||
_val = &mut rx => {
|
||||
let _ = graceful.shutdown();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let handler = handler.clone();
|
||||
|
||||
let stream = stream.into_std().unwrap();
|
||||
stream
|
||||
.set_read_timeout(Some(std::time::Duration::new(5, 0)))
|
||||
.unwrap();
|
||||
let stream = TcpStream::from_std(stream).unwrap();
|
||||
|
||||
let http = http1::Builder::new();
|
||||
let conn = http.serve_connection(
|
||||
TokioIo::new(stream),
|
||||
service_fn(move |req: HyperRequest<Incoming>| {
|
||||
let mut response =
|
||||
HyperResponse::new(Empty::new().map_err(|_| unreachable!()).boxed());
|
||||
handler(req, &mut response);
|
||||
ready(Ok::<_, Infallible>(response))
|
||||
}),
|
||||
);
|
||||
let conn = graceful.watch(conn);
|
||||
HANDLE.spawn(async move {
|
||||
let _ = conn.await;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
HANDLE.lock().unwrap().spawn(server);
|
||||
let _ = HANDLE.spawn(server);
|
||||
(
|
||||
Server {
|
||||
close_channel: tx,
|
||||
|
@ -281,37 +310,40 @@ where
|
|||
|
||||
/// Given a path to a file containing PEM certificates, load and parse them into
|
||||
/// a vector of RusTLS [Certificate]s.
|
||||
fn load_certificates_from_pem(path: &PathBuf) -> std::io::Result<Vec<Certificate>> {
|
||||
fn load_certificates_from_pem(path: &PathBuf) -> std::io::Result<Vec<CertificateDer<'static>>> {
|
||||
let file = File::open(path)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let certs = certs(&mut reader)?;
|
||||
Ok(certs.into_iter().map(Certificate).collect())
|
||||
certs(&mut reader).collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
/// Given a path to a file containing PEM keys, load and parse them into
|
||||
/// a vector of RusTLS [PrivateKey]s.
|
||||
fn load_private_key_from_file(path: &PathBuf) -> Result<PrivateKey, Box<dyn std::error::Error>> {
|
||||
fn load_private_key_from_file(
|
||||
path: &PathBuf,
|
||||
) -> Result<PrivateKeyDer<'static>, Box<dyn std::error::Error>> {
|
||||
let file = File::open(&path)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let mut keys = pkcs8_private_keys(&mut reader)?;
|
||||
let mut keys = pkcs8_private_keys(&mut reader).collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
match keys.len() {
|
||||
0 => Err(format!("No PKCS8-encoded private key found in {path:?}").into()),
|
||||
1 => Ok(PrivateKey(keys.remove(0))),
|
||||
1 => Ok(PrivateKeyDer::try_from(keys.remove(0))?),
|
||||
_ => Err(format!("More than one PKCS8-encoded private key found in {path:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_ssl_server<H>(handler: H) -> (Server, ServoUrl)
|
||||
where
|
||||
H: Fn(HyperRequest<Body>, &mut HyperResponse<Body>) + Send + Sync + 'static,
|
||||
H: Fn(HyperRequest<Incoming>, &mut HyperResponse<BoxBody<Bytes, hyper::Error>>)
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
{
|
||||
let handler = Arc::new(handler);
|
||||
let listener = StdTcpListener::bind("[::0]:0").unwrap();
|
||||
let listener = HANDLE
|
||||
.lock()
|
||||
.unwrap()
|
||||
.block_on(async move { TcpListener::from_std(listener).unwrap() });
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let listener = HANDLE.block_on(async move { TcpListener::from_std(listener).unwrap() });
|
||||
|
||||
let url_string = format!("http://localhost:{}", listener.local_addr().unwrap().port());
|
||||
let url = ServoUrl::parse(&url_string).unwrap();
|
||||
|
||||
|
@ -325,27 +357,20 @@ where
|
|||
let key = load_private_key_from_file(&key_path).expect("Invalid key");
|
||||
|
||||
let config = rustls::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certificates.clone(), key)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))
|
||||
.expect("Could not create rustls ServerConfig");
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
|
||||
let mut listener = TcpListenerStream::new(listener);
|
||||
let (tx, mut rx) = tokio::sync::oneshot::channel::<()>();
|
||||
let server = async move {
|
||||
loop {
|
||||
let stream = tokio::select! {
|
||||
stream = listener.next() => stream,
|
||||
stream = listener.accept() => stream.unwrap().0,
|
||||
_ = &mut rx => break
|
||||
};
|
||||
|
||||
let stream = match stream {
|
||||
Some(stream) => stream.expect("Could not accept stream: "),
|
||||
_ => break,
|
||||
};
|
||||
|
||||
let stream = stream.into_std().unwrap();
|
||||
stream
|
||||
.set_read_timeout(Some(std::time::Duration::new(5, 0)))
|
||||
|
@ -363,11 +388,12 @@ where
|
|||
},
|
||||
};
|
||||
|
||||
let _ = Http::new()
|
||||
let _ = http1::Builder::new()
|
||||
.serve_connection(
|
||||
stream,
|
||||
service_fn(move |req: HyperRequest<Body>| {
|
||||
let mut response = HyperResponse::new(Body::empty());
|
||||
TokioIo::new(stream),
|
||||
service_fn(move |req: HyperRequest<Incoming>| {
|
||||
let mut response =
|
||||
HyperResponse::new(Empty::new().map_err(|_| unreachable!()).boxed());
|
||||
handler(req, &mut response);
|
||||
ready(Ok::<_, Infallible>(response))
|
||||
}),
|
||||
|
@ -376,7 +402,7 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
HANDLE.lock().unwrap().spawn(server);
|
||||
HANDLE.spawn(server);
|
||||
|
||||
(
|
||||
Server {
|
||||
|
@ -386,3 +412,9 @@ where
|
|||
url,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn make_body(bytes: Vec<u8>) -> BoxBody<Bytes, hyper::Error> {
|
||||
Full::new(Bytes::from(bytes))
|
||||
.map_err(|_| unreachable!())
|
||||
.boxed()
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ headers = { workspace = true }
|
|||
http = { workspace = true }
|
||||
hyper = { workspace = true }
|
||||
hyper_serde = { workspace = true }
|
||||
hyper-util = { workspace = true, features = ["client-legacy"] }
|
||||
ipc-channel = { workspace = true }
|
||||
log = { workspace = true }
|
||||
malloc_size_of = { workspace = true }
|
||||
|
@ -31,7 +32,7 @@ mime = { workspace = true }
|
|||
num-traits = { workspace = true }
|
||||
percent-encoding = { workspace = true }
|
||||
pixels = { path = "../../pixels" }
|
||||
rustls = { workspace = true }
|
||||
rustls-pki-types = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
servo_arc = { workspace = true }
|
||||
servo_rand = { path = "../../rand" }
|
||||
|
|
|
@ -15,8 +15,8 @@ use cookie::Cookie;
|
|||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
|
||||
use http::{Error as HttpError, HeaderMap, StatusCode};
|
||||
use hyper::Error as HyperError;
|
||||
use hyper_serde::Serde;
|
||||
use hyper_util::client::legacy::Error as HyperError;
|
||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use ipc_channel::Error as IpcError;
|
||||
|
@ -24,7 +24,7 @@ use malloc_size_of::malloc_size_of_is_0;
|
|||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use mime::Mime;
|
||||
use request::RequestId;
|
||||
use rustls::Certificate;
|
||||
use rustls_pki_types::CertificateDer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_rand::RngCore;
|
||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||
|
@ -882,10 +882,10 @@ pub enum NetworkError {
|
|||
}
|
||||
|
||||
impl NetworkError {
|
||||
pub fn from_hyper_error(error: &HyperError, certificate: Option<Certificate>) -> Self {
|
||||
pub fn from_hyper_error(error: &HyperError, certificate: Option<CertificateDer>) -> Self {
|
||||
let error_string = error.to_string();
|
||||
match certificate {
|
||||
Some(certificate) => NetworkError::SslValidation(error_string, certificate.0),
|
||||
Some(certificate) => NetworkError::SslValidation(error_string, certificate.to_vec()),
|
||||
_ => NetworkError::Internal(error_string),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ compositing_traits = { workspace = true }
|
|||
cookie = { workspace = true }
|
||||
crossbeam-channel = { workspace = true }
|
||||
euclid = { workspace = true }
|
||||
http = { workspace = true }
|
||||
http = { version = "0.2" }
|
||||
image = { workspace = true }
|
||||
ipc-channel = { workspace = true }
|
||||
keyboard-types = { workspace = true }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue