1use std::sync::Arc;
2
3#[cfg(target_arch = "wasm32")]
4use sapp_jsutils::JsObject;
5
6pub struct HttpConfig {
8 pub(crate) headers: Vec<(String, String)>,
9 pub(crate) body: Option<Vec<u8>>,
10}
11
12impl HttpConfig {
13 pub(crate) fn new() -> Self {
14 Self {
15 headers: Vec::new(),
16 body: None,
17 }
18 }
19
20 pub fn header(&mut self, key: &str, value: &str) -> &mut Self {
22 self.headers.push((key.to_owned(), value.to_owned()));
23 self
24 }
25
26 pub fn body(&mut self, body: &str) -> &mut Self {
28 self.body = Some(body.as_bytes().to_vec());
29 self
30 }
31
32 pub fn body_bytes(&mut self, body: Vec<u8>) -> &mut Self {
34 self.body = Some(body);
35 self
36 }
37}
38
39pub struct Response {
41 status: u16,
42 body: Vec<u8>,
43}
44
45impl Response {
46 pub(crate) fn new(status: u16, body: Vec<u8>) -> Self {
47 Self { status, body }
48 }
49
50 pub fn status(&self) -> u16 {
52 self.status
53 }
54
55 pub fn text(&self) -> &str {
57 std::str::from_utf8(&self.body).unwrap_or("")
58 }
59
60 pub fn bytes(&self) -> &[u8] {
62 &self.body
63 }
64
65 #[cfg(feature = "net-json")]
67 pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T, String> {
68 serde_json::from_slice(&self.body).map_err(|e| e.to_string())
69 }
70}
71
72pub struct Request {
76 pub(crate) id: u64,
77}
78
79impl Request {
80 pub fn response(&self) -> Option<Result<Arc<Response>, String>> {
86 let mut mgr = super::NET_MANAGER.lock().unwrap();
87 let entry = mgr.http_requests.get_mut(&self.id)?;
88 entry.frames_not_accessed = 0;
89
90 match &mut entry.state {
92 HttpRequestState::Pending(pending) => {
93 if let Some(result) = pending.try_recv() {
94 match result {
95 Ok(resp) => {
96 entry.state = HttpRequestState::Done(Arc::new(resp));
97 }
98 Err(e) => {
99 entry.state = HttpRequestState::Error(e);
100 }
101 }
102 }
103 }
104 _ => {}
105 }
106
107 match &entry.state {
108 HttpRequestState::Pending(_) => None,
109 HttpRequestState::Done(resp) => Some(Ok(Arc::clone(resp))),
110 HttpRequestState::Error(e) => Some(Err(e.clone())),
111 }
112 }
113
114 pub fn cancel(self) {
116 let mut mgr = super::NET_MANAGER.lock().unwrap();
117 mgr.http_requests.remove(&self.id);
118 }
119}
120
121pub(crate) struct PendingHttp {
123 #[cfg(not(target_arch = "wasm32"))]
124 rx: std::sync::mpsc::Receiver<Result<Response, String>>,
125 #[cfg(target_arch = "wasm32")]
126 cid: i32,
127}
128
129impl PendingHttp {
130 #[cfg(not(target_arch = "wasm32"))]
131 pub fn new(rx: std::sync::mpsc::Receiver<Result<Response, String>>) -> Self {
132 Self { rx }
133 }
134
135 #[cfg(target_arch = "wasm32")]
136 pub fn new(cid: i32) -> Self {
137 Self { cid }
138 }
139
140 pub fn try_recv(&mut self) -> Option<Result<Response, String>> {
142 #[cfg(not(target_arch = "wasm32"))]
143 {
144 self.rx.try_recv().ok()
145 }
146
147 #[cfg(target_arch = "wasm32")]
148 {
149 let js_obj = unsafe { ply_net_http_try_recv(self.cid) };
150 if js_obj.is_nil() {
151 return None;
152 }
153
154 if js_obj.have_field("error") {
156 let mut error_str = String::new();
157 js_obj.field("error").to_string(&mut error_str);
158 if !error_str.is_empty() {
159 return Some(Err(error_str));
160 }
161 }
162
163 let status = js_obj.field_u32("status") as u16;
164 let mut body = Vec::new();
165 js_obj.field("body").to_byte_buffer(&mut body);
166 Some(Ok(Response::new(status, body)))
167 }
168 }
169}
170
171pub(crate) enum HttpRequestState {
173 Pending(PendingHttp),
175 Done(Arc<Response>),
177 Error(String),
179}
180
181#[cfg(target_arch = "wasm32")]
183extern "C" {
184 pub(crate) fn ply_net_http_make_request(
185 scheme: i32,
186 url: JsObject,
187 body: JsObject,
188 headers: JsObject,
189 ) -> i32;
190 fn ply_net_http_try_recv(cid: i32) -> JsObject;
191}