web: Fix cors issues with http (close ruffle-rs#1486)

This commit is contained in:
Sam Morrow 2020-12-11 11:41:44 +00:00 committed by Mike Welsh
parent 8f34023ad2
commit c4d7b24629
7 changed files with 86 additions and 18 deletions

View File

@ -198,6 +198,12 @@ pub trait NavigatorBackend {
/// current document's base URL, while the most obvious base for a desktop
/// client would be the file-URL form of the current path.
fn resolve_relative_url<'a>(&mut self, url: &'a str) -> Cow<'a, str>;
/// Handle any context specific pre-processing
///
/// Changing http -> https for example. This function may alter any part of the
/// URL (generally only if configured to do so by the user).
fn pre_process_url(&self, url: Url) -> Url;
}
/// A null implementation of an event loop that only supports blocking.
@ -378,4 +384,8 @@ impl NavigatorBackend for NullNavigatorBackend {
url.into()
}
}
fn pre_process_url(&self, url: Url) -> Url {
url
}
}

View File

@ -76,6 +76,10 @@ struct Opt {
/// (Optional) Proxy to use when loading movies via URL
#[clap(long, case_insensitive = true)]
proxy: Option<Url>,
/// (Optional) Replace all embedded http URLs with https
#[clap(long, case_insensitive = true, takes_value = false)]
upgrade_to_https: bool,
}
#[cfg(feature = "render_trace")]
@ -207,6 +211,7 @@ fn run_player(opt: Opt) -> Result<(), Box<dyn std::error::Error>> {
chan,
event_loop.create_proxy(),
opt.proxy,
opt.upgrade_to_https,
)); //TODO: actually implement this backend type
let input = Box::new(input::WinitInputBackend::new(window.clone()));
let storage = Box::new(DiskStorageBackend::new(

View File

@ -34,6 +34,8 @@ pub struct ExternalNavigatorBackend {
// Client to use for network requests
client: Option<Rc<HttpClient>>,
upgrade_to_https: bool,
}
impl ExternalNavigatorBackend {
@ -44,6 +46,7 @@ impl ExternalNavigatorBackend {
channel: Sender<OwnedFuture<(), Error>>,
event_loop: EventLoopProxy<RuffleEvent>,
proxy: Option<Url>,
upgrade_to_https: bool,
) -> Self {
let proxy = proxy.and_then(|url| url.as_str().parse().ok());
let builder = HttpClient::builder()
@ -58,6 +61,7 @@ impl ExternalNavigatorBackend {
client,
movie_url,
start_time: Instant::now(),
upgrade_to_https,
}
}
}
@ -96,36 +100,37 @@ impl NavigatorBackend for ExternalNavigatorBackend {
}
}
parsed_url.into_string()
parsed_url
}
None => url,
None => parsed_url,
};
match webbrowser::open(&modified_url) {
let processed_url = self.pre_process_url(modified_url);
match webbrowser::open(&processed_url.to_string()) {
Ok(_output) => {}
Err(e) => log::error!("Could not open URL {}: {}", modified_url, e),
Err(e) => log::error!("Could not open URL {}: {}", processed_url.as_str(), e),
};
}
fn time_since_launch(&mut self) -> Duration {
Instant::now().duration_since(self.start_time)
}
fn fetch(&self, url: &str, options: RequestOptions) -> OwnedFuture<Vec<u8>, Error> {
// TODO: honor sandbox type (local-with-filesystem, local-with-network, remote, ...)
let full_url = self.movie_url.clone().join(url).unwrap();
let processed_url = self.pre_process_url(full_url);
let client = self.client.clone();
match full_url.scheme() {
match processed_url.scheme() {
"file" => Box::pin(async move {
fs::read(full_url.to_file_path().unwrap()).map_err(Error::NetworkError)
fs::read(processed_url.to_file_path().unwrap()).map_err(Error::NetworkError)
}),
_ => Box::pin(async move {
let client = client.ok_or(Error::NetworkUnavailable)?;
let request = match options.method() {
NavigationMethod::GET => Request::get(full_url.to_string()),
NavigationMethod::POST => Request::post(full_url.to_string()),
NavigationMethod::GET => Request::get(processed_url.to_string()),
NavigationMethod::POST => Request::post(processed_url.to_string()),
};
let (body_data, _) = options.body().clone().unwrap_or_default();
@ -143,6 +148,10 @@ impl NavigatorBackend for ExternalNavigatorBackend {
}
}
fn time_since_launch(&mut self) -> Duration {
Instant::now().duration_since(self.start_time)
}
fn spawn_future(&mut self, future: OwnedFuture<(), Error>) {
self.channel.send(future).expect("working channel send");
@ -161,6 +170,13 @@ impl NavigatorBackend for ExternalNavigatorBackend {
url.into()
}
}
fn pre_process_url(&self, mut url: Url) -> Url {
if self.upgrade_to_https && url.scheme() == "http" && url.set_scheme("https").is_err() {
log::error!("Url::set_scheme failed on: {}", url);
}
url
}
}
fn response_to_bytes(res: Response<Body>) -> Result<Vec<u8>, std::io::Error> {

View File

@ -69,6 +69,19 @@ export interface BaseLoadOptions {
* @default UnmuteOverlay.Visible
*/
unmuteOverlay?: UnmuteOverlay;
/**
* Whether or not to auto-upgrade all embedded URLs to https.
*
* Flash content that embeds http urls will be blocked from
* accessing those urls by the browser when Ruffle is loaded
* in a https context. Set to `true` to automatically change
* `http://` to `https://` for all embedded URLs when Ruffle is
* loaded in an https context.
*
* @default true
*/
upgradeToHttps?: boolean;
}
/**

View File

@ -314,7 +314,9 @@ export class RufflePlayer extends HTMLElement {
this.instance = new ruffleConstructor(
this.container,
this,
this.allowScriptAccess
this.allowScriptAccess,
config.upgradeToHttps !== false &&
window.location.protocol === "https:"
);
console.log("New Ruffle instance created.");

View File

@ -115,6 +115,7 @@ impl Ruffle {
parent: HtmlElement,
js_player: JavascriptPlayer,
allow_script_access: bool,
upgrade_to_https: bool,
) -> Result<Ruffle, JsValue> {
if RUFFLE_GLOBAL_PANIC.is_completed() {
// If an actual panic happened, then we can't trust the state it left us in.
@ -122,7 +123,7 @@ impl Ruffle {
return Err("Ruffle is panicking!".into());
}
set_panic_handler();
Ruffle::new_internal(parent, js_player, allow_script_access)
Ruffle::new_internal(parent, js_player, allow_script_access, upgrade_to_https)
.map_err(|_| "Error creating player".into())
}
@ -284,6 +285,7 @@ impl Ruffle {
parent: HtmlElement,
js_player: JavascriptPlayer,
allow_script_access: bool,
upgrade_to_https: bool,
) -> Result<Ruffle, Box<dyn Error>> {
let _ = console_log::init_with_level(log::Level::Trace);
@ -296,7 +298,7 @@ impl Ruffle {
.into_js_result()?;
let audio = Box::new(WebAudioBackend::new()?);
let navigator = Box::new(WebNavigatorBackend::new());
let navigator = Box::new(WebNavigatorBackend::new(upgrade_to_https));
let input = Box::new(WebInputBackend::new(&canvas));
let locale = Box::new(WebLocaleBackend::new());

View File

@ -1,5 +1,4 @@
//! Navigator backend for web
use js_sys::{Array, ArrayBuffer, Uint8Array};
use ruffle_core::backend::navigator::{
url_from_relative_url, NavigationMethod, NavigatorBackend, OwnedFuture, RequestOptions,
@ -8,6 +7,7 @@ use ruffle_core::indexmap::IndexMap;
use ruffle_core::loader::Error;
use std::borrow::Cow;
use std::time::Duration;
use url::Url;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::{spawn_local, JsFuture};
use web_sys::{window, Blob, BlobPropertyBag, Performance, Request, RequestInit, Response};
@ -15,16 +15,18 @@ use web_sys::{window, Blob, BlobPropertyBag, Performance, Request, RequestInit,
pub struct WebNavigatorBackend {
performance: Performance,
start_time: f64,
upgrade_to_https: bool,
}
impl WebNavigatorBackend {
pub fn new() -> Self {
pub fn new(upgrade_to_https: bool) -> Self {
let window = web_sys::window().expect("window()");
let performance = window.performance().expect("window.performance()");
WebNavigatorBackend {
start_time: performance.now(),
performance,
upgrade_to_https,
}
}
}
@ -37,6 +39,12 @@ impl NavigatorBackend for WebNavigatorBackend {
vars_method: Option<(NavigationMethod, IndexMap<String, String>)>,
) {
if let Some(window) = window() {
let url = if let Ok(parsed_url) = Url::parse(&url) {
self.pre_process_url(parsed_url).to_string()
} else {
url.to_string()
};
//TODO: Should we return a result for failed opens? Does Flash care?
#[allow(unused_must_use)]
match (vars_method, window_spec) {
@ -95,7 +103,12 @@ impl NavigatorBackend for WebNavigatorBackend {
}
fn fetch(&self, url: &str, options: RequestOptions) -> OwnedFuture<Vec<u8>, Error> {
let url = url.to_string();
let url = if let Ok(parsed_url) = Url::parse(url) {
self.pre_process_url(parsed_url).to_string()
} else {
url.to_string()
};
Box::pin(async move {
let mut init = RequestInit::new();
@ -172,4 +185,11 @@ impl NavigatorBackend for WebNavigatorBackend {
url.into()
}
fn pre_process_url(&self, mut url: Url) -> Url {
if self.upgrade_to_https && url.scheme() == "http" && url.set_scheme("https").is_err() {
log::error!("Url::set_scheme failed on: {}", url);
}
url
}
}