Feat/web (PatchSet 1) (#436)
Some checks are pending
EasyTier Core / pre_job (push) Waiting to run
EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Blocked by required conditions
EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Blocked by required conditions
EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Blocked by required conditions
EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier Core / core-result (push) Blocked by required conditions
EasyTier GUI / pre_job (push) Waiting to run
EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Blocked by required conditions
EasyTier GUI / gui-result (push) Blocked by required conditions
EasyTier Mobile / pre_job (push) Waiting to run
EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Blocked by required conditions
EasyTier Mobile / mobile-result (push) Blocked by required conditions
EasyTier Test / pre_job (push) Waiting to run
EasyTier Test / test (push) Blocked by required conditions

* move rpc-build out of easytier dir and make it a independant project
* easytier core use launcher
* fix flags not print on launch
* allow launcher not fetch node info
* abstract out peer rpc impl
* fix arm gui ci. see https://github.com/actions/runner-images/pull/10807
* add easytier-web crate
* fix manual_connector test case
This commit is contained in:
Sijie.Sun 2024-10-19 18:10:02 +08:00 committed by GitHub
parent 2134bc9139
commit 0bf42c53cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 575 additions and 353 deletions

View File

@ -37,28 +37,28 @@ jobs:
matrix: matrix:
include: include:
- TARGET: aarch64-unknown-linux-musl - TARGET: aarch64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-aarch64 ARTIFACT_NAME: linux-aarch64
- TARGET: x86_64-unknown-linux-musl - TARGET: x86_64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-x86_64 ARTIFACT_NAME: linux-x86_64
- TARGET: mips-unknown-linux-musl - TARGET: mips-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-mips ARTIFACT_NAME: linux-mips
- TARGET: mipsel-unknown-linux-musl - TARGET: mipsel-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-mipsel ARTIFACT_NAME: linux-mipsel
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested - TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-armv7hf ARTIFACT_NAME: linux-armv7hf
- TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested - TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-armv7 ARTIFACT_NAME: linux-armv7
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested - TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-armhf ARTIFACT_NAME: linux-armhf
- TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested - TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: linux-arm ARTIFACT_NAME: linux-arm
- TARGET: x86_64-apple-darwin - TARGET: x86_64-apple-darwin
@ -73,7 +73,7 @@ jobs:
ARTIFACT_NAME: windows-x86_64 ARTIFACT_NAME: windows-x86_64
- TARGET: x86_64-unknown-freebsd - TARGET: x86_64-unknown-freebsd
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: freebsd-13.2-x86_64 ARTIFACT_NAME: freebsd-13.2-x86_64
BSD_VERSION: 13.2 BSD_VERSION: 13.2

View File

@ -36,11 +36,11 @@ jobs:
matrix: matrix:
include: include:
- TARGET: aarch64-unknown-linux-musl - TARGET: aarch64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
GUI_TARGET: aarch64-unknown-linux-gnu GUI_TARGET: aarch64-unknown-linux-gnu
ARTIFACT_NAME: linux-aarch64 ARTIFACT_NAME: linux-aarch64
- TARGET: x86_64-unknown-linux-musl - TARGET: x86_64-unknown-linux-musl
OS: ubuntu-latest OS: ubuntu-22.04
GUI_TARGET: x86_64-unknown-linux-gnu GUI_TARGET: x86_64-unknown-linux-gnu
ARTIFACT_NAME: linux-x86_64 ARTIFACT_NAME: linux-x86_64
@ -123,30 +123,30 @@ jobs:
if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }} if: ${{ matrix.TARGET == 'aarch64-unknown-linux-musl' }}
run: | run: |
# see https://tauri.app/v1/guides/building/linux/ # see https://tauri.app/v1/guides/building/linux/
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble main restricted" | sudo tee /etc/apt/sources.list echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy main restricted" | sudo tee /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-updates main restricted" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble universe" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy universe" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-updates universe" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-updates universe" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-updates multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-updates multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ noble-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ noble-security main restricted" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ jammy-security main restricted" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ noble-security universe" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ jammy-security universe" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ noble-security multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu/ jammy-security multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble main restricted" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble universe" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy universe" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates universe" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates universe" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-security main restricted" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-security universe" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security universe" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports noble-security multiverse" | sudo tee -a /etc/apt/sources.list echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security multiverse" | sudo tee -a /etc/apt/sources.list
sudo dpkg --add-architecture arm64 sudo dpkg --add-architecture arm64
sudo apt-get update && sudo apt-get upgrade -y sudo apt-get update
sudo apt install -f -o Dpkg::Options::="--force-overwrite" libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu sudo apt install -f -o Dpkg::Options::="--force-overwrite" libwebkit2gtk-4.1-dev:arm64 libssl-dev:arm64 gcc-aarch64-linux-gnu
echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV" echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV" echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/" >> "$GITHUB_ENV"
@ -157,7 +157,7 @@ jobs:
with: with:
projectPath: ./easytier-gui projectPath: ./easytier-gui
# https://tauri.app/v1/guides/building/linux/#cross-compiling-tauri-applications-for-arm-based-devices # https://tauri.app/v1/guides/building/linux/#cross-compiling-tauri-applications-for-arm-based-devices
args: --verbose --target ${{ matrix.GUI_TARGET }} ${{ matrix.OS == 'ubuntu-latest' && contains(matrix.TARGET, 'aarch64') && '--bundles deb' || '' }} args: --verbose --target ${{ matrix.GUI_TARGET }} ${{ matrix.OS == 'ubuntu-22.04' && contains(matrix.TARGET, 'aarch64') && '--bundles deb' || '' }}
- name: Compress - name: Compress
run: | run: |

View File

@ -36,7 +36,7 @@ jobs:
matrix: matrix:
include: include:
- TARGET: android - TARGET: android
OS: ubuntu-latest OS: ubuntu-22.04
ARTIFACT_NAME: android ARTIFACT_NAME: android
runs-on: ${{ matrix.OS }} runs-on: ${{ matrix.OS }}
env: env:

View File

@ -30,7 +30,7 @@ jobs:
skip_after_successful_duplicate: 'true' skip_after_successful_duplicate: 'true'
paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/test.yml"]' paths: '["Cargo.toml", "Cargo.lock", "easytier/**", ".github/workflows/test.yml"]'
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
needs: pre_job needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true' if: needs.pre_job.outputs.should_skip != 'true'
steps: steps:

35
Cargo.lock generated
View File

@ -1564,6 +1564,7 @@ dependencies = [
"dashmap", "dashmap",
"defguard_wireguard_rs", "defguard_wireguard_rs",
"derivative", "derivative",
"easytier-rpc-build 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding", "encoding",
"futures", "futures",
"futures-util", "futures-util",
@ -1592,7 +1593,6 @@ dependencies = [
"reqwest 0.11.27", "reqwest 0.11.27",
"ring 0.17.8", "ring 0.17.8",
"ringbuf", "ringbuf",
"rpc_build",
"rstest", "rstest",
"rust-i18n", "rust-i18n",
"rustls", "rustls",
@ -1656,6 +1656,31 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "easytier-rpc-build"
version = "0.1.0"
dependencies = [
"heck 0.5.0",
"prost-build",
]
[[package]]
name = "easytier-rpc-build"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24829168c28f6a448f57d18116c255dcbd2b8c25e76dbc60f6cd16d68ad2cf07"
dependencies = [
"heck 0.5.0",
"prost-build",
]
[[package]]
name = "easytier-web"
version = "0.1.0"
dependencies = [
"easytier",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.13.0" version = "1.13.0"
@ -4904,14 +4929,6 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "rpc_build"
version = "0.1.0"
dependencies = [
"heck 0.5.0",
"prost-build",
]
[[package]] [[package]]
name = "rstest" name = "rstest"
version = "0.18.2" version = "0.18.2"

View File

@ -1,6 +1,6 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = ["easytier", "easytier-gui/src-tauri"] members = ["easytier", "easytier-gui/src-tauri", "easytier-rpc-build", "easytier-web"]
default-members = ["easytier"] default-members = ["easytier"]
[profile.dev] [profile.dev]

View File

@ -14,6 +14,10 @@
{ {
"name": "vpnservice", "name": "vpnservice",
"path": "tauri-plugin-vpnservice" "path": "tauri-plugin-vpnservice"
},
{
"name": "rpc-build",
"path": "easytier-rpc-build"
} }
], ],
"settings": { "settings": {

View File

@ -16,7 +16,6 @@ use easytier::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tauri::Manager as _; use tauri::Manager as _;
use tauri::RunEvent;
pub const AUTOSTART_ARG: &str = "--autostart"; pub const AUTOSTART_ARG: &str = "--autostart";
@ -336,7 +335,7 @@ pub fn run() {
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_vpnservice::init()); .plugin(tauri_plugin_vpnservice::init());
let mut app = builder let app = builder
.setup(|app| { .setup(|app| {
// for logging config // for logging config
let Ok(log_dir) = app.path().app_log_dir() else { let Ok(log_dir) = app.path().app_log_dir() else {
@ -402,6 +401,8 @@ pub fn run() {
app.run(|_app, _event| {}); app.run(|_app, _event| {});
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{
use tauri::RunEvent;
app.run(|app, event| match event { app.run(|app, event| match event {
RunEvent::Reopen { .. } => { RunEvent::Reopen { .. } => {
toggle_window_visibility(app); toggle_window_visibility(app);
@ -409,3 +410,4 @@ pub fn run() {
_ => {} _ => {}
}); });
} }
}

View File

@ -0,0 +1,21 @@
[package]
name = "easytier-rpc-build"
description = "Protobuf RPC Service Generator for EasyTier"
version = "0.1.0"
edition = "2021"
homepage = "https://github.com/EasyTier/EasyTier"
repository = "https://github.com/EasyTier/EasyTier"
authors = ["kkrainbow"]
keywords = ["vpn", "p2p", "network", "easytier"]
categories = ["network-programming", "command-line-utilities"]
rust-version = "1.77.0"
license-file = "LICENSE"
readme = "README.md"
[dependencies]
heck = "0.5"
prost-build = "0.13"
[features]
default = []
internal-namespace = []

1
easytier-rpc-build/LICENSE Symbolic link
View File

@ -0,0 +1 @@
../LICENSE

View File

@ -0,0 +1,3 @@
# Introduction
This is a protobuf rpc service stub generator for [EasyTier](https://github.com/EasyTier/EasyTier) project.

View File

@ -3,8 +3,12 @@ extern crate prost_build;
use std::fmt; use std::fmt;
#[cfg(feature = "internal-namespace")]
const NAMESPACE: &str = "crate::proto::rpc_types"; const NAMESPACE: &str = "crate::proto::rpc_types";
#[cfg(not(feature = "internal-namespace"))]
const NAMESPACE: &str = "easytier::proto::rpc_types";
/// The service generator to be used with `prost-build` to generate RPC implementations for /// The service generator to be used with `prost-build` to generate RPC implementations for
/// `prost-simple-rpc`. /// `prost-simple-rpc`.
/// ///

View File

@ -1,8 +1,7 @@
[package] [package]
name = "rpc_build" name = "easytier-web"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
heck = "0.5" easytier = { path = "../easytier" }
prost-build = "0.13"

3
easytier-web/src/main.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View File

@ -197,7 +197,7 @@ tonic-build = "0.12"
globwalk = "0.8.1" globwalk = "0.8.1"
regex = "1" regex = "1"
prost-build = "0.13.2" prost-build = "0.13.2"
rpc_build = { path = "src/proto/rpc_build" } rpc_build = { package = "easytier-rpc-build", version = "0.1.0", features = ["internal-namespace"] }
[target.'cfg(windows)'.build-dependencies] [target.'cfg(windows)'.build-dependencies]
reqwest = { version = "0.11", features = ["blocking"] } reqwest = { version = "0.11", features = ["blocking"] }

View File

@ -134,6 +134,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
prost_build::Config::new() prost_build::Config::new()
.protoc_arg("--experimental_allow_proto3_optional")
.type_attribute(".common", "#[derive(serde::Serialize, serde::Deserialize)]") .type_attribute(".common", "#[derive(serde::Serialize, serde::Deserialize)]")
.type_attribute(".error", "#[derive(serde::Serialize, serde::Deserialize)]") .type_attribute(".error", "#[derive(serde::Serialize, serde::Deserialize)]")
.type_attribute(".cli", "#[derive(serde::Serialize, serde::Deserialize)]") .type_attribute(".cli", "#[derive(serde::Serialize, serde::Deserialize)]")

View File

@ -164,7 +164,7 @@ pub struct Flags {
pub enable_ipv6: bool, pub enable_ipv6: bool,
#[derivative(Default(value = "1380"))] #[derivative(Default(value = "1380"))]
pub mtu: u16, pub mtu: u16,
#[derivative(Default(value = "true"))] #[derivative(Default(value = "false"))]
pub latency_first: bool, pub latency_first: bool,
#[derivative(Default(value = "false"))] #[derivative(Default(value = "false"))]
pub enable_exit_node: bool, pub enable_exit_node: bool,
@ -182,6 +182,8 @@ pub struct Flags {
pub disable_udp_hole_punching: bool, pub disable_udp_hole_punching: bool,
#[derivative(Default(value = "\"udp://[::]:0\".to_string()"))] #[derivative(Default(value = "\"udp://[::]:0\".to_string()"))]
pub ipv6_listener: String, pub ipv6_listener: String,
#[derivative(Default(value = "false"))]
pub multi_thread: bool,
} }
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
@ -529,7 +531,28 @@ impl ConfigLoader for TomlConfigLoader {
} }
fn dump(&self) -> String { fn dump(&self) -> String {
toml::to_string_pretty(&*self.config.lock().unwrap()).unwrap() let default_flags_json = serde_json::to_string(&Flags::default()).unwrap();
let default_flags_hashmap =
serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(&default_flags_json)
.unwrap();
let cur_flags_json = serde_json::to_string(&self.get_flags()).unwrap();
let cur_flags_hashmap =
serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(&cur_flags_json)
.unwrap();
let mut flag_map: serde_json::Map<String, serde_json::Value> = Default::default();
for (key, value) in default_flags_hashmap {
if let Some(v) = cur_flags_hashmap.get(&key) {
if *v != value {
flag_map.insert(key, v.clone());
}
}
}
let mut config = self.config.lock().unwrap().clone();
config.flags = Some(flag_map);
toml::to_string_pretty(&config).unwrap()
} }
fn get_routes(&self) -> Option<Vec<cidr::Ipv4Cidr>> { fn get_routes(&self) -> Option<Vec<cidr::Ipv4Cidr>> {

View File

@ -44,8 +44,8 @@ pub enum GlobalCtxEvent {
DhcpIpv4Conflicted(Option<cidr::Ipv4Inet>), DhcpIpv4Conflicted(Option<cidr::Ipv4Inet>),
} }
type EventBus = tokio::sync::broadcast::Sender<GlobalCtxEvent>; pub type EventBus = tokio::sync::broadcast::Sender<GlobalCtxEvent>;
type EventBusSubscriber = tokio::sync::broadcast::Receiver<GlobalCtxEvent>; pub type EventBusSubscriber = tokio::sync::broadcast::Receiver<GlobalCtxEvent>;
pub struct GlobalCtx { pub struct GlobalCtx {
pub inst_name: String, pub inst_name: String,

View File

@ -19,6 +19,7 @@ mod common;
mod connector; mod connector;
mod gateway; mod gateway;
mod instance; mod instance;
mod launcher;
mod peer_center; mod peer_center;
mod peers; mod peers;
mod proto; mod proto;
@ -29,8 +30,9 @@ mod vpn_portal;
use common::{ use common::{
config::{ConsoleLoggerConfig, FileLoggerConfig, NetworkIdentity, PeerConfig, VpnPortalConfig}, config::{ConsoleLoggerConfig, FileLoggerConfig, NetworkIdentity, PeerConfig, VpnPortalConfig},
constants::EASYTIER_VERSION, constants::EASYTIER_VERSION,
global_ctx::EventBusSubscriber,
scoped_task::ScopedTask,
}; };
use instance::instance::Instance;
use tokio::net::TcpSocket; use tokio::net::TcpSocket;
use utils::setup_panic_handler; use utils::setup_panic_handler;
@ -525,6 +527,7 @@ impl From<Cli> for TomlConfigLoader {
.with_context(|| format!("failed to parse ipv6 listener: {}", ipv6_listener)) .with_context(|| format!("failed to parse ipv6 listener: {}", ipv6_listener))
.unwrap(); .unwrap();
} }
f.multi_thread = cli.multi_thread;
cfg.set_flags(f); cfg.set_flags(f);
cfg.set_exit_nodes(cli.exit_nodes.clone()); cfg.set_exit_nodes(cli.exit_nodes.clone());
@ -549,13 +552,7 @@ fn peer_conn_info_to_string(p: crate::proto::cli::PeerConnInfo) -> String {
} }
#[tracing::instrument] #[tracing::instrument]
pub async fn async_main(cli: Cli) { pub fn handle_event(mut events: EventBusSubscriber) -> tokio::task::JoinHandle<()> {
let cfg: TomlConfigLoader = cli.into();
init_logger(&cfg, false).unwrap();
let mut inst = Instance::new(cfg.clone());
let mut events = inst.get_global_ctx().subscribe();
tokio::spawn(async move { tokio::spawn(async move {
while let Ok(e) = events.recv().await { while let Ok(e) = events.recv().await {
match e { match e {
@ -658,39 +655,28 @@ pub async fn async_main(cli: Cli) {
} }
} }
} }
}); })
println!("Starting easytier with config:");
println!("############### TOML ###############\n");
println!("{}", cfg.dump());
println!("-----------------------------------");
inst.run().await.unwrap();
inst.wait().await;
} }
fn main() { #[tokio::main]
async fn main() {
setup_panic_handler(); setup_panic_handler();
let locale = sys_locale::get_locale().unwrap_or_else(|| String::from("en-US")); let locale = sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"));
rust_i18n::set_locale(&locale); rust_i18n::set_locale(&locale);
let cli = Cli::parse(); let cli = Cli::parse();
tracing::info!(cli = ?cli, "cli args parsed"); let cfg = TomlConfigLoader::from(cli);
init_logger(&cfg, false).unwrap();
if cli.multi_thread { println!("Starting easytier with config:");
tokio::runtime::Builder::new_multi_thread() println!("############### TOML ###############\n");
.worker_threads(2) println!("{}", cfg.dump());
.enable_all() println!("-----------------------------------");
.build()
.unwrap() let mut l = launcher::NetworkInstance::new(cfg).set_fetch_node_info(false);
.block_on(async move { async_main(cli).await }) let _t = ScopedTask::from(handle_event(l.start().unwrap()));
} else { if let Some(e) = l.wait().await {
tokio::runtime::Builder::new_current_thread() panic!("launcher error: {:?}", e);
.enable_all()
.build()
.unwrap()
.block_on(async move { async_main(cli).await })
} }
} }

View File

@ -7,7 +7,7 @@ use crate::{
common::{ common::{
config::{ConfigLoader, TomlConfigLoader}, config::{ConfigLoader, TomlConfigLoader},
constants::EASYTIER_VERSION, constants::EASYTIER_VERSION,
global_ctx::GlobalCtxEvent, global_ctx::{EventBusSubscriber, GlobalCtxEvent},
stun::StunInfoCollectorTrait, stun::StunInfoCollectorTrait,
}, },
instance::instance::Instance, instance::instance::Instance,
@ -21,7 +21,7 @@ use crate::{
}; };
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::task::JoinSet; use tokio::{sync::broadcast, task::JoinSet};
#[derive(Default, Clone, Debug, Serialize, Deserialize)] #[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct MyNodeInfo { pub struct MyNodeInfo {
@ -34,14 +34,31 @@ pub struct MyNodeInfo {
pub vpn_portal_cfg: Option<String>, pub vpn_portal_cfg: Option<String>,
} }
#[derive(Default, Clone)]
struct EasyTierData { struct EasyTierData {
events: Arc<RwLock<VecDeque<(DateTime<Local>, GlobalCtxEvent)>>>, events: RwLock<VecDeque<(DateTime<Local>, GlobalCtxEvent)>>,
node_info: Arc<RwLock<MyNodeInfo>>, node_info: RwLock<MyNodeInfo>,
routes: Arc<RwLock<Vec<Route>>>, routes: RwLock<Vec<Route>>,
peers: Arc<RwLock<Vec<PeerInfo>>>, peers: RwLock<Vec<PeerInfo>>,
tun_fd: Arc<RwLock<Option<i32>>>, tun_fd: Arc<RwLock<Option<i32>>>,
tun_dev_name: Arc<RwLock<String>>, tun_dev_name: RwLock<String>,
event_subscriber: RwLock<broadcast::Sender<GlobalCtxEvent>>,
instance_stop_notifier: Arc<tokio::sync::Notify>,
}
impl Default for EasyTierData {
fn default() -> Self {
let (tx, _) = broadcast::channel(100);
Self {
event_subscriber: RwLock::new(tx),
events: RwLock::new(VecDeque::new()),
node_info: RwLock::new(MyNodeInfo::default()),
routes: RwLock::new(Vec::new()),
peers: RwLock::new(Vec::new()),
tun_fd: Arc::new(RwLock::new(None)),
tun_dev_name: RwLock::new(String::new()),
instance_stop_notifier: Arc::new(tokio::sync::Notify::new()),
}
}
} }
pub struct EasyTierLauncher { pub struct EasyTierLauncher {
@ -49,27 +66,30 @@ pub struct EasyTierLauncher {
stop_flag: Arc<AtomicBool>, stop_flag: Arc<AtomicBool>,
thread_handle: Option<std::thread::JoinHandle<()>>, thread_handle: Option<std::thread::JoinHandle<()>>,
running_cfg: String, running_cfg: String,
fetch_node_info: bool,
error_msg: Arc<RwLock<Option<String>>>, error_msg: Arc<RwLock<Option<String>>>,
data: EasyTierData, data: Arc<EasyTierData>,
} }
impl EasyTierLauncher { impl EasyTierLauncher {
pub fn new() -> Self { pub fn new(fetch_node_info: bool) -> Self {
let instance_alive = Arc::new(AtomicBool::new(false)); let instance_alive = Arc::new(AtomicBool::new(false));
Self { Self {
instance_alive, instance_alive,
thread_handle: None, thread_handle: None,
error_msg: Arc::new(RwLock::new(None)), error_msg: Arc::new(RwLock::new(None)),
running_cfg: String::new(), running_cfg: String::new(),
fetch_node_info,
stop_flag: Arc::new(AtomicBool::new(false)), stop_flag: Arc::new(AtomicBool::new(false)),
data: EasyTierData::default(), data: Arc::new(EasyTierData::default()),
} }
} }
async fn handle_easytier_event(event: GlobalCtxEvent, data: EasyTierData) { async fn handle_easytier_event(event: GlobalCtxEvent, data: &EasyTierData) {
let mut events = data.events.write().unwrap(); let mut events = data.events.write().unwrap();
let _ = data.event_subscriber.read().unwrap().send(event.clone());
events.push_back((chrono::Local::now(), event)); events.push_back((chrono::Local::now(), event));
if events.len() > 100 { if events.len() > 100 {
events.pop_front(); events.pop_front();
@ -113,7 +133,8 @@ impl EasyTierLauncher {
async fn easytier_routine( async fn easytier_routine(
cfg: TomlConfigLoader, cfg: TomlConfigLoader,
stop_signal: Arc<tokio::sync::Notify>, stop_signal: Arc<tokio::sync::Notify>,
data: EasyTierData, data: Arc<EasyTierData>,
fetch_node_info: bool,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
let mut instance = Instance::new(cfg); let mut instance = Instance::new(cfg);
let peer_mgr = instance.get_peer_manager(); let peer_mgr = instance.get_peer_manager();
@ -126,11 +147,12 @@ impl EasyTierLauncher {
tasks.spawn(async move { tasks.spawn(async move {
let mut receiver = global_ctx.subscribe(); let mut receiver = global_ctx.subscribe();
while let Ok(event) = receiver.recv().await { while let Ok(event) = receiver.recv().await {
Self::handle_easytier_event(event, data_c.clone()).await; Self::handle_easytier_event(event, &data_c).await;
} }
}); });
// update my node info // update my node info
if fetch_node_info {
let data_c = data.clone(); let data_c = data.clone();
let global_ctx_c = instance.get_global_ctx(); let global_ctx_c = instance.get_global_ctx();
let peer_mgr_c = peer_mgr.clone(); let peer_mgr_c = peer_mgr.clone();
@ -138,7 +160,8 @@ impl EasyTierLauncher {
tasks.spawn(async move { tasks.spawn(async move {
loop { loop {
// Update TUN Device Name // Update TUN Device Name
*data_c.tun_dev_name.write().unwrap() = global_ctx_c.get_flags().dev_name.clone(); *data_c.tun_dev_name.write().unwrap() =
global_ctx_c.get_flags().dev_name.clone();
let node_info = MyNodeInfo { let node_info = MyNodeInfo {
virtual_ipv4: global_ctx_c virtual_ipv4: global_ctx_c
@ -170,6 +193,7 @@ impl EasyTierLauncher {
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
} }
}); });
}
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
Self::run_routine_for_android(&instance, &data, &mut tasks).await; Self::run_routine_for_android(&instance, &data, &mut tasks).await;
@ -188,13 +212,15 @@ impl EasyTierLauncher {
F: FnOnce() -> Result<TomlConfigLoader, anyhow::Error> + Send + Sync, F: FnOnce() -> Result<TomlConfigLoader, anyhow::Error> + Send + Sync,
{ {
let error_msg = self.error_msg.clone(); let error_msg = self.error_msg.clone();
let cfg = cfg_generator(); let cfg = match cfg_generator() {
if let Err(e) = cfg { Err(e) => {
error_msg.write().unwrap().replace(e.to_string()); error_msg.write().unwrap().replace(e.to_string());
return; return;
} }
Ok(cfg) => cfg,
};
self.running_cfg = cfg.as_ref().unwrap().dump(); self.running_cfg = cfg.dump();
let stop_flag = self.stop_flag.clone(); let stop_flag = self.stop_flag.clone();
@ -202,12 +228,21 @@ impl EasyTierLauncher {
instance_alive.store(true, std::sync::atomic::Ordering::Relaxed); instance_alive.store(true, std::sync::atomic::Ordering::Relaxed);
let data = self.data.clone(); let data = self.data.clone();
let fetch_node_info = self.fetch_node_info;
self.thread_handle = Some(std::thread::spawn(move || { self.thread_handle = Some(std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = if cfg.get_flags().multi_thread {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all() .enable_all()
.build() .build()
} else {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
}
.unwrap(); .unwrap();
let stop_notifier = Arc::new(tokio::sync::Notify::new()); let stop_notifier = Arc::new(tokio::sync::Notify::new());
let stop_notifier_clone = stop_notifier.clone(); let stop_notifier_clone = stop_notifier.clone();
@ -218,15 +253,18 @@ impl EasyTierLauncher {
stop_notifier_clone.notify_one(); stop_notifier_clone.notify_one();
}); });
let notifier = data.instance_stop_notifier.clone();
let ret = rt.block_on(Self::easytier_routine( let ret = rt.block_on(Self::easytier_routine(
cfg.unwrap(), cfg,
stop_notifier.clone(), stop_notifier.clone(),
data, data,
fetch_node_info,
)); ));
if let Err(e) = ret { if let Err(e) = ret {
error_msg.write().unwrap().replace(e.to_string()); error_msg.write().unwrap().replace(e.to_string());
} }
instance_alive.store(false, std::sync::atomic::Ordering::Relaxed); instance_alive.store(false, std::sync::atomic::Ordering::Relaxed);
notifier.notify_one();
})); }));
} }
@ -289,6 +327,8 @@ pub struct NetworkInstanceRunningInfo {
pub struct NetworkInstance { pub struct NetworkInstance {
config: TomlConfigLoader, config: TomlConfigLoader,
launcher: Option<EasyTierLauncher>, launcher: Option<EasyTierLauncher>,
fetch_node_info: bool,
} }
impl NetworkInstance { impl NetworkInstance {
@ -296,9 +336,15 @@ impl NetworkInstance {
Self { Self {
config, config,
launcher: None, launcher: None,
fetch_node_info: true,
} }
} }
pub fn set_fetch_node_info(mut self, fetch_node_info: bool) -> Self {
self.fetch_node_info = fetch_node_info;
self
}
pub fn is_easytier_running(&self) -> bool { pub fn is_easytier_running(&self) -> bool {
self.launcher.is_some() && self.launcher.as_ref().unwrap().running() self.launcher.is_some() && self.launcher.as_ref().unwrap().running()
} }
@ -333,15 +379,37 @@ impl NetworkInstance {
} }
} }
pub fn start(&mut self) -> Result<(), anyhow::Error> { pub fn start(&mut self) -> Result<EventBusSubscriber, anyhow::Error> {
if self.is_easytier_running() { if self.is_easytier_running() {
return Ok(()); return Ok(self.subscribe_event().unwrap());
} }
let mut launcher = EasyTierLauncher::new(); let launcher = EasyTierLauncher::new(self.fetch_node_info);
launcher.start(|| Ok(self.config.clone()));
self.launcher = Some(launcher); self.launcher = Some(launcher);
Ok(()) let ev = self.subscribe_event().unwrap();
self.launcher
.as_mut()
.unwrap()
.start(|| Ok(self.config.clone()));
Ok(ev)
}
fn subscribe_event(&self) -> Option<broadcast::Receiver<GlobalCtxEvent>> {
if let Some(launcher) = self.launcher.as_ref() {
Some(launcher.data.event_subscriber.read().unwrap().subscribe())
} else {
None
}
}
pub async fn wait(&self) -> Option<String> {
if let Some(launcher) = self.launcher.as_ref() {
launcher.data.instance_stop_notifier.notified().await;
launcher.error_msg.read().unwrap().clone()
} else {
None
}
} }
} }

View File

@ -6,11 +6,11 @@ mod gateway;
mod instance; mod instance;
mod peer_center; mod peer_center;
mod peers; mod peers;
mod proto;
mod vpn_portal; mod vpn_portal;
pub mod common; pub mod common;
pub mod launcher; pub mod launcher;
pub mod proto;
pub mod tunnel; pub mod tunnel;
pub mod utils; pub mod utils;

View File

@ -1,12 +1,12 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use futures::StreamExt; use futures::{SinkExt as _, StreamExt};
use tokio::task::JoinSet; use tokio::task::JoinSet;
use crate::{ use crate::{
common::{error::Error, PeerId}, common::{error::Error, PeerId},
proto::rpc_impl, proto::rpc_impl::{self, bidirect::BidirectRpcManager},
tunnel::packet_def::{PacketType, ZCPacket}, tunnel::packet_def::ZCPacket,
}; };
const RPC_PACKET_CONTENT_MTU: usize = 1300; const RPC_PACKET_CONTENT_MTU: usize = 1300;
@ -25,9 +25,7 @@ pub trait PeerRpcManagerTransport: Send + Sync + 'static {
// handle rpc request from one peer // handle rpc request from one peer
pub struct PeerRpcManager { pub struct PeerRpcManager {
tspt: Arc<Box<dyn PeerRpcManagerTransport>>, tspt: Arc<Box<dyn PeerRpcManagerTransport>>,
rpc_client: rpc_impl::client::Client, bidirect_rpc: BidirectRpcManager,
rpc_server: rpc_impl::server::Server,
tasks: Arc<Mutex<JoinSet<()>>>, tasks: Arc<Mutex<JoinSet<()>>>,
} }
@ -43,78 +41,41 @@ impl PeerRpcManager {
pub fn new(tspt: impl PeerRpcManagerTransport) -> Self { pub fn new(tspt: impl PeerRpcManagerTransport) -> Self {
Self { Self {
tspt: Arc::new(Box::new(tspt)), tspt: Arc::new(Box::new(tspt)),
rpc_client: rpc_impl::client::Client::new(), bidirect_rpc: BidirectRpcManager::new(),
rpc_server: rpc_impl::server::Server::new(),
tasks: Arc::new(Mutex::new(JoinSet::new())), tasks: Arc::new(Mutex::new(JoinSet::new())),
} }
} }
pub fn run(&self) { pub fn run(&self) {
self.rpc_client.run(); let ret = self.bidirect_rpc.run_and_create_tunnel();
self.rpc_server.run(); let (mut rx, mut tx) = ret.split();
let (server_tx, mut server_rx) = (
self.rpc_server.get_transport_sink(),
self.rpc_server.get_transport_stream(),
);
let (client_tx, mut client_rx) = (
self.rpc_client.get_transport_sink(),
self.rpc_client.get_transport_stream(),
);
let tspt = self.tspt.clone(); let tspt = self.tspt.clone();
self.tasks.lock().unwrap().spawn(async move { self.tasks.lock().unwrap().spawn(async move {
loop { while let Some(Ok(packet)) = rx.next().await {
let packet = tokio::select! {
Some(Ok(packet)) = server_rx.next() => {
tracing::trace!(?packet, "recv rpc packet from server");
packet
}
Some(Ok(packet)) = client_rx.next() => {
tracing::trace!(?packet, "recv rpc packet from client");
packet
}
else => {
tracing::warn!("rpc transport read aborted, exiting");
break;
}
};
let dst_peer_id = packet.peer_manager_header().unwrap().to_peer_id.into(); let dst_peer_id = packet.peer_manager_header().unwrap().to_peer_id.into();
if let Err(e) = tspt.send(packet, dst_peer_id).await { if let Err(e) = tspt.send(packet, dst_peer_id).await {
tracing::error!(error = ?e, dst_peer_id = ?dst_peer_id, "send to peer failed"); tracing::error!("send to rpc tspt error: {:?}", e);
} }
} }
}); });
let tspt = self.tspt.clone(); let tspt = self.tspt.clone();
self.tasks.lock().unwrap().spawn(async move { self.tasks.lock().unwrap().spawn(async move {
loop { while let Ok(packet) = tspt.recv().await {
let Ok(o) = tspt.recv().await else { if let Err(e) = tx.send(packet).await {
tracing::warn!("peer rpc transport read aborted, exiting"); tracing::error!("send to rpc tspt error: {:?}", e);
break;
};
if o.peer_manager_header().unwrap().packet_type == PacketType::RpcReq as u8 {
server_tx.send(o).await.unwrap();
continue;
} else if o.peer_manager_header().unwrap().packet_type == PacketType::RpcResp as u8
{
client_tx.send(o).await.unwrap();
continue;
} }
} }
}); });
} }
pub fn rpc_client(&self) -> &rpc_impl::client::Client { pub fn rpc_client(&self) -> &rpc_impl::client::Client {
&self.rpc_client self.bidirect_rpc.rpc_client()
} }
pub fn rpc_server(&self) -> &rpc_impl::server::Server { pub fn rpc_server(&self) -> &rpc_impl::server::Server {
&self.rpc_server self.bidirect_rpc.rpc_server()
} }
pub fn my_peer_id(&self) -> PeerId { pub fn my_peer_id(&self) -> PeerId {

View File

@ -0,0 +1,164 @@
use std::sync::{Arc, Mutex};
use futures::{SinkExt as _, StreamExt};
use tokio::{task::JoinSet, time::timeout};
use crate::{
proto::rpc_types::error::Error,
tunnel::{packet_def::PacketType, ring::create_ring_tunnel_pair, Tunnel},
};
use super::{client::Client, server::Server};
pub struct BidirectRpcManager {
rpc_client: Client,
rpc_server: Server,
rx_timeout: Option<std::time::Duration>,
error: Arc<Mutex<Option<Error>>>,
tunnel: Mutex<Option<Box<dyn Tunnel>>>,
tasks: Mutex<Option<JoinSet<()>>>,
}
impl BidirectRpcManager {
pub fn new() -> Self {
Self {
rpc_client: Client::new(),
rpc_server: Server::new(),
rx_timeout: None,
error: Arc::new(Mutex::new(None)),
tunnel: Mutex::new(None),
tasks: Mutex::new(None),
}
}
pub fn set_rx_timeout(mut self, timeout: Option<std::time::Duration>) -> Self {
self.rx_timeout = timeout;
self
}
pub fn run_and_create_tunnel(&self) -> Box<dyn Tunnel> {
let (ret, inner) = create_ring_tunnel_pair();
self.run_with_tunnel(inner);
ret
}
pub fn run_with_tunnel(&self, inner: Box<dyn Tunnel>) {
let mut tasks = JoinSet::new();
self.rpc_client.run();
self.rpc_server.run();
let (server_tx, mut server_rx) = (
self.rpc_server.get_transport_sink(),
self.rpc_server.get_transport_stream(),
);
let (client_tx, mut client_rx) = (
self.rpc_client.get_transport_sink(),
self.rpc_client.get_transport_stream(),
);
let (mut inner_rx, mut inner_tx) = inner.split();
self.tunnel.lock().unwrap().replace(inner);
let e_clone = self.error.clone();
tasks.spawn(async move {
loop {
let packet = tokio::select! {
Some(Ok(packet)) = server_rx.next() => {
tracing::trace!(?packet, "recv rpc packet from server");
packet
}
Some(Ok(packet)) = client_rx.next() => {
tracing::trace!(?packet, "recv rpc packet from client");
packet
}
else => {
tracing::warn!("rpc transport read aborted, exiting");
break;
}
};
if let Err(e) = inner_tx.send(packet).await {
tracing::error!(error = ?e, "send to peer failed");
e_clone.lock().unwrap().replace(Error::from(e));
}
}
});
let recv_timeout = self.rx_timeout;
let e_clone = self.error.clone();
tasks.spawn(async move {
loop {
let ret = if let Some(recv_timeout) = recv_timeout {
match timeout(recv_timeout, inner_rx.next()).await {
Ok(ret) => ret,
Err(e) => {
e_clone.lock().unwrap().replace(e.into());
break;
}
}
} else {
inner_rx.next().await
};
let o = match ret {
Some(Ok(o)) => o,
Some(Err(e)) => {
tracing::error!(error = ?e, "recv from peer failed");
e_clone.lock().unwrap().replace(Error::from(e));
break;
}
None => {
tracing::warn!("peer rpc transport read aborted, exiting");
e_clone.lock().unwrap().replace(Error::Shutdown);
break;
}
};
if o.peer_manager_header().unwrap().packet_type == PacketType::RpcReq as u8 {
server_tx.send(o).await.unwrap();
continue;
} else if o.peer_manager_header().unwrap().packet_type == PacketType::RpcResp as u8
{
client_tx.send(o).await.unwrap();
continue;
}
}
});
self.tasks.lock().unwrap().replace(tasks);
}
pub fn rpc_client(&self) -> &Client {
&self.rpc_client
}
pub fn rpc_server(&self) -> &Server {
&self.rpc_server
}
pub async fn stop(&self) {
let Some(mut tasks) = self.tasks.lock().unwrap().take() else {
return;
};
tasks.abort_all();
while let Some(_) = tasks.join_next().await {}
}
pub fn take_error(&self) -> Option<Error> {
self.error.lock().unwrap().take()
}
pub async fn wait(&self) {
let Some(mut tasks) = self.tasks.lock().unwrap().take() else {
return;
};
while let Some(_) = tasks.join_next().await {
// when any task is done, abort all tasks
tasks.abort_all();
}
}
}

View File

@ -2,6 +2,7 @@ use crate::tunnel::{mpsc::MpscTunnel, Tunnel};
pub type RpcController = super::rpc_types::controller::BaseController; pub type RpcController = super::rpc_types::controller::BaseController;
pub mod bidirect;
pub mod client; pub mod client;
pub mod packet; pub mod packet;
pub mod server; pub mod server;

View File

@ -59,6 +59,14 @@ impl ServiceRegistry {
} }
} }
pub fn replace_registry(&self, registry: &ServiceRegistry) {
self.table.clear();
for item in registry.table.iter() {
let (k, v) = item.pair();
self.table.insert(k.clone(), v.clone());
}
}
pub fn register<H: Handler<Controller = RpcController>>(&self, h: H, domain_name: &str) { pub fn register<H: Handler<Controller = RpcController>>(&self, h: H, domain_name: &str) {
let desc = h.service_descriptor(); let desc = h.service_descriptor();
let key = ServiceKey { let key = ServiceKey {

View File

@ -4,66 +4,18 @@ use std::{
}; };
use anyhow::Context as _; use anyhow::Context as _;
use futures::{SinkExt as _, StreamExt};
use tokio::task::JoinSet; use tokio::task::JoinSet;
use crate::{ use crate::{
common::join_joinset_background, common::join_joinset_background,
proto::rpc_types::{__rt::RpcClientFactory, error::Error}, proto::{
rpc_impl::bidirect::BidirectRpcManager,
rpc_types::{__rt::RpcClientFactory, error::Error},
},
tunnel::{Tunnel, TunnelConnector, TunnelListener}, tunnel::{Tunnel, TunnelConnector, TunnelListener},
}; };
use super::{client::Client, server::Server, service_registry::ServiceRegistry}; use super::service_registry::ServiceRegistry;
struct StandAloneServerOneTunnel {
tunnel: Box<dyn Tunnel>,
rpc_server: Server,
}
impl StandAloneServerOneTunnel {
pub fn new(tunnel: Box<dyn Tunnel>, registry: Arc<ServiceRegistry>) -> Self {
let rpc_server = Server::new_with_registry(registry);
StandAloneServerOneTunnel { tunnel, rpc_server }
}
pub async fn run(self) {
use tokio_stream::StreamExt as _;
let (tunnel_rx, tunnel_tx) = self.tunnel.split();
let (rpc_rx, rpc_tx) = (
self.rpc_server.get_transport_stream(),
self.rpc_server.get_transport_sink(),
);
let mut tasks = JoinSet::new();
tasks.spawn(async move {
let ret = tunnel_rx.timeout(Duration::from_secs(60));
tokio::pin!(ret);
while let Ok(Some(Ok(p))) = ret.try_next().await {
if let Err(e) = rpc_tx.send(p).await {
tracing::error!("tunnel_rx send to rpc_tx error: {:?}", e);
break;
}
}
tracing::info!("forward tunnel_rx to rpc_tx done");
});
tasks.spawn(async move {
let ret = rpc_rx.forward(tunnel_tx).await;
tracing::info!("rpc_rx forward tunnel_tx done: {:?}", ret);
});
self.rpc_server.run();
while let Some(ret) = tasks.join_next().await {
self.rpc_server.close();
tracing::info!("task done: {:?}", ret);
}
tracing::info!("all tasks done");
}
}
pub struct StandAloneServer<L> { pub struct StandAloneServer<L> {
registry: Arc<ServiceRegistry>, registry: Arc<ServiceRegistry>,
@ -102,11 +54,15 @@ impl<L: TunnelListener + 'static> StandAloneServer<L> {
self.tasks.lock().unwrap().spawn(async move { self.tasks.lock().unwrap().spawn(async move {
while let Ok(tunnel) = listener.accept().await { while let Ok(tunnel) = listener.accept().await {
let server = StandAloneServerOneTunnel::new(tunnel, registry.clone()); let registry = registry.clone();
let inflight_server = inflight_server.clone(); let inflight_server = inflight_server.clone();
inflight_server.fetch_add(1, std::sync::atomic::Ordering::Relaxed); inflight_server.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
tasks.lock().unwrap().spawn(async move { tasks.lock().unwrap().spawn(async move {
server.run().await; let server =
BidirectRpcManager::new().set_rx_timeout(Some(Duration::from_secs(60)));
server.rpc_server().registry().replace_registry(&registry);
server.run_with_tunnel(tunnel);
server.wait().await;
inflight_server.fetch_sub(1, std::sync::atomic::Ordering::Relaxed); inflight_server.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
}); });
} }
@ -122,86 +78,9 @@ impl<L: TunnelListener + 'static> StandAloneServer<L> {
} }
} }
struct StandAloneClientOneTunnel {
rpc_client: Client,
tasks: Arc<Mutex<JoinSet<()>>>,
error: Arc<Mutex<Option<Error>>>,
}
impl StandAloneClientOneTunnel {
pub fn new(tunnel: Box<dyn Tunnel>) -> Self {
let rpc_client = Client::new();
let (mut rpc_rx, rpc_tx) = (
rpc_client.get_transport_stream(),
rpc_client.get_transport_sink(),
);
let tasks = Arc::new(Mutex::new(JoinSet::new()));
let (mut tunnel_rx, mut tunnel_tx) = tunnel.split();
let error_store = Arc::new(Mutex::new(None));
let error = error_store.clone();
tasks.lock().unwrap().spawn(async move {
while let Some(p) = rpc_rx.next().await {
match p {
Ok(p) => {
if let Err(e) = tunnel_tx
.send(p)
.await
.with_context(|| "failed to send packet")
{
*error.lock().unwrap() = Some(e.into());
}
}
Err(e) => {
*error.lock().unwrap() = Some(anyhow::Error::from(e).into());
}
}
}
*error.lock().unwrap() = Some(anyhow::anyhow!("rpc_rx next exit").into());
});
let error = error_store.clone();
tasks.lock().unwrap().spawn(async move {
while let Some(p) = tunnel_rx.next().await {
match p {
Ok(p) => {
if let Err(e) = rpc_tx
.send(p)
.await
.with_context(|| "failed to send packet")
{
*error.lock().unwrap() = Some(e.into());
}
}
Err(e) => {
*error.lock().unwrap() = Some(anyhow::Error::from(e).into());
}
}
}
*error.lock().unwrap() = Some(anyhow::anyhow!("tunnel_rx next exit").into());
});
rpc_client.run();
StandAloneClientOneTunnel {
rpc_client,
tasks,
error: error_store,
}
}
pub fn take_error(&self) -> Option<Error> {
self.error.lock().unwrap().take()
}
}
pub struct StandAloneClient<C: TunnelConnector> { pub struct StandAloneClient<C: TunnelConnector> {
connector: C, connector: C,
client: Option<StandAloneClientOneTunnel>, client: Option<BidirectRpcManager>,
} }
impl<C: TunnelConnector> StandAloneClient<C> { impl<C: TunnelConnector> StandAloneClient<C> {
@ -230,7 +109,9 @@ impl<C: TunnelConnector> StandAloneClient<C> {
if c.is_none() || error.is_some() { if c.is_none() || error.is_some() {
tracing::info!("reconnect due to error: {:?}", error); tracing::info!("reconnect due to error: {:?}", error);
let tunnel = self.connect().await?; let tunnel = self.connect().await?;
c = Some(StandAloneClientOneTunnel::new(tunnel)); let mgr = BidirectRpcManager::new().set_rx_timeout(Some(Duration::from_secs(60)));
mgr.run_with_tunnel(tunnel);
c = Some(mgr);
} }
self.client = c; self.client = c;
@ -239,7 +120,7 @@ impl<C: TunnelConnector> StandAloneClient<C> {
.client .client
.as_ref() .as_ref()
.unwrap() .unwrap()
.rpc_client .rpc_client()
.scoped_client::<F>(1, 1, domain_name)) .scoped_client::<F>(1, 1, domain_name))
} }
} }

View File

@ -29,6 +29,9 @@ pub enum Error {
#[error("Tunnel error: {0}")] #[error("Tunnel error: {0}")]
TunnelError(#[from] crate::tunnel::TunnelError), TunnelError(#[from] crate::tunnel::TunnelError),
#[error("Shutdown")]
Shutdown,
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;

View File

@ -300,3 +300,75 @@ async fn standalone_rpc_test() {
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
assert_eq!(0, server.inflight_server()); assert_eq!(0, server.inflight_server());
} }
#[tokio::test]
async fn test_bidirect_rpc_manager() {
use crate::common::scoped_task::ScopedTask;
use crate::proto::rpc_impl::bidirect::BidirectRpcManager;
use crate::tunnel::tcp::{TcpTunnelConnector, TcpTunnelListener};
use crate::tunnel::{TunnelConnector, TunnelListener};
let c = BidirectRpcManager::new();
let s = BidirectRpcManager::new();
let service = GreetingServer::new(GreetingService {
delay_ms: 0,
prefix: "Hello Client".to_string(),
});
c.rpc_server().registry().register(service, "test");
let service = GreetingServer::new(GreetingService {
delay_ms: 0,
prefix: "Hello Server".to_string(),
});
s.rpc_server().registry().register(service, "test");
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();
let tunnel = tcp_listener.accept().await.unwrap();
s.run_with_tunnel(tunnel);
let s_c = s
.rpc_client()
.scoped_client::<GreetingClientFactory<RpcController>>(1, 1, "test".to_string());
let ret = s_c
.say_hello(
RpcController::default(),
SayHelloRequest {
name: "world".to_string(),
},
)
.await
.unwrap();
assert_eq!(ret.greeting, "Hello Client world!");
println!("server done, {:?}", ret);
s.wait().await;
})
.into();
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let mut tcp_connector = TcpTunnelConnector::new("tcp://0.0.0.0:55443".parse().unwrap());
let c_tunnel = tcp_connector.connect().await.unwrap();
c.run_with_tunnel(c_tunnel);
let c_c = c
.rpc_client()
.scoped_client::<GreetingClientFactory<RpcController>>(1, 1, "test".to_string());
let ret = c_c
.say_hello(
RpcController::default(),
SayHelloRequest {
name: "world".to_string(),
},
)
.await
.unwrap();
assert_eq!(ret.greeting, "Hello Server world!");
println!("client done, {:?}", ret);
drop(c);
s_task.await.unwrap();
}

View File

@ -789,7 +789,7 @@ pub async fn manual_reconnector(#[values(true, false)] is_foreign: bool) {
.await .await
.unwrap(); .unwrap();
assert_eq!(1, conns.len()); assert!(conns.len() >= 1);
wait_for_condition( wait_for_condition(
|| async { ping_test("net_b", "10.144.145.2", None).await }, || async { ping_test("net_b", "10.144.145.2", None).await },