mirror of
https://github.com/FastTunnel/FastTunnel.git
synced 2025-02-08 02:39:29 +08:00
使用pipline.io
This commit is contained in:
parent
a709796bc6
commit
4ef6ac1402
|
@ -7,6 +7,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.0-preview.3.22175.4" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="5.0.0-dev-00095" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.1-dev-00337" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.2-dev-00890" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" />
|
||||
</ItemGroup>
|
||||
|
@ -16,6 +17,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.Development.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
@ -28,9 +32,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties appsettings_1json__JsonSchema="" />
|
||||
</VisualStudio>
|
||||
<VisualStudio><UserProperties appsettings_1development_1json__JsonSchema="https://json.schemastore.org/appsettings.json" appsettings_1json__JsonSchema="" /></VisualStudio>
|
||||
</ProjectExtensions>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -30,20 +30,18 @@ class Program
|
|||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.UseSerilog((hostBuilderContext, services, loggerConfiguration) =>
|
||||
.UseSerilog((context, services, loggerConfiguration) =>
|
||||
{
|
||||
var enableFileLog = (bool)(hostBuilderContext.Configuration.GetSection("EnableFileLog")?.Get(typeof(bool)) ?? false);
|
||||
loggerConfiguration.WriteTo.Console();
|
||||
if (enableFileLog)
|
||||
{
|
||||
loggerConfiguration.WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7);
|
||||
}
|
||||
loggerConfiguration.ReadFrom.Configuration(context.Configuration)
|
||||
.ReadFrom.Services(services)
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console();
|
||||
})
|
||||
.UseWindowsService()
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
// -------------------FastTunnel START------------------
|
||||
services.AddFastTunnelClient(hostContext.Configuration.GetSection("ClientSettings"));
|
||||
services.AddFastTunnelClient(hostContext.Configuration.GetSection("FastTunnel"));
|
||||
// -------------------FastTunnel EDN--------------------
|
||||
});
|
||||
}
|
||||
|
|
7
FastTunnel.Client/appsettings.Development.json
Normal file
7
FastTunnel.Client/appsettings.Development.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Debug"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,24 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
// Trace Debug Information Warning Error
|
||||
"Serilog": {
|
||||
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
"Override": {
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"WriteTo": [
|
||||
//{
|
||||
// "Name": "File",
|
||||
// "Args": {
|
||||
// "path": "Logs/log-.log",
|
||||
// "rollingInterval": 3
|
||||
// }
|
||||
//}
|
||||
]
|
||||
},
|
||||
// 是否启用文件日志输出
|
||||
"EnableFileLog": true,
|
||||
"ClientSettings": {
|
||||
"FastTunnel": {
|
||||
"Server": {
|
||||
// [必选] 服务端ip/域名(来自服务端配置文件的urls参数)
|
||||
"ServerAddr": "127.0.0.1",
|
||||
|
|
|
@ -4,153 +4,158 @@
|
|||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using FastTunnel.Core.Config;
|
||||
using FastTunnel.Core.Models;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FastTunnel.Core.Extensions;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using FastTunnel.Core.Handlers.Client;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Buffers;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.WebSockets;
|
||||
using FastTunnel.Core.Utilitys;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FastTunnel.Core.Config;
|
||||
using FastTunnel.Core.Extensions;
|
||||
using FastTunnel.Core.Handlers.Client;
|
||||
using FastTunnel.Core.Models;
|
||||
using FastTunnel.Core.Models.Massage;
|
||||
using FastTunnel.Core.Utilitys;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace FastTunnel.Core.Client
|
||||
namespace FastTunnel.Core.Client;
|
||||
|
||||
public class FastTunnelClient : IFastTunnelClient
|
||||
{
|
||||
public class FastTunnelClient : IFastTunnelClient
|
||||
private ClientWebSocket socket;
|
||||
|
||||
protected readonly ILogger<FastTunnelClient> _logger;
|
||||
protected DefaultClientConfig ClientConfig { get; private set; }
|
||||
|
||||
private readonly SwapHandler swapHandler;
|
||||
private readonly LogHandler logHandler;
|
||||
|
||||
private static ReadOnlySpan<byte> EndSpan => new ReadOnlySpan<byte>(new byte[] { (byte)'\n' });
|
||||
|
||||
public SuiDaoServer Server { get; protected set; }
|
||||
|
||||
public FastTunnelClient(
|
||||
ILogger<FastTunnelClient> logger,
|
||||
SwapHandler newCustomerHandler,
|
||||
LogHandler logHandler,
|
||||
IOptionsMonitor<DefaultClientConfig> configuration)
|
||||
{
|
||||
private ClientWebSocket socket;
|
||||
ReadOnlySpan<int> span = new ReadOnlySpan<int>();
|
||||
_logger = logger;
|
||||
swapHandler = newCustomerHandler;
|
||||
this.logHandler = logHandler;
|
||||
ClientConfig = configuration.CurrentValue;
|
||||
Server = ClientConfig.Server;
|
||||
}
|
||||
|
||||
protected readonly ILogger<FastTunnelClient> _logger;
|
||||
private readonly SwapHandler _newCustomerHandler;
|
||||
private readonly LogHandler _logHandler;
|
||||
/// <summary>
|
||||
/// 启动客户端
|
||||
/// </summary>
|
||||
public virtual async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("===== FastTunnel Client Start =====");
|
||||
|
||||
public DefaultClientConfig ClientConfig { get; private set; }
|
||||
|
||||
public SuiDaoServer Server { get; protected set; }
|
||||
|
||||
public FastTunnelClient(
|
||||
ILogger<FastTunnelClient> logger,
|
||||
SwapHandler newCustomerHandler,
|
||||
LogHandler logHandler,
|
||||
IOptionsMonitor<DefaultClientConfig> configuration)
|
||||
{
|
||||
_logger = logger;
|
||||
_newCustomerHandler = newCustomerHandler;
|
||||
_logHandler = logHandler;
|
||||
ClientConfig = configuration.CurrentValue;
|
||||
Server = ClientConfig.Server;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动客户端
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="customLoginMsg">自定义登录信息,可进行扩展业务</param>
|
||||
public async void StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("===== FastTunnel Client Start =====");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await loginAsync(cancellationToken);
|
||||
await ReceiveServerAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("===== FastTunnel Client End =====");
|
||||
}
|
||||
|
||||
private async Task loginAsync(CancellationToken cancellationToken)
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var logMsg = GetLoginMsg(cancellationToken);
|
||||
|
||||
// 连接到的目标IP
|
||||
socket = new ClientWebSocket();
|
||||
socket.Options.RemoteCertificateValidationCallback = delegate { return true; };
|
||||
socket.Options.SetRequestHeader(FastTunnelConst.FASTTUNNEL_VERSION, AssemblyUtility.GetVersion().ToString());
|
||||
socket.Options.SetRequestHeader(FastTunnelConst.FASTTUNNEL_TOKEN, ClientConfig.Token);
|
||||
|
||||
_logger.LogInformation($"正在连接服务端 {Server.ServerAddr}:{Server.ServerPort}");
|
||||
await socket.ConnectAsync(
|
||||
new Uri($"{Server.Protocol}://{Server.ServerAddr}:{Server.ServerPort}"), cancellationToken);
|
||||
|
||||
_logger.LogDebug("连接服务端成功");
|
||||
|
||||
// 登录
|
||||
await socket.SendCmdAsync(MessageType.LogIn, logMsg, cancellationToken);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string GetLoginMsg(CancellationToken cancellationToken)
|
||||
{
|
||||
Server = ClientConfig.Server;
|
||||
return new LogInMassage
|
||||
{
|
||||
Webs = ClientConfig.Webs,
|
||||
Forwards = ClientConfig.Forwards,
|
||||
}.ToJson();
|
||||
}
|
||||
|
||||
private async Task ReceiveServerAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
byte[] buffer = new byte[FastTunnelConst.MAX_CMD_LENGTH];
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var res = await socket.ReceiveAsync(buffer, cancellationToken);
|
||||
var type = buffer[0];
|
||||
var content = Encoding.UTF8.GetString(buffer, 1, res.Count - 1);
|
||||
HandleServerRequestAsync(type, content, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private async void HandleServerRequestAsync(byte cmd, string ctx, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
try
|
||||
{
|
||||
IClientHandler handler;
|
||||
switch ((MessageType)cmd)
|
||||
{
|
||||
case MessageType.SwapMsg:
|
||||
case MessageType.Forward:
|
||||
handler = _newCustomerHandler;
|
||||
break;
|
||||
case MessageType.Log:
|
||||
handler = _logHandler;
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"未处理的消息:cmd={cmd}");
|
||||
}
|
||||
|
||||
await handler.HandlerMsgAsync(this, ctx, cancellationToken);
|
||||
await loginAsync(cancellationToken);
|
||||
await ReceiveServerAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex);
|
||||
_logger.LogError(ex.Message);
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop(CancellationToken cancellationToken)
|
||||
_logger.LogInformation("===== FastTunnel Client End =====");
|
||||
}
|
||||
|
||||
private async Task loginAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var logMsg = GetLoginMsg(cancellationToken);
|
||||
if (socket != null)
|
||||
{
|
||||
_logger.LogInformation("===== FastTunnel Client Stoping =====");
|
||||
socket.Abort();
|
||||
}
|
||||
|
||||
// 连接到的目标IP
|
||||
socket = new ClientWebSocket();
|
||||
socket.Options.RemoteCertificateValidationCallback = delegate { return true; };
|
||||
socket.Options.SetRequestHeader(FastTunnelConst.FASTTUNNEL_VERSION, AssemblyUtility.GetVersion().ToString());
|
||||
socket.Options.SetRequestHeader(FastTunnelConst.FASTTUNNEL_TOKEN, ClientConfig.Token);
|
||||
|
||||
_logger.LogInformation($"正在连接服务端 {Server.ServerAddr}:{Server.ServerPort}");
|
||||
await socket.ConnectAsync(
|
||||
new Uri($"{Server.Protocol}://{Server.ServerAddr}:{Server.ServerPort}"), cancellationToken);
|
||||
|
||||
_logger.LogDebug("连接服务端成功");
|
||||
|
||||
// 登录
|
||||
await socket.SendCmdAsync(MessageType.LogIn, logMsg, cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual string GetLoginMsg(CancellationToken cancellationToken)
|
||||
{
|
||||
Server = ClientConfig.Server;
|
||||
return new LogInMassage
|
||||
{
|
||||
Webs = ClientConfig.Webs,
|
||||
Forwards = ClientConfig.Forwards,
|
||||
}.ToJson();
|
||||
}
|
||||
|
||||
|
||||
protected async Task ReceiveServerAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var utility = new WebSocketUtility(socket, ProcessLine);
|
||||
await utility.ProcessLinesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private void ProcessLine(ReadOnlySequence<byte> line, CancellationToken cancellationToken)
|
||||
{
|
||||
HandleServerRequestAsync(line, cancellationToken);
|
||||
}
|
||||
|
||||
private void HandleServerRequestAsync(ReadOnlySequence<byte> line, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var row = line.ToArray();
|
||||
var cmd = row[0];
|
||||
IClientHandler handler;
|
||||
switch ((MessageType)cmd)
|
||||
{
|
||||
case MessageType.SwapMsg:
|
||||
case MessageType.Forward:
|
||||
handler = swapHandler;
|
||||
break;
|
||||
case MessageType.Log:
|
||||
handler = logHandler;
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"未处理的消息:cmd={cmd}");
|
||||
}
|
||||
|
||||
var content = Encoding.UTF8.GetString(line.Slice(1));
|
||||
handler.HandlerMsgAsync(this, content, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("===== FastTunnel Client Stoping =====");
|
||||
if (socket != null)
|
||||
{
|
||||
socket.Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace FastTunnel.Core.Client
|
|||
/// 客户端登录
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
internal void OnClientLogin(TunnelClient client)
|
||||
internal void ClientLogin(TunnelClient client)
|
||||
{
|
||||
Interlocked.Increment(ref ConnectedClientCount);
|
||||
logger.LogInformation($"客户端连接 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");
|
||||
|
@ -60,7 +60,7 @@ namespace FastTunnel.Core.Client
|
|||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
internal void OnClientLogout(TunnelClient client)
|
||||
internal void ClientLogout(TunnelClient client)
|
||||
{
|
||||
Interlocked.Decrement(ref ConnectedClientCount);
|
||||
logger.LogInformation($"客户端关闭 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// You may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastTunnel.Core.Client
|
||||
{
|
||||
public interface IFastTunnelClient
|
||||
{
|
||||
void StartAsync(CancellationToken cancellationToken);
|
||||
Task StartAsync(CancellationToken cancellationToken);
|
||||
|
||||
void Stop(CancellationToken cancellationToken);
|
||||
Task StopAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,113 +4,97 @@
|
|||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FastTunnel.Core.Client;
|
||||
using FastTunnel.Core.Config;
|
||||
using FastTunnel.Core.Forwarder.MiddleWare;
|
||||
using FastTunnel.Core.Filters;
|
||||
using FastTunnel.Core.Forwarder;
|
||||
using FastTunnel.Core.Forwarder.MiddleWare;
|
||||
using FastTunnel.Core.Handlers.Client;
|
||||
using FastTunnel.Core.Handlers.Server;
|
||||
using FastTunnel.Core.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Yarp.ReverseProxy.Forwarder;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using FastTunnel.Core.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using FastTunnel.Core.Models;
|
||||
using FastTunnel.Core.Handlers.Server;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using Yarp.ReverseProxy.Forwarder;
|
||||
|
||||
/* 项目“FastTunnel.Core (net5.0)”的未合并的更改
|
||||
在此之前:
|
||||
using Microsoft.AspNetCore.Http;
|
||||
namespace FastTunnel.Core.Extensions;
|
||||
|
||||
namespace FastTunnel.Core
|
||||
在此之后:
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using FastTunnel.Core.Extensions;
|
||||
using FastTunnel;
|
||||
using FastTunnel.Core;
|
||||
|
||||
namespace FastTunnel.Core
|
||||
*/
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace FastTunnel.Core.Extensions
|
||||
public static class ServicesExtensions
|
||||
{
|
||||
public static class ServicesExtensions
|
||||
/// <summary>
|
||||
/// 客户端依赖及HostedService
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public static void AddFastTunnelClient(this IServiceCollection services, IConfigurationSection configurationSection)
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户端依赖及HostedService
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public static void AddFastTunnelClient(this IServiceCollection services, IConfigurationSection configurationSection)
|
||||
services.Configure<DefaultClientConfig>(configurationSection);
|
||||
services.AddFastTunnelClient();
|
||||
}
|
||||
|
||||
public static void AddFastTunnelClient(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IFastTunnelClient, FastTunnelClient>()
|
||||
.AddSingleton<IExceptionFilter, FastTunnelExceptionFilter>()
|
||||
.AddSingleton<LogHandler>()
|
||||
.AddSingleton<SwapHandler>();
|
||||
|
||||
services.AddHostedService<ServiceFastTunnelClient>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加服务端后台进程
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public static void AddFastTunnelServer(this IServiceCollection services, IConfigurationSection configurationSection)
|
||||
{
|
||||
services.AddReverseProxy().LoadFromMemory();
|
||||
services.AddSingleton<IForwarderHttpClientFactory, FastTunnelForwarderHttpClientFactory>();
|
||||
services.AddHttpContextAccessor();
|
||||
|
||||
services.Configure<DefaultServerConfig>(configurationSection)
|
||||
.AddSingleton<IExceptionFilter, FastTunnelExceptionFilter>()
|
||||
.AddTransient<ILoginHandler, LoginHandler>()
|
||||
.AddSingleton<FastTunnelClientHandler>()
|
||||
.AddSingleton<FastTunnelSwapHandler>()
|
||||
.AddSingleton<FastTunnelServer>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务端中间件
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
public static void UseFastTunnelServer(this IApplicationBuilder app)
|
||||
{
|
||||
app.UseWebSockets();
|
||||
|
||||
var swapHandler = app.ApplicationServices.GetRequiredService<FastTunnelSwapHandler>();
|
||||
var clientHandler = app.ApplicationServices.GetRequiredService<FastTunnelClientHandler>();
|
||||
app.Use(clientHandler.Handle);
|
||||
app.Use(swapHandler.Handle);
|
||||
}
|
||||
|
||||
public static void MapFastTunnelServer(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapReverseProxy();
|
||||
endpoints.MapFallback(context =>
|
||||
{
|
||||
services.Configure<DefaultClientConfig>(configurationSection);
|
||||
services.AddFastTunnelClient();
|
||||
}
|
||||
|
||||
public static void AddFastTunnelClient(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IFastTunnelClient, FastTunnelClient>()
|
||||
.AddSingleton<IExceptionFilter, FastTunnelExceptionFilter>()
|
||||
.AddSingleton<LogHandler>()
|
||||
.AddSingleton<SwapHandler>();
|
||||
|
||||
services.AddHostedService<ServiceFastTunnelClient>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加服务端后台进程
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public static void AddFastTunnelServer(this IServiceCollection services, IConfigurationSection configurationSection)
|
||||
{
|
||||
services.AddReverseProxy().LoadFromMemory();
|
||||
services.AddSingleton<IForwarderHttpClientFactory, FastTunnelForwarderHttpClientFactory>();
|
||||
services.AddHttpContextAccessor();
|
||||
|
||||
services.Configure<DefaultServerConfig>(configurationSection)
|
||||
.AddSingleton<IExceptionFilter, FastTunnelExceptionFilter>()
|
||||
.AddTransient<ILoginHandler, LoginHandler>()
|
||||
.AddSingleton<FastTunnelClientHandler>()
|
||||
.AddSingleton<FastTunnelSwapHandler>()
|
||||
.AddSingleton<FastTunnelServer>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务端中间件
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
public static void UseFastTunnelServer(this IApplicationBuilder app)
|
||||
{
|
||||
app.UseWebSockets();
|
||||
|
||||
var swapHandler = app.ApplicationServices.GetRequiredService<FastTunnelSwapHandler>();
|
||||
var clientHandler = app.ApplicationServices.GetRequiredService<FastTunnelClientHandler>();
|
||||
app.Use(clientHandler.Handle);
|
||||
app.Use(swapHandler.Handle);
|
||||
}
|
||||
|
||||
public static void MapFastTunnelServer(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapReverseProxy();
|
||||
endpoints.MapFallback(context =>
|
||||
var options = context.RequestServices.GetRequiredService<IOptionsMonitor<DefaultServerConfig>>();
|
||||
var host = context.Request.Host.Host;
|
||||
if (!host.EndsWith(options.CurrentValue.WebDomain) || host.Equals(options.CurrentValue.WebDomain))
|
||||
{
|
||||
var options = context.RequestServices.GetRequiredService<IOptionsMonitor<DefaultServerConfig>>();
|
||||
var host = context.Request.Host.Host;
|
||||
if (!host.EndsWith(options.CurrentValue.WebDomain) || host.Equals(options.CurrentValue.WebDomain))
|
||||
{
|
||||
context.Response.StatusCode = 404;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.WriteAsync(TunnelResource.Page_NotFound, CancellationToken.None);
|
||||
context.Response.StatusCode = 404;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.WriteAsync(TunnelResource.Page_NotFound, CancellationToken.None);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,9 +27,6 @@ namespace FastTunnel.Core.Extensions
|
|||
}
|
||||
|
||||
var buffer = Encoding.UTF8.GetBytes($"{(char)type}{content}\n");
|
||||
if (type != MessageType.LogIn && buffer.Length > FastTunnelConst.MAX_CMD_LENGTH)
|
||||
throw new ArgumentOutOfRangeException(nameof(content));
|
||||
|
||||
await socket.SendAsync(buffer, WebSocketMessageType.Binary, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,5 @@ namespace FastTunnel.Core
|
|||
public const string FASTTUNNEL_VERSION = "FT_VERSION";
|
||||
public const string FASTTUNNEL_MSGID = "FT_MSGID";
|
||||
public const string FASTTUNNEL_TOKEN = "FT_TOKEN";
|
||||
|
||||
public const int MAX_CMD_LENGTH = 100;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,11 +52,6 @@ namespace FastTunnel.Core.Forwarder
|
|||
var contextRequest = _httpContextAccessor.HttpContext;
|
||||
//var lifetime = contextRequest.Features.Get<IConnectionLifetimeFeature>()!;
|
||||
|
||||
contextRequest.RequestAborted.Register(() =>
|
||||
{
|
||||
logger.LogDebug($"[ConnectionClosed]");
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var res = await proxyAsync(host, context, contextRequest.RequestAborted);
|
||||
|
|
|
@ -7,6 +7,8 @@ using FastTunnel.Core.Extensions;
|
|||
using FastTunnel.Core.Handlers.Server;
|
||||
using FastTunnel.Core.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
|
@ -66,19 +68,23 @@ namespace FastTunnel.Core.Forwarder.MiddleWare
|
|||
return;
|
||||
}
|
||||
|
||||
var client = new TunnelClient(webSocket, fastTunnelServer, loginHandler, context.Connection.RemoteIpAddress);
|
||||
var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
|
||||
var log = loggerFactory.CreateLogger<TunnelClient>();
|
||||
var client = new TunnelClient(webSocket, fastTunnelServer, loginHandler, context.Connection.RemoteIpAddress, log);
|
||||
client.ConnectionPort = context.Connection.LocalPort;
|
||||
|
||||
try
|
||||
{
|
||||
fastTunnelServer.OnClientLogin(client);
|
||||
await client.ReviceAsync(CancellationToken.None);
|
||||
|
||||
fastTunnelServer.OnClientLogout(client);
|
||||
fastTunnelServer.ClientLogin(client);
|
||||
await client.ReviceAsync(context.RequestAborted);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
fastTunnelServer.OnClientLogout(client);
|
||||
logger.LogError(ex, "客户端异常");
|
||||
}
|
||||
finally
|
||||
{
|
||||
fastTunnelServer.ClientLogout(client);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,13 @@ namespace FastTunnel.Core.Handlers.Client
|
|||
{
|
||||
public interface IClientHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理消息
|
||||
/// </summary>
|
||||
/// <param name="cleint"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,30 +4,25 @@
|
|||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using FastTunnel.Core.Config;
|
||||
using FastTunnel.Core.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using FastTunnel.Core.Extensions;
|
||||
using FastTunnel.Core.Client;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FastTunnel.Core.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FastTunnel.Core.Handlers.Client
|
||||
namespace FastTunnel.Core.Handlers.Client;
|
||||
|
||||
public class LogHandler : IClientHandler
|
||||
{
|
||||
public class LogHandler : IClientHandler
|
||||
private readonly ILogger<LogHandler> _logger;
|
||||
|
||||
public LogHandler(ILogger<LogHandler> logger)
|
||||
{
|
||||
ILogger<LogHandler> _logger;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LogHandler(ILogger<LogHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation(msg.Replace("\n", string.Empty));
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation(msg);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2019-2022 Gui.H. https://github.com/FastTunnel/FastTunnel
|
||||
// Copyright (c) 2019-2022 Gui.H. https://github.com/FastTunnel/FastTunnel
|
||||
// The FastTunnel licenses this file to you under the Apache License Version 2.0.
|
||||
// For more details,You may obtain License file at: https://github.com/FastTunnel/FastTunnel/blob/v2/LICENSE
|
||||
|
||||
|
@ -30,8 +30,14 @@ namespace FastTunnel.Core.Handlers.Client
|
|||
var requestId = msgs[0];
|
||||
var address = msgs[1];
|
||||
|
||||
await swap(cleint, requestId, address, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task swap(FastTunnelClient cleint, string requestId, string address, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug($"======Swap {requestId} Start======");
|
||||
using (Stream serverStream = await createRemote(requestId, cleint, cancellationToken))
|
||||
using (Stream localStream = await createLocal(requestId, address, cancellationToken))
|
||||
{
|
||||
|
@ -45,6 +51,10 @@ namespace FastTunnel.Core.Handlers.Client
|
|||
{
|
||||
_logger.LogError(ex, $"Swap error {requestId}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.LogDebug($"======Swap {requestId} End======");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Stream> createLocal(string requestId, string localhost, CancellationToken cancellationToken)
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FastTunnel.Core.Client;
|
||||
using FastTunnel.Core.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastTunnel.Core.Handlers.Server
|
||||
namespace FastTunnel.Core.Handlers.Server;
|
||||
|
||||
public interface ILoginHandler
|
||||
{
|
||||
public interface ILoginHandler
|
||||
{
|
||||
Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd);
|
||||
}
|
||||
Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd, CancellationToken cancellationToken);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FastTunnel.Core.Client;
|
||||
using FastTunnel.Core.Extensions;
|
||||
using FastTunnel.Core.Forwarder;
|
||||
|
@ -11,134 +17,126 @@ using FastTunnel.Core.Listener;
|
|||
using FastTunnel.Core.Models;
|
||||
using FastTunnel.Core.Models.Massage;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Yarp.ReverseProxy.Configuration;
|
||||
|
||||
namespace FastTunnel.Core.Handlers.Server
|
||||
{
|
||||
public class LoginHandler : ILoginHandler
|
||||
{
|
||||
readonly ILogger logger;
|
||||
readonly IProxyConfigProvider proxyConfig;
|
||||
public const bool NeedRecive = true;
|
||||
namespace FastTunnel.Core.Handlers.Server;
|
||||
|
||||
public LoginHandler(ILogger<LoginHandler> logger, IProxyConfigProvider proxyConfig)
|
||||
public class LoginHandler : ILoginHandler
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly IProxyConfigProvider proxyConfig;
|
||||
public const bool NeedRecive = true;
|
||||
|
||||
public LoginHandler(ILogger<LoginHandler> logger, IProxyConfigProvider proxyConfig)
|
||||
{
|
||||
this.proxyConfig = proxyConfig;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
protected async Task HandleLoginAsync(FastTunnelServer server, TunnelClient client, LogInMassage requet, CancellationToken cancellationToken)
|
||||
{
|
||||
var hasTunnel = false;
|
||||
|
||||
var tips = new List<string>();
|
||||
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $"穿透协议 | 映射关系(公网=>内网)", cancellationToken);
|
||||
Thread.Sleep(300);
|
||||
|
||||
if (requet.Webs != null && requet.Webs.Any())
|
||||
{
|
||||
this.proxyConfig = proxyConfig;
|
||||
this.logger = logger;
|
||||
hasTunnel = true;
|
||||
foreach (var item in requet.Webs)
|
||||
{
|
||||
var hostName = $"{item.SubDomain}.{server.ServerOption.CurrentValue.WebDomain}".Trim().ToLower();
|
||||
var info = new WebInfo { Socket = client.webSocket, WebConfig = item };
|
||||
|
||||
logger.LogDebug($"new domain '{hostName}'");
|
||||
server.WebList.AddOrUpdate(hostName, info, (key, oldInfo) => { return info; });
|
||||
(proxyConfig as FastTunnelInMemoryConfigProvider).AddWeb(hostName);
|
||||
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $" HTTP | http://{hostName}:{client.ConnectionPort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
|
||||
client.AddWeb(info);
|
||||
|
||||
if (item.WWW != null)
|
||||
{
|
||||
foreach (var www in item.WWW)
|
||||
{
|
||||
// TODO:validateDomain
|
||||
hostName = www.Trim().ToLower();
|
||||
server.WebList.AddOrUpdate(www, info, (key, oldInfo) => { return info; });
|
||||
(proxyConfig as FastTunnelInMemoryConfigProvider).AddWeb(www);
|
||||
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $" HTTP | http://{www}:{client.ConnectionPort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
|
||||
client.AddWeb(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task HandleLoginAsync(FastTunnelServer server, TunnelClient client, LogInMassage requet)
|
||||
if (requet.Forwards != null && requet.Forwards.Any())
|
||||
{
|
||||
bool hasTunnel = false;
|
||||
|
||||
List<string> tips = new List<string>();
|
||||
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $"穿透协议 | 映射关系(公网=>内网)", CancellationToken.None);
|
||||
Thread.Sleep(300);
|
||||
|
||||
if (requet.Webs != null && requet.Webs.Any())
|
||||
if (server.ServerOption.CurrentValue.EnableForward)
|
||||
{
|
||||
hasTunnel = true;
|
||||
foreach (var item in requet.Webs)
|
||||
|
||||
foreach (var item in requet.Forwards)
|
||||
{
|
||||
var hostName = $"{item.SubDomain}.{server.ServerOption.CurrentValue.WebDomain}".Trim().ToLower();
|
||||
var info = new WebInfo { Socket = client.webSocket, WebConfig = item };
|
||||
|
||||
logger.LogDebug($"new domain '{hostName}'");
|
||||
server.WebList.AddOrUpdate(hostName, info, (key, oldInfo) => { return info; });
|
||||
(proxyConfig as FastTunnelInMemoryConfigProvider).AddWeb(hostName);
|
||||
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $" HTTP | http://{hostName}:{client.ConnectionPort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
|
||||
client.AddWeb(info);
|
||||
|
||||
if (item.WWW != null)
|
||||
try
|
||||
{
|
||||
foreach (var www in item.WWW)
|
||||
{
|
||||
// TODO:validateDomain
|
||||
hostName = www.Trim().ToLower();
|
||||
server.WebList.AddOrUpdate(www, info, (key, oldInfo) => { return info; });
|
||||
(proxyConfig as FastTunnelInMemoryConfigProvider).AddWeb(www);
|
||||
if (item.LocalPort == 3389)
|
||||
tips.Add("您已将3389端口暴露,请确保您的PC密码足够安全。");
|
||||
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $" HTTP | http://{www}:{client.ConnectionPort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
|
||||
client.AddWeb(info);
|
||||
if (item.LocalPort == 22)
|
||||
tips.Add("您已将22端口暴露,请确保您的PC密码足够安全。");
|
||||
|
||||
if (server.ForwardList.TryGetValue(item.RemotePort, out var old))
|
||||
{
|
||||
logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}");
|
||||
old.Listener.Stop();
|
||||
server.ForwardList.TryRemove(item.RemotePort, out var _);
|
||||
}
|
||||
|
||||
// TODO: 客户端离线时销毁
|
||||
var ls = new PortProxyListener("0.0.0.0", item.RemotePort, logger, client.webSocket);
|
||||
ls.Start(new ForwardDispatcher(logger, server, item));
|
||||
|
||||
var forwardInfo = new ForwardInfo<ForwardHandlerArg> { Listener = ls, Socket = client.webSocket, SSHConfig = item };
|
||||
|
||||
// TODO: 客户端离线时销毁
|
||||
server.ForwardList.TryAdd(item.RemotePort, forwardInfo);
|
||||
logger.LogDebug($"SSH proxy success: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");
|
||||
|
||||
client.AddForward(forwardInfo);
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $" TCP | {server.ServerOption.CurrentValue.WebDomain}:{item.RemotePort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError($"SSH proxy error: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");
|
||||
logger.LogError(ex.Message);
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, ex.Message, CancellationToken.None);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requet.Forwards != null && requet.Forwards.Any())
|
||||
else
|
||||
{
|
||||
if (server.ServerOption.CurrentValue.EnableForward)
|
||||
{
|
||||
hasTunnel = true;
|
||||
|
||||
foreach (var item in requet.Forwards)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (item.LocalPort == 3389)
|
||||
tips.Add("您已将3389端口暴露,请确保您的PC密码足够安全。");
|
||||
|
||||
if (item.LocalPort == 22)
|
||||
tips.Add("您已将22端口暴露,请确保您的PC密码足够安全。");
|
||||
|
||||
ForwardInfo<ForwardHandlerArg> old;
|
||||
if (server.ForwardList.TryGetValue(item.RemotePort, out old))
|
||||
{
|
||||
logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}");
|
||||
old.Listener.Stop();
|
||||
server.ForwardList.TryRemove(item.RemotePort, out ForwardInfo<ForwardHandlerArg> _);
|
||||
}
|
||||
|
||||
// TODO: 客户端离线时销毁
|
||||
var ls = new PortProxyListener("0.0.0.0", item.RemotePort, logger, client.webSocket);
|
||||
ls.Start(new ForwardDispatcher(logger, server, item));
|
||||
|
||||
var forwardInfo = new ForwardInfo<ForwardHandlerArg> { Listener = ls, Socket = client.webSocket, SSHConfig = item };
|
||||
|
||||
// TODO: 客户端离线时销毁
|
||||
server.ForwardList.TryAdd(item.RemotePort, forwardInfo);
|
||||
logger.LogDebug($"SSH proxy success: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");
|
||||
|
||||
client.AddForward(forwardInfo);
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, $" TCP | {server.ServerOption.CurrentValue.WebDomain}:{item.RemotePort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError($"SSH proxy error: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");
|
||||
logger.LogError(ex.Message);
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, ex.Message, CancellationToken.None);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.ForwardDisabled, CancellationToken.None);
|
||||
}
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.ForwardDisabled, CancellationToken.None);
|
||||
}
|
||||
|
||||
foreach (var item in tips)
|
||||
{
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, item, CancellationToken.None);
|
||||
}
|
||||
|
||||
if (!hasTunnel)
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.NoTunnel, CancellationToken.None);
|
||||
}
|
||||
|
||||
public virtual async Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd)
|
||||
foreach (var item in tips)
|
||||
{
|
||||
var msg = JsonSerializer.Deserialize<LogInMassage>(lineCmd);
|
||||
await HandleLoginAsync(fastTunnelServer, tunnelClient, msg);
|
||||
return NeedRecive;
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, item, CancellationToken.None);
|
||||
}
|
||||
|
||||
if (!hasTunnel)
|
||||
await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.NoTunnel, CancellationToken.None);
|
||||
}
|
||||
|
||||
public virtual async Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd, CancellationToken cancellationToken)
|
||||
{
|
||||
var msg = JsonSerializer.Deserialize<LogInMassage>(lineCmd);
|
||||
await HandleLoginAsync(fastTunnelServer, tunnelClient, msg, cancellationToken);
|
||||
return NeedRecive;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,104 +4,108 @@
|
|||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using FastTunnel.Core.Client;
|
||||
using FastTunnel.Core.Handlers.Server;
|
||||
using FastTunnel.Core.Protocol;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FastTunnel.Core.Client;
|
||||
using FastTunnel.Core.Handlers.Server;
|
||||
using FastTunnel.Core.Utilitys;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FastTunnel.Core.Models
|
||||
namespace FastTunnel.Core.Models;
|
||||
|
||||
public class TunnelClient
|
||||
{
|
||||
public class TunnelClient
|
||||
public WebSocket webSocket { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 服务端端口号
|
||||
/// </summary>
|
||||
public int ConnectionPort { get; set; }
|
||||
|
||||
private readonly FastTunnelServer fastTunnelServer;
|
||||
private readonly ILoginHandler loginHandler;
|
||||
private readonly ILogger<TunnelClient> logger;
|
||||
|
||||
public IPAddress RemoteIpAddress { get; private set; }
|
||||
|
||||
private readonly IList<WebInfo> webInfos = new List<WebInfo>();
|
||||
private readonly IList<ForwardInfo<ForwardHandlerArg>> forwardInfos = new List<ForwardInfo<ForwardHandlerArg>>();
|
||||
|
||||
public TunnelClient(
|
||||
WebSocket webSocket, FastTunnelServer fastTunnelServer,
|
||||
ILoginHandler loginHandler, IPAddress remoteIpAddress, ILogger<TunnelClient> logger)
|
||||
{
|
||||
public WebSocket webSocket { get; private set; }
|
||||
this.logger = logger;
|
||||
this.webSocket = webSocket;
|
||||
this.fastTunnelServer = fastTunnelServer;
|
||||
this.loginHandler = loginHandler;
|
||||
this.RemoteIpAddress = remoteIpAddress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务端端口号
|
||||
/// </summary>
|
||||
public int ConnectionPort { get; set; }
|
||||
internal void AddWeb(WebInfo info)
|
||||
{
|
||||
webInfos.Add(info);
|
||||
}
|
||||
|
||||
readonly FastTunnelServer fastTunnelServer;
|
||||
readonly ILoginHandler loginHandler;
|
||||
internal void AddForward(ForwardInfo<ForwardHandlerArg> forwardInfo)
|
||||
{
|
||||
forwardInfos.Add(forwardInfo);
|
||||
}
|
||||
|
||||
public IPAddress RemoteIpAddress { get; private set; }
|
||||
/// <summary>
|
||||
/// 接收客户端的消息
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ReviceAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var utility = new WebSocketUtility(webSocket, ProcessLine);
|
||||
await utility.ProcessLinesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
readonly IList<WebInfo> webInfos = new List<WebInfo>();
|
||||
readonly IList<ForwardInfo<ForwardHandlerArg>> forwardInfos = new List<ForwardInfo<ForwardHandlerArg>>();
|
||||
|
||||
public TunnelClient(WebSocket webSocket, FastTunnelServer fastTunnelServer, ILoginHandler loginHandler, IPAddress remoteIpAddress)
|
||||
private async void ProcessLine(ReadOnlySequence<byte> line, CancellationToken cancellationToken)
|
||||
{
|
||||
var cmd = Encoding.UTF8.GetString(line);
|
||||
await HandleCmdAsync(this, cmd, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<bool> HandleCmdAsync(TunnelClient tunnelClient, string lineCmd, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.webSocket = webSocket;
|
||||
this.fastTunnelServer = fastTunnelServer;
|
||||
this.loginHandler = loginHandler;
|
||||
this.RemoteIpAddress = remoteIpAddress;
|
||||
return await loginHandler.HandlerMsg(fastTunnelServer, tunnelClient, lineCmd.Substring(1), cancellationToken);
|
||||
}
|
||||
|
||||
internal void AddWeb(WebInfo info)
|
||||
catch (Exception ex)
|
||||
{
|
||||
webInfos.Add(info);
|
||||
}
|
||||
|
||||
internal void AddForward(ForwardInfo<ForwardHandlerArg> forwardInfo)
|
||||
{
|
||||
forwardInfos.Add(forwardInfo);
|
||||
}
|
||||
|
||||
public async Task ReviceAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var buffer = new byte[FastTunnelConst.MAX_CMD_LENGTH];
|
||||
var tunnelProtocol = new TunnelProtocol();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var res = await webSocket.ReceiveAsync(buffer, cancellationToken);
|
||||
var cmds = tunnelProtocol.HandleBuffer(buffer, 0, res.Count);
|
||||
if (cmds == null) continue;
|
||||
|
||||
foreach (var item in cmds)
|
||||
{
|
||||
if (!await HandleCmdAsync(this, item))
|
||||
{
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
private async Task<bool> HandleCmdAsync(TunnelClient tunnelClient, string lineCmd)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await loginHandler.HandlerMsg(fastTunnelServer, tunnelClient, lineCmd.Substring(1));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理客户端消息失败:cmd={lineCmd} {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Logout()
|
||||
{
|
||||
// forward监听终止
|
||||
if (forwardInfos != null)
|
||||
{
|
||||
foreach (var item in forwardInfos)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.Listener.Stop();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
webSocket.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
|
||||
Console.WriteLine($"处理客户端消息失败:cmd={lineCmd} {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Logout()
|
||||
{
|
||||
// forward监听终止
|
||||
if (forwardInfos != null)
|
||||
{
|
||||
foreach (var item in forwardInfos)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.Listener.Stop();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
webSocket.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// You may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using FastTunnel.Core.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace FastTunnel.Core.Protocol
|
||||
{
|
||||
public class TunnelProtocol
|
||||
{
|
||||
string massgeTemp;
|
||||
string m_sectionFlag = "\n";
|
||||
|
||||
public IEnumerable<string> HandleBuffer(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var words = buffer.GetString(offset, count);
|
||||
var sum = massgeTemp + words;
|
||||
|
||||
if (sum.Contains(m_sectionFlag))
|
||||
{
|
||||
var array = (sum).Split(m_sectionFlag);
|
||||
massgeTemp = null;
|
||||
var fullMsg = words.EndsWith(m_sectionFlag);
|
||||
|
||||
if (!fullMsg)
|
||||
{
|
||||
massgeTemp = array[array.Length - 1];
|
||||
}
|
||||
|
||||
return array.Take(array.Length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
massgeTemp = sum;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,14 +33,12 @@ namespace FastTunnel.Core.Services
|
|||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_fastTunnelClient.StartAsync(cancellationToken);
|
||||
await Task.CompletedTask;
|
||||
await _fastTunnelClient.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_fastTunnelClient.Stop(cancellationToken);
|
||||
return Task.CompletedTask;
|
||||
await _fastTunnelClient.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
|
|
132
FastTunnel.Core/Utilitys/WebSocketUtility.cs
Normal file
132
FastTunnel.Core/Utilitys/WebSocketUtility.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// You may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
|
||||
// Copyright (c) 2019 Gui.H
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FastTunnel.Core.Utilitys;
|
||||
|
||||
public class WebSocketUtility
|
||||
{
|
||||
private readonly WebSocket webSocket;
|
||||
|
||||
public WebSocketUtility(WebSocket webSocket, Action<ReadOnlySequence<byte>, CancellationToken> processLine)
|
||||
{
|
||||
this.webSocket = webSocket;
|
||||
ProcessLine = processLine;
|
||||
}
|
||||
|
||||
public Action<ReadOnlySequence<byte>, CancellationToken> ProcessLine { get; }
|
||||
|
||||
public async Task ProcessLinesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var pipe = new Pipe();
|
||||
var writing = FillPipeAsync(webSocket, pipe.Writer, cancellationToken);
|
||||
var reading = ReadPipeAsync(pipe.Reader, cancellationToken);
|
||||
|
||||
await Task.WhenAll(reading, writing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取socket收到的消息写入Pipe
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private async Task FillPipeAsync(WebSocket socket, PipeWriter writer, CancellationToken cancellationToken)
|
||||
{
|
||||
const int minimumBufferSize = 512;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Allocate at least 512 bytes from the PipeWriter.
|
||||
var memory = writer.GetMemory(minimumBufferSize);
|
||||
|
||||
try
|
||||
{
|
||||
var bytesRead = await socket.ReceiveAsync(memory, cancellationToken);
|
||||
if (bytesRead.Count == 0 || bytesRead.EndOfMessage || bytesRead.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Tell the PipeWriter how much was read from the Socket.
|
||||
writer.Advance(bytesRead.Count);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Make the data available to the PipeReader.
|
||||
var result = await writer.FlushAsync(cancellationToken);
|
||||
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// By completing PipeWriter, tell the PipeReader that there's no more data coming.
|
||||
await writer.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从Pipe中读取收到的消息
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
private async Task ReadPipeAsync(PipeReader reader, CancellationToken cancellationToken)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var result = await reader.ReadAsync(cancellationToken);
|
||||
var buffer = result.Buffer;
|
||||
|
||||
while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line))
|
||||
{
|
||||
// Process the line.
|
||||
ProcessLine(line, cancellationToken);
|
||||
}
|
||||
|
||||
// Tell the PipeReader how much of the buffer has been consumed.
|
||||
reader.AdvanceTo(buffer.Start, buffer.End);
|
||||
|
||||
// Stop reading if there's no more data coming.
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the PipeReader as complete.
|
||||
await reader.CompleteAsync();
|
||||
}
|
||||
|
||||
bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)
|
||||
{
|
||||
// Look for a EOL in the buffer.
|
||||
SequencePosition? position = buffer.PositionOf((byte)'\n');
|
||||
|
||||
if (position == null)
|
||||
{
|
||||
line = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip the line + the \n.
|
||||
line = buffer.Slice(0, position.Value);
|
||||
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -36,4 +36,6 @@
|
|||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties config_4appsettings_1development_1json__JsonSchema="https://json.schemastore.org/appsettings.json" /></VisualStudio></ProjectExtensions>
|
||||
</Project>
|
||||
|
|
|
@ -21,15 +21,11 @@ public class Program
|
|||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.UseSerilog((hostBuilderContext, services, loggerConfiguration) =>
|
||||
{
|
||||
var enableFileLog = (bool)(hostBuilderContext.Configuration.GetSection("EnableFileLog")?.Get(typeof(bool)) ?? false);
|
||||
loggerConfiguration.WriteTo.Console();
|
||||
if (enableFileLog)
|
||||
{
|
||||
loggerConfiguration.WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7);
|
||||
}
|
||||
})
|
||||
.UseSerilog((context, services, configuration) => configuration
|
||||
.ReadFrom.Configuration(context.Configuration)
|
||||
.ReadFrom.Services(services)
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console())
|
||||
.UseWindowsService()
|
||||
.ConfigureWebHost(webHostBuilder =>
|
||||
{
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
// Trace Debug Information Warning Error
|
||||
"Default": "Debug",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Debug"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"EnableFileLog": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
// Trace Debug Information Warning Error
|
||||
"Serilog": {
|
||||
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
"Override": {
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"WriteTo": [
|
||||
//{
|
||||
// "Name": "File",
|
||||
// "Args": {
|
||||
// "path": "Logs/log-.log",
|
||||
// "rollingInterval": 3
|
||||
// }
|
||||
//}
|
||||
]
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
// Http&客户端通讯端口
|
||||
|
|
Loading…
Reference in New Issue
Block a user