mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-03 01:25:37 +00:00
Add FFI JNI JSON RPC bridge (#2326)
* Add FFI JNI JSON RPC bridge * Add FFI instance list API
This commit is contained in:
@@ -73,6 +73,181 @@ unsafe extern "C" fn record_config_server_event(event_json: *const c_char, user_
|
||||
);
|
||||
}
|
||||
|
||||
fn take_last_error() -> Option<String> {
|
||||
unsafe {
|
||||
let mut error_ptr: *const c_char = std::ptr::null();
|
||||
get_error_msg(&mut error_ptr);
|
||||
if error_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
let error = CStr::from_ptr(error_ptr).to_string_lossy().into_owned();
|
||||
free_string(error_ptr);
|
||||
Some(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn free_key_value_pairs(infos: &[KeyValuePair]) {
|
||||
for info in infos {
|
||||
free_string(info.key);
|
||||
free_string(info.value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_instance_returns_instance_names_and_ids() {
|
||||
let instance_id = Uuid::new_v4();
|
||||
let instance_name = format!("list-instance-{}", instance_id);
|
||||
let cfg = TomlConfigLoader::default();
|
||||
cfg.set_id(instance_id);
|
||||
cfg.set_inst_name(instance_name.clone());
|
||||
INSTANCE_MANAGER
|
||||
.run_network_instance(cfg, false, ConfigFileControl::STATIC_CONFIG)
|
||||
.unwrap();
|
||||
INSTANCE_NAME_ID_MAP.insert(instance_name.clone(), instance_id);
|
||||
|
||||
let mut infos = vec![
|
||||
KeyValuePair {
|
||||
key: std::ptr::null(),
|
||||
value: std::ptr::null(),
|
||||
};
|
||||
16
|
||||
];
|
||||
let count = unsafe { list_instance(infos.as_mut_ptr(), infos.len()) };
|
||||
assert!(count > 0);
|
||||
|
||||
let mut found = false;
|
||||
for info in infos.iter().take(count as usize) {
|
||||
let key = unsafe { CStr::from_ptr(info.key) }.to_string_lossy();
|
||||
let value = unsafe { CStr::from_ptr(info.value) }.to_string_lossy();
|
||||
if key == instance_name {
|
||||
assert_eq!(value, instance_id.to_string());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
free_key_value_pairs(&infos[..count as usize]);
|
||||
INSTANCE_MANAGER
|
||||
.delete_network_instance(vec![instance_id])
|
||||
.unwrap();
|
||||
remove_instance_name_ids(&[instance_id]);
|
||||
assert!(found);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_instance_allows_zero_length() {
|
||||
assert_eq!(unsafe { list_instance(std::ptr::null_mut(), 0) }, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_instance_rejects_null_output_pointer() {
|
||||
assert_eq!(unsafe { list_instance(std::ptr::null_mut(), 1) }, -1);
|
||||
assert!(take_last_error().unwrap().contains("infos is null"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_json_rpc_returns_logger_response() {
|
||||
let service = CString::new("api.logger.LoggerRpcService").unwrap();
|
||||
let method = CString::new("get_logger_config").unwrap();
|
||||
let payload = CString::new("{}").unwrap();
|
||||
let mut response_ptr: *const c_char = std::ptr::null();
|
||||
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
call_json_rpc(
|
||||
service.as_ptr(),
|
||||
method.as_ptr(),
|
||||
std::ptr::null(),
|
||||
payload.as_ptr(),
|
||||
&mut response_ptr,
|
||||
)
|
||||
},
|
||||
0
|
||||
);
|
||||
assert!(!response_ptr.is_null());
|
||||
let response = unsafe { CStr::from_ptr(response_ptr) }
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
free_string(response_ptr);
|
||||
let response: Value = serde_json::from_str(&response).unwrap();
|
||||
assert!(response.get("level").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_json_rpc_rejects_instance_management_service() {
|
||||
let service = CString::new("api.manage.WebClientService").unwrap();
|
||||
let method = CString::new("list_network_instance").unwrap();
|
||||
let payload = CString::new("{}").unwrap();
|
||||
let mut response_ptr: *const c_char = std::ptr::null();
|
||||
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
call_json_rpc(
|
||||
service.as_ptr(),
|
||||
method.as_ptr(),
|
||||
std::ptr::null(),
|
||||
payload.as_ptr(),
|
||||
&mut response_ptr,
|
||||
)
|
||||
},
|
||||
-1
|
||||
);
|
||||
assert!(response_ptr.is_null());
|
||||
assert!(take_last_error().unwrap().contains("not exposed"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_json_rpc_rejects_malformed_payload_json() {
|
||||
let service = CString::new("api.logger.LoggerRpcService").unwrap();
|
||||
let method = CString::new("get_logger_config").unwrap();
|
||||
let payload = CString::new("{").unwrap();
|
||||
let mut response_ptr: *const c_char = std::ptr::null();
|
||||
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
call_json_rpc(
|
||||
service.as_ptr(),
|
||||
method.as_ptr(),
|
||||
std::ptr::null(),
|
||||
payload.as_ptr(),
|
||||
&mut response_ptr,
|
||||
)
|
||||
},
|
||||
-1
|
||||
);
|
||||
assert!(response_ptr.is_null());
|
||||
assert!(
|
||||
take_last_error()
|
||||
.unwrap()
|
||||
.contains("failed to parse payload_json")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_json_rpc_rejects_null_output_pointer() {
|
||||
let service = CString::new("api.logger.LoggerRpcService").unwrap();
|
||||
let method = CString::new("get_logger_config").unwrap();
|
||||
let payload = CString::new("{}").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
call_json_rpc(
|
||||
service.as_ptr(),
|
||||
method.as_ptr(),
|
||||
std::ptr::null(),
|
||||
payload.as_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
},
|
||||
-1
|
||||
);
|
||||
assert!(
|
||||
take_last_error()
|
||||
.unwrap()
|
||||
.contains("out_response_json is null")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn config_server_hooks_emit_run_event() {
|
||||
let events: Mutex<Vec<String>> = Mutex::new(Vec::new());
|
||||
@@ -395,10 +570,28 @@ async fn config_server_hooks_suppress_late_run_events_while_stopping() {
|
||||
fn config_server_callback_context_rejects_nested_blocking_ffi_calls() {
|
||||
let _callback_scope = ConfigServerCallbackScope::enter();
|
||||
assert_eq!(is_config_server_client_connected(), 0);
|
||||
let service = CString::new("api.logger.LoggerRpcService").unwrap();
|
||||
let method = CString::new("get_logger_config").unwrap();
|
||||
let payload = CString::new("{}").unwrap();
|
||||
let mut response_ptr: *const c_char = std::ptr::null();
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
call_json_rpc(
|
||||
service.as_ptr(),
|
||||
method.as_ptr(),
|
||||
std::ptr::null(),
|
||||
payload.as_ptr(),
|
||||
&mut response_ptr,
|
||||
)
|
||||
},
|
||||
-1
|
||||
);
|
||||
assert!(response_ptr.is_null());
|
||||
assert_eq!(
|
||||
unsafe { collect_network_infos(std::ptr::null_mut(), 0) },
|
||||
-1
|
||||
);
|
||||
assert_eq!(unsafe { list_instance(std::ptr::null_mut(), 0) }, -1);
|
||||
let cfg = CString::new("inst_name = \"callback-test\"\nlisteners = []").unwrap();
|
||||
assert_eq!(unsafe { run_network_instance(cfg.as_ptr()) }, -1);
|
||||
assert_eq!(unsafe { retain_network_instance(std::ptr::null(), 0) }, -1);
|
||||
|
||||
Reference in New Issue
Block a user