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 indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use std::future::Future;
|
||||
|
@ -186,18 +187,20 @@ impl Request {
|
|||
}
|
||||
|
||||
/// A response to a successful fetch request.
|
||||
pub struct SuccessResponse {
|
||||
pub trait SuccessResponse {
|
||||
/// The final URL obtained after any redirects.
|
||||
pub url: String,
|
||||
fn url(&self) -> Cow<str>;
|
||||
|
||||
/// The contents of the response body.
|
||||
pub body: Vec<u8>,
|
||||
/// Retrieve the contents of the response body.
|
||||
///
|
||||
/// This method consumes the response.
|
||||
fn body(self: Box<Self>) -> OwnedFuture<Vec<u8>, Error>;
|
||||
|
||||
/// The status code of the response.
|
||||
pub status: u16,
|
||||
fn status(&self) -> u16;
|
||||
|
||||
/// The field to indicate if the request has been redirected.
|
||||
pub redirected: bool,
|
||||
/// Indicates if the request has been redirected.
|
||||
fn redirected(&self) -> bool;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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
|
||||
/// 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)
|
||||
}
|
||||
|
||||
|
@ -449,7 +452,7 @@ pub fn async_return<SuccessType: 'static, ErrorType: 'static>(
|
|||
pub fn create_fetch_error<ErrorType: Display>(
|
||||
url: &str,
|
||||
error: ErrorType,
|
||||
) -> Result<SuccessResponse, ErrorResponse> {
|
||||
) -> Result<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||
create_specific_fetch_error("Invalid URL", url, error)
|
||||
}
|
||||
|
||||
|
@ -459,7 +462,7 @@ pub fn create_specific_fetch_error<ErrorType: Display>(
|
|||
reason: &str,
|
||||
url: &str,
|
||||
error: ErrorType,
|
||||
) -> Result<SuccessResponse, ErrorResponse> {
|
||||
) -> Result<Box<dyn SuccessResponse>, ErrorResponse> {
|
||||
let message = if error.to_string() == "" {
|
||||
format!("{reason} {url}")
|
||||
} else {
|
||||
|
@ -543,7 +546,34 @@ pub fn fetch_path<NavigatorType: NavigatorBackend>(
|
|||
navigator_name: &str,
|
||||
url: &str,
|
||||
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) {
|
||||
Ok(url) => url,
|
||||
Err(e) => return async_return(create_fetch_error(url, e)),
|
||||
|
@ -584,15 +614,13 @@ pub fn fetch_path<NavigatorType: NavigatorBackend>(
|
|||
};
|
||||
|
||||
Box::pin(async move {
|
||||
let body = match std::fs::read(path) {
|
||||
Ok(body) => body,
|
||||
Err(e) => return create_specific_fetch_error("Can't open file", url.as_str(), e),
|
||||
};
|
||||
Ok(SuccessResponse {
|
||||
let response: Box<dyn SuccessResponse> = Box::new(LocalResponse {
|
||||
url: url.to_string(),
|
||||
body,
|
||||
path,
|
||||
status: 0,
|
||||
redirected: false,
|
||||
})
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::avm2::{
|
|||
Activation as Avm2Activation, Avm2, BitmapDataObject, Domain as Avm2Domain,
|
||||
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::bitmap::bitmap_data::Color;
|
||||
use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper};
|
||||
|
@ -854,6 +854,21 @@ impl<'gc> Loader<'gc> {
|
|||
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.
|
||||
fn root_movie_loader(
|
||||
&mut self,
|
||||
|
@ -875,7 +890,6 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
Box::pin(async move {
|
||||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||
|
||||
let response = fetch.await.map_err(|error| {
|
||||
player
|
||||
.lock()
|
||||
|
@ -884,13 +898,22 @@ impl<'gc> Loader<'gc> {
|
|||
.display_root_movie_download_failed_message(false);
|
||||
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.
|
||||
let swf_url = player
|
||||
.lock()
|
||||
.unwrap()
|
||||
.compatibility_rules()
|
||||
.rewrite_swf_url(response.url);
|
||||
.rewrite_swf_url(url);
|
||||
let spoofed_or_swf_url = player
|
||||
.lock()
|
||||
.unwrap()
|
||||
|
@ -899,7 +922,7 @@ impl<'gc> Loader<'gc> {
|
|||
.unwrap_or(swf_url);
|
||||
|
||||
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
|
||||
.lock()
|
||||
.unwrap()
|
||||
|
@ -970,25 +993,25 @@ impl<'gc> Loader<'gc> {
|
|||
Loader::movie_loader_start(handle, uc)
|
||||
})?;
|
||||
|
||||
match fetch.await {
|
||||
Ok(response) if replacing_root_movie => {
|
||||
ContentType::sniff(&response.body).expect(ContentType::Swf)?;
|
||||
match Self::wait_for_full_response(fetch).await {
|
||||
Ok((body, url, _status, _redirected)) if replacing_root_movie => {
|
||||
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| {
|
||||
uc.set_root_movie(movie);
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
Ok(response) => {
|
||||
Ok((body, url, status, redirected)) => {
|
||||
player.lock().unwrap().mutate_with_update_context(|uc| {
|
||||
Loader::movie_loader_data(
|
||||
handle,
|
||||
uc,
|
||||
&response.body,
|
||||
response.url,
|
||||
response.status,
|
||||
response.redirected,
|
||||
&body,
|
||||
url.to_string(),
|
||||
status,
|
||||
redirected,
|
||||
loader_url,
|
||||
)
|
||||
})?;
|
||||
|
@ -1084,6 +1107,7 @@ impl<'gc> Loader<'gc> {
|
|||
let fetch = player.lock().unwrap().navigator().fetch(request);
|
||||
|
||||
let response = fetch.await.map_err(|e| e.error)?;
|
||||
let body = response.body().await?;
|
||||
|
||||
// Fire the load handler.
|
||||
player.lock().unwrap().update(|uc| {
|
||||
|
@ -1099,7 +1123,7 @@ impl<'gc> Loader<'gc> {
|
|||
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 v = AvmString::new_utf8(activation.context.gc_context, v);
|
||||
that.set(k, v.into(), &mut activation)?;
|
||||
|
@ -1145,8 +1169,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
Box::pin(async move {
|
||||
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.
|
||||
player.lock().unwrap().update(|uc| {
|
||||
|
@ -1160,9 +1183,9 @@ impl<'gc> Loader<'gc> {
|
|||
let mut activation =
|
||||
Activation::from_stub(uc.reborrow(), ActivationIdentifier::root("[Loader]"));
|
||||
|
||||
match data {
|
||||
Ok(response) => {
|
||||
let length = response.body.len();
|
||||
match response {
|
||||
Ok((body, _, status, _)) => {
|
||||
let length = body.len();
|
||||
|
||||
// Set the properties used by the getBytesTotal and getBytesLoaded methods.
|
||||
that.set("_bytesTotal", length.into(), &mut activation)?;
|
||||
|
@ -1172,7 +1195,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
let _ = that.call_method(
|
||||
"onHTTPStatus".into(),
|
||||
&[response.status.into()],
|
||||
&[status.into()],
|
||||
&mut activation,
|
||||
ExecutionReason::Special,
|
||||
);
|
||||
|
@ -1184,7 +1207,7 @@ impl<'gc> Loader<'gc> {
|
|||
} else {
|
||||
AvmString::new_utf8(
|
||||
activation.context.gc_context,
|
||||
UTF_8.decode(&response.body).0,
|
||||
UTF_8.decode(&body).0,
|
||||
)
|
||||
.into()
|
||||
};
|
||||
|
@ -1247,7 +1270,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
Box::pin(async move {
|
||||
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| {
|
||||
let loader = uc.load_manager.get_loader(handle);
|
||||
|
@ -1299,8 +1322,8 @@ impl<'gc> Loader<'gc> {
|
|||
}
|
||||
|
||||
match response {
|
||||
Ok(response) => {
|
||||
let total_len = response.body.len();
|
||||
Ok((body, _, status, redirected)) => {
|
||||
let total_len = body.len();
|
||||
|
||||
// FIXME - the "open" event should be fired earlier, just before
|
||||
// we start to fetch the data.
|
||||
|
@ -1314,7 +1337,7 @@ impl<'gc> Loader<'gc> {
|
|||
let open_evt =
|
||||
Avm2EventObject::bare_default_event(&mut activation.context, "open");
|
||||
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
|
||||
// just at the end
|
||||
|
@ -1346,8 +1369,8 @@ impl<'gc> Loader<'gc> {
|
|||
"httpStatus".into(),
|
||||
false.into(),
|
||||
false.into(),
|
||||
response.status.into(),
|
||||
response.redirected.into(),
|
||||
status.into(),
|
||||
redirected.into(),
|
||||
],
|
||||
)
|
||||
.map_err(|e| Error::Avm2Error(e.to_string()))?;
|
||||
|
@ -1436,7 +1459,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
Box::pin(async move {
|
||||
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.
|
||||
player.lock().unwrap().update(|uc| {
|
||||
|
@ -1447,10 +1470,10 @@ impl<'gc> Loader<'gc> {
|
|||
_ => return Err(Error::NotSoundLoader),
|
||||
};
|
||||
|
||||
let success = data
|
||||
let success = response
|
||||
.map_err(|e| e.error)
|
||||
.and_then(|data| {
|
||||
let handle = uc.audio.register_mp3(&data.body)?;
|
||||
.and_then(|(body, _, _, _)| {
|
||||
let handle = uc.audio.register_mp3(&body)?;
|
||||
sound_object.set_sound(uc.gc_context, Some(handle));
|
||||
let duration = uc
|
||||
.audio
|
||||
|
@ -1499,7 +1522,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
Box::pin(async move {
|
||||
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| {
|
||||
let loader = uc.load_manager.get_loader(handle);
|
||||
|
@ -1510,8 +1533,8 @@ impl<'gc> Loader<'gc> {
|
|||
};
|
||||
|
||||
match response {
|
||||
Ok(response) => {
|
||||
let handle = uc.audio.register_mp3(&response.body)?;
|
||||
Ok((body, _, _, _)) => {
|
||||
let handle = uc.audio.register_mp3(&body)?;
|
||||
if let Err(e) = sound_object
|
||||
.as_sound_object()
|
||||
.expect("Not a sound object")
|
||||
|
@ -1520,7 +1543,7 @@ impl<'gc> Loader<'gc> {
|
|||
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.
|
||||
let mut activation = Avm2Activation::from_nothing(uc.reborrow());
|
||||
|
@ -1598,7 +1621,7 @@ impl<'gc> Loader<'gc> {
|
|||
|
||||
Box::pin(async move {
|
||||
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| {
|
||||
let loader = uc.load_manager.get_loader(handle);
|
||||
|
@ -1609,9 +1632,9 @@ impl<'gc> Loader<'gc> {
|
|||
};
|
||||
|
||||
match response {
|
||||
Ok(mut response) => {
|
||||
Ok((mut body, _, _, _)) => {
|
||||
stream.reset_buffer(uc);
|
||||
stream.load_buffer(uc, &mut response.body);
|
||||
stream.load_buffer(uc, &mut body);
|
||||
}
|
||||
Err(response) => {
|
||||
stream.report_error(response.error);
|
||||
|
@ -2702,7 +2725,7 @@ impl<'gc> Loader<'gc> {
|
|||
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 download_res = future.await;
|
||||
let download_res = Self::wait_for_full_response(future).await;
|
||||
|
||||
// Fire the load handler.
|
||||
player.lock().unwrap().update(|uc| -> Result<(), Error> {
|
||||
|
@ -2740,7 +2763,7 @@ impl<'gc> Loader<'gc> {
|
|||
)?;
|
||||
|
||||
match download_res {
|
||||
Ok(download_res) => {
|
||||
Ok((body, _, _, _)) => {
|
||||
as_broadcaster::broadcast_internal(
|
||||
&mut activation,
|
||||
target_object,
|
||||
|
@ -2753,14 +2776,14 @@ impl<'gc> Loader<'gc> {
|
|||
// perspective of AS, we want to refresh the file_ref internal data
|
||||
// before invoking the callbacks
|
||||
|
||||
dialog_result.write(&download_res.body);
|
||||
dialog_result.write(&body);
|
||||
dialog_result.refresh();
|
||||
file_ref.init_from_dialog_result(
|
||||
&mut activation,
|
||||
dialog_result.borrow(),
|
||||
);
|
||||
|
||||
let total_bytes = download_res.body.len();
|
||||
let total_bytes = body.len();
|
||||
|
||||
as_broadcaster::broadcast_internal(
|
||||
&mut activation,
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::avm2::object::{
|
|||
NetConnectionObject as Avm2NetConnectionObject, ResponderObject as Avm2ResponderObject,
|
||||
};
|
||||
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::loader::Error;
|
||||
use crate::string::AvmString;
|
||||
|
@ -455,7 +455,18 @@ impl FlashRemoting {
|
|||
.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 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,
|
||||
Err(response) => {
|
||||
player.lock().unwrap().update(|uc| {
|
||||
|
@ -497,7 +508,7 @@ impl FlashRemoting {
|
|||
};
|
||||
|
||||
// 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| {
|
||||
for message in response_packet.messages {
|
||||
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, ...)
|
||||
let mut processed_url = match self.resolve_url(request.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(),
|
||||
body,
|
||||
status: 0,
|
||||
redirected: false,
|
||||
})
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
}),
|
||||
_ => Box::pin(async move {
|
||||
let client = client.ok_or_else(|| ErrorResponse {
|
||||
|
@ -321,12 +348,13 @@ impl NavigatorBackend for ExternalNavigatorBackend {
|
|||
error: Error::FetchError(e.to_string()),
|
||||
})?;
|
||||
|
||||
Ok(SuccessResponse {
|
||||
let response: Box<dyn SuccessResponse> = Box::new(DesktopResponse {
|
||||
url,
|
||||
body,
|
||||
status,
|
||||
redirected,
|
||||
})
|
||||
});
|
||||
Ok(response)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,37 @@ use ruffle_core::indexmap::IndexMap;
|
|||
use ruffle_core::loader::Error;
|
||||
use ruffle_core::socket::{ConnectionState, SocketAction, SocketHandle};
|
||||
use ruffle_socket_format::SocketEvent;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::time::Duration;
|
||||
use url::{ParseError, Url};
|
||||
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.
|
||||
///
|
||||
/// 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") {
|
||||
return Box::pin(async move {
|
||||
Ok(SuccessResponse {
|
||||
let response: Box<dyn SuccessResponse> = Box::new(TestResponse {
|
||||
url: request.url().to_string(),
|
||||
body: b"Hello, World!".to_vec(),
|
||||
status: 200,
|
||||
redirected: false,
|
||||
})
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -184,12 +212,15 @@ impl NavigatorBackend for TestNavigatorBackend {
|
|||
url: url.to_string(),
|
||||
error: Error::FetchError(error.to_string()),
|
||||
})?;
|
||||
Ok(SuccessResponse {
|
||||
|
||||
let response: Box<dyn SuccessResponse> = Box::new(TestResponse {
|
||||
url: url.to_string(),
|
||||
body,
|
||||
status: 0,
|
||||
redirected: false,
|
||||
})
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::SocketProxy;
|
|||
use async_channel::Receiver;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use gloo_net::websocket::{futures::WebSocket, Message};
|
||||
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
||||
use js_sys::{Array, Uint8Array};
|
||||
use ruffle_core::backend::navigator::{
|
||||
async_return, create_fetch_error, create_specific_fetch_error, ErrorResponse, NavigationMethod,
|
||||
NavigatorBackend, OpenURLMode, OwnedFuture, Request, SuccessResponse,
|
||||
|
@ -12,6 +12,7 @@ use ruffle_core::config::NetworkingAccessMode;
|
|||
use ruffle_core::indexmap::IndexMap;
|
||||
use ruffle_core::loader::Error;
|
||||
use ruffle_core::socket::{ConnectionState, SocketAction, SocketHandle};
|
||||
use std::borrow::Cow;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::Arc;
|
||||
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()) {
|
||||
Ok(url) => {
|
||||
if url.scheme() == "file" {
|
||||
|
@ -326,32 +327,9 @@ impl NavigatorBackend for WebNavigatorBackend {
|
|||
return Err(ErrorResponse { url, error });
|
||||
}
|
||||
|
||||
let body: ArrayBuffer = JsFuture::from(response.array_buffer().map_err(|_| {
|
||||
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();
|
||||
let wrapper: Box<dyn SuccessResponse> = Box::new(WebResponseWrapper(response));
|
||||
|
||||
Ok(SuccessResponse {
|
||||
url,
|
||||
body,
|
||||
status,
|
||||
redirected,
|
||||
})
|
||||
Ok(wrapper)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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