LumaTunnel
【WinUI】LumaTunnel 是一个面向个人多设备的 Windows 代理系统。客户端在本机提供 HTTP/HTTPS CONNECT 与 SOCKS5 TCP 代理,并通过一个受信任 TLS 证书保护的 WSS 会话,将多个 TCP 流复用到自建 Windows Server 节点。
关注
0
Fork
0
Star
0
using LumaTunnel.Server.Core.Runtime;
using LumaTunnel.Server.Core.Services;
using XFEExtension.NetCore.ServerInteractive.Utilities.Extensions;
using XFEExtension.NetCore.ServerInteractive.Utilities.Server;
namespace LumaTunnel.Server;
public sealed partial class ServerWorker(ILogger<ServerWorker> logger) : BackgroundService
{
private XFEServerCore? _serverCore;
private ServerRuntime? _runtime;
private FileSystemWatcher? _deviceWatcher;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_runtime = await ServerRuntime.InitializeAsync(cancellationToken: stoppingToken).ConfigureAwait(false);
_deviceWatcher = new FileSystemWatcher(_runtime.Paths.DataDirectory, Path.GetFileName(_runtime.Paths.DevicesFile))
{
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.CreationTime,
EnableRaisingEvents = true
};
_deviceWatcher.Changed += OnDeviceFileChanged;
_deviceWatcher.Created += OnDeviceFileChanged;
_deviceWatcher.Renamed += OnDeviceFileChanged;
_serverCore = XFEServerCoreBuilder.CreateBuilder()
.AddVerifyService<ApiRateLimitVerifyService>()
.AddOriginalService<TunnelGatewayOriginalService>()
.AddService<HealthService>()
.AddService<EnrollmentService>()
.AddService<NodeStatusService>()
.Build(options =>
{
options.AcceptGet = true;
options.AcceptPost = true;
options.AcceptNonStandardJson = false;
options.MainEntryPoint = "api";
options.ServerCoreName = "LumaTunnelServer";
foreach (var url in _runtime.Settings.BindingUrls)
options.BindIP(url);
});
var server = XFEServerBuilder.CreateBuilder()
.UseXFEServer()
.AddServerCore(_serverCore)
.Build();
using var registration = stoppingToken.Register(() =>
{
try
{
_serverCore.CyberCommServer.StopCyberCommServer();
}
catch (Exception exception)
{
LogStopFailure(logger, exception);
}
});
LogStarting(logger,
_runtime.Settings.NodeName,
_runtime.Settings.NodeId,
string.Join(", ", _runtime.Settings.BindingUrls));
var flushTask = FlushLoopAsync(stoppingToken);
try
{
await server.Start().ConfigureAwait(false);
}
catch (Exception) when (stoppingToken.IsCancellationRequested)
{
LogStopped(logger);
}
await flushTask.ConfigureAwait(false);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
if (_runtime?.TunnelConnections is { } tunnels)
{
var shutdown = tunnels.DisposeAsync().AsTask();
await Task.WhenAny(shutdown, Task.Delay(TimeSpan.FromSeconds(5), cancellationToken)).ConfigureAwait(false);
}
if (_runtime is not null)
await _runtime.FlushAsync(cancellationToken).ConfigureAwait(false);
_serverCore?.CyberCommServer.StopCyberCommServer();
if (_deviceWatcher is not null)
{
_deviceWatcher.EnableRaisingEvents = false;
_deviceWatcher.Dispose();
_deviceWatcher = null;
}
await base.StopAsync(cancellationToken).ConfigureAwait(false);
}
private async void OnDeviceFileChanged(object sender, FileSystemEventArgs e)
{
try
{
await Task.Delay(50).ConfigureAwait(false);
if (_runtime is not null)
await _runtime.Devices.ReloadAsync().ConfigureAwait(false);
}
catch (IOException)
{
}
}
private async Task FlushLoopAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken).ConfigureAwait(false);
if (_runtime is not null)
await _runtime.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
}
}
[LoggerMessage(1, LogLevel.Warning, "Failed to stop the HTTP.sys listener cleanly.")]
private static partial void LogStopFailure(ILogger logger, Exception exception);
[LoggerMessage(2, LogLevel.Information, "LumaTunnel node {NodeName} ({NodeId}) is starting on {Bindings}.")]
private static partial void LogStarting(ILogger logger, string nodeName, string nodeId, string bindings);
[LoggerMessage(3, LogLevel.Information, "LumaTunnel listener stopped during service shutdown.")]
private static partial void LogStopped(ILogger logger);
}
using LumaTunnel.Server.Core.Runtime;
using LumaTunnel.Server.Core.Services;
using XFEExtension.NetCore.ServerInteractive.Utilities.Extensions;
using XFEExtension.NetCore.ServerInteractive.Utilities.Server;
namespace LumaTunnel.Server;
public sealed partial class ServerWorker(ILogger<ServerWorker> logger) : BackgroundService
{
private XFEServerCore? _serverCore;
private ServerRuntime? _runtime;
private FileSystemWatcher? _deviceWatcher;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_runtime = await ServerRuntime.InitializeAsync(cancellationToken: stoppingToken).ConfigureAwait(false);
_deviceWatcher = new FileSystemWatcher(_runtime.Paths.DataDirectory, Path.GetFileName(_runtime.Paths.DevicesFile))
{
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.CreationTime,
EnableRaisingEvents = true
};
_deviceWatcher.Changed += OnDeviceFileChanged;
_deviceWatcher.Created += OnDeviceFileChanged;
_deviceWatcher.Renamed += OnDeviceFileChanged;
_serverCore = XFEServerCoreBuilder.CreateBuilder()
.AddVerifyService<ApiRateLimitVerifyService>()
.AddOriginalService<TunnelGatewayOriginalService>()
.AddService<HealthService>()
.AddService<EnrollmentService>()
.AddService<NodeStatusService>()
.Build(options =>
{
options.AcceptGet = true;
options.AcceptPost = true;
options.AcceptNonStandardJson = false;
options.MainEntryPoint = "api";
options.ServerCoreName = "LumaTunnelServer";
foreach (var url in _runtime.Settings.BindingUrls)
options.BindIP(url);
});
var server = XFEServerBuilder.CreateBuilder()
.UseXFEServer()
.AddServerCore(_serverCore)
.Build();
using var registration = stoppingToken.Register(() =>
{
try
{
_serverCore.CyberCommServer.StopCyberCommServer();
}
catch (Exception exception)
{
LogStopFailure(logger, exception);
}
});
LogStarting(logger,
_runtime.Settings.NodeName,
_runtime.Settings.NodeId,
string.Join(", ", _runtime.Settings.BindingUrls));
var flushTask = FlushLoopAsync(stoppingToken);
try
{
await server.Start().ConfigureAwait(false);
}
catch (Exception) when (stoppingToken.IsCancellationRequested)
{
LogStopped(logger);
}
await flushTask.ConfigureAwait(false);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
if (_runtime?.TunnelConnections is { } tunnels)
{
var shutdown = tunnels.DisposeAsync().AsTask();
await Task.WhenAny(shutdown, Task.Delay(TimeSpan.FromSeconds(5), cancellationToken)).ConfigureAwait(false);
}
if (_runtime is not null)
await _runtime.FlushAsync(cancellationToken).ConfigureAwait(false);
_serverCore?.CyberCommServer.StopCyberCommServer();
if (_deviceWatcher is not null)
{
_deviceWatcher.EnableRaisingEvents = false;
_deviceWatcher.Dispose();
_deviceWatcher = null;
}
await base.StopAsync(cancellationToken).ConfigureAwait(false);
}
private async void OnDeviceFileChanged(object sender, FileSystemEventArgs e)
{
try
{
await Task.Delay(50).ConfigureAwait(false);
if (_runtime is not null)
await _runtime.Devices.ReloadAsync().ConfigureAwait(false);
}
catch (IOException)
{
}
}
private async Task FlushLoopAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken).ConfigureAwait(false);
if (_runtime is not null)
await _runtime.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
}
}
[LoggerMessage(1, LogLevel.Warning, "Failed to stop the HTTP.sys listener cleanly.")]
private static partial void LogStopFailure(ILogger logger, Exception exception);
[LoggerMessage(2, LogLevel.Information, "LumaTunnel node {NodeName} ({NodeId}) is starting on {Bindings}.")]
private static partial void LogStarting(ILogger logger, string nodeName, string nodeId, string bindings);
[LoggerMessage(3, LogLevel.Information, "LumaTunnel listener stopped during service shutdown.")]
private static partial void LogStopped(ILogger logger);
}