mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-07 10:14:35 +00:00
feat/web (Patchset 2) (#444)
This patch implement a restful server without any auth.
usage:
```bash
# run easytier-web, which acts as an gateway and registry for all easytier-core
$> easytier-web
# run easytier-core and connect to easytier-web with a token
$> easytier-core --config-server udp://127.0.0.1:22020/fdsafdsa
# use restful api to list session
$> curl -H "Content-Type: application/json" -X GET 127.0.0.1:11211/api/v1/sessions
[{"token":"fdsafdsa","client_url":"udp://127.0.0.1:48915","machine_id":"de3f5b8f-0f2f-d9d0-fb30-a2ac8951d92f"}]%
# use restful api to run a network instance
$> curl -H "Content-Type: application/json" -X POST 127.0.0.1:11211/api/v1/network/de3f5b8f-0f2f-d9d0-fb30-a2ac8951d92f -d '{"config": "listeners = [\"udp://0.0.0.0:12344\"]"}'
# use restful api to get network instance info
$> curl -H "Content-Type: application/json" -X GET 127.0.0.1:11211/api/v1/network/de3f5b8f-0f2f-d9d0-fb30-a2ac8951d92f/65437e50-b286-4098-a624-74429f2cb839
```
This commit is contained in:
@@ -56,6 +56,11 @@ message Route {
|
||||
common.PeerFeatureFlag feature_flag = 10;
|
||||
}
|
||||
|
||||
message PeerRoutePair {
|
||||
Route route = 1;
|
||||
PeerInfo peer = 2;
|
||||
}
|
||||
|
||||
message NodeInfo {
|
||||
uint32 peer_id = 1;
|
||||
string ipv4_addr = 2;
|
||||
|
||||
@@ -1 +1,113 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/cli.rs"));
|
||||
|
||||
impl PeerRoutePair {
|
||||
pub fn get_latency_ms(&self) -> Option<f64> {
|
||||
let mut ret = u64::MAX;
|
||||
let p = self.peer.as_ref()?;
|
||||
for conn in p.conns.iter() {
|
||||
let Some(stats) = &conn.stats else {
|
||||
continue;
|
||||
};
|
||||
ret = ret.min(stats.latency_us);
|
||||
}
|
||||
|
||||
if ret == u64::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(f64::from(ret as u32) / 1000.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rx_bytes(&self) -> Option<u64> {
|
||||
let mut ret = 0;
|
||||
let p = self.peer.as_ref()?;
|
||||
for conn in p.conns.iter() {
|
||||
let Some(stats) = &conn.stats else {
|
||||
continue;
|
||||
};
|
||||
ret += stats.rx_bytes;
|
||||
}
|
||||
|
||||
if ret == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tx_bytes(&self) -> Option<u64> {
|
||||
let mut ret = 0;
|
||||
let p = self.peer.as_ref()?;
|
||||
for conn in p.conns.iter() {
|
||||
let Some(stats) = &conn.stats else {
|
||||
continue;
|
||||
};
|
||||
ret += stats.tx_bytes;
|
||||
}
|
||||
|
||||
if ret == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_loss_rate(&self) -> Option<f64> {
|
||||
let mut ret = 0.0;
|
||||
let p = self.peer.as_ref()?;
|
||||
for conn in p.conns.iter() {
|
||||
ret += conn.loss_rate;
|
||||
}
|
||||
|
||||
if ret == 0.0 {
|
||||
None
|
||||
} else {
|
||||
Some(ret as f64)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_conn_protos(&self) -> Option<Vec<String>> {
|
||||
let mut ret = vec![];
|
||||
let p = self.peer.as_ref()?;
|
||||
for conn in p.conns.iter() {
|
||||
let Some(tunnel_info) = &conn.tunnel else {
|
||||
continue;
|
||||
};
|
||||
// insert if not exists
|
||||
if !ret.contains(&tunnel_info.tunnel_type) {
|
||||
ret.push(tunnel_info.tunnel_type.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if ret.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_udp_nat_type(self: &Self) -> String {
|
||||
use crate::proto::common::NatType;
|
||||
let mut ret = NatType::Unknown;
|
||||
if let Some(r) = &self.route.clone().unwrap_or_default().stun_info {
|
||||
ret = NatType::try_from(r.udp_nat_type).unwrap();
|
||||
}
|
||||
format!("{:?}", ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_peer_route_pair(peers: Vec<PeerInfo>, routes: Vec<Route>) -> Vec<PeerRoutePair> {
|
||||
let mut pairs: Vec<PeerRoutePair> = vec![];
|
||||
|
||||
for route in routes.iter() {
|
||||
let peer = peers.iter().find(|peer| peer.peer_id == route.peer_id);
|
||||
let pair = PeerRoutePair {
|
||||
route: Some(route.clone()),
|
||||
peer: peer.cloned(),
|
||||
};
|
||||
|
||||
pairs.push(pair);
|
||||
}
|
||||
|
||||
pairs
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ message MalformatRpcPacket { string error_message = 1; }
|
||||
message Timeout { string error_message = 1; }
|
||||
|
||||
message Error {
|
||||
oneof error {
|
||||
oneof error_kind {
|
||||
OtherError other_error = 1;
|
||||
InvalidMethodIndex invalid_method_index = 2;
|
||||
InvalidService invalid_service = 3;
|
||||
|
||||
+17
-17
@@ -6,44 +6,44 @@ include!(concat!(env!("OUT_DIR"), "/error.rs"));
|
||||
|
||||
impl From<&rpc_types::error::Error> for Error {
|
||||
fn from(e: &rpc_types::error::Error) -> Self {
|
||||
use super::error::error::Error as ProtoError;
|
||||
use super::error::error::ErrorKind as ProtoError;
|
||||
match e {
|
||||
rpc_types::error::Error::ExecutionError(e) => Self {
|
||||
error: Some(ProtoError::ExecuteError(ExecuteError {
|
||||
error_message: e.to_string(),
|
||||
error_kind: Some(ProtoError::ExecuteError(ExecuteError {
|
||||
error_message: format!("{:?}", e),
|
||||
})),
|
||||
},
|
||||
rpc_types::error::Error::DecodeError(_) => Self {
|
||||
error: Some(ProtoError::ProstDecodeError(ProstDecodeError {})),
|
||||
error_kind: Some(ProtoError::ProstDecodeError(ProstDecodeError {})),
|
||||
},
|
||||
rpc_types::error::Error::EncodeError(_) => Self {
|
||||
error: Some(ProtoError::ProstEncodeError(ProstEncodeError {})),
|
||||
error_kind: Some(ProtoError::ProstEncodeError(ProstEncodeError {})),
|
||||
},
|
||||
rpc_types::error::Error::InvalidMethodIndex(m, s) => Self {
|
||||
error: Some(ProtoError::InvalidMethodIndex(InvalidMethodIndex {
|
||||
error_kind: Some(ProtoError::InvalidMethodIndex(InvalidMethodIndex {
|
||||
method_index: *m as u32,
|
||||
service_name: s.to_string(),
|
||||
service_name: format!("{:?}", s),
|
||||
})),
|
||||
},
|
||||
rpc_types::error::Error::InvalidServiceKey(s, _) => Self {
|
||||
error: Some(ProtoError::InvalidService(InvalidService {
|
||||
service_name: s.to_string(),
|
||||
error_kind: Some(ProtoError::InvalidService(InvalidService {
|
||||
service_name: format!("{:?}", s),
|
||||
})),
|
||||
},
|
||||
rpc_types::error::Error::MalformatRpcPacket(e) => Self {
|
||||
error: Some(ProtoError::MalformatRpcPacket(MalformatRpcPacket {
|
||||
error_message: e.to_string(),
|
||||
error_kind: Some(ProtoError::MalformatRpcPacket(MalformatRpcPacket {
|
||||
error_message: format!("{:?}", e),
|
||||
})),
|
||||
},
|
||||
rpc_types::error::Error::Timeout(e) => Self {
|
||||
error: Some(ProtoError::Timeout(Timeout {
|
||||
error_message: e.to_string(),
|
||||
error_kind: Some(ProtoError::Timeout(Timeout {
|
||||
error_message: format!("{:?}", e),
|
||||
})),
|
||||
},
|
||||
#[allow(unreachable_patterns)]
|
||||
e => Self {
|
||||
error: Some(ProtoError::OtherError(OtherError {
|
||||
error_message: e.to_string(),
|
||||
error_kind: Some(ProtoError::OtherError(OtherError {
|
||||
error_message: format!("{:?}", e),
|
||||
})),
|
||||
},
|
||||
}
|
||||
@@ -52,8 +52,8 @@ impl From<&rpc_types::error::Error> for Error {
|
||||
|
||||
impl From<&Error> for rpc_types::error::Error {
|
||||
fn from(e: &Error) -> Self {
|
||||
use super::error::error::Error as ProtoError;
|
||||
match &e.error {
|
||||
use super::error::error::ErrorKind as ProtoError;
|
||||
match &e.error_kind {
|
||||
Some(ProtoError::ExecuteError(e)) => {
|
||||
Self::ExecutionError(anyhow::anyhow!(e.error_message.clone()))
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod cli;
|
||||
pub mod common;
|
||||
pub mod error;
|
||||
pub mod peer_rpc;
|
||||
pub mod web;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{atomic::AtomicBool, Arc, Mutex};
|
||||
|
||||
use futures::{SinkExt as _, StreamExt};
|
||||
use tokio::{task::JoinSet, time::timeout};
|
||||
|
||||
use crate::{
|
||||
defer,
|
||||
proto::rpc_types::error::Error,
|
||||
tunnel::{packet_def::PacketType, ring::create_ring_tunnel_pair, Tunnel},
|
||||
};
|
||||
@@ -17,6 +18,7 @@ pub struct BidirectRpcManager {
|
||||
rx_timeout: Option<std::time::Duration>,
|
||||
error: Arc<Mutex<Option<Error>>>,
|
||||
tunnel: Mutex<Option<Box<dyn Tunnel>>>,
|
||||
running: Arc<AtomicBool>,
|
||||
|
||||
tasks: Mutex<Option<JoinSet<()>>>,
|
||||
}
|
||||
@@ -30,6 +32,7 @@ impl BidirectRpcManager {
|
||||
rx_timeout: None,
|
||||
error: Arc::new(Mutex::new(None)),
|
||||
tunnel: Mutex::new(None),
|
||||
running: Arc::new(AtomicBool::new(false)),
|
||||
|
||||
tasks: Mutex::new(None),
|
||||
}
|
||||
@@ -50,6 +53,8 @@ impl BidirectRpcManager {
|
||||
let mut tasks = JoinSet::new();
|
||||
self.rpc_client.run();
|
||||
self.rpc_server.run();
|
||||
self.running
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
let (server_tx, mut server_rx) = (
|
||||
self.rpc_server.get_transport_sink(),
|
||||
@@ -64,7 +69,11 @@ impl BidirectRpcManager {
|
||||
self.tunnel.lock().unwrap().replace(inner);
|
||||
|
||||
let e_clone = self.error.clone();
|
||||
let r_clone = self.running.clone();
|
||||
tasks.spawn(async move {
|
||||
defer! {
|
||||
r_clone.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
loop {
|
||||
let packet = tokio::select! {
|
||||
Some(Ok(packet)) = server_rx.next() => {
|
||||
@@ -90,7 +99,11 @@ impl BidirectRpcManager {
|
||||
|
||||
let recv_timeout = self.rx_timeout;
|
||||
let e_clone = self.error.clone();
|
||||
let r_clone = self.running.clone();
|
||||
tasks.spawn(async move {
|
||||
defer! {
|
||||
r_clone.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
loop {
|
||||
let ret = if let Some(recv_timeout) = recv_timeout {
|
||||
match timeout(recv_timeout, inner_rx.next()).await {
|
||||
@@ -161,4 +174,8 @@ impl BidirectRpcManager {
|
||||
tasks.abort_all();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.running.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +307,7 @@ async fn test_bidirect_rpc_manager() {
|
||||
use crate::proto::rpc_impl::bidirect::BidirectRpcManager;
|
||||
use crate::tunnel::tcp::{TcpTunnelConnector, TcpTunnelListener};
|
||||
use crate::tunnel::{TunnelConnector, TunnelListener};
|
||||
use tokio::sync::Notify;
|
||||
|
||||
let c = BidirectRpcManager::new();
|
||||
let s = BidirectRpcManager::new();
|
||||
@@ -323,6 +324,8 @@ async fn test_bidirect_rpc_manager() {
|
||||
});
|
||||
s.rpc_server().registry().register(service, "test");
|
||||
|
||||
let server_test_done = Arc::new(Notify::new());
|
||||
let server_test_done_clone = server_test_done.clone();
|
||||
let mut tcp_listener = TcpTunnelListener::new("tcp://0.0.0.0:55443".parse().unwrap());
|
||||
let s_task: ScopedTask<()> = tokio::spawn(async move {
|
||||
tcp_listener.listen().await.unwrap();
|
||||
@@ -344,6 +347,8 @@ async fn test_bidirect_rpc_manager() {
|
||||
assert_eq!(ret.greeting, "Hello Client world!");
|
||||
println!("server done, {:?}", ret);
|
||||
|
||||
server_test_done_clone.notify_one();
|
||||
|
||||
s.wait().await;
|
||||
})
|
||||
.into();
|
||||
@@ -369,6 +374,7 @@ async fn test_bidirect_rpc_manager() {
|
||||
assert_eq!(ret.greeting, "Hello Server world!");
|
||||
println!("client done, {:?}", ret);
|
||||
|
||||
server_test_done.notified().await;
|
||||
drop(c);
|
||||
s_task.await.unwrap();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "common.proto";
|
||||
import "peer_rpc.proto";
|
||||
import "cli.proto";
|
||||
|
||||
package web;
|
||||
|
||||
message MyNodeInfo {
|
||||
common.Ipv4Addr virtual_ipv4 = 1;
|
||||
string hostname = 2;
|
||||
string version = 3;
|
||||
peer_rpc.GetIpListResponse ips = 4;
|
||||
common.StunInfo stun_info = 5;
|
||||
repeated common.Url listeners = 6;
|
||||
optional string vpn_portal_cfg = 7;
|
||||
}
|
||||
|
||||
message NetworkInstanceRunningInfo {
|
||||
string dev_name = 1;
|
||||
MyNodeInfo my_node_info = 2;
|
||||
map<string, string> events = 3;
|
||||
MyNodeInfo node_info = 4;
|
||||
repeated cli.Route routes = 5;
|
||||
repeated cli.PeerInfo peers = 6;
|
||||
repeated cli.PeerRoutePair peer_route_pairs = 7;
|
||||
bool running = 8;
|
||||
optional string error_msg = 9;
|
||||
}
|
||||
|
||||
message NetworkInstanceRunningInfoMap {
|
||||
map<string, NetworkInstanceRunningInfo> map = 1;
|
||||
}
|
||||
|
||||
message HeartbeatRequest {
|
||||
common.UUID machine_id = 1;
|
||||
common.UUID inst_id = 2;
|
||||
string user_token = 3;
|
||||
}
|
||||
|
||||
message HeartbeatResponse {
|
||||
}
|
||||
|
||||
service WebServerService {
|
||||
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) {}
|
||||
}
|
||||
|
||||
message ValidateConfigRequest {
|
||||
string config = 1;
|
||||
}
|
||||
|
||||
message ValidateConfigResponse {
|
||||
}
|
||||
|
||||
message RunNetworkInstanceRequest {
|
||||
string config = 1;
|
||||
}
|
||||
|
||||
message RunNetworkInstanceResponse {
|
||||
}
|
||||
|
||||
message RetainNetworkInstanceRequest {
|
||||
repeated common.UUID inst_ids = 1;
|
||||
}
|
||||
|
||||
message RetainNetworkInstanceResponse {
|
||||
repeated common.UUID remain_inst_ids = 1;
|
||||
}
|
||||
|
||||
message CollectNetworkInfoRequest {
|
||||
repeated common.UUID inst_ids = 1;
|
||||
}
|
||||
|
||||
message CollectNetworkInfoResponse {
|
||||
NetworkInstanceRunningInfoMap info = 1;
|
||||
}
|
||||
|
||||
message ListNetworkInstanceRequest {
|
||||
}
|
||||
|
||||
message ListNetworkInstanceResponse {
|
||||
repeated common.UUID inst_ids = 1;
|
||||
}
|
||||
|
||||
message DeleteNetworkInstanceRequest {
|
||||
repeated common.UUID inst_ids = 1;
|
||||
}
|
||||
|
||||
message DeleteNetworkInstanceResponse {
|
||||
repeated common.UUID remain_inst_ids = 1;
|
||||
}
|
||||
|
||||
service WebClientService {
|
||||
rpc ValidateConfig(ValidateConfigRequest) returns (ValidateConfigResponse) {}
|
||||
rpc RunNetworkInstance(RunNetworkInstanceRequest) returns (RunNetworkInstanceResponse) {}
|
||||
rpc RetainNetworkInstance(RetainNetworkInstanceRequest) returns (RetainNetworkInstanceResponse) {}
|
||||
rpc CollectNetworkInfo(CollectNetworkInfoRequest) returns (CollectNetworkInfoResponse) {}
|
||||
rpc ListNetworkInstance(ListNetworkInstanceRequest) returns (ListNetworkInstanceResponse) {}
|
||||
rpc DeleteNetworkInstance(DeleteNetworkInstanceRequest) returns (DeleteNetworkInstanceResponse) {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/web.rs"));
|
||||
Reference in New Issue
Block a user