Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions core/src/backend/navigator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@
Ask,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum FetchReason {
LoadSwf,
UrlLoader,
Other,
}

impl NavigationMethod {
/// Convert an SWF method enum into a NavigationMethod.
pub fn from_send_vars_method(s: SendVarsMethod) -> Option<Self> {
Expand Down Expand Up @@ -148,6 +155,10 @@
&self.url
}

pub fn set_url(&mut self, url: String) {
self.url = url;
}

Check warning on line 160 in core/src/backend/navigator.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (158–160)

/// Retrieve the navigation method for this request.
pub fn method(&self) -> NavigationMethod {
self.method
Expand Down Expand Up @@ -176,6 +187,9 @@
/// The final URL obtained after any redirects.
fn url(&self) -> Cow<'_, str>;

/// Rewrite the URL to a different one.
fn set_url(&mut self, url: String);

/// Retrieve the contents of the response body.
///
/// This method consumes the response.
Expand Down Expand Up @@ -567,6 +581,10 @@
Cow::Borrowed(&self.url)
}

fn set_url(&mut self, url: String) {
self.url = url;
}

Check warning on line 586 in core/src/backend/navigator.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (584–586)

fn body(self: Box<Self>) -> OwnedFuture<Vec<u8>, Error> {
Box::pin(async move {
std::fs::read(self.path).map_err(|e| Error::FetchError(e.to_string()))
Expand Down
87 changes: 71 additions & 16 deletions core/src/compatibility_rules.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
use std::borrow::Cow;
use std::collections::HashSet;
use url::Url;

use crate::backend::navigator::FetchReason;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UrlRewriteStage {
/// Perform URL rewrite before sending the request.
/// The request will be sent to a different URL.
BeforeRequest,

/// Perform URL rewrite after receiving the response.
/// The response URL will be rewritten, and SWFs will see
/// the rewritten URL.
AfterResponse,
}

#[derive(Debug, Clone)]
pub struct UrlRewriteRule {
pub stage: UrlRewriteStage,
pub fetch_reasons: HashSet<FetchReason>,
pub host: String,
pub replacement: String,
}

impl UrlRewriteRule {
pub fn new(host: impl ToString, replacement: impl ToString) -> Self {
pub fn new(
stage: UrlRewriteStage,
fetch_reasons: Vec<FetchReason>,
host: impl ToString,
replacement: impl ToString,
) -> Self {

Check warning on line 33 in core/src/compatibility_rules.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (28–33)
Self {
stage,
fetch_reasons: fetch_reasons.into_iter().collect(),

Check warning on line 36 in core/src/compatibility_rules.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (35–36)
host: host.to_string(),
replacement: replacement.to_string(),
}
Expand All @@ -18,7 +43,7 @@
#[derive(Debug, Clone)]
pub struct RuleSet {
name: String,
swf_domain_rewrite_rules: Vec<UrlRewriteRule>,
domain_rewrite_rules: Vec<UrlRewriteRule>,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -48,31 +73,55 @@
/// - Only affect content that cannot run anymore, such as requiring lost assets
/// - Not allow people to easily pirate or cheat games more than they can already
pub fn builtin_rules() -> Self {
// Replaces konggames.com domains with kongregate.com to fool old sitelocks that no longer work.
let kongregate_sitelock = RuleSet {
name: "kongregate_sitelock".to_string(),
swf_domain_rewrite_rules: vec![UrlRewriteRule::new(
"*.konggames.com",
"chat.kongregate.com",
)],
};

Self {
rule_sets: vec![kongregate_sitelock],
rule_sets: vec![
// Replaces konggames.com domains with kongregate.com to fool old sitelocks that no longer work.
RuleSet {
name: "kongregate_sitelock".to_string(),
domain_rewrite_rules: vec![UrlRewriteRule::new(
UrlRewriteStage::AfterResponse,
vec![FetchReason::LoadSwf],
"*.konggames.com",
"chat.kongregate.com",
)],
},
// Replaces fpdownload.adobe.com with Ruffle's CDN. fpdownload.adobe.com hosts SWZ files
// which do not work on web due to CORS (and the reliability of fpdownload.adobe.com is
// questionable).
RuleSet {
name: "fpdownload".to_string(),
domain_rewrite_rules: vec![UrlRewriteRule::new(
UrlRewriteStage::BeforeRequest,
vec![FetchReason::UrlLoader],
"fpdownload.adobe.com",
"cdn.ruffle.rs",
)],
},
],

Check warning on line 100 in core/src/compatibility_rules.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (77–100)
}
}

pub fn rewrite_swf_url(&self, original_url: String) -> String {
pub fn rewrite_swf_url(
&self,
original_url: Cow<'_, str>,
stage: UrlRewriteStage,
fetch_reason: FetchReason,
) -> Option<String> {
let mut url = match Url::parse(&original_url) {
Ok(url) => url,
Err(e) => {
tracing::warn!("Couldn't rewrite swf url {original_url}: {e}");
return original_url;
return None;
}
};
let mut rewritten = false;

for rule_set in &self.rule_sets {
for rule in &rule_set.swf_domain_rewrite_rules {
for rule in &rule_set.domain_rewrite_rules {
if rule.stage != stage || !rule.fetch_reasons.contains(&fetch_reason) {
continue;
}

Check warning on line 123 in core/src/compatibility_rules.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (120–123)

if let Some(host) = url.host_str() {
if domain_matches(&rule.host, host) {
tracing::info!(
Expand All @@ -84,13 +133,19 @@
"Couldn't rewrite swf host to {}: {e}",
rule.replacement
);
} else {
rewritten = true;

Check warning on line 137 in core/src/compatibility_rules.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered lines (136–137)
}
}
}
}
}

url.to_string()
if rewritten {
Some(url.to_string())

Check warning on line 145 in core/src/compatibility_rules.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (145)
} else {
None
}
}
}

Expand Down
38 changes: 19 additions & 19 deletions core/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
Object as Avm2Object,
};
use crate::avm2_stub_method_context;
use crate::backend::navigator::{ErrorResponse, OwnedFuture, Request, SuccessResponse};
use crate::backend::navigator::{
ErrorResponse, FetchReason, OwnedFuture, Request, SuccessResponse,
};
use crate::backend::ui::DialogResultFuture;
use crate::bitmap::bitmap_data::BitmapData;
use crate::bitmap::bitmap_data::Color;
Expand Down Expand Up @@ -318,7 +320,7 @@
let importer_movie = MovieClipHandle::stash(uc, importer_movie);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::LoadSwf);

match wait_for_full_response(fetch).await {
Ok((body, url, _status, _redirected)) => {
Expand Down Expand Up @@ -646,7 +648,7 @@
let request_url = request.url().to_string();
let resolved_url = player.lock().unwrap().navigator().resolve_url(&request_url);

let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::LoadSwf);

let mut replacing_root_movie = false;
player.lock().unwrap().update(|uc| -> Result<(), Error> {
Expand Down Expand Up @@ -835,7 +837,7 @@
let player = uc.player_handle();

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::LoadSwf);

Check warning on line 840 in core/src/loader.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (840)
let response = fetch.await.map_err(|error| {
player
.lock()
Expand All @@ -844,7 +846,7 @@
.display_root_movie_download_failed_message(false, error.error.to_string());
error.error
})?;
let url = response.url().into_owned();
let swf_url = response.url().into_owned();

Check warning on line 849 in core/src/loader.rs

View workflow job for this annotation

GitHub Actions / Coverage Report

Coverage

Uncovered line (849)
let body = response.body().await.inspect_err(|error| {
player
.lock()
Expand All @@ -854,11 +856,6 @@
})?;

// The spoofed root movie URL takes precedence over the actual URL.
let swf_url = player
.lock()
.unwrap()
.compatibility_rules()
.rewrite_swf_url(url);
let spoofed_or_swf_url = player
.lock()
.unwrap()
Expand Down Expand Up @@ -896,7 +893,7 @@
let target_object = ObjectHandle::stash(uc, target_object);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::Other);

let response = fetch.await.map_err(|e| e.error)?;
let response_encoding = response.text_encoding();
Expand Down Expand Up @@ -970,7 +967,7 @@
let target_object = ObjectHandle::stash(uc, target_object);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::Other);
let response = wait_for_full_response(fetch).await;

// Fire the load handler.
Expand Down Expand Up @@ -1054,7 +1051,7 @@
let target_object = ObjectHandle::stash(uc, target_object);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::Other);
let response = wait_for_full_response(fetch).await;

// Fire the load handler.
Expand Down Expand Up @@ -1113,7 +1110,10 @@
let target = Avm2ScriptObjectHandle::stash(uc, target);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player
.lock()
.unwrap()
.fetch(request, FetchReason::UrlLoader);
let response = wait_for_full_response(fetch).await;

player.lock().unwrap().update(|uc| {
Expand Down Expand Up @@ -1269,7 +1269,7 @@
let sound_object = ObjectHandle::stash(uc, sound_object);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::Other);
let response = wait_for_full_response(fetch).await;

// Fire the load handler.
Expand Down Expand Up @@ -1329,7 +1329,7 @@
let sound = SoundObjectHandle::stash(uc, sound);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::Other);
let response = wait_for_full_response(fetch).await;

player.lock().unwrap().update(|uc| {
Expand Down Expand Up @@ -1402,7 +1402,7 @@
let stream = NetStreamHandle::stash(uc, stream);

Box::pin(async move {
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::Other);
match fetch.await {
Ok(mut response) => {
let expected_length = response.expected_length();
Expand Down Expand Up @@ -2425,7 +2425,7 @@
// Download the data
let req = Request::get(url.clone());
// Doing this in two steps to prevent holding the player lock during fetch
let future = player.lock().unwrap().navigator().fetch(req);
let future = player.lock().unwrap().fetch(req, FetchReason::Other);
let download_res = wait_for_full_response(future).await;

// Fire the load handler.
Expand Down Expand Up @@ -2642,7 +2642,7 @@
)),
);
// Doing this in two steps to prevent holding the player lock during fetch
let future = player.lock().unwrap().navigator().fetch(req);
let future = player.lock().unwrap().fetch(req, FetchReason::Other);
let result = future.await;

// Fire the load handler.
Expand Down
6 changes: 4 additions & 2 deletions core/src/net_connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use crate::avm2::object::{
NetConnectionObject as Avm2NetConnectionObject, ResponderObject as Avm2ResponderObject,
};
use crate::avm2::{Activation as Avm2Activation, Avm2, EventObject as Avm2EventObject};
use crate::backend::navigator::{ErrorResponse, NavigatorBackend, OwnedFuture, Request};
use crate::backend::navigator::{
ErrorResponse, FetchReason, NavigatorBackend, OwnedFuture, Request,
};
use crate::context::UpdateContext;
use crate::loader::Error;
use crate::Player;
Expand Down Expand Up @@ -517,7 +519,7 @@ impl FlashRemoting {
let bytes = flash_lso::packet::write::write_to_bytes(&packet, true)
.expect("Must be able to serialize a packet");
let request = Request::post(url, Some((bytes, "application/x-amf".to_string())));
let fetch = player.lock().unwrap().navigator().fetch(request);
let fetch = player.lock().unwrap().fetch(request, FetchReason::Other);
let response: Result<_, ErrorResponse> = async {
let response = fetch.await?;
let url = response.url().to_string();
Expand Down
Loading
Loading