1use crate::{backend::Backend, settings::GLOBAL_SETTINGS};
6use crate::{error, msg_types, warn, Error};
7use ffi_support::{ByteBuffer, FfiStr};
8
9ffi_support::implement_into_ffi_by_protobuf!(msg_types::Request);
10
11impl From<crate::Request> for msg_types::Request {
12 fn from(request: crate::Request) -> Self {
13 let settings = GLOBAL_SETTINGS.read();
14 msg_types::Request {
15 url: request.url.to_string(),
16 body: request.body,
17 method: request.method as i32,
20 headers: request.headers.into(),
21 follow_redirects: settings.follow_redirects,
22 use_caches: settings.use_caches,
23 connect_timeout_secs: settings.connect_timeout.map_or(0, |d| d.as_secs() as i32),
24 read_timeout_secs: settings.read_timeout.map_or(0, |d| d.as_secs() as i32),
25 }
26 }
27}
28
29macro_rules! backend_error {
30 ($($args:tt)*) => {{
31 let msg = format!($($args)*);
32 error!("{}", msg);
33 Error::BackendError(msg)
34 }};
35}
36
37pub struct FfiBackend;
38impl Backend for FfiBackend {
39 fn send(&self, request: crate::Request) -> Result<crate::Response, Error> {
40 use ffi_support::IntoFfi;
41 use prost::Message;
42 super::note_backend("FFI (trusted)");
43
44 let method = request.method;
45 let fetch = callback_holder::get_callback().ok_or(Error::BackendNotInitialized)?;
46 let proto_req: msg_types::Request = request.into();
47 let buf = proto_req.into_ffi_value();
48 let response = unsafe { fetch(buf) };
49 let response_bytes = response.destroy_into_vec();
52
53 let response: msg_types::Response = match Message::decode(response_bytes.as_slice()) {
54 Ok(v) => v,
55 Err(e) => {
56 panic!(
57 "Failed to parse protobuf returned from fetch callback! {}",
58 e
59 );
60 }
61 };
62
63 if let Some(exn) = response.exception_message {
64 return Err(Error::NetworkError(format!("Java error: {:?}", exn)));
65 }
66 let status = response
67 .status
68 .ok_or_else(|| backend_error!("Missing HTTP status"))?;
69
70 if status < 0 || status > i32::from(u16::MAX) {
71 return Err(backend_error!("Illegal HTTP status: {}", status));
72 }
73
74 let mut headers = crate::Headers::with_capacity(response.headers.len());
75 for (name, val) in response.headers {
76 let hname = match crate::HeaderName::new(name) {
77 Ok(name) => name,
78 Err(e) => {
79 warn!("Server sent back invalid header name: '{}'", e);
81 continue;
82 }
83 };
84 headers.insert_header(crate::Header::new_unchecked(hname, val));
86 }
87
88 let url = url::Url::parse(
89 &response
90 .url
91 .ok_or_else(|| backend_error!("Response has no URL"))?,
92 )
93 .map_err(|e| backend_error!("Response has illegal URL: {}", e))?;
94
95 Ok(crate::Response {
96 url,
97 request_method: method,
98 body: response.body.unwrap_or_default(),
99 status: status as u16,
100 headers,
101 })
102 }
103}
104
105type FetchCallback = unsafe extern "C" fn(ByteBuffer) -> ByteBuffer;
118
119mod callback_holder {
121 use super::FetchCallback;
122 use crate::error;
123 use std::sync::atomic::{AtomicUsize, Ordering};
124
125 static CALLBACK_PTR: AtomicUsize = AtomicUsize::new(0);
127
128 ffi_support::static_assert!(
136 STATIC_ASSERT_USIZE_EQ_FUNC_SIZE,
137 std::mem::size_of::<usize>() == std::mem::size_of::<FetchCallback>()
138 );
139
140 ffi_support::static_assert!(
141 STATIC_ASSERT_USIZE_EQ_OPT_FUNC_SIZE,
142 std::mem::size_of::<usize>() == std::mem::size_of::<Option<FetchCallback>>()
143 );
144
145 pub(super) fn get_callback() -> Option<FetchCallback> {
148 let ptr_value = CALLBACK_PTR.load(Ordering::SeqCst);
149 unsafe { std::mem::transmute::<usize, Option<FetchCallback>>(ptr_value) }
150 }
151
152 pub(super) fn set_callback(h: FetchCallback) -> bool {
154 let as_usize = h as usize;
155 match CALLBACK_PTR.compare_exchange(0, as_usize, Ordering::SeqCst, Ordering::SeqCst) {
156 Ok(_) => true,
157 Err(_) => {
158 error!("Bug: Initialized CALLBACK_PTR multiple times");
163 false
164 }
165 }
166 }
167}
168
169#[no_mangle]
172pub extern "C" fn viaduct_alloc_bytebuffer(sz: i32) -> ByteBuffer {
173 let mut error = ffi_support::ExternError::default();
174 let buffer =
175 ffi_support::call_with_output(&mut error, || ByteBuffer::new_with_size(sz.max(0) as usize));
176 error.consume_and_log_if_error();
177 buffer
178}
179
180#[no_mangle]
181pub extern "C" fn viaduct_log_error(s: FfiStr<'_>) {
182 let mut error = ffi_support::ExternError::default();
183 ffi_support::call_with_output(&mut error, || error!("Viaduct Ffi Error: {}", s.as_str()));
184 error.consume_and_log_if_error();
185}
186
187#[no_mangle]
188pub extern "C" fn viaduct_initialize(callback: FetchCallback) -> u8 {
189 ffi_support::abort_on_panic::call_with_output(|| callback_holder::set_callback(callback))
190}
191
192#[no_mangle]
198pub extern "C" fn viaduct_allow_android_emulator_loopback() {
199 let mut error = ffi_support::ExternError::default();
200 ffi_support::call_with_output(&mut error, || {
201 let url = url::Url::parse("http://10.0.2.2").unwrap();
202 let mut settings = GLOBAL_SETTINGS.write();
203 settings.addn_allowed_insecure_url = Some(url);
204 });
205 error.consume_and_log_if_error();
206}
207
208ffi_support::define_bytebuffer_destructor!(viaduct_destroy_bytebuffer);