servo/components/net/tests/http_loader.rs
Usman Yahaya Baba 2ad5b24225
Send WillNavigate earlier during navigation startup (#37778)
The will-navigate message tells the devtools client to expect a
navigation for a browsing context. This makes the network monitor clear
any previous entries and show the requests for the new page that is
loaded. In order to support this correctly, we need to send the
navigation notification from the constellation instead of the script
thread, otherwise we silently ignore navigations triggered by the
browser URL bar.




Testing: Ran servo in devtools mode , now the requests appear for new
loaded page
Fixes: https://github.com/servo/servo/issues/37334

---------

Signed-off-by: Uthman Yahaya Baba <uthmanyahayababa@gmail.com>
2025-07-05 11:35:37 +00:00

1816 lines
59 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#![cfg(not(target_os = "windows"))]
use std::collections::HashMap;
use std::io::Write;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::Duration;
use base::id::{TEST_PIPELINE_ID, TEST_WEBVIEW_ID};
use content_security_policy as csp;
use cookie::Cookie as CookiePair;
use crossbeam_channel::{Receiver, unbounded};
use devtools_traits::{
ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
HttpResponse as DevtoolsHttpResponse, NetworkEvent,
};
use embedder_traits::AuthenticationResponse;
use flate2::Compression;
use flate2::write::{GzEncoder, ZlibEncoder};
use headers::authorization::Basic;
use headers::{
Authorization, ContentLength, Date, HeaderMapExt, Host, StrictTransportSecurity, UserAgent,
};
use http::header::{self, HeaderMap, HeaderValue};
use http::uri::Authority;
use http::{HeaderName, Method, StatusCode};
use http_body_util::combinators::BoxBody;
use hyper::body::{Body, Bytes, Incoming};
use hyper::{Request as HyperRequest, Response as HyperResponse};
use net::cookie::ServoCookie;
use net::cookie_storage::CookieStorage;
use net::fetch::methods::{self};
use net::http_loader::{determine_requests_referrer, serialize_origin};
use net::resource_thread::AuthCacheEntry;
use net::test::{DECODER_BUFFER_SIZE, replace_host_table};
use net_traits::http_status::HttpStatus;
use net_traits::request::{
CredentialsMode, Destination, Referrer, Request, RequestBuilder, RequestMode,
create_request_body_with_content,
};
use net_traits::response::{Response, ResponseBody};
use net_traits::{CookieSource, FetchTaskTarget, NetworkError, ReferrerPolicy};
use servo_url::{ImmutableOrigin, ServoUrl};
use url::Url;
use crate::{
create_embedder_proxy_and_receiver, fetch, fetch_with_context, make_body, make_server,
new_fetch_context, receive_credential_prompt_msgs,
};
fn mock_origin() -> ImmutableOrigin {
ServoUrl::parse("http://servo.org").unwrap().origin()
}
fn assert_cookie_for_domain(
cookie_jar: &RwLock<CookieStorage>,
domain: &str,
cookie: Option<&str>,
) {
let mut cookie_jar = cookie_jar.write().unwrap();
let url = ServoUrl::parse(&*domain).unwrap();
let cookies = cookie_jar.cookies_for_url(&url, CookieSource::HTTP);
assert_eq!(cookies.as_ref().map(|c| &**c), cookie);
}
pub fn expect_devtools_http_request(
devtools_port: &Receiver<DevtoolsControlMsg>,
) -> DevtoolsHttpRequest {
match devtools_port.recv().unwrap() {
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent(_, net_event)) => {
match net_event {
NetworkEvent::HttpRequest(httprequest) => httprequest,
_ => panic!("No HttpRequest Received"),
}
},
_ => panic!("No HttpRequest Received"),
}
}
pub fn expect_devtools_http_response(
devtools_port: &Receiver<DevtoolsControlMsg>,
) -> DevtoolsHttpResponse {
match devtools_port.recv().unwrap() {
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent(
_,
net_event_response,
)) => match net_event_response {
NetworkEvent::HttpResponse(httpresponse) => httpresponse,
_ => panic!("No HttpResponse Received"),
},
_ => panic!("No HttpResponse Received"),
}
}
#[test]
fn test_check_default_headers_loaded_in_every_request() {
let expected_headers = Arc::new(Mutex::new(None));
let expected_headers_clone = expected_headers.clone();
let handler = move |request: HyperRequest<Incoming>,
_: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request.headers().clone(),
expected_headers_clone.lock().unwrap().take().unwrap()
);
};
let (server, url) = make_server(handler);
let mut headers = HeaderMap::new();
headers.insert(
header::ACCEPT_ENCODING,
HeaderValue::from_static("gzip, deflate, br"),
);
headers.typed_insert(Host::from(
format!("{}:{}", url.host_str().unwrap(), url.port().unwrap())
.parse::<Authority>()
.unwrap(),
));
headers.insert(
header::ACCEPT,
HeaderValue::from_static("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
);
headers.insert(
header::ACCEPT_LANGUAGE,
HeaderValue::from_static("en-US,en;q=0.5"),
);
headers.typed_insert::<UserAgent>(crate::DEFAULT_USER_AGENT.parse().unwrap());
// Append fetch metadata headers
headers.insert(
HeaderName::from_static("sec-fetch-dest"),
HeaderValue::from_static("document"),
);
headers.insert(
HeaderName::from_static("sec-fetch-mode"),
HeaderValue::from_static("no-cors"),
);
headers.insert(
HeaderName::from_static("sec-fetch-site"),
HeaderValue::from_static("same-origin"),
);
headers.insert(
HeaderName::from_static("sec-fetch-user"),
HeaderValue::from_static("?1"),
);
*expected_headers.lock().unwrap() = Some(headers.clone());
// Testing for method.GET
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(url.clone().origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = dbg!(fetch(request, None));
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
// Testing for method.POST
let mut post_headers = headers.clone();
post_headers.typed_insert(ContentLength(0 as u64));
let url_str = url.as_str();
// request gets header "Origin: http://example.com" but expected_headers has
// "Origin: http://example.com/" which do not match for equality so strip trailing '/'
post_headers.insert(
header::ORIGIN,
HeaderValue::from_str(&url_str[..url_str.len() - 1]).unwrap(),
);
*expected_headers.lock().unwrap() = Some(post_headers);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::POST)
.destination(Destination::Document)
.origin(url.clone().origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
let _ = server.close();
}
#[test]
fn test_load_when_request_is_not_get_or_head_and_there_is_no_body_content_length_should_be_set_to_0()
{
let handler = move |request: HyperRequest<Incoming>,
_: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request.headers().typed_get::<ContentLength>(),
Some(ContentLength(0))
);
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::POST)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
let _ = server.close();
}
#[test]
fn test_request_and_response_data_with_network_messages() {
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response
.headers_mut()
.typed_insert(Host::from("foo.bar".parse::<Authority>().unwrap()));
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut request_headers = HeaderMap::new();
request_headers.typed_insert(Host::from("bar.foo".parse::<Authority>().unwrap()));
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.headers(request_headers)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let (devtools_chan, devtools_port) = unbounded();
let response = fetch(request, Some(devtools_chan));
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
let _ = server.close();
// notification received from devtools
let devhttprequest = expect_devtools_http_request(&devtools_port);
let devhttpresponse = expect_devtools_http_response(&devtools_port);
//Creating default headers for request
let mut headers = HeaderMap::new();
headers.insert(
header::ACCEPT,
HeaderValue::from_static("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
);
headers.insert(
header::ACCEPT_LANGUAGE,
HeaderValue::from_static("en-US,en;q=0.5"),
);
headers.typed_insert::<UserAgent>(crate::DEFAULT_USER_AGENT.parse().unwrap());
headers.insert(
header::ACCEPT_ENCODING,
HeaderValue::from_static("gzip, deflate, br"),
);
// Append fetch metadata headers
headers.insert(
HeaderName::from_static("sec-fetch-dest"),
HeaderValue::from_static("document"),
);
headers.insert(
HeaderName::from_static("sec-fetch-mode"),
HeaderValue::from_static("no-cors"),
);
headers.insert(
HeaderName::from_static("sec-fetch-site"),
HeaderValue::from_static("cross-site"),
);
headers.insert(
HeaderName::from_static("sec-fetch-user"),
HeaderValue::from_static("?1"),
);
let httprequest = DevtoolsHttpRequest {
url: url,
method: Method::GET,
headers: headers,
body: Some(vec![]),
pipeline_id: TEST_PIPELINE_ID,
started_date_time: devhttprequest.started_date_time,
time_stamp: devhttprequest.time_stamp,
connect_time: devhttprequest.connect_time,
send_time: devhttprequest.send_time,
is_xhr: false,
browsing_context_id: TEST_WEBVIEW_ID.0,
};
let content = "Yay!";
let mut response_headers = HeaderMap::new();
response_headers.typed_insert(ContentLength(content.len() as u64));
response_headers.typed_insert(Host::from("foo.bar".parse::<Authority>().unwrap()));
response_headers.typed_insert(
devhttpresponse
.headers
.as_ref()
.unwrap()
.typed_get::<Date>()
.unwrap()
.clone(),
);
let httpresponse = DevtoolsHttpResponse {
headers: Some(response_headers),
status: HttpStatus::default(),
body: None,
pipeline_id: TEST_PIPELINE_ID,
browsing_context_id: TEST_WEBVIEW_ID.0,
};
assert_eq!(devhttprequest, httprequest);
assert_eq!(devhttpresponse, httpresponse);
}
#[test]
#[cfg(not(target_os = "windows"))]
fn test_request_and_response_message_from_devtool_without_pipeline_id() {
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response
.headers_mut()
.typed_insert(Host::from("foo.bar".parse::<Authority>().unwrap()));
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(None)
.build();
let (devtools_chan, devtools_port) = unbounded();
let response = fetch(request, Some(devtools_chan));
assert!(response.actual_response().status.code().is_success());
let _ = server.close();
// notification received from devtools
assert!(devtools_port.try_recv().is_err());
}
#[test]
fn test_redirected_request_to_devtools() {
let post_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.method(), Method::GET);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (post_server, post_url) = make_server(post_handler);
let post_redirect_url = post_url.clone();
let pre_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.method(), Method::POST);
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
};
let (pre_server, pre_url) = make_server(pre_handler);
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), pre_url.clone(), Referrer::NoReferrer)
.method(Method::POST)
.destination(Destination::Document)
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let (devtools_chan, devtools_port) = unbounded();
fetch(request, Some(devtools_chan));
let _ = pre_server.close();
let _ = post_server.close();
let devhttprequest = expect_devtools_http_request(&devtools_port);
let devhttpresponse = expect_devtools_http_response(&devtools_port);
assert_eq!(devhttprequest.method, Method::POST);
assert_eq!(devhttprequest.url, pre_url);
assert_eq!(
devhttpresponse.status,
HttpStatus::from(StatusCode::MOVED_PERMANENTLY)
);
let devhttprequest = expect_devtools_http_request(&devtools_port);
let devhttpresponse = expect_devtools_http_response(&devtools_port);
assert_eq!(devhttprequest.method, Method::GET);
assert_eq!(devhttprequest.url, post_url);
assert_eq!(devhttpresponse.status, HttpStatus::default());
}
#[test]
fn test_load_when_redirecting_from_a_post_should_rewrite_next_request_as_get() {
let post_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.method(), Method::GET);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (post_server, post_url) = make_server(post_handler);
let post_redirect_url = post_url.clone();
let pre_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.method(), Method::POST);
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
};
let (pre_server, pre_url) = make_server(pre_handler);
let request = RequestBuilder::new(None, pre_url.clone(), Referrer::NoReferrer)
.method(Method::POST)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = pre_server.close();
let _ = post_server.close();
assert!(response.to_actual().status.code().is_success());
}
#[test]
fn test_load_should_decode_the_response_as_deflate_when_response_headers_have_content_encoding_deflate()
{
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response.headers_mut().insert(
header::CONTENT_ENCODING,
HeaderValue::from_static("deflate"),
);
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
e.write(b"Yay!").unwrap();
let encoded_content = e.finish().unwrap();
*response.body_mut() = make_body(encoded_content);
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server.close();
let internal_response = response.internal_response.unwrap();
assert!(internal_response.status.clone().code().is_success());
assert_eq!(
*internal_response.body.lock().unwrap(),
ResponseBody::Done(b"Yay!".to_vec())
);
}
#[test]
fn test_load_should_decode_the_response_as_gzip_when_response_headers_have_content_encoding_gzip() {
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response
.headers_mut()
.insert(header::CONTENT_ENCODING, HeaderValue::from_static("gzip"));
let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write(b"Yay!").unwrap();
let encoded_content = e.finish().unwrap();
*response.body_mut() = make_body(encoded_content);
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server.close();
let internal_response = response.internal_response.unwrap();
assert!(internal_response.status.clone().code().is_success());
assert_eq!(
*internal_response.body.lock().unwrap(),
ResponseBody::Done(b"Yay!".to_vec())
);
}
#[test]
fn test_load_doesnt_send_request_body_on_any_redirect() {
let post_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.method(), Method::GET);
assert_eq!(request.size_hint().exact(), Some(0));
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (post_server, post_url) = make_server(post_handler);
let post_redirect_url = post_url.clone();
let pre_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.size_hint().exact(), Some(13));
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
};
let (pre_server, pre_url) = make_server(pre_handler);
let content = "Body on POST!";
let request_body = create_request_body_with_content(content);
let request = RequestBuilder::new(None, pre_url.clone(), Referrer::NoReferrer)
.body(Some(request_body))
.method(Method::POST)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = pre_server.close();
let _ = post_server.close();
assert!(response.to_actual().status.code().is_success());
}
#[test]
fn test_load_doesnt_add_host_to_hsts_list_when_url_is_http_even_if_hsts_headers_are_present() {
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, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let mut context = new_fetch_context(None, None, None);
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
assert_eq!(
context
.state
.hsts_list
.read()
.unwrap()
.is_host_secure(url.host_str().unwrap()),
false
);
}
#[test]
fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_in_response() {
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response.headers_mut().insert(
header::SET_COOKIE,
HeaderValue::from_static("mozillaIs=theBest"),
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut context = new_fetch_context(None, None, None);
assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
assert_cookie_for_domain(
&context.state.cookie_jar,
url.as_str(),
Some("mozillaIs=theBest"),
);
}
#[test]
fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_resource_manager() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request.headers().get(header::COOKIE).unwrap().as_bytes(),
b"mozillaIs=theBest"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut context = new_fetch_context(None, None, None);
{
let mut cookie_jar = context.state.cookie_jar.write().unwrap();
let cookie = ServoCookie::new_wrapped(
CookiePair::new("mozillaIs".to_owned(), "theBest".to_owned()),
&url,
CookieSource::HTTP,
)
.unwrap();
cookie_jar.push(cookie, &url, CookieSource::HTTP);
}
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_load_sends_cookie_if_nonhttp() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request.headers().get(header::COOKIE).unwrap().as_bytes(),
b"mozillaIs=theBest"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut context = new_fetch_context(None, None, None);
{
let mut cookie_jar = context.state.cookie_jar.write().unwrap();
let cookie = ServoCookie::new_wrapped(
CookiePair::new("mozillaIs".to_owned(), "theBest".to_owned()),
&url,
CookieSource::NonHTTP,
)
.unwrap();
cookie_jar.push(cookie, &url, CookieSource::HTTP);
}
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
#[cfg(not(target_os = "windows"))]
fn test_cookie_set_with_httponly_should_not_be_available_using_getcookiesforurl() {
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response.headers_mut().insert(
header::SET_COOKIE,
HeaderValue::from_static("mozillaIs=theBest; HttpOnly"),
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut context = new_fetch_context(None, None, None);
assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
assert_cookie_for_domain(
&context.state.cookie_jar,
url.as_str(),
Some("mozillaIs=theBest"),
);
let mut cookie_jar = context.state.cookie_jar.write().unwrap();
assert!(
cookie_jar
.cookies_for_url(&url, CookieSource::NonHTTP)
.is_none()
);
}
#[test]
#[cfg(not(target_os = "windows"))]
fn test_when_cookie_received_marked_secure_is_ignored_for_http() {
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response.headers_mut().insert(
header::SET_COOKIE,
HeaderValue::from_static("mozillaIs=theBest; Secure"),
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut context = new_fetch_context(None, None, None);
assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(response.actual_response().status.code().is_success());
assert_cookie_for_domain(&context.state.cookie_jar, url.as_str(), None);
}
#[test]
fn test_load_sets_content_length_to_length_of_request_body() {
let content = "This is a request body";
let content_bytes = content.as_bytes();
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let content_length = ContentLength(content_bytes.len() as u64);
assert_eq!(
request.headers().typed_get::<ContentLength>(),
Some(content_length)
);
*response.body_mut() = make_body(content_bytes.to_vec());
};
let (server, url) = make_server(handler);
let request_body = create_request_body_with_content(content);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::POST)
.body(Some(request_body))
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_load_uses_explicit_accept_from_headers_in_load_data() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request
.headers()
.get(header::ACCEPT)
.unwrap()
.to_str()
.unwrap(),
"text/html"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut accept_headers = HeaderMap::new();
accept_headers.insert(header::ACCEPT, HeaderValue::from_static("text/html"));
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.headers(accept_headers)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_load_sets_default_accept_to_html_xhtml_xml_and_then_anything_else() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request
.headers()
.get(header::ACCEPT)
.unwrap()
.to_str()
.unwrap(),
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_load_uses_explicit_accept_encoding_from_load_data_headers() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request
.headers()
.get(header::ACCEPT_ENCODING)
.unwrap()
.to_str()
.unwrap(),
"chunked"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let mut accept_encoding_headers = HeaderMap::new();
accept_encoding_headers.insert(header::ACCEPT_ENCODING, HeaderValue::from_static("chunked"));
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.headers(accept_encoding_headers)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_load_sets_default_accept_encoding_to_gzip_and_deflate() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(
request
.headers()
.get(header::ACCEPT_ENCODING)
.unwrap()
.to_str()
.unwrap(),
"gzip, deflate, br"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_load_errors_when_there_a_redirect_loop() {
let url_b_for_a = Arc::new(Mutex::new(None::<ServoUrl>));
let url_b_for_a_clone = url_b_for_a.clone();
let handler_a =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(
&url_b_for_a_clone
.lock()
.unwrap()
.as_ref()
.unwrap()
.to_string(),
)
.unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
};
let (server_a, url_a) = make_server(handler_a);
let url_a_for_b = url_a.clone();
let handler_b =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&url_a_for_b.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
};
let (server_b, url_b) = make_server(handler_b);
*url_b_for_a.lock().unwrap() = Some(url_b.clone());
let request = RequestBuilder::new(None, url_a.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server_a.close();
let _ = server_b.close();
assert_eq!(
response.get_network_error(),
Some(&NetworkError::Internal("Too many redirects".to_owned()))
);
}
#[test]
fn test_load_succeeds_with_a_redirect_loop() {
let url_b_for_a = Arc::new(Mutex::new(None::<ServoUrl>));
let url_b_for_a_clone = url_b_for_a.clone();
let handled_a = AtomicBool::new(false);
let handler_a =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
if !handled_a.swap(true, Ordering::SeqCst) {
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(
&url_b_for_a_clone
.lock()
.unwrap()
.as_ref()
.unwrap()
.to_string(),
)
.unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
} else {
*response.body_mut() = make_body(b"Success".to_vec());
}
};
let (server_a, url_a) = make_server(handler_a);
let url_a_for_b = url_a.clone();
let handler_b =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&url_a_for_b.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
};
let (server_b, url_b) = make_server(handler_b);
*url_b_for_a.lock().unwrap() = Some(url_b.clone());
let request = RequestBuilder::new(None, url_a.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = server_a.close();
let _ = server_b.close();
let response = response.to_actual();
assert_eq!(response.url_list, [url_a.clone(), url_b, url_a]);
assert_eq!(
*response.body.lock().unwrap(),
ResponseBody::Done(b"Success".to_vec())
);
}
#[test]
fn test_load_follows_a_redirect() {
let post_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.method(), Method::GET);
*response.body_mut() = make_body(b"Yay!".to_vec());
};
let (post_server, post_url) = make_server(post_handler);
let post_redirect_url = post_url.clone();
let pre_handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
assert_eq!(request.method(), Method::GET);
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&post_redirect_url.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
};
let (pre_server, pre_url) = make_server(pre_handler);
let request = RequestBuilder::new(None, pre_url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
let response = fetch(request, None);
let _ = pre_server.close();
let _ = post_server.close();
let internal_response = response.internal_response.unwrap();
assert!(internal_response.status.clone().code().is_success());
assert_eq!(
*internal_response.body.lock().unwrap(),
ResponseBody::Done(b"Yay!".to_vec())
);
}
#[test]
fn test_redirect_from_x_to_y_provides_y_cookies_from_y() {
let shared_url_y = Arc::new(Mutex::new(None::<ServoUrl>));
let shared_url_y_clone = shared_url_y.clone();
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let path = request.uri().path();
if path == "/com/" {
assert_eq!(
request.headers().get(header::COOKIE).unwrap().as_bytes(),
b"mozillaIsNot=dotOrg"
);
let location = shared_url_y.lock().unwrap().as_ref().unwrap().to_string();
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&location.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
} else if path == "/org/" {
assert_eq!(
request.headers().get(header::COOKIE).unwrap().as_bytes(),
b"mozillaIs=theBest"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
} else {
panic!("unexpected path {:?}", path)
}
};
let (server, url) = make_server(handler);
let port = url.port().unwrap();
assert_eq!(url.host_str(), Some("localhost"));
let ip = "127.0.0.1".parse().unwrap();
let mut host_table = HashMap::new();
host_table.insert("mozilla.com".to_owned(), ip);
host_table.insert("mozilla.org".to_owned(), ip);
replace_host_table(host_table);
let url_x = ServoUrl::parse(&format!("http://mozilla.com:{}/com/", port)).unwrap();
let url_y = ServoUrl::parse(&format!("http://mozilla.org:{}/org/", port)).unwrap();
*shared_url_y_clone.lock().unwrap() = Some(url_y.clone());
let mut context = new_fetch_context(None, None, None);
{
let mut cookie_jar = context.state.cookie_jar.write().unwrap();
let cookie_x = ServoCookie::new_wrapped(
CookiePair::new("mozillaIsNot".to_owned(), "dotOrg".to_owned()),
&url_x,
CookieSource::HTTP,
)
.unwrap();
cookie_jar.push(cookie_x, &url_x, CookieSource::HTTP);
let cookie_y = ServoCookie::new_wrapped(
CookiePair::new("mozillaIs".to_owned(), "theBest".to_owned()),
&url_y,
CookieSource::HTTP,
)
.unwrap();
cookie_jar.push(cookie_y, &url_y, CookieSource::HTTP);
}
let request = RequestBuilder::new(None, url_x.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch_with_context(request, &mut context);
let _ = server.close();
let internal_response = response.internal_response.unwrap();
assert!(internal_response.status.clone().code().is_success());
assert_eq!(
*internal_response.body.lock().unwrap(),
ResponseBody::Done(b"Yay!".to_vec())
);
}
#[test]
fn test_redirect_from_x_to_x_provides_x_with_cookie_from_first_response() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let path = request.uri().path();
if path == "/initial/" {
response.headers_mut().insert(
header::SET_COOKIE,
HeaderValue::from_static("mozillaIs=theBest; path=/;"),
);
let location = "/subsequent/".to_string();
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_str(&location.to_string()).unwrap(),
);
*response.status_mut() = StatusCode::MOVED_PERMANENTLY;
} else if path == "/subsequent/" {
assert_eq!(
request.headers().get(header::COOKIE).unwrap().as_bytes(),
b"mozillaIs=theBest"
);
*response.body_mut() = make_body(b"Yay!".to_vec());
} else {
panic!("unexpected path {:?}", path)
}
};
let (server, url) = make_server(handler);
let url = url.join("/initial/").unwrap();
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch(request, None);
let _ = server.close();
let internal_response = response.internal_response.unwrap();
assert!(internal_response.status.clone().code().is_success());
assert_eq!(
*internal_response.body.lock().unwrap(),
ResponseBody::Done(b"Yay!".to_vec())
);
}
#[test]
fn test_if_auth_creds_not_in_url_but_in_cache_it_sets_it() {
let handler =
move |request: HyperRequest<Incoming>,
_response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let expected = Authorization::basic("username", "test");
assert_eq!(
request.headers().typed_get::<Authorization<Basic>>(),
Some(expected)
);
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.mode(RequestMode::Navigate)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let mut context = new_fetch_context(None, None, None);
let auth_entry = AuthCacheEntry {
user_name: "username".to_owned(),
password: "test".to_owned(),
};
context
.state
.auth_cache
.write()
.unwrap()
.entries
.insert(url.origin().clone().ascii_serialization(), auth_entry);
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_auth_ui_needs_www_auth() {
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
*response.status_mut() = StatusCode::UNAUTHORIZED;
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.mode(RequestMode::Navigate)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let response = fetch(request, None);
let _ = server.close();
assert_eq!(
response.internal_response.unwrap().status,
StatusCode::UNAUTHORIZED
);
}
#[test]
fn test_determine_requests_referrer_shorter_than_4k() {
let url_str = "http://username:password@example.com/such/short/referer?query#fragment";
let referrer_source = ServoUrl::parse(url_str).unwrap();
let current_url = ServoUrl::parse("http://example.com/current/url").unwrap();
let referrer_policy = ReferrerPolicy::UnsafeUrl;
let referer = determine_requests_referrer(referrer_policy, referrer_source, current_url);
assert_eq!(
referer.unwrap().as_str(),
"http://example.com/such/short/referer?query"
);
}
#[test]
fn test_determine_requests_referrer_longer_than_4k() {
let long_url_str = format!(
"http://username:password@example.com/such/{}/referer?query#fragment",
"long".repeat(1024)
);
let referrer_source = ServoUrl::parse(&long_url_str).unwrap();
let current_url = ServoUrl::parse("http://example.com/current/url").unwrap();
let referrer_policy = ReferrerPolicy::UnsafeUrl;
let referer = determine_requests_referrer(referrer_policy, referrer_source, current_url);
assert_eq!(referer.unwrap().as_str(), "http://example.com/");
}
#[test]
fn test_fetch_compressed_response_update_count() {
// contents of ../../tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br
const DATA_BROTLI_COMPRESSED: [u8; 15] = [
0xe1, 0x18, 0x48, 0xc1, 0x2f, 0x65, 0xf6, 0x16, 0x9f, 0x05, 0x01, 0xbb, 0x20, 0x00, 0x06,
];
const DATA_DECOMPRESSED_LEN: usize = 10500;
let handler =
move |_: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
response
.headers_mut()
.insert(header::CONTENT_ENCODING, HeaderValue::from_static("br"));
*response.body_mut() = make_body(DATA_BROTLI_COMPRESSED.to_vec());
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.build();
struct FetchResponseCollector {
sender: Option<tokio::sync::oneshot::Sender<usize>>,
update_count: usize,
}
impl FetchTaskTarget for FetchResponseCollector {
fn process_request_body(&mut self, _: &Request) {}
fn process_request_eof(&mut self, _: &Request) {}
fn process_response(&mut self, _: &Request, _: &Response) {}
fn process_response_chunk(&mut self, _: &Request, _: Vec<u8>) {
self.update_count += 1;
}
/// Fired when the response is fully fetched
fn process_response_eof(&mut self, _: &Request, _: &Response) {
let _ = self.sender.take().unwrap().send(self.update_count);
}
fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
}
let (sender, receiver) = tokio::sync::oneshot::channel();
let mut target = FetchResponseCollector {
sender: Some(sender),
update_count: 0,
};
let response_update_count = crate::HANDLE.block_on(async move {
methods::fetch(
request,
&mut target,
&mut new_fetch_context(None, None, None),
)
.await;
receiver.await.unwrap()
});
server.close();
const EXPECTED_UPDATE_COUNT: usize =
(DATA_DECOMPRESSED_LEN + DECODER_BUFFER_SIZE - 1) / DECODER_BUFFER_SIZE;
assert_eq!(response_update_count, EXPECTED_UPDATE_COUNT);
}
#[test]
fn test_origin_serialization_compatability() {
let ensure_serialiations_match = |url_string| {
let url = Url::parse(url_string).unwrap();
let origin = ImmutableOrigin::new(url.origin());
let serialized = format!("{}", serialize_origin(&origin));
assert_eq!(serialized, origin.ascii_serialization());
};
ensure_serialiations_match("https://example.com");
ensure_serialiations_match("https://example.com:443");
ensure_serialiations_match("http://example.com");
ensure_serialiations_match("http://example.com:80");
ensure_serialiations_match("https://example.com:1234");
ensure_serialiations_match("http://example.com:1234");
ensure_serialiations_match("data:,dataurltexta");
}
#[test]
fn test_user_credentials_prompt_when_proxy_authentication_is_required() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let expected = Authorization::basic("username", "test");
if let Some(credentials) = request.headers().typed_get::<Authorization<Basic>>() {
if credentials == expected {
*response.status_mut() = StatusCode::OK;
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
} else {
*response.status_mut() = StatusCode::PROXY_AUTHENTICATION_REQUIRED;
}
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.mode(RequestMode::Navigate)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let (embedder_proxy, embedder_receiver) = create_embedder_proxy_and_receiver();
let _ = receive_credential_prompt_msgs(
embedder_receiver,
Some(AuthenticationResponse {
username: "username".into(),
password: "test".into(),
}),
);
let mut context = new_fetch_context(None, Some(embedder_proxy), None);
let response = fetch_with_context(request, &mut context);
let _ = server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_prompt_credentials_when_client_receives_unauthorized_response() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let expected = Authorization::basic("username", "test");
if let Some(credentials) = request.headers().typed_get::<Authorization<Basic>>() {
if credentials == expected {
*response.status_mut() = StatusCode::OK;
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.mode(RequestMode::Navigate)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let (embedder_proxy, embedder_receiver) = create_embedder_proxy_and_receiver();
let _ = receive_credential_prompt_msgs(
embedder_receiver,
Some(AuthenticationResponse {
username: "username".into(),
password: "test".into(),
}),
);
let mut context = new_fetch_context(None, Some(embedder_proxy), None);
let response = fetch_with_context(request, &mut context);
server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_success()
);
}
#[test]
fn test_prompt_credentials_user_cancels_dialog_input() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let expected = Authorization::basic("username", "test");
if let Some(credentials) = request.headers().typed_get::<Authorization<Basic>>() {
if credentials == expected {
*response.status_mut() = StatusCode::OK;
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.mode(RequestMode::Navigate)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let (embedder_proxy, embedder_receiver) = create_embedder_proxy_and_receiver();
let _ = receive_credential_prompt_msgs(embedder_receiver, None);
let mut context = new_fetch_context(None, Some(embedder_proxy), None);
let response = fetch_with_context(request, &mut context);
server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_client_error()
);
}
#[test]
fn test_prompt_credentials_user_input_incorrect_credentials() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let expected = Authorization::basic("username", "test");
if let Some(credentials) = request.headers().typed_get::<Authorization<Basic>>() {
if credentials == expected {
*response.status_mut() = StatusCode::OK;
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.mode(RequestMode::Navigate)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let (embedder_proxy, embedder_receiver) = create_embedder_proxy_and_receiver();
let _ = receive_credential_prompt_msgs(
embedder_receiver,
Some(AuthenticationResponse {
username: "test".into(),
password: "test".into(),
}),
);
let mut context = new_fetch_context(None, Some(embedder_proxy), None);
let response = fetch_with_context(request, &mut context);
server.close();
assert!(
response
.internal_response
.unwrap()
.status
.code()
.is_client_error()
);
}
#[test]
fn test_prompt_credentials_user_input_incorrect_mode() {
let handler =
move |request: HyperRequest<Incoming>,
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
let expected = Authorization::basic("test", "test");
if let Some(credentials) = request.headers().typed_get::<Authorization<Basic>>() {
if credentials == expected {
*response.status_mut() = StatusCode::OK;
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
} else {
*response.status_mut() = StatusCode::UNAUTHORIZED;
}
};
let (server, url) = make_server(handler);
let request = RequestBuilder::new(Some(TEST_WEBVIEW_ID), url.clone(), Referrer::NoReferrer)
.method(Method::GET)
.body(None)
.destination(Destination::Document)
.mode(RequestMode::SameOrigin)
.origin(mock_origin())
.pipeline_id(Some(TEST_PIPELINE_ID))
.credentials_mode(CredentialsMode::Include)
.build();
let (embedder_proxy, embedder_receiver) = create_embedder_proxy_and_receiver();
let _ = receive_credential_prompt_msgs(
embedder_receiver,
Some(AuthenticationResponse {
username: "test".into(),
password: "test".into(),
}),
);
let mut context = new_fetch_context(None, Some(embedder_proxy), None);
let response = fetch_with_context(request, &mut context);
server.close();
assert!(response.internal_response.is_none());
}