core: `Request` is now a trait. Body download is deferred to a second async method.
This commit is contained in:
parent
36210c9f20
commit
06eb2e1ee8
|
@ -6,6 +6,7 @@ use crate::string::WStr;
|
||||||
use async_channel::Receiver;
|
use async_channel::Receiver;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
@ -186,18 +187,20 @@ impl Request {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A response to a successful fetch request.
|
/// A response to a successful fetch request.
|
||||||
pub struct SuccessResponse {
|
pub trait SuccessResponse {
|
||||||
/// The final URL obtained after any redirects.
|
/// The final URL obtained after any redirects.
|
||||||
pub url: String,
|
fn url(&self) -> Cow<str>;
|
||||||
|
|
||||||
/// The contents of the response body.
|
/// Retrieve the contents of the response body.
|
||||||
pub body: Vec<u8>,
|
///
|
||||||
|
/// This method consumes the response.
|
||||||
|
fn body(self: Box<Self>) -> OwnedFuture<Vec<u8>, Error>;
|
||||||
|
|
||||||
/// The status code of the response.
|
/// The status code of the response.
|
||||||
pub status: u16,
|
fn status(&self) -> u16;
|
||||||
|
|
||||||
/// The field to indicate if the request has been redirected.
|
/// Indicates if the request has been redirected.
|
||||||
pub redirected: bool,
|
fn redirected(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A response to a non-successful fetch request.
|
/// A response to a non-successful fetch request.
|
||||||
|
@ -245,7 +248,7 @@ pub trait NavigatorBackend {
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Fetch data and return it some time in the future.
|
/// Fetch data and return it some time in the future.
|
||||||
fn fetch(&self, request: Request) -> OwnedFuture<SuccessResponse, ErrorResponse>;
|
fn fetch(&self, request: Request) -> OwnedFuture<Box<dyn SuccessResponse>, ErrorResponse>;
|
||||||
|
|
||||||
/// Take a URL string and resolve it to the actual URL from which a file
|
/// Take a URL string and resolve it to the actual URL from which a file
|
||||||
/// can be fetched. This includes handling of relative links and pre-processing.
|
/// can be fetched. This includes handling of relative links and pre-processing.
|
||||||
|
@ -402,7 +405,7 @@ impl NavigatorBackend for NullNavigatorBackend {
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self, request: Request) -> OwnedFuture<SuccessResponse, ErrorResponse> {
|
fn fetch(&self, request: Request) -> OwnedFuture<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||||
fetch_path(self, "NullNavigatorBackend", request.url(), None)
|
fetch_path(self, "NullNavigatorBackend", request.url(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,7 +452,7 @@ pub fn async_return<SuccessType: 'static, ErrorType: 'static>(
|
||||||
pub fn create_fetch_error<ErrorType: Display>(
|
pub fn create_fetch_error<ErrorType: Display>(
|
||||||
url: &str,
|
url: &str,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
) -> Result<SuccessResponse, ErrorResponse> {
|
) -> Result<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||||
create_specific_fetch_error("Invalid URL", url, error)
|
create_specific_fetch_error("Invalid URL", url, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,7 +462,7 @@ pub fn create_specific_fetch_error<ErrorType: Display>(
|
||||||
reason: &str,
|
reason: &str,
|
||||||
url: &str,
|
url: &str,
|
||||||
error: ErrorType,
|
error: ErrorType,
|
||||||
) -> Result<SuccessResponse, ErrorResponse> {
|
) -> Result<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||||
let message = if error.to_string() == "" {
|
let message = if error.to_string() == "" {
|
||||||
format!("{reason} {url}")
|
format!("{reason} {url}")
|
||||||
} else {
|
} else {
|
||||||
|
@ -543,7 +546,34 @@ pub fn fetch_path<NavigatorType: NavigatorBackend>(
|
||||||
navigator_name: &str,
|
navigator_name: &str,
|
||||||
url: &str,
|
url: &str,
|
||||||
base_path: Option<&Path>,
|
base_path: Option<&Path>,
|
||||||
) -> OwnedFuture<SuccessResponse, ErrorResponse> {
|
) -> OwnedFuture<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||||
|
struct LocalResponse {
|
||||||
|
url: String,
|
||||||
|
path: PathBuf,
|
||||||
|
status: u16,
|
||||||
|
redirected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SuccessResponse for LocalResponse {
|
||||||
|
fn url(&self) -> Cow<str> {
|
||||||
|
Cow::Borrowed(&self.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
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()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> u16 {
|
||||||
|
self.status
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redirected(&self) -> bool {
|
||||||
|
self.redirected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let url = match navigator.resolve_url(url) {
|
let url = match navigator.resolve_url(url) {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
Err(e) => return async_return(create_fetch_error(url, e)),
|
Err(e) => return async_return(create_fetch_error(url, e)),
|
||||||
|
@ -584,15 +614,13 @@ pub fn fetch_path<NavigatorType: NavigatorBackend>(
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let body = match std::fs::read(path) {
|
let response: Box<dyn SuccessResponse> = Box::new(LocalResponse {
|
||||||
Ok(body) => body,
|
|
||||||
Err(e) => return create_specific_fetch_error("Can't open file", url.as_str(), e),
|
|
||||||
};
|
|
||||||
Ok(SuccessResponse {
|
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
body,
|
path,
|
||||||
status: 0,
|
status: 0,
|
||||||
redirected: false,
|
redirected: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::avm2::{
|
||||||
Activation as Avm2Activation, Avm2, BitmapDataObject, Domain as Avm2Domain,
|
Activation as Avm2Activation, Avm2, BitmapDataObject, Domain as Avm2Domain,
|
||||||
Object as Avm2Object, Value as Avm2Value,
|
Object as Avm2Object, Value as Avm2Value,
|
||||||
};
|
};
|
||||||
use crate::backend::navigator::{OwnedFuture, Request};
|
use crate::backend::navigator::{ErrorResponse, OwnedFuture, Request, SuccessResponse};
|
||||||
use crate::backend::ui::DialogResultFuture;
|
use crate::backend::ui::DialogResultFuture;
|
||||||
use crate::bitmap::bitmap_data::Color;
|
use crate::bitmap::bitmap_data::Color;
|
||||||
use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper};
|
use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper};
|
||||||
|
@ -854,6 +854,21 @@ impl<'gc> Loader<'gc> {
|
||||||
Ok(did_finish)
|
Ok(did_finish)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn wait_for_full_response(
|
||||||
|
response: OwnedFuture<Box<dyn SuccessResponse>, ErrorResponse>,
|
||||||
|
) -> Result<(Vec<u8>, String, u16, bool), ErrorResponse> {
|
||||||
|
let response = response.await?;
|
||||||
|
let url = response.url().to_string();
|
||||||
|
let status = response.status();
|
||||||
|
let redirected = response.redirected();
|
||||||
|
let body = response.body().await;
|
||||||
|
|
||||||
|
match body {
|
||||||
|
Ok(body) => Ok((body, url, status, redirected)),
|
||||||
|
Err(error) => Err(ErrorResponse { url, error }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct a future for the root movie loader.
|
/// Construct a future for the root movie loader.
|
||||||
fn root_movie_loader(
|
fn root_movie_loader(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -875,7 +890,6 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||||
|
|
||||||
let response = fetch.await.map_err(|error| {
|
let response = fetch.await.map_err(|error| {
|
||||||
player
|
player
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -884,13 +898,22 @@ impl<'gc> Loader<'gc> {
|
||||||
.display_root_movie_download_failed_message(false);
|
.display_root_movie_download_failed_message(false);
|
||||||
error.error
|
error.error
|
||||||
})?;
|
})?;
|
||||||
|
let url = response.url().into_owned();
|
||||||
|
let body = response.body().await.map_err(|error| {
|
||||||
|
player
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.ui()
|
||||||
|
.display_root_movie_download_failed_message(true);
|
||||||
|
error
|
||||||
|
})?;
|
||||||
|
|
||||||
// The spoofed root movie URL takes precedence over the actual URL.
|
// The spoofed root movie URL takes precedence over the actual URL.
|
||||||
let swf_url = player
|
let swf_url = player
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.compatibility_rules()
|
.compatibility_rules()
|
||||||
.rewrite_swf_url(response.url);
|
.rewrite_swf_url(url);
|
||||||
let spoofed_or_swf_url = player
|
let spoofed_or_swf_url = player
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -899,7 +922,7 @@ impl<'gc> Loader<'gc> {
|
||||||
.unwrap_or(swf_url);
|
.unwrap_or(swf_url);
|
||||||
|
|
||||||
let mut movie =
|
let mut movie =
|
||||||
SwfMovie::from_data(&response.body, spoofed_or_swf_url, None).map_err(|error| {
|
SwfMovie::from_data(&body, spoofed_or_swf_url, None).map_err(|error| {
|
||||||
player
|
player
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -970,25 +993,25 @@ impl<'gc> Loader<'gc> {
|
||||||
Loader::movie_loader_start(handle, uc)
|
Loader::movie_loader_start(handle, uc)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
match fetch.await {
|
match Self::wait_for_full_response(fetch).await {
|
||||||
Ok(response) if replacing_root_movie => {
|
Ok((body, url, _status, _redirected)) if replacing_root_movie => {
|
||||||
ContentType::sniff(&response.body).expect(ContentType::Swf)?;
|
ContentType::sniff(&body).expect(ContentType::Swf)?;
|
||||||
|
|
||||||
let movie = SwfMovie::from_data(&response.body, response.url, loader_url)?;
|
let movie = SwfMovie::from_data(&body, url.to_string(), loader_url)?;
|
||||||
player.lock().unwrap().mutate_with_update_context(|uc| {
|
player.lock().unwrap().mutate_with_update_context(|uc| {
|
||||||
uc.set_root_movie(movie);
|
uc.set_root_movie(movie);
|
||||||
});
|
});
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Ok(response) => {
|
Ok((body, url, status, redirected)) => {
|
||||||
player.lock().unwrap().mutate_with_update_context(|uc| {
|
player.lock().unwrap().mutate_with_update_context(|uc| {
|
||||||
Loader::movie_loader_data(
|
Loader::movie_loader_data(
|
||||||
handle,
|
handle,
|
||||||
uc,
|
uc,
|
||||||
&response.body,
|
&body,
|
||||||
response.url,
|
url.to_string(),
|
||||||
response.status,
|
status,
|
||||||
response.redirected,
|
redirected,
|
||||||
loader_url,
|
loader_url,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
@ -1084,6 +1107,7 @@ impl<'gc> Loader<'gc> {
|
||||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||||
|
|
||||||
let response = fetch.await.map_err(|e| e.error)?;
|
let response = fetch.await.map_err(|e| e.error)?;
|
||||||
|
let body = response.body().await?;
|
||||||
|
|
||||||
// Fire the load handler.
|
// Fire the load handler.
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
|
@ -1099,7 +1123,7 @@ impl<'gc> Loader<'gc> {
|
||||||
ActivationIdentifier::root("[Form Loader]"),
|
ActivationIdentifier::root("[Form Loader]"),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (k, v) in form_urlencoded::parse(&response.body) {
|
for (k, v) in form_urlencoded::parse(&body) {
|
||||||
let k = AvmString::new_utf8(activation.context.gc_context, k);
|
let k = AvmString::new_utf8(activation.context.gc_context, k);
|
||||||
let v = AvmString::new_utf8(activation.context.gc_context, v);
|
let v = AvmString::new_utf8(activation.context.gc_context, v);
|
||||||
that.set(k, v.into(), &mut activation)?;
|
that.set(k, v.into(), &mut activation)?;
|
||||||
|
@ -1145,8 +1169,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||||
|
let response = Self::wait_for_full_response(fetch).await;
|
||||||
let data = fetch.await;
|
|
||||||
|
|
||||||
// Fire the load handler.
|
// Fire the load handler.
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
|
@ -1160,9 +1183,9 @@ impl<'gc> Loader<'gc> {
|
||||||
let mut activation =
|
let mut activation =
|
||||||
Activation::from_stub(uc.reborrow(), ActivationIdentifier::root("[Loader]"));
|
Activation::from_stub(uc.reborrow(), ActivationIdentifier::root("[Loader]"));
|
||||||
|
|
||||||
match data {
|
match response {
|
||||||
Ok(response) => {
|
Ok((body, _, status, _)) => {
|
||||||
let length = response.body.len();
|
let length = body.len();
|
||||||
|
|
||||||
// Set the properties used by the getBytesTotal and getBytesLoaded methods.
|
// Set the properties used by the getBytesTotal and getBytesLoaded methods.
|
||||||
that.set("_bytesTotal", length.into(), &mut activation)?;
|
that.set("_bytesTotal", length.into(), &mut activation)?;
|
||||||
|
@ -1172,7 +1195,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
let _ = that.call_method(
|
let _ = that.call_method(
|
||||||
"onHTTPStatus".into(),
|
"onHTTPStatus".into(),
|
||||||
&[response.status.into()],
|
&[status.into()],
|
||||||
&mut activation,
|
&mut activation,
|
||||||
ExecutionReason::Special,
|
ExecutionReason::Special,
|
||||||
);
|
);
|
||||||
|
@ -1184,7 +1207,7 @@ impl<'gc> Loader<'gc> {
|
||||||
} else {
|
} else {
|
||||||
AvmString::new_utf8(
|
AvmString::new_utf8(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
UTF_8.decode(&response.body).0,
|
UTF_8.decode(&body).0,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
|
@ -1247,7 +1270,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||||
let response = fetch.await;
|
let response = Self::wait_for_full_response(fetch).await;
|
||||||
|
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
let loader = uc.load_manager.get_loader(handle);
|
let loader = uc.load_manager.get_loader(handle);
|
||||||
|
@ -1299,8 +1322,8 @@ impl<'gc> Loader<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
Ok(response) => {
|
Ok((body, _, status, redirected)) => {
|
||||||
let total_len = response.body.len();
|
let total_len = body.len();
|
||||||
|
|
||||||
// FIXME - the "open" event should be fired earlier, just before
|
// FIXME - the "open" event should be fired earlier, just before
|
||||||
// we start to fetch the data.
|
// we start to fetch the data.
|
||||||
|
@ -1314,7 +1337,7 @@ impl<'gc> Loader<'gc> {
|
||||||
let open_evt =
|
let open_evt =
|
||||||
Avm2EventObject::bare_default_event(&mut activation.context, "open");
|
Avm2EventObject::bare_default_event(&mut activation.context, "open");
|
||||||
Avm2::dispatch_event(&mut activation.context, open_evt, target);
|
Avm2::dispatch_event(&mut activation.context, open_evt, target);
|
||||||
set_data(response.body, &mut activation, target, data_format);
|
set_data(body, &mut activation, target, data_format);
|
||||||
|
|
||||||
// FIXME - we should fire "progress" events as we receive data, not
|
// FIXME - we should fire "progress" events as we receive data, not
|
||||||
// just at the end
|
// just at the end
|
||||||
|
@ -1346,8 +1369,8 @@ impl<'gc> Loader<'gc> {
|
||||||
"httpStatus".into(),
|
"httpStatus".into(),
|
||||||
false.into(),
|
false.into(),
|
||||||
false.into(),
|
false.into(),
|
||||||
response.status.into(),
|
status.into(),
|
||||||
response.redirected.into(),
|
redirected.into(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.map_err(|e| Error::Avm2Error(e.to_string()))?;
|
.map_err(|e| Error::Avm2Error(e.to_string()))?;
|
||||||
|
@ -1436,7 +1459,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||||
let data = fetch.await;
|
let response = Self::wait_for_full_response(fetch).await;
|
||||||
|
|
||||||
// Fire the load handler.
|
// Fire the load handler.
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
|
@ -1447,10 +1470,10 @@ impl<'gc> Loader<'gc> {
|
||||||
_ => return Err(Error::NotSoundLoader),
|
_ => return Err(Error::NotSoundLoader),
|
||||||
};
|
};
|
||||||
|
|
||||||
let success = data
|
let success = response
|
||||||
.map_err(|e| e.error)
|
.map_err(|e| e.error)
|
||||||
.and_then(|data| {
|
.and_then(|(body, _, _, _)| {
|
||||||
let handle = uc.audio.register_mp3(&data.body)?;
|
let handle = uc.audio.register_mp3(&body)?;
|
||||||
sound_object.set_sound(uc.gc_context, Some(handle));
|
sound_object.set_sound(uc.gc_context, Some(handle));
|
||||||
let duration = uc
|
let duration = uc
|
||||||
.audio
|
.audio
|
||||||
|
@ -1499,7 +1522,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||||
let response = fetch.await;
|
let response = Self::wait_for_full_response(fetch).await;
|
||||||
|
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
let loader = uc.load_manager.get_loader(handle);
|
let loader = uc.load_manager.get_loader(handle);
|
||||||
|
@ -1510,8 +1533,8 @@ impl<'gc> Loader<'gc> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
Ok(response) => {
|
Ok((body, _, _, _)) => {
|
||||||
let handle = uc.audio.register_mp3(&response.body)?;
|
let handle = uc.audio.register_mp3(&body)?;
|
||||||
if let Err(e) = sound_object
|
if let Err(e) = sound_object
|
||||||
.as_sound_object()
|
.as_sound_object()
|
||||||
.expect("Not a sound object")
|
.expect("Not a sound object")
|
||||||
|
@ -1520,7 +1543,7 @@ impl<'gc> Loader<'gc> {
|
||||||
tracing::error!("Encountered AVM2 error when setting sound: {}", e);
|
tracing::error!("Encountered AVM2 error when setting sound: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let total_len = response.body.len();
|
let total_len = body.len();
|
||||||
|
|
||||||
// FIXME - the "open" event should be fired earlier, and not fired in case of ioerror.
|
// FIXME - the "open" event should be fired earlier, and not fired in case of ioerror.
|
||||||
let mut activation = Avm2Activation::from_nothing(uc.reborrow());
|
let mut activation = Avm2Activation::from_nothing(uc.reborrow());
|
||||||
|
@ -1598,7 +1621,7 @@ impl<'gc> Loader<'gc> {
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||||
let response = fetch.await;
|
let response = Self::wait_for_full_response(fetch).await;
|
||||||
|
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
let loader = uc.load_manager.get_loader(handle);
|
let loader = uc.load_manager.get_loader(handle);
|
||||||
|
@ -1609,9 +1632,9 @@ impl<'gc> Loader<'gc> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
Ok(mut response) => {
|
Ok((mut body, _, _, _)) => {
|
||||||
stream.reset_buffer(uc);
|
stream.reset_buffer(uc);
|
||||||
stream.load_buffer(uc, &mut response.body);
|
stream.load_buffer(uc, &mut body);
|
||||||
}
|
}
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
stream.report_error(response.error);
|
stream.report_error(response.error);
|
||||||
|
@ -2702,7 +2725,7 @@ impl<'gc> Loader<'gc> {
|
||||||
let req = Request::get(url.clone());
|
let req = Request::get(url.clone());
|
||||||
// Doing this in two steps to prevent holding the player lock during fetch
|
// 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().navigator().fetch(req);
|
||||||
let download_res = future.await;
|
let download_res = Self::wait_for_full_response(future).await;
|
||||||
|
|
||||||
// Fire the load handler.
|
// Fire the load handler.
|
||||||
player.lock().unwrap().update(|uc| -> Result<(), Error> {
|
player.lock().unwrap().update(|uc| -> Result<(), Error> {
|
||||||
|
@ -2740,7 +2763,7 @@ impl<'gc> Loader<'gc> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match download_res {
|
match download_res {
|
||||||
Ok(download_res) => {
|
Ok((body, _, _, _)) => {
|
||||||
as_broadcaster::broadcast_internal(
|
as_broadcaster::broadcast_internal(
|
||||||
&mut activation,
|
&mut activation,
|
||||||
target_object,
|
target_object,
|
||||||
|
@ -2753,14 +2776,14 @@ impl<'gc> Loader<'gc> {
|
||||||
// perspective of AS, we want to refresh the file_ref internal data
|
// perspective of AS, we want to refresh the file_ref internal data
|
||||||
// before invoking the callbacks
|
// before invoking the callbacks
|
||||||
|
|
||||||
dialog_result.write(&download_res.body);
|
dialog_result.write(&body);
|
||||||
dialog_result.refresh();
|
dialog_result.refresh();
|
||||||
file_ref.init_from_dialog_result(
|
file_ref.init_from_dialog_result(
|
||||||
&mut activation,
|
&mut activation,
|
||||||
dialog_result.borrow(),
|
dialog_result.borrow(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let total_bytes = download_res.body.len();
|
let total_bytes = body.len();
|
||||||
|
|
||||||
as_broadcaster::broadcast_internal(
|
as_broadcaster::broadcast_internal(
|
||||||
&mut activation,
|
&mut activation,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::avm2::object::{
|
||||||
NetConnectionObject as Avm2NetConnectionObject, ResponderObject as Avm2ResponderObject,
|
NetConnectionObject as Avm2NetConnectionObject, ResponderObject as Avm2ResponderObject,
|
||||||
};
|
};
|
||||||
use crate::avm2::{Activation as Avm2Activation, Avm2, EventObject as Avm2EventObject};
|
use crate::avm2::{Activation as Avm2Activation, Avm2, EventObject as Avm2EventObject};
|
||||||
use crate::backend::navigator::{NavigatorBackend, OwnedFuture, Request};
|
use crate::backend::navigator::{ErrorResponse, NavigatorBackend, OwnedFuture, Request};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::loader::Error;
|
use crate::loader::Error;
|
||||||
use crate::string::AvmString;
|
use crate::string::AvmString;
|
||||||
|
@ -455,7 +455,18 @@ impl FlashRemoting {
|
||||||
.expect("Must be able to serialize a packet");
|
.expect("Must be able to serialize a packet");
|
||||||
let request = Request::post(url, Some((bytes, "application/x-amf".to_string())));
|
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().navigator().fetch(request);
|
||||||
let response = match fetch.await {
|
let response: Result<_, ErrorResponse> = async {
|
||||||
|
let response = fetch.await?;
|
||||||
|
let url = response.url().to_string();
|
||||||
|
let body = response
|
||||||
|
.body()
|
||||||
|
.await
|
||||||
|
.map_err(|error| ErrorResponse { url, error })?;
|
||||||
|
|
||||||
|
Ok(body)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
let response = match response {
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
|
@ -497,7 +508,7 @@ impl FlashRemoting {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flash completely ignores invalid responses, it seems
|
// Flash completely ignores invalid responses, it seems
|
||||||
if let Ok(response_packet) = flash_lso::packet::read::parse(&response.body) {
|
if let Ok(response_packet) = flash_lso::packet::read::parse(&response) {
|
||||||
player.lock().unwrap().update(|uc| {
|
player.lock().unwrap().update(|uc| {
|
||||||
for message in response_packet.messages {
|
for message in response_packet.messages {
|
||||||
if let Some(target_uri) = message.target_uri.strip_prefix('/') {
|
if let Some(target_uri) = message.target_uri.strip_prefix('/') {
|
||||||
|
|
|
@ -171,7 +171,32 @@ impl NavigatorBackend for ExternalNavigatorBackend {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self, request: Request) -> OwnedFuture<SuccessResponse, ErrorResponse> {
|
fn fetch(&self, request: Request) -> OwnedFuture<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||||
|
struct DesktopResponse {
|
||||||
|
url: String,
|
||||||
|
body: Vec<u8>,
|
||||||
|
status: u16,
|
||||||
|
redirected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SuccessResponse for DesktopResponse {
|
||||||
|
fn url(&self) -> std::borrow::Cow<str> {
|
||||||
|
std::borrow::Cow::Borrowed(&self.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body(self: Box<Self>) -> OwnedFuture<Vec<u8>, Error> {
|
||||||
|
Box::pin(async { Ok(self.body) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> u16 {
|
||||||
|
self.status
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redirected(&self) -> bool {
|
||||||
|
self.redirected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: honor sandbox type (local-with-filesystem, local-with-network, remote, ...)
|
// TODO: honor sandbox type (local-with-filesystem, local-with-network, remote, ...)
|
||||||
let mut processed_url = match self.resolve_url(request.url()) {
|
let mut processed_url = match self.resolve_url(request.url()) {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
|
@ -236,12 +261,14 @@ impl NavigatorBackend for ExternalNavigatorBackend {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(SuccessResponse {
|
let response: Box<dyn SuccessResponse> = Box::new(DesktopResponse {
|
||||||
url: response_url.to_string(),
|
url: response_url.to_string(),
|
||||||
body,
|
body,
|
||||||
status: 0,
|
status: 0,
|
||||||
redirected: false,
|
redirected: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
}),
|
}),
|
||||||
_ => Box::pin(async move {
|
_ => Box::pin(async move {
|
||||||
let client = client.ok_or_else(|| ErrorResponse {
|
let client = client.ok_or_else(|| ErrorResponse {
|
||||||
|
@ -321,12 +348,13 @@ impl NavigatorBackend for ExternalNavigatorBackend {
|
||||||
error: Error::FetchError(e.to_string()),
|
error: Error::FetchError(e.to_string()),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(SuccessResponse {
|
let response: Box<dyn SuccessResponse> = Box::new(DesktopResponse {
|
||||||
url,
|
url,
|
||||||
body,
|
body,
|
||||||
status,
|
status,
|
||||||
redirected,
|
redirected,
|
||||||
})
|
});
|
||||||
|
Ok(response)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,37 @@ use ruffle_core::indexmap::IndexMap;
|
||||||
use ruffle_core::loader::Error;
|
use ruffle_core::loader::Error;
|
||||||
use ruffle_core::socket::{ConnectionState, SocketAction, SocketHandle};
|
use ruffle_core::socket::{ConnectionState, SocketAction, SocketHandle};
|
||||||
use ruffle_socket_format::SocketEvent;
|
use ruffle_socket_format::SocketEvent;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
use vfs::VfsPath;
|
use vfs::VfsPath;
|
||||||
|
|
||||||
|
struct TestResponse {
|
||||||
|
url: String,
|
||||||
|
body: Vec<u8>,
|
||||||
|
status: u16,
|
||||||
|
redirected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SuccessResponse for TestResponse {
|
||||||
|
fn url(&self) -> Cow<str> {
|
||||||
|
Cow::Borrowed(&self.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body(self: Box<Self>) -> OwnedFuture<Vec<u8>, Error> {
|
||||||
|
Box::pin(async move { Ok(self.body) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> u16 {
|
||||||
|
self.status
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redirected(&self) -> bool {
|
||||||
|
self.redirected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A `NavigatorBackend` used by tests that supports logging fetch requests.
|
/// A `NavigatorBackend` used by tests that supports logging fetch requests.
|
||||||
///
|
///
|
||||||
/// This can be used by tests that fetch data to verify that the request is correct.
|
/// This can be used by tests that fetch data to verify that the request is correct.
|
||||||
|
@ -71,15 +97,17 @@ impl NavigatorBackend for TestNavigatorBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self, request: Request) -> OwnedFuture<SuccessResponse, ErrorResponse> {
|
fn fetch(&self, request: Request) -> OwnedFuture<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||||
if request.url().contains("?debug-success") {
|
if request.url().contains("?debug-success") {
|
||||||
return Box::pin(async move {
|
return Box::pin(async move {
|
||||||
Ok(SuccessResponse {
|
let response: Box<dyn SuccessResponse> = Box::new(TestResponse {
|
||||||
url: request.url().to_string(),
|
url: request.url().to_string(),
|
||||||
body: b"Hello, World!".to_vec(),
|
body: b"Hello, World!".to_vec(),
|
||||||
status: 200,
|
status: 200,
|
||||||
redirected: false,
|
redirected: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,12 +212,15 @@ impl NavigatorBackend for TestNavigatorBackend {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
error: Error::FetchError(error.to_string()),
|
error: Error::FetchError(error.to_string()),
|
||||||
})?;
|
})?;
|
||||||
Ok(SuccessResponse {
|
|
||||||
|
let response: Box<dyn SuccessResponse> = Box::new(TestResponse {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
body,
|
body,
|
||||||
status: 0,
|
status: 0,
|
||||||
redirected: false,
|
redirected: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::SocketProxy;
|
||||||
use async_channel::Receiver;
|
use async_channel::Receiver;
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
use gloo_net::websocket::{futures::WebSocket, Message};
|
use gloo_net::websocket::{futures::WebSocket, Message};
|
||||||
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
use js_sys::{Array, Uint8Array};
|
||||||
use ruffle_core::backend::navigator::{
|
use ruffle_core::backend::navigator::{
|
||||||
async_return, create_fetch_error, create_specific_fetch_error, ErrorResponse, NavigationMethod,
|
async_return, create_fetch_error, create_specific_fetch_error, ErrorResponse, NavigationMethod,
|
||||||
NavigatorBackend, OpenURLMode, OwnedFuture, Request, SuccessResponse,
|
NavigatorBackend, OpenURLMode, OwnedFuture, Request, SuccessResponse,
|
||||||
|
@ -12,6 +12,7 @@ use ruffle_core::config::NetworkingAccessMode;
|
||||||
use ruffle_core::indexmap::IndexMap;
|
use ruffle_core::indexmap::IndexMap;
|
||||||
use ruffle_core::loader::Error;
|
use ruffle_core::loader::Error;
|
||||||
use ruffle_core::socket::{ConnectionState, SocketAction, SocketHandle};
|
use ruffle_core::socket::{ConnectionState, SocketAction, SocketHandle};
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -223,7 +224,7 @@ impl NavigatorBackend for WebNavigatorBackend {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self, request: Request) -> OwnedFuture<SuccessResponse, ErrorResponse> {
|
fn fetch(&self, request: Request) -> OwnedFuture<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||||
let url = match self.resolve_url(request.url()) {
|
let url = match self.resolve_url(request.url()) {
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
if url.scheme() == "file" {
|
if url.scheme() == "file" {
|
||||||
|
@ -326,32 +327,9 @@ impl NavigatorBackend for WebNavigatorBackend {
|
||||||
return Err(ErrorResponse { url, error });
|
return Err(ErrorResponse { url, error });
|
||||||
}
|
}
|
||||||
|
|
||||||
let body: ArrayBuffer = JsFuture::from(response.array_buffer().map_err(|_| {
|
let wrapper: Box<dyn SuccessResponse> = Box::new(WebResponseWrapper(response));
|
||||||
ErrorResponse {
|
|
||||||
url: url.clone(),
|
|
||||||
error: Error::FetchError("Got JS error".to_string()),
|
|
||||||
}
|
|
||||||
})?)
|
|
||||||
.await
|
|
||||||
.map_err(|_| ErrorResponse {
|
|
||||||
url: url.clone(),
|
|
||||||
error: Error::FetchError(
|
|
||||||
"Could not allocate array buffer for response".to_string(),
|
|
||||||
),
|
|
||||||
})?
|
|
||||||
.dyn_into()
|
|
||||||
.map_err(|_| ErrorResponse {
|
|
||||||
url: url.clone(),
|
|
||||||
error: Error::FetchError("array_buffer result wasn't an ArrayBuffer".to_string()),
|
|
||||||
})?;
|
|
||||||
let body = Uint8Array::new(&body).to_vec();
|
|
||||||
|
|
||||||
Ok(SuccessResponse {
|
Ok(wrapper)
|
||||||
url,
|
|
||||||
body,
|
|
||||||
status,
|
|
||||||
redirected,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,3 +440,40 @@ impl NavigatorBackend for WebNavigatorBackend {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct WebResponseWrapper(WebResponse);
|
||||||
|
|
||||||
|
impl SuccessResponse for WebResponseWrapper {
|
||||||
|
fn url(&self) -> Cow<str> {
|
||||||
|
Cow::Owned(self.0.url())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body(self: Box<Self>) -> OwnedFuture<Vec<u8>, Error> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let body = JsFuture::from(
|
||||||
|
self.0
|
||||||
|
.array_buffer()
|
||||||
|
.map_err(|_| Error::FetchError("Got JS error".to_string()))?,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::FetchError("Could not allocate array buffer for response".to_string())
|
||||||
|
})?
|
||||||
|
.dyn_into()
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::FetchError("array_buffer result wasn't an ArrayBuffer".to_string())
|
||||||
|
})?;
|
||||||
|
let body = Uint8Array::new(&body).to_vec();
|
||||||
|
|
||||||
|
Ok(body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> u16 {
|
||||||
|
self.0.status()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redirected(&self) -> bool {
|
||||||
|
self.0.redirected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue