1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! HTTP requests with the `remote-asset` feature enabled.
//!
//! ```rust,no_run
//! use freya_components::{
//! Url,
//! http,
//! };
//!
//! # async fn load() -> anyhow::Result<()> {
//! let url = Url::parse("https://example.com")?;
//! let bytes = http::fetch(url).await?;
//! # Ok(())
//! # }
//! ```
//!
//! Native requests use a default client unless you provide one at launch:
//!
//! ```rust,no_run
//! # use freya::prelude::*;
//! # fn app() -> impl IntoElement { "Hello" }
//! fn main() -> Result<(), reqwest::Error> {
//! let client = reqwest::blocking::Client::builder()
//! .user_agent("MyApp/1.0")
//! .build()?;
//! launch(
//! LaunchConfig::new()
//! .with_global_context(client)
//! .with_window(WindowConfig::new(app)),
//! );
//! Ok(())
//! }
//! ```
//!
//! On web, requests use the browser and follow its CORS rules.
use bytes::Bytes;
use url::Url;
/// Fetches `url` using the app-wide HTTP client on native platforms.
#[cfg(not(target_os = "emscripten"))]
pub async fn fetch(url: Url) -> anyhow::Result<Bytes> {
let client: reqwest::blocking::Client = freya_core::prelude::GlobalContexts::get()
.get_context_or_insert(reqwest::blocking::Client::new);
freya_core::prelude::thread(move || Ok(client.get(url).send()?.error_for_status()?.bytes()?))
.await
}
/// Fetches `url` using the browser's HTTP client on web.
///
/// Browser CORS rules apply. Returns an error if the request fails.
#[cfg(target_os = "emscripten")]
pub async fn fetch(url: Url) -> anyhow::Result<Bytes> {
use std::ffi::{
CString,
c_char,
c_int,
c_void,
};
use anyhow::Context;
use futures_channel::oneshot::{
Sender,
channel,
};
unsafe extern "C" {
fn emscripten_async_wget_data(
url: *const c_char,
argument: *mut c_void,
on_load: extern "C" fn(*mut c_void, *mut c_void, c_int),
on_error: extern "C" fn(*mut c_void),
);
}
extern "C" fn on_load(argument: *mut c_void, data: *mut c_void, size: c_int) {
let sender = unsafe { Box::from_raw(argument.cast::<Sender<Option<Bytes>>>()) };
let data = unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size as usize) };
sender.send(Some(Bytes::copy_from_slice(data))).ok();
}
extern "C" fn on_error(argument: *mut c_void) {
let sender = unsafe { Box::from_raw(argument.cast::<Sender<Option<Bytes>>>()) };
sender.send(None).ok();
}
let c_url = CString::new(url.as_str())?;
let (sender, receiver) = channel::<Option<Bytes>>();
let argument = Box::into_raw(Box::new(sender)).cast();
unsafe { emscripten_async_wget_data(c_url.as_ptr(), argument, on_load, on_error) };
receiver
.await
.ok()
.flatten()
.with_context(|| format!("Failed to fetch {url}."))
}