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:
Sijie.Sun
2024-10-26 00:04:22 +08:00
committed by GitHub
parent b5c3726e67
commit a78b759741
33 changed files with 1539 additions and 263 deletions

View File

@@ -0,0 +1,134 @@
pub mod session;
pub mod storage;
use std::sync::Arc;
use dashmap::DashMap;
use easytier::{common::scoped_task::ScopedTask, tunnel::TunnelListener};
use session::Session;
use storage::{Storage, StorageToken};
#[derive(Debug)]
pub struct ClientManager {
accept_task: Option<ScopedTask<()>>,
clear_task: Option<ScopedTask<()>>,
client_sessions: Arc<DashMap<url::Url, Arc<Session>>>,
storage: Storage,
}
impl ClientManager {
pub fn new() -> Self {
ClientManager {
accept_task: None,
clear_task: None,
client_sessions: Arc::new(DashMap::new()),
storage: Storage::new(),
}
}
pub async fn serve<L: TunnelListener + 'static>(
&mut self,
mut listener: L,
) -> Result<(), anyhow::Error> {
listener.listen().await?;
let sessions = self.client_sessions.clone();
let storage = self.storage.weak_ref();
let task = tokio::spawn(async move {
while let Ok(tunnel) = listener.accept().await {
let info = tunnel.info().unwrap();
let client_url: url::Url = info.remote_addr.unwrap().into();
println!("New session from {:?}", tunnel.info());
let session = Session::new(tunnel, storage.clone(), client_url.clone());
sessions.insert(client_url, Arc::new(session));
}
});
self.accept_task = Some(ScopedTask::from(task));
let sessions = self.client_sessions.clone();
let task = tokio::spawn(async move {
loop {
tokio::time::sleep(std::time::Duration::from_secs(15)).await;
sessions.retain(|_, session| session.is_running());
}
});
self.clear_task = Some(ScopedTask::from(task));
Ok(())
}
pub fn is_running(&self) -> bool {
self.accept_task.is_some() && self.clear_task.is_some()
}
pub async fn list_sessions(&self) -> Vec<StorageToken> {
let sessions = self
.client_sessions
.iter()
.map(|item| item.value().clone())
.collect::<Vec<_>>();
let mut ret: Vec<StorageToken> = vec![];
for s in sessions {
if let Some(t) = s.get_token().await {
ret.push(t);
}
}
ret
}
pub fn get_session_by_machine_id(&self, machine_id: &uuid::Uuid) -> Option<Arc<Session>> {
let c_url = self.storage.get_client_url_by_machine_id(machine_id)?;
self.client_sessions
.get(&c_url)
.map(|item| item.value().clone())
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use easytier::{
tunnel::{
common::tests::wait_for_condition,
udp::{UdpTunnelConnector, UdpTunnelListener},
},
web_client::WebClient,
};
use crate::client_manager::ClientManager;
#[tokio::test]
async fn test_client() {
let listener = UdpTunnelListener::new("udp://0.0.0.0:54333".parse().unwrap());
let mut mgr = ClientManager::new();
mgr.serve(Box::new(listener)).await.unwrap();
let connector = UdpTunnelConnector::new("udp://127.0.0.1:54333".parse().unwrap());
let _c = WebClient::new(connector, "test");
wait_for_condition(
|| async { mgr.client_sessions.len() == 1 },
Duration::from_secs(6),
)
.await;
let mut a = mgr
.client_sessions
.iter()
.next()
.unwrap()
.data()
.read()
.await
.heartbeat_waiter();
let req = a.recv().await.unwrap();
println!("{:?}", req);
println!("{:?}", mgr);
}
}