Compare commits
6 Commits
v2.0.2
...
releases/v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
861d658405 | ||
|
|
d87a440c04 | ||
|
|
55efd62798 | ||
|
|
70a41275c1 | ||
|
|
dd941681ce | ||
|
|
9824d0adaa |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -21,7 +21,7 @@ on:
|
||||
version:
|
||||
description: 'Version for this release'
|
||||
type: string
|
||||
default: 'v2.0.2'
|
||||
default: 'v2.0.3'
|
||||
required: true
|
||||
make_latest:
|
||||
description: 'Mark this release as latest'
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -1539,7 +1539,7 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||
|
||||
[[package]]
|
||||
name = "easytier"
|
||||
version = "2.0.2"
|
||||
version = "2.0.3"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
@@ -1631,7 +1631,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "easytier-gui"
|
||||
version = "2.0.2"
|
||||
version = "2.0.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
||||
@@ -4,84 +4,23 @@
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"name": "gui",
|
||||
"path": "easytier-gui"
|
||||
},
|
||||
{
|
||||
"name": "core",
|
||||
"path": "easytier"
|
||||
},
|
||||
{
|
||||
"name": "vpnservice",
|
||||
"path": "tauri-plugin-vpnservice"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"eslint.useFlatConfig": true,
|
||||
"i18n-ally.sourceLanguage": "cn",
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
// Disable the default formatter
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"eslint.rules.customizations": [
|
||||
{
|
||||
"rule": "style/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "style/eol-last",
|
||||
"severity": "error"
|
||||
},
|
||||
{
|
||||
"rule": "format/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-indent",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spacing",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spaces",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-order",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-dangle",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-newline",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*quotes",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*semi",
|
||||
"severity": "off"
|
||||
}
|
||||
],
|
||||
"eslint.validate": [
|
||||
"code-workspace",
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"gql",
|
||||
"graphql"
|
||||
],
|
||||
"i18n-ally.localesPaths": [
|
||||
"easytier-gui/locales"
|
||||
]
|
||||
}
|
||||
}
|
||||
82
easytier-gui/.vscode/settings.json
vendored
82
easytier-gui/.vscode/settings.json
vendored
@@ -1,5 +1,81 @@
|
||||
{
|
||||
"i18n-ally.localesPaths": [
|
||||
"locales"
|
||||
"cSpell.words": [
|
||||
"easytier",
|
||||
"Vite",
|
||||
"vueuse",
|
||||
"pinia",
|
||||
"demi",
|
||||
"antfu",
|
||||
"iconify",
|
||||
"intlify",
|
||||
"vitejs",
|
||||
"unplugin",
|
||||
"pnpm"
|
||||
],
|
||||
"i18n-ally.localesPaths": "locales",
|
||||
"editor.formatOnSave": false,
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{
|
||||
"rule": "style/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "format/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-indent",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spacing",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spaces",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-order",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-dangle",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-newline",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*quotes",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*semi",
|
||||
"severity": "off"
|
||||
}
|
||||
],
|
||||
// The following is optional.
|
||||
// It's better to put under project setting `.vscode/settings.json`
|
||||
// to avoid conflicts with working with different eslint configs
|
||||
// that does not support all formats.
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,8 @@ loss_rate: 丢包率
|
||||
status:
|
||||
version: 内核版本
|
||||
local: 本机
|
||||
server: 服务器
|
||||
relay: 中继
|
||||
|
||||
run_network: 运行网络
|
||||
stop_network: 停止网络
|
||||
@@ -91,3 +93,23 @@ about:
|
||||
license: 许可证
|
||||
description: 一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
|
||||
check_update: 检查更新
|
||||
|
||||
event:
|
||||
Unknown: 未知
|
||||
TunDeviceReady: Tun设备就绪
|
||||
TunDeviceError: Tun设备错误
|
||||
PeerAdded: 对端添加
|
||||
PeerRemoved: 对端移除
|
||||
PeerConnAdded: 对端连接添加
|
||||
PeerConnRemoved: 对端连接移除
|
||||
ListenerAdded: 监听器添加
|
||||
ListenerAddFailed: 监听器添加失败
|
||||
ListenerAcceptFailed: 监听器接受连接失败
|
||||
ConnectionAccepted: 连接已接受
|
||||
ConnectionError: 连接错误
|
||||
Connecting: 正在连接
|
||||
ConnectError: 连接错误
|
||||
VpnPortalClientConnected: VPN门户客户端已连接
|
||||
VpnPortalClientDisconnected: VPN门户客户端已断开连接
|
||||
DhcpIpv4Changed: DHCP IPv4地址更改
|
||||
DhcpIpv4Conflicted: DHCP IPv4地址冲突
|
||||
|
||||
@@ -71,6 +71,8 @@ loss_rate: Loss Rate
|
||||
status:
|
||||
version: Version
|
||||
local: Local
|
||||
server: Server
|
||||
relay: Relay
|
||||
|
||||
run_network: Run Network
|
||||
stop_network: Stop Network
|
||||
@@ -90,3 +92,23 @@ about:
|
||||
license: License
|
||||
description: 'EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.'
|
||||
check_update: Check Update
|
||||
|
||||
event:
|
||||
Unknown: Unknown
|
||||
TunDeviceReady: TunDeviceReady
|
||||
TunDeviceError: TunDeviceError
|
||||
PeerAdded: PeerAdded
|
||||
PeerRemoved: PeerRemoved
|
||||
PeerConnAdded: PeerConnAdded
|
||||
PeerConnRemoved: PeerConnRemoved
|
||||
ListenerAdded: ListenerAdded
|
||||
ListenerAddFailed: ListenerAddFailed
|
||||
ListenerAcceptFailed: ListenerAcceptFailed
|
||||
ConnectionAccepted: ConnectionAccepted
|
||||
ConnectionError: ConnectionError
|
||||
Connecting: Connecting
|
||||
ConnectError: ConnectError
|
||||
VpnPortalClientConnected: VpnPortalClientConnected
|
||||
VpnPortalClientDisconnected: VpnPortalClientDisconnected
|
||||
DhcpIpv4Changed: DhcpIpv4Changed
|
||||
DhcpIpv4Conflicted: DhcpIpv4Conflicted
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "easytier-gui",
|
||||
"type": "module",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -18,6 +18,7 @@
|
||||
"@tauri-apps/plugin-os": "2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-process": "2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-shell": "2.0.0-rc.1",
|
||||
"@vueuse/core": "^11.1.0",
|
||||
"aura": "link:@primevue\\themes\\aura",
|
||||
"ip-num": "1.5.1",
|
||||
"pinia": "^2.2.4",
|
||||
@@ -38,7 +39,7 @@
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"@vue-macros/volar": "^0.29.1",
|
||||
"@vue-macros/volar": "0.30.3",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.12.0",
|
||||
"eslint-plugin-format": "^0.1.2",
|
||||
@@ -57,5 +58,6 @@
|
||||
"vite-plugin-vue-layouts": "^0.11.0",
|
||||
"vue-i18n": "^10.0.0",
|
||||
"vue-tsc": "^2.1.6"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4"
|
||||
}
|
||||
|
||||
1159
easytier-gui/pnpm-lock.yaml
generated
1159
easytier-gui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "easytier-gui"
|
||||
version = "2.0.2"
|
||||
version = "2.0.3"
|
||||
description = "EasyTier GUI"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "migrated",
|
||||
"description": "permissions that were migrated from v1",
|
||||
"local": true,
|
||||
@@ -13,6 +14,7 @@
|
||||
"core:window:allow-show",
|
||||
"core:window:allow-hide",
|
||||
"core:window:allow-set-focus",
|
||||
"core:window:allow-set-title",
|
||||
"core:app:default",
|
||||
"core:resources:default",
|
||||
"core:menu:default",
|
||||
@@ -24,7 +26,6 @@
|
||||
"shell:default",
|
||||
"process:default",
|
||||
"clipboard-manager:default",
|
||||
"core:tray:default",
|
||||
"core:tray:allow-new",
|
||||
"core:tray:allow-set-menu",
|
||||
"core:tray:allow-set-title",
|
||||
|
||||
@@ -41,6 +41,7 @@ struct NetworkConfig {
|
||||
|
||||
dhcp: bool,
|
||||
virtual_ipv4: String,
|
||||
network_length: i32,
|
||||
hostname: Option<String>,
|
||||
network_name: String,
|
||||
network_secret: String,
|
||||
@@ -83,9 +84,15 @@ impl NetworkConfig {
|
||||
|
||||
if !self.dhcp {
|
||||
if self.virtual_ipv4.len() > 0 {
|
||||
cfg.set_ipv4(Some(self.virtual_ipv4.parse().with_context(|| {
|
||||
format!("failed to parse ipv4 address: {}", self.virtual_ipv4)
|
||||
})?))
|
||||
let ip = format!("{}/{}", self.virtual_ipv4, self.network_length)
|
||||
.parse()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to parse ipv4 inet address: {}, {}",
|
||||
self.virtual_ipv4, self.network_length
|
||||
)
|
||||
})?;
|
||||
cfg.set_ipv4(Some(ip));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"createUpdaterArtifacts": false
|
||||
},
|
||||
"productName": "easytier-gui",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"identifier": "com.kkrainbow.easytier",
|
||||
"plugins": {},
|
||||
"app": {
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import pkg from '~/../package.json'
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await getCurrentWindow().setTitle(`Easytier GUI: v${pkg.version}`)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
6
easytier-gui/src/auto-imports.d.ts
vendored
6
easytier-gui/src/auto-imports.d.ts
vendored
@@ -21,6 +21,7 @@ declare global {
|
||||
const definePage: typeof import('unplugin-vue-router/runtime')['definePage']
|
||||
const defineStore: typeof import('pinia')['defineStore']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const event2human: typeof import('./composables/utils')['event2human']
|
||||
const generateMenuItem: typeof import('./composables/tray')['generateMenuItem']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
@@ -44,6 +45,8 @@ declare global {
|
||||
const mapWritableState: typeof import('pinia')['mapWritableState']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const num2ipv4: typeof import('./composables/utils')['num2ipv4']
|
||||
const num2ipv6: typeof import('./composables/utils')['num2ipv6']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||
@@ -81,6 +84,7 @@ declare global {
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||
const timeAgoCn: typeof import('./composables/utils')['timeAgoCn']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
@@ -150,6 +154,8 @@ declare module 'vue' {
|
||||
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly num2ipv4: UnwrapRef<typeof import('./composables/utils')['num2ipv4']>
|
||||
readonly num2ipv6: UnwrapRef<typeof import('./composables/utils')['num2ipv6']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import InputGroup from 'primevue/inputgroup'
|
||||
import InputGroupAddon from 'primevue/inputgroupaddon'
|
||||
import { ping } from 'tauri-plugin-vpnservice-api'
|
||||
import { getOsHostname } from '~/composables/network'
|
||||
|
||||
import { NetworkingMethod } from '~/types/network'
|
||||
@@ -42,10 +41,11 @@ function searchUrlSuggestions(e: { query: string }): string[] {
|
||||
if (query.match(/^\w+:.*/)) {
|
||||
// if query is a valid url, then add to suggestions
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new URL(query)
|
||||
ret.push(query)
|
||||
}
|
||||
catch (e) {}
|
||||
catch {}
|
||||
}
|
||||
else {
|
||||
for (const proto in protos) {
|
||||
@@ -85,6 +85,20 @@ function searchPeerSuggestions(e: { query: string }) {
|
||||
peerSuggestions.value = searchUrlSuggestions(e)
|
||||
}
|
||||
|
||||
const inetSuggestions = ref([''])
|
||||
|
||||
function searchInetSuggestions(e: { query: string }) {
|
||||
if (e.query.search('/') >= 0) {
|
||||
inetSuggestions.value = [e.query]
|
||||
} else {
|
||||
const ret = []
|
||||
for (let i = 0; i < 32; i++) {
|
||||
ret.push(`${e.query}/${i}`)
|
||||
}
|
||||
inetSuggestions.value = ret
|
||||
}
|
||||
}
|
||||
|
||||
const listenerSuggestions = ref([''])
|
||||
|
||||
function searchListenerSuggestiong(e: { query: string }) {
|
||||
@@ -128,18 +142,12 @@ const osHostname = ref<string>('')
|
||||
|
||||
onMounted(async () => {
|
||||
osHostname.value = await getOsHostname()
|
||||
osHostname.value = await ping('ffdklsajflkdsjl') || ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-column h-full">
|
||||
<div class="flex flex-column">
|
||||
<div class="w-10/12 self-center mb-3">
|
||||
<Message severity="warn">
|
||||
{{ t('dhcp_experimental_warning') }}
|
||||
</Message>
|
||||
</div>
|
||||
<div class="w-10/12 self-center ">
|
||||
<Panel :header="t('basic_settings')">
|
||||
<div class="flex flex-column gap-y-2">
|
||||
@@ -159,8 +167,9 @@ onMounted(async () => {
|
||||
aria-describedby="virtual_ipv4-help"
|
||||
/>
|
||||
<InputGroupAddon>
|
||||
<span>/24</span>
|
||||
<span>/</span>
|
||||
</InputGroupAddon>
|
||||
<InputNumber v-model="curNetwork.network_length" :disabled="curNetwork.dhcp" inputId="horizontal-buttons" showButtons :step="1" mode="decimal" :min="1" :max="32" fluid class="max-w-20"/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</div>
|
||||
@@ -227,9 +236,10 @@ onMounted(async () => {
|
||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-column gap-2 grow p-fluid">
|
||||
<label for="username">{{ t('proxy_cidrs') }}</label>
|
||||
<Chips
|
||||
id="chips" v-model="curNetwork.proxy_cidrs"
|
||||
:placeholder="t('chips_placeholder', ['10.0.0.0/24'])" separator=" " class="w-full"
|
||||
<AutoComplete
|
||||
id="subnet-proxy"
|
||||
v-model="curNetwork.proxy_cidrs" :placeholder="t('chips_placeholder', ['10.0.0.0/24'])"
|
||||
class="w-full" multiple fluid :suggestions="inetSuggestions" @complete="searchInetSuggestions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
32
easytier-gui/src/components/HumanEvent.vue
Normal file
32
easytier-gui/src/components/HumanEvent.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { EventType } from '~/types/network'
|
||||
|
||||
const props = defineProps<{
|
||||
event: {
|
||||
[key: string]: any
|
||||
}
|
||||
}>()
|
||||
const { t } = useI18n()
|
||||
|
||||
const eventKey = computed(() => {
|
||||
const key = Object.keys(props.event)[0]
|
||||
return Object.keys(EventType).includes(key) ? key : 'Unknown'
|
||||
})
|
||||
|
||||
const eventValue = computed(() => {
|
||||
const value = props.event[eventKey.value]
|
||||
return typeof value === 'object' ? value : value
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Fieldset :legend="t(`event.${eventKey}`)">
|
||||
<template v-if="eventKey !== 'Unknown'">
|
||||
<div v-if="event.DhcpIpv4Changed">
|
||||
{{ `${eventValue[0]} -> ${eventValue[1]}` }}
|
||||
</div>
|
||||
<pre v-else>{{ eventValue }}</pre>
|
||||
</template>
|
||||
<pre v-else>{{ eventValue }}</pre>
|
||||
</Fieldset>
|
||||
</template>
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useTimeAgo } from '@vueuse/core'
|
||||
import { IPv4, IPv6 } from 'ip-num/IPNumber'
|
||||
import type { NodeInfo, PeerRoutePair } from '~/types/network'
|
||||
|
||||
@@ -111,6 +112,13 @@ function version(info: PeerRoutePair) {
|
||||
return info.route.version === '' ? 'unknown' : info.route.version
|
||||
}
|
||||
|
||||
function ipFormat(info: PeerRoutePair) {
|
||||
const ip = info.route.ipv4_addr
|
||||
if (typeof ip === 'string')
|
||||
return ip
|
||||
return ip ? `${num2ipv4(ip.address)}/${ip.network_length}` : ''
|
||||
}
|
||||
|
||||
const myNodeInfo = computed(() => {
|
||||
if (!curNetworkInst.value)
|
||||
return {} as NodeInfo
|
||||
@@ -151,7 +159,7 @@ const myNodeInfoChips = computed(() => {
|
||||
const local_ipv4s = my_node_info.ips?.interface_ipv4s
|
||||
for (const [idx, ip] of local_ipv4s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv4 ${idx}: ${IPv4.fromNumber(ip.addr)}`,
|
||||
label: `Local IPv4 ${idx}: ${num2ipv4(ip)}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
@@ -160,11 +168,7 @@ const myNodeInfoChips = computed(() => {
|
||||
const local_ipv6s = my_node_info.ips?.interface_ipv6s
|
||||
for (const [idx, ip] of local_ipv6s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv6 ${idx}: ${IPv6.fromBigInt((BigInt(ip.part1) << BigInt(96))
|
||||
+ (BigInt(ip.part2) << BigInt(64))
|
||||
+ (BigInt(ip.part3) << BigInt(32))
|
||||
+ BigInt(ip.part4),
|
||||
)}`,
|
||||
label: `Local IPv6 ${idx}: ${num2ipv6(ip)}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
@@ -210,6 +214,8 @@ const myNodeInfoChips = computed(() => {
|
||||
PortRestricted = 5,
|
||||
Symmetric = 6,
|
||||
SymUdpFirewall = 7,
|
||||
SymmetricEasyInc = 8,
|
||||
SymmetricEasyDec = 9,
|
||||
};
|
||||
const udpNatType: NatType = my_node_info.stun_info?.udp_nat_type
|
||||
if (udpNatType !== undefined) {
|
||||
@@ -222,6 +228,8 @@ const myNodeInfoChips = computed(() => {
|
||||
[NatType.PortRestricted]: 'Port Restricted',
|
||||
[NatType.Symmetric]: 'Symmetric',
|
||||
[NatType.SymUdpFirewall]: 'Symmetric UDP Firewall',
|
||||
[NatType.SymmetricEasyInc]: 'Symmetric Easy Inc',
|
||||
[NatType.SymmetricEasyDec]: 'Symmetric Easy Dec',
|
||||
}
|
||||
|
||||
chips.push({
|
||||
@@ -312,16 +320,18 @@ function showEventLogs() {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog v-model:visible="dialogVisible" modal :header="t(dialogHeader)" :style="{ width: '70%' }">
|
||||
<Panel>
|
||||
<ScrollPanel style="width: 100%; height: 400px">
|
||||
<pre>{{ dialogContent }}</pre>
|
||||
</ScrollPanel>
|
||||
</Panel>
|
||||
<Divider />
|
||||
<div class="flex justify-content-end gap-2">
|
||||
<Button type="button" :label="t('close')" @click="dialogVisible = false" />
|
||||
</div>
|
||||
<Dialog v-model:visible="dialogVisible" modal :header="t(dialogHeader)" class="w-2/3 h-auto">
|
||||
<ScrollPanel v-if="dialogHeader === 'vpn_portal_config'">
|
||||
<pre>{{ dialogContent }}</pre>
|
||||
</ScrollPanel>
|
||||
<Timeline v-else :value="dialogContent">
|
||||
<template #opposite="slotProps">
|
||||
<small class="text-surface-500 dark:text-surface-400">{{ useTimeAgo(Date.parse(slotProps.item[0])) }}</small>
|
||||
</template>
|
||||
<template #content="slotProps">
|
||||
<HumanEvent :event="slotProps.item[1]" />
|
||||
</template>
|
||||
</Timeline>
|
||||
</Dialog>
|
||||
|
||||
<Card v-if="curNetworkInst?.error_msg">
|
||||
@@ -404,18 +414,46 @@ function showEventLogs() {
|
||||
{{ t('peer_info') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<DataTable :value="peerRouteInfos" column-resize-mode="fit" table-style="width: 100%">
|
||||
<Column field="route.ipv4_addr" style="width: 100px;" :header="t('virtual_ipv4')" />
|
||||
<Column field="route.hostname" style="max-width: 250px;" :header="t('hostname')" />
|
||||
<Column :field="routeCost" style="width: 100px;" :header="t('route_cost')" />
|
||||
<Column :field="latencyMs" style="width: 80px;" :header="t('latency')" />
|
||||
<Column :field="txBytes" style="width: 80px;" :header="t('upload_bytes')" />
|
||||
<Column :field="rxBytes" style="width: 80px;" :header="t('download_bytes')" />
|
||||
<Column :field="lossRate" style="width: 100px;" :header="t('loss_rate')" />
|
||||
<Column :field="version" style="width: 100px;" :header="t('status.version')" />
|
||||
<DataTable :value="peerRouteInfos" column-resize-mode="fit" table-class="w-full">
|
||||
<Column :field="ipFormat" :header="t('virtual_ipv4')" />
|
||||
<Column :header="t('hostname')">
|
||||
<template #body="slotProps">
|
||||
<div
|
||||
v-if="!slotProps.data.route.cost || !slotProps.data.route.feature_flag.is_public_server"
|
||||
v-tooltip="slotProps.data.route.hostname"
|
||||
>
|
||||
{{
|
||||
slotProps.data.route.hostname }}
|
||||
</div>
|
||||
<div v-else v-tooltip="slotProps.data.route.hostname" class="space-x-1">
|
||||
<Tag v-if="slotProps.data.route.feature_flag.is_public_server" severity="info" value="Info">
|
||||
{{ t('status.server') }}
|
||||
</Tag>
|
||||
<Tag v-if="slotProps.data.route.no_relay_data" severity="warn" value="Warn">
|
||||
{{ t('status.relay') }}
|
||||
</Tag>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column :field="routeCost" :header="t('route_cost')" />
|
||||
<Column :field="latencyMs" :header="t('latency')" />
|
||||
<Column :field="txBytes" :header="t('upload_bytes')" />
|
||||
<Column :field="rxBytes" :header="t('download_bytes')" />
|
||||
<Column :field="lossRate" :header="t('loss_rate')" />
|
||||
<Column :header="t('status.version')">
|
||||
<template #body="slotProps">
|
||||
<span>{{ version(slotProps.data) }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.p-timeline :deep(.p-timeline-event-opposite) {
|
||||
@apply flex-none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -48,7 +48,7 @@ async function doStartVpn(ipv4Addr: string, cidr: number, routes: string[]) {
|
||||
|
||||
console.log('start vpn')
|
||||
const start_ret = await start_vpn({
|
||||
ipv4Addr: `${ipv4Addr}/${cidr}`,
|
||||
ipv4Addr: `${ipv4Addr}`,
|
||||
routes,
|
||||
disallowedApplications: ['com.kkrainbow.easytier'],
|
||||
mtu: 1300,
|
||||
|
||||
15
easytier-gui/src/composables/utils.ts
Normal file
15
easytier-gui/src/composables/utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { IPv4, IPv6 } from 'ip-num/IPNumber'
|
||||
import type { Ipv4Addr, Ipv6Addr } from '~/types/network'
|
||||
|
||||
export function num2ipv4(ip: Ipv4Addr) {
|
||||
return IPv4.fromNumber(ip.addr)
|
||||
}
|
||||
|
||||
export function num2ipv6(ip: Ipv6Addr) {
|
||||
return IPv6.fromBigInt(
|
||||
(BigInt(ip.part1) << BigInt(96))
|
||||
+ (BigInt(ip.part2) << BigInt(64))
|
||||
+ (BigInt(ip.part3) << BigInt(32))
|
||||
+ BigInt(ip.part4),
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,16 @@ import { disable, enable, isEnabled } from '@tauri-apps/plugin-autostart'
|
||||
|
||||
export async function loadAutoLaunchStatusAsync(target_enable: boolean): Promise<boolean> {
|
||||
try {
|
||||
target_enable ? await enable() : await disable()
|
||||
if (target_enable) {
|
||||
await enable()
|
||||
}
|
||||
else {
|
||||
// 消除没有配置自启动时进行关闭操作报错
|
||||
try {
|
||||
await disable()
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
localStorage.setItem('auto_launch', JSON.stringify(await isEnabled()))
|
||||
return isEnabled()
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ const setting_menu_items = ref([
|
||||
label: () => t('logging_open_dir'),
|
||||
icon: 'pi pi-folder-open',
|
||||
command: async () => {
|
||||
console.log('open log dir', await appLogDir())
|
||||
// console.log('open log dir', await appLogDir())
|
||||
await open(await appLogDir())
|
||||
},
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface NetworkConfig {
|
||||
|
||||
dhcp: boolean
|
||||
virtual_ipv4: string
|
||||
network_length: number,
|
||||
hostname?: string
|
||||
network_name: string
|
||||
network_secret: string
|
||||
@@ -42,6 +43,7 @@ export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
|
||||
|
||||
dhcp: true,
|
||||
virtual_ipv4: '',
|
||||
network_length: 24,
|
||||
network_name: 'easytier',
|
||||
network_secret: '',
|
||||
|
||||
@@ -137,7 +139,10 @@ export interface StunInfo {
|
||||
|
||||
export interface Route {
|
||||
peer_id: number
|
||||
ipv4_addr: string
|
||||
ipv4_addr: {
|
||||
address: Ipv4Addr
|
||||
network_length: number
|
||||
} | string | null
|
||||
next_hop_peer_id: number
|
||||
cost: number
|
||||
proxy_cidrs: string[]
|
||||
@@ -155,6 +160,7 @@ export interface PeerInfo {
|
||||
export interface PeerConnInfo {
|
||||
conn_id: string
|
||||
my_peer_id: number
|
||||
is_client: boolean
|
||||
peer_id: number
|
||||
features: string[]
|
||||
tunnel?: TunnelInfo
|
||||
@@ -180,3 +186,28 @@ export interface PeerConnStats {
|
||||
tx_packets: number
|
||||
latency_us: number
|
||||
}
|
||||
|
||||
export enum EventType {
|
||||
TunDeviceReady = 'TunDeviceReady', // string
|
||||
TunDeviceError = 'TunDeviceError', // string
|
||||
|
||||
PeerAdded = 'PeerAdded', // number
|
||||
PeerRemoved = 'PeerRemoved', // number
|
||||
PeerConnAdded = 'PeerConnAdded', // PeerConnInfo
|
||||
PeerConnRemoved = 'PeerConnRemoved', // PeerConnInfo
|
||||
|
||||
ListenerAdded = 'ListenerAdded', // any
|
||||
ListenerAddFailed = 'ListenerAddFailed', // any, string
|
||||
ListenerAcceptFailed = 'ListenerAcceptFailed', // any, string
|
||||
ConnectionAccepted = 'ConnectionAccepted', // string, string
|
||||
ConnectionError = 'ConnectionError', // string, string, string
|
||||
|
||||
Connecting = 'Connecting', // any
|
||||
ConnectError = 'ConnectError', // string, string, string
|
||||
|
||||
VpnPortalClientConnected = 'VpnPortalClientConnected', // string, string
|
||||
VpnPortalClientDisconnected = 'VpnPortalClientDisconnected', // string, string, string
|
||||
|
||||
DhcpIpv4Changed = 'DhcpIpv4Changed', // ipv4 | null, ipv4 | null
|
||||
DhcpIpv4Conflicted = 'DhcpIpv4Conflicted', // ipv4 | null
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "easytier"
|
||||
description = "A full meshed p2p VPN, connecting all your devices in one network with one command."
|
||||
homepage = "https://github.com/EasyTier/EasyTier"
|
||||
repository = "https://github.com/EasyTier/EasyTier"
|
||||
version = "2.0.2"
|
||||
version = "2.0.3"
|
||||
edition = "2021"
|
||||
authors = ["kkrainbow"]
|
||||
keywords = ["vpn", "p2p", "network", "easytier"]
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{copy, Cursor},
|
||||
path::PathBuf,
|
||||
};
|
||||
use std::{env, io::Cursor, path::PathBuf};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
struct WindowsBuild {}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{net::SocketAddr, sync::Mutex, time::Duration, vec};
|
||||
|
||||
use anyhow::{Context, Ok};
|
||||
use clap::{command, Args, Parser, Subcommand};
|
||||
use common::stun::StunInfoCollectorTrait;
|
||||
use common::{constants::EASYTIER_VERSION, stun::StunInfoCollectorTrait};
|
||||
use proto::{
|
||||
common::NatType,
|
||||
peer_rpc::{GetGlobalPeerMapRequest, PeerCenterRpc, PeerCenterRpcClientFactory},
|
||||
@@ -30,7 +30,7 @@ use humansize::format_size;
|
||||
use tabled::settings::Style;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "easytier-cli", author, version, about, long_about = None)]
|
||||
#[command(name = "easytier-cli", author, version = EASYTIER_VERSION, about, long_about = None)]
|
||||
struct Cli {
|
||||
/// the instance name
|
||||
#[arg(short = 'p', long, default_value = "127.0.0.1:15888")]
|
||||
|
||||
@@ -26,8 +26,9 @@ mod tunnel;
|
||||
mod utils;
|
||||
mod vpn_portal;
|
||||
|
||||
use common::config::{
|
||||
ConsoleLoggerConfig, FileLoggerConfig, NetworkIdentity, PeerConfig, VpnPortalConfig,
|
||||
use common::{
|
||||
config::{ConsoleLoggerConfig, FileLoggerConfig, NetworkIdentity, PeerConfig, VpnPortalConfig},
|
||||
constants::EASYTIER_VERSION,
|
||||
};
|
||||
use instance::instance::Instance;
|
||||
use tokio::net::TcpSocket;
|
||||
@@ -49,7 +50,7 @@ use mimalloc_rust::*;
|
||||
static GLOBAL_MIMALLOC: GlobalMiMalloc = GlobalMiMalloc;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "easytier-core", author, version, about, long_about = None)]
|
||||
#[command(name = "easytier-core", author, version = EASYTIER_VERSION , about, long_about = None)]
|
||||
struct Cli {
|
||||
#[arg(
|
||||
short,
|
||||
|
||||
@@ -93,7 +93,7 @@ impl PeerConn {
|
||||
let peer_conn_tunnel_filter = StatsRecorderTunnelFilter::new();
|
||||
let throughput = peer_conn_tunnel_filter.filter_output();
|
||||
let peer_conn_tunnel = TunnelWithFilter::new(tunnel, peer_conn_tunnel_filter);
|
||||
let mut mpsc_tunnel = MpscTunnel::new(peer_conn_tunnel);
|
||||
let mut mpsc_tunnel = MpscTunnel::new(peer_conn_tunnel, Some(Duration::from_secs(7)));
|
||||
|
||||
let (recv, sink) = (mpsc_tunnel.get_stream(), mpsc_tunnel.get_sink());
|
||||
|
||||
|
||||
@@ -1387,7 +1387,9 @@ impl PeerRouteServiceImpl {
|
||||
if resp.error.is_some() {
|
||||
let err = resp.error.unwrap();
|
||||
if err == Error::DuplicatePeerId as i32 {
|
||||
panic!("duplicate peer id");
|
||||
if !self.global_ctx.get_feature_flags().is_public_server {
|
||||
panic!("duplicate peer id");
|
||||
}
|
||||
} else {
|
||||
tracing::error!(?ret, ?my_peer_id, ?dst_peer_id, "sync_route_info failed");
|
||||
session
|
||||
|
||||
@@ -61,8 +61,8 @@ impl Client {
|
||||
pub fn new() -> Self {
|
||||
let (ring_a, ring_b) = create_ring_tunnel_pair();
|
||||
Self {
|
||||
mpsc: Mutex::new(MpscTunnel::new(ring_a)),
|
||||
transport: Mutex::new(MpscTunnel::new(ring_b)),
|
||||
mpsc: Mutex::new(MpscTunnel::new(ring_a, None)),
|
||||
transport: Mutex::new(MpscTunnel::new(ring_b, None)),
|
||||
inflight_requests: Arc::new(DashMap::new()),
|
||||
tasks: Arc::new(Mutex::new(JoinSet::new())),
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ impl Server {
|
||||
|
||||
Self {
|
||||
registry,
|
||||
mpsc: Mutex::new(Some(MpscTunnel::new(ring_a))),
|
||||
transport: Mutex::new(MpscTunnel::new(ring_b)),
|
||||
mpsc: Mutex::new(Some(MpscTunnel::new(ring_a, None))),
|
||||
transport: Mutex::new(MpscTunnel::new(ring_b, None)),
|
||||
tasks: Arc::new(Mutex::new(JoinSet::new())),
|
||||
packet_mergers: Arc::new(DashMap::new()),
|
||||
}
|
||||
|
||||
@@ -175,6 +175,83 @@ async fn rpc_timeout_test() {
|
||||
assert_eq!(0, ctx.server.inflight_count());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rpc_tunnel_stuck_test() {
|
||||
use crate::proto::rpc_types;
|
||||
use crate::tunnel::ring::RING_TUNNEL_CAP;
|
||||
|
||||
let rpc_server = Server::new();
|
||||
rpc_server.run();
|
||||
let server = GreetingServer::new(GreetingService {
|
||||
delay_ms: 0,
|
||||
prefix: "Hello".to_string(),
|
||||
});
|
||||
rpc_server.registry().register(server, "test");
|
||||
|
||||
let client = Client::new();
|
||||
client.run();
|
||||
|
||||
let rpc_tasks = Arc::new(Mutex::new(JoinSet::new()));
|
||||
let (mut rx, tx) = (
|
||||
rpc_server.get_transport_stream(),
|
||||
client.get_transport_sink(),
|
||||
);
|
||||
|
||||
rpc_tasks.lock().unwrap().spawn(async move {
|
||||
while let Some(Ok(packet)) = rx.next().await {
|
||||
if let Err(err) = tx.send(packet).await {
|
||||
println!("{:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// mock server is stuck (no task to do forwards)
|
||||
|
||||
let mut tasks = JoinSet::new();
|
||||
for _ in 0..RING_TUNNEL_CAP + 15 {
|
||||
let out =
|
||||
client.scoped_client::<GreetingClientFactory<RpcController>>(1, 1, "test".to_string());
|
||||
tasks.spawn(async move {
|
||||
let mut ctrl = RpcController::default();
|
||||
ctrl.timeout_ms = 1000;
|
||||
|
||||
let input = SayHelloRequest {
|
||||
name: "world".to_string(),
|
||||
};
|
||||
|
||||
out.say_hello(ctrl, input).await
|
||||
});
|
||||
}
|
||||
while let Some(ret) = tasks.join_next().await {
|
||||
assert!(matches!(ret, Ok(Err(rpc_types::error::Error::Timeout(_)))));
|
||||
}
|
||||
|
||||
// start server consumer, new requests should be processed
|
||||
let (mut rx, tx) = (
|
||||
client.get_transport_stream(),
|
||||
rpc_server.get_transport_sink(),
|
||||
);
|
||||
rpc_tasks.lock().unwrap().spawn(async move {
|
||||
while let Some(Ok(packet)) = rx.next().await {
|
||||
if let Err(err) = tx.send(packet).await {
|
||||
println!("{:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let out =
|
||||
client.scoped_client::<GreetingClientFactory<RpcController>>(1, 1, "test".to_string());
|
||||
let mut ctrl = RpcController::default();
|
||||
ctrl.timeout_ms = 1000;
|
||||
let input = SayHelloRequest {
|
||||
name: "fuck world".to_string(),
|
||||
};
|
||||
let ret = out.say_hello(ctrl, input).await.unwrap();
|
||||
assert_eq!(ret.greeting, "Hello fuck world!");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn standalone_rpc_test() {
|
||||
use crate::proto::rpc_impl::standalone::{StandAloneClient, StandAloneServer};
|
||||
|
||||
@@ -41,13 +41,13 @@ pub struct MpscTunnel<T> {
|
||||
}
|
||||
|
||||
impl<T: Tunnel> MpscTunnel<T> {
|
||||
pub fn new(tunnel: T) -> Self {
|
||||
pub fn new(tunnel: T, send_timeout: Option<Duration>) -> Self {
|
||||
let (tx, mut rx) = channel(32);
|
||||
let (stream, mut sink) = tunnel.split();
|
||||
|
||||
let task = tokio::spawn(async move {
|
||||
loop {
|
||||
if let Err(e) = Self::forward_one_round(&mut rx, &mut sink).await {
|
||||
if let Err(e) = Self::forward_one_round(&mut rx, &mut sink, send_timeout).await {
|
||||
tracing::error!(?e, "forward error");
|
||||
break;
|
||||
}
|
||||
@@ -68,21 +68,44 @@ impl<T: Tunnel> MpscTunnel<T> {
|
||||
async fn forward_one_round(
|
||||
rx: &mut Receiver<ZCPacket>,
|
||||
sink: &mut Pin<Box<dyn ZCPacketSink>>,
|
||||
send_timeout_ms: Option<Duration>,
|
||||
) -> Result<(), TunnelError> {
|
||||
let item = rx.recv().await.with_context(|| "recv error")?;
|
||||
if let Some(timeout_ms) = send_timeout_ms {
|
||||
Self::forward_one_round_with_timeout(rx, sink, item, timeout_ms).await
|
||||
} else {
|
||||
Self::forward_one_round_no_timeout(rx, sink, item).await
|
||||
}
|
||||
}
|
||||
|
||||
match timeout(Duration::from_secs(10), async move {
|
||||
sink.feed(item).await?;
|
||||
while let Ok(item) = rx.try_recv() {
|
||||
match sink.feed(item).await {
|
||||
Err(e) => {
|
||||
tracing::error!(?e, "feed error");
|
||||
return Err(e);
|
||||
}
|
||||
Ok(_) => {}
|
||||
async fn forward_one_round_no_timeout(
|
||||
rx: &mut Receiver<ZCPacket>,
|
||||
sink: &mut Pin<Box<dyn ZCPacketSink>>,
|
||||
initial_item: ZCPacket,
|
||||
) -> Result<(), TunnelError> {
|
||||
sink.feed(initial_item).await?;
|
||||
|
||||
while let Ok(item) = rx.try_recv() {
|
||||
match sink.feed(item).await {
|
||||
Err(e) => {
|
||||
tracing::error!(?e, "feed error");
|
||||
return Err(e);
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
sink.flush().await
|
||||
}
|
||||
|
||||
sink.flush().await
|
||||
}
|
||||
|
||||
async fn forward_one_round_with_timeout(
|
||||
rx: &mut Receiver<ZCPacket>,
|
||||
sink: &mut Pin<Box<dyn ZCPacketSink>>,
|
||||
initial_item: ZCPacket,
|
||||
timeout_ms: Duration,
|
||||
) -> Result<(), TunnelError> {
|
||||
match timeout(timeout_ms, async move {
|
||||
Self::forward_one_round_no_timeout(rx, sink, initial_item).await
|
||||
})
|
||||
.await
|
||||
{
|
||||
@@ -112,17 +135,12 @@ impl<T: Tunnel> MpscTunnel<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Tunnel> From<T> for MpscTunnel<T> {
|
||||
fn from(tunnel: T) -> Self {
|
||||
Self::new(tunnel)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures::StreamExt;
|
||||
|
||||
use crate::tunnel::{
|
||||
ring::{create_ring_tunnel_pair, RING_TUNNEL_CAP},
|
||||
tcp::{TcpTunnelConnector, TcpTunnelListener},
|
||||
TunnelConnector, TunnelListener,
|
||||
};
|
||||
@@ -162,7 +180,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let tunnel = connector.connect().await.unwrap();
|
||||
let mpsc_tunnel = MpscTunnel::from(tunnel);
|
||||
let mpsc_tunnel = MpscTunnel::new(tunnel, None);
|
||||
|
||||
let sink1 = mpsc_tunnel.get_sink();
|
||||
let t2 = tokio::spawn(async move {
|
||||
@@ -213,4 +231,24 @@ mod tests {
|
||||
|
||||
let _ = tokio::join!(t1, t2, t3, t4);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mpsc_slow_receiver_with_send_timeout() {
|
||||
let (a, _b) = create_ring_tunnel_pair();
|
||||
let mpsc_tunnel = MpscTunnel::new(a, Some(Duration::from_secs(1)));
|
||||
let s = mpsc_tunnel.get_sink();
|
||||
for _ in 0..RING_TUNNEL_CAP {
|
||||
s.send(ZCPacket::new_with_payload(&[0; 1024]))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(1500)).await;
|
||||
let e = s.send(ZCPacket::new_with_payload(&[0; 1024])).await;
|
||||
assert!(e.is_ok());
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(1500)).await;
|
||||
|
||||
let e = s.send(ZCPacket::new_with_payload(&[0; 1024])).await;
|
||||
assert!(e.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ use super::{
|
||||
StreamItem, Tunnel, TunnelConnector, TunnelError, TunnelInfo, TunnelListener,
|
||||
};
|
||||
|
||||
static RING_TUNNEL_CAP: usize = 128;
|
||||
pub static RING_TUNNEL_CAP: usize = 128;
|
||||
static RING_TUNNEL_RESERVERD_CAP: usize = 4;
|
||||
|
||||
type RingLock = parking_lot::Mutex<()>;
|
||||
|
||||
@@ -81,7 +81,7 @@ impl WireGuardImpl {
|
||||
wg_peer_ip_table: WgPeerIpTable,
|
||||
) {
|
||||
let info = t.info().unwrap_or_default();
|
||||
let mut mpsc_tunnel = MpscTunnel::new(t);
|
||||
let mut mpsc_tunnel = MpscTunnel::new(t, None);
|
||||
let mut stream = mpsc_tunnel.get_stream();
|
||||
let mut ip_registered = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user