LumaTunnel
【WinUI】LumaTunnel 是一个面向个人多设备的 Windows 代理系统。客户端在本机提供 HTTP/HTTPS CONNECT 与 SOCKS5 TCP 代理,并通过一个受信任 TLS 证书保护的 WSS 会话,将多个 TCP 流复用到自建 Windows Server 节点。
关注
0
Fork
0
Star
0
using System.Text.Json;
using LumaTunnel.Shared.Protocol;
namespace LumaTunnel.Client.Profiles;
public sealed class ClientProfileStore : IDisposable
{
private readonly SemaphoreSlim _gate = new(1, 1);
public string RootPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LumaTunnel");
public string ProfilePath => Path.Combine(RootPath, "config", "client.json");
public string LogPath => Path.Combine(RootPath, "logs");
public string StatePath => Path.Combine(RootPath, "state");
public async Task<ClientProfile> LoadAsync(CancellationToken cancellationToken = default)
{
Directory.CreateDirectory(Path.GetDirectoryName(ProfilePath)!);
Directory.CreateDirectory(LogPath);
Directory.CreateDirectory(StatePath);
if (!File.Exists(ProfilePath))
{
var profile = new ClientProfile();
await SaveAsync(profile, cancellationToken).ConfigureAwait(false);
return profile;
}
await using var stream = File.OpenRead(ProfilePath);
return await JsonSerializer.DeserializeAsync<ClientProfile>(stream, TunnelMessageSerializer.Options, cancellationToken).ConfigureAwait(false)
?? new ClientProfile();
}
public async Task SaveAsync(ClientProfile profile, CancellationToken cancellationToken = default)
{
await _gate.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
Directory.CreateDirectory(Path.GetDirectoryName(ProfilePath)!);
var temp = ProfilePath + ".tmp";
try
{
await using (var stream = new FileStream(temp, FileMode.Create, FileAccess.Write, FileShare.None, 8192, FileOptions.WriteThrough | FileOptions.Asynchronous))
await JsonSerializer.SerializeAsync(stream, profile, TunnelMessageSerializer.Options, cancellationToken).ConfigureAwait(false);
File.Move(temp, ProfilePath, true);
}
finally
{
if (File.Exists(temp)) File.Delete(temp);
}
}
finally
{
_gate.Release();
}
}
public void Dispose()
{
_gate.Dispose();
GC.SuppressFinalize(this);
}
}
using System.Text.Json;
using LumaTunnel.Shared.Protocol;
namespace LumaTunnel.Client.Profiles;
public sealed class ClientProfileStore : IDisposable
{
private readonly SemaphoreSlim _gate = new(1, 1);
public string RootPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LumaTunnel");
public string ProfilePath => Path.Combine(RootPath, "config", "client.json");
public string LogPath => Path.Combine(RootPath, "logs");
public string StatePath => Path.Combine(RootPath, "state");
public async Task<ClientProfile> LoadAsync(CancellationToken cancellationToken = default)
{
Directory.CreateDirectory(Path.GetDirectoryName(ProfilePath)!);
Directory.CreateDirectory(LogPath);
Directory.CreateDirectory(StatePath);
if (!File.Exists(ProfilePath))
{
var profile = new ClientProfile();
await SaveAsync(profile, cancellationToken).ConfigureAwait(false);
return profile;
}
await using var stream = File.OpenRead(ProfilePath);
return await JsonSerializer.DeserializeAsync<ClientProfile>(stream, TunnelMessageSerializer.Options, cancellationToken).ConfigureAwait(false)
?? new ClientProfile();
}
public async Task SaveAsync(ClientProfile profile, CancellationToken cancellationToken = default)
{
await _gate.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
Directory.CreateDirectory(Path.GetDirectoryName(ProfilePath)!);
var temp = ProfilePath + ".tmp";
try
{
await using (var stream = new FileStream(temp, FileMode.Create, FileAccess.Write, FileShare.None, 8192, FileOptions.WriteThrough | FileOptions.Asynchronous))
await JsonSerializer.SerializeAsync(stream, profile, TunnelMessageSerializer.Options, cancellationToken).ConfigureAwait(false);
File.Move(temp, ProfilePath, true);
}
finally
{
if (File.Exists(temp)) File.Delete(temp);
}
}
finally
{
_gate.Release();
}
}
public void Dispose()
{
_gate.Dispose();
GC.SuppressFinalize(this);
}
}