使用pipline.io

This commit is contained in:
Gui.H 2022-07-10 22:34:13 +08:00
parent a709796bc6
commit 4ef6ac1402
25 changed files with 657 additions and 551 deletions

View File

@ -7,6 +7,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.0-preview.3.22175.4" /> <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.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.Console" Version="4.0.2-dev-00890" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" />
</ItemGroup> </ItemGroup>
@ -16,6 +17,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="appsettings.json"> <None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
@ -28,9 +32,7 @@
</ItemGroup> </ItemGroup>
<ProjectExtensions> <ProjectExtensions>
<VisualStudio> <VisualStudio><UserProperties appsettings_1development_1json__JsonSchema="https://json.schemastore.org/appsettings.json" appsettings_1json__JsonSchema="" /></VisualStudio>
<UserProperties appsettings_1json__JsonSchema="" />
</VisualStudio>
</ProjectExtensions> </ProjectExtensions>
</Project> </Project>

View File

@ -30,20 +30,18 @@ class Program
public static IHostBuilder CreateHostBuilder(string[] args) => public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args) Host.CreateDefaultBuilder(args)
.UseSerilog((hostBuilderContext, services, loggerConfiguration) => .UseSerilog((context, services, loggerConfiguration) =>
{ {
var enableFileLog = (bool)(hostBuilderContext.Configuration.GetSection("EnableFileLog")?.Get(typeof(bool)) ?? false); loggerConfiguration.ReadFrom.Configuration(context.Configuration)
loggerConfiguration.WriteTo.Console(); .ReadFrom.Services(services)
if (enableFileLog) .Enrich.FromLogContext()
{ .WriteTo.Console();
loggerConfiguration.WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7);
}
}) })
.UseWindowsService() .UseWindowsService()
.ConfigureServices((hostContext, services) => .ConfigureServices((hostContext, services) =>
{ {
// -------------------FastTunnel START------------------ // -------------------FastTunnel START------------------
services.AddFastTunnelClient(hostContext.Configuration.GetSection("ClientSettings")); services.AddFastTunnelClient(hostContext.Configuration.GetSection("FastTunnel"));
// -------------------FastTunnel EDN-------------------- // -------------------FastTunnel EDN--------------------
}); });
} }

View File

@ -0,0 +1,7 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug"
}
}
}

View File

@ -1,15 +1,24 @@
{ {
"Logging": { "Serilog": {
"LogLevel": { "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
// Trace Debug Information Warning Error "MinimumLevel": {
"Default": "Information", "Default": "Information",
"Override": {
"Microsoft": "Warning", "Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information" "Microsoft.Hosting.Lifetime": "Information"
} }
}, },
// "WriteTo": [
"EnableFileLog": true, //{
"ClientSettings": { // "Name": "File",
// "Args": {
// "path": "Logs/log-.log",
// "rollingInterval": 3
// }
//}
]
},
"FastTunnel": {
"Server": { "Server": {
// [] ip/urls // [] ip/urls
"ServerAddr": "127.0.0.1", "ServerAddr": "127.0.0.1",

View File

@ -4,31 +4,36 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE // https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using FastTunnel.Core.Config;
using FastTunnel.Core.Models;
using System; using System;
using System.Text; using System.Buffers;
using System.Threading.Tasks; using System.Net.Sockets;
using FastTunnel.Core.Extensions;
using System.Threading;
using Microsoft.Extensions.Logging;
using FastTunnel.Core.Handlers.Client;
using Microsoft.Extensions.Options;
using System.Net.WebSockets; 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.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; private ClientWebSocket socket;
protected readonly ILogger<FastTunnelClient> _logger; protected readonly ILogger<FastTunnelClient> _logger;
private readonly SwapHandler _newCustomerHandler; protected DefaultClientConfig ClientConfig { get; private set; }
private readonly LogHandler _logHandler;
public 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 SuiDaoServer Server { get; protected set; }
@ -38,9 +43,10 @@ namespace FastTunnel.Core.Client
LogHandler logHandler, LogHandler logHandler,
IOptionsMonitor<DefaultClientConfig> configuration) IOptionsMonitor<DefaultClientConfig> configuration)
{ {
ReadOnlySpan<int> span = new ReadOnlySpan<int>();
_logger = logger; _logger = logger;
_newCustomerHandler = newCustomerHandler; swapHandler = newCustomerHandler;
_logHandler = logHandler; this.logHandler = logHandler;
ClientConfig = configuration.CurrentValue; ClientConfig = configuration.CurrentValue;
Server = ClientConfig.Server; Server = ClientConfig.Server;
} }
@ -48,9 +54,7 @@ namespace FastTunnel.Core.Client
/// <summary> /// <summary>
/// 启动客户端 /// 启动客户端
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> public virtual async Task StartAsync(CancellationToken cancellationToken)
/// <param name="customLoginMsg">自定义登录信息,可进行扩展业务</param>
public async void StartAsync(CancellationToken cancellationToken)
{ {
_logger.LogInformation("===== FastTunnel Client Start ====="); _logger.LogInformation("===== FastTunnel Client Start =====");
@ -72,10 +76,12 @@ namespace FastTunnel.Core.Client
} }
private async Task loginAsync(CancellationToken cancellationToken) private async Task loginAsync(CancellationToken cancellationToken)
{
try
{ {
var logMsg = GetLoginMsg(cancellationToken); var logMsg = GetLoginMsg(cancellationToken);
if (socket != null)
{
socket.Abort();
}
// 连接到的目标IP // 连接到的目标IP
socket = new ClientWebSocket(); socket = new ClientWebSocket();
@ -92,13 +98,8 @@ namespace FastTunnel.Core.Client
// 登录 // 登录
await socket.SendCmdAsync(MessageType.LogIn, logMsg, cancellationToken); await socket.SendCmdAsync(MessageType.LogIn, logMsg, cancellationToken);
} }
catch (Exception)
{
throw;
}
}
public virtual string GetLoginMsg(CancellationToken cancellationToken) protected virtual string GetLoginMsg(CancellationToken cancellationToken)
{ {
Server = ClientConfig.Server; Server = ClientConfig.Server;
return new LogInMassage return new LogInMassage
@ -108,39 +109,40 @@ namespace FastTunnel.Core.Client
}.ToJson(); }.ToJson();
} }
private async Task ReceiveServerAsync(CancellationToken cancellationToken)
protected async Task ReceiveServerAsync(CancellationToken cancellationToken)
{ {
byte[] buffer = new byte[FastTunnelConst.MAX_CMD_LENGTH]; var utility = new WebSocketUtility(socket, ProcessLine);
while (!cancellationToken.IsCancellationRequested) await utility.ProcessLinesAsync(cancellationToken);
{
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) private void ProcessLine(ReadOnlySequence<byte> line, CancellationToken cancellationToken)
{ {
await Task.Yield(); HandleServerRequestAsync(line, cancellationToken);
}
private void HandleServerRequestAsync(ReadOnlySequence<byte> line, CancellationToken cancellationToken)
{
try try
{ {
var row = line.ToArray();
var cmd = row[0];
IClientHandler handler; IClientHandler handler;
switch ((MessageType)cmd) switch ((MessageType)cmd)
{ {
case MessageType.SwapMsg: case MessageType.SwapMsg:
case MessageType.Forward: case MessageType.Forward:
handler = _newCustomerHandler; handler = swapHandler;
break; break;
case MessageType.Log: case MessageType.Log:
handler = _logHandler; handler = logHandler;
break; break;
default: default:
throw new Exception($"未处理的消息cmd={cmd}"); throw new Exception($"未处理的消息cmd={cmd}");
} }
await handler.HandlerMsgAsync(this, ctx, cancellationToken); var content = Encoding.UTF8.GetString(line.Slice(1));
handler.HandlerMsgAsync(this, content, cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -148,9 +150,12 @@ namespace FastTunnel.Core.Client
} }
} }
public void Stop(CancellationToken cancellationToken) public async Task StopAsync(CancellationToken cancellationToken)
{ {
_logger.LogInformation("===== FastTunnel Client Stoping ====="); _logger.LogInformation("===== FastTunnel Client Stoping =====");
if (socket != null)
{
socket.Abort();
} }
} }
} }

View File

@ -48,7 +48,7 @@ namespace FastTunnel.Core.Client
/// 客户端登录 /// 客户端登录
/// </summary> /// </summary>
/// <param name="client"></param> /// <param name="client"></param>
internal void OnClientLogin(TunnelClient client) internal void ClientLogin(TunnelClient client)
{ {
Interlocked.Increment(ref ConnectedClientCount); Interlocked.Increment(ref ConnectedClientCount);
logger.LogInformation($"客户端连接 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}"); logger.LogInformation($"客户端连接 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");
@ -60,7 +60,7 @@ namespace FastTunnel.Core.Client
/// </summary> /// </summary>
/// <param name="client"></param> /// <param name="client"></param>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
internal void OnClientLogout(TunnelClient client) internal void ClientLogout(TunnelClient client)
{ {
Interlocked.Decrement(ref ConnectedClientCount); Interlocked.Decrement(ref ConnectedClientCount);
logger.LogInformation($"客户端关闭 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}"); logger.LogInformation($"客户端关闭 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");

View File

@ -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 not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE // https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Client namespace FastTunnel.Core.Client
{ {
public interface IFastTunnelClient public interface IFastTunnelClient
{ {
void StartAsync(CancellationToken cancellationToken); Task StartAsync(CancellationToken cancellationToken);
void Stop(CancellationToken cancellationToken); Task StopAsync(CancellationToken cancellationToken);
} }
} }

View File

@ -4,44 +4,29 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE // https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Core.Config; using FastTunnel.Core.Config;
using FastTunnel.Core.Forwarder.MiddleWare; using FastTunnel.Core.Filters;
using FastTunnel.Core.Forwarder; using FastTunnel.Core.Forwarder;
using FastTunnel.Core.Forwarder.MiddleWare;
using FastTunnel.Core.Handlers.Client; using FastTunnel.Core.Handlers.Client;
using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Services; 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.Configuration;
using Microsoft.Extensions.DependencyInjection; 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 Microsoft.Extensions.Options;
using System.Threading.Tasks; using Yarp.ReverseProxy.Forwarder;
using System.Threading;
/* FastTunnel.Core (net5.0) namespace FastTunnel.Core.Extensions;
:
using Microsoft.AspNetCore.Http;
namespace FastTunnel.Core public static class ServicesExtensions
:
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
{
/// <summary> /// <summary>
/// 客户端依赖及HostedService /// 客户端依赖及HostedService
/// </summary> /// </summary>
@ -112,5 +97,4 @@ namespace FastTunnel.Core.Extensions
return Task.CompletedTask; return Task.CompletedTask;
}); });
} }
}
} }

View File

@ -27,9 +27,6 @@ namespace FastTunnel.Core.Extensions
} }
var buffer = Encoding.UTF8.GetBytes($"{(char)type}{content}\n"); 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); await socket.SendAsync(buffer, WebSocketMessageType.Binary, false, cancellationToken);
} }
} }

View File

@ -17,7 +17,5 @@ namespace FastTunnel.Core
public const string FASTTUNNEL_VERSION = "FT_VERSION"; public const string FASTTUNNEL_VERSION = "FT_VERSION";
public const string FASTTUNNEL_MSGID = "FT_MSGID"; public const string FASTTUNNEL_MSGID = "FT_MSGID";
public const string FASTTUNNEL_TOKEN = "FT_TOKEN"; public const string FASTTUNNEL_TOKEN = "FT_TOKEN";
public const int MAX_CMD_LENGTH = 100;
} }
} }

View File

@ -52,11 +52,6 @@ namespace FastTunnel.Core.Forwarder
var contextRequest = _httpContextAccessor.HttpContext; var contextRequest = _httpContextAccessor.HttpContext;
//var lifetime = contextRequest.Features.Get<IConnectionLifetimeFeature>()!; //var lifetime = contextRequest.Features.Get<IConnectionLifetimeFeature>()!;
contextRequest.RequestAborted.Register(() =>
{
logger.LogDebug($"[ConnectionClosed]");
});
try try
{ {
var res = await proxyAsync(host, context, contextRequest.RequestAborted); var res = await proxyAsync(host, context, contextRequest.RequestAborted);

View File

@ -7,6 +7,8 @@ using FastTunnel.Core.Extensions;
using FastTunnel.Core.Handlers.Server; using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Models; using FastTunnel.Core.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Net.WebSockets; using System.Net.WebSockets;
@ -66,19 +68,23 @@ namespace FastTunnel.Core.Forwarder.MiddleWare
return; 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; client.ConnectionPort = context.Connection.LocalPort;
try try
{ {
fastTunnelServer.OnClientLogin(client); fastTunnelServer.ClientLogin(client);
await client.ReviceAsync(CancellationToken.None); await client.ReviceAsync(context.RequestAborted);
fastTunnelServer.OnClientLogout(client);
} }
catch (Exception) catch (Exception ex)
{ {
fastTunnelServer.OnClientLogout(client); logger.LogError(ex, "客户端异常");
}
finally
{
fastTunnelServer.ClientLogout(client);
} }
} }

View File

@ -17,6 +17,13 @@ namespace FastTunnel.Core.Handlers.Client
{ {
public interface IClientHandler 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); Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken);
} }
} }

View File

@ -4,20 +4,16 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE // https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H // 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;
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;
{
ILogger<LogHandler> _logger;
public LogHandler(ILogger<LogHandler> logger) public LogHandler(ILogger<LogHandler> logger)
{ {
@ -26,8 +22,7 @@ namespace FastTunnel.Core.Handlers.Client
public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken) public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken)
{ {
_logger.LogInformation(msg.Replace("\n", string.Empty)); _logger.LogInformation(msg);
await Task.CompletedTask; await Task.CompletedTask;
} }
}
} }

View File

@ -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. // 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 // 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 requestId = msgs[0];
var address = msgs[1]; var address = msgs[1];
await swap(cleint, requestId, address, cancellationToken);
}
private async Task swap(FastTunnelClient cleint, string requestId, string address, CancellationToken cancellationToken)
{
try try
{ {
_logger.LogDebug($"======Swap {requestId} Start======");
using (Stream serverStream = await createRemote(requestId, cleint, cancellationToken)) using (Stream serverStream = await createRemote(requestId, cleint, cancellationToken))
using (Stream localStream = await createLocal(requestId, address, cancellationToken)) using (Stream localStream = await createLocal(requestId, address, cancellationToken))
{ {
@ -45,6 +51,10 @@ namespace FastTunnel.Core.Handlers.Client
{ {
_logger.LogError(ex, $"Swap error {requestId}"); _logger.LogError(ex, $"Swap error {requestId}");
} }
finally
{
_logger.LogDebug($"======Swap {requestId} End======");
}
} }
private async Task<Stream> createLocal(string requestId, string localhost, CancellationToken cancellationToken) private async Task<Stream> createLocal(string requestId, string localhost, CancellationToken cancellationToken)

View File

@ -4,14 +4,14 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE // https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Core.Models; 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, CancellationToken cancellationToken);
{
Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd);
}
} }

View File

@ -4,6 +4,12 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE // https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H // 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.Client;
using FastTunnel.Core.Extensions; using FastTunnel.Core.Extensions;
using FastTunnel.Core.Forwarder; using FastTunnel.Core.Forwarder;
@ -11,20 +17,14 @@ using FastTunnel.Core.Listener;
using FastTunnel.Core.Models; using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage; using FastTunnel.Core.Models.Massage;
using Microsoft.Extensions.Logging; 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; using Yarp.ReverseProxy.Configuration;
namespace FastTunnel.Core.Handlers.Server namespace FastTunnel.Core.Handlers.Server;
public class LoginHandler : ILoginHandler
{ {
public class LoginHandler : ILoginHandler private readonly ILogger logger;
{ private readonly IProxyConfigProvider proxyConfig;
readonly ILogger logger;
readonly IProxyConfigProvider proxyConfig;
public const bool NeedRecive = true; public const bool NeedRecive = true;
public LoginHandler(ILogger<LoginHandler> logger, IProxyConfigProvider proxyConfig) public LoginHandler(ILogger<LoginHandler> logger, IProxyConfigProvider proxyConfig)
@ -33,13 +33,13 @@ namespace FastTunnel.Core.Handlers.Server
this.logger = logger; this.logger = logger;
} }
protected async Task HandleLoginAsync(FastTunnelServer server, TunnelClient client, LogInMassage requet) protected async Task HandleLoginAsync(FastTunnelServer server, TunnelClient client, LogInMassage requet, CancellationToken cancellationToken)
{ {
bool hasTunnel = false; var hasTunnel = false;
List<string> tips = new List<string>(); var tips = new List<string>();
await client.webSocket.SendCmdAsync(MessageType.Log, $"穿透协议 | 映射关系(公网=>内网)", CancellationToken.None); await client.webSocket.SendCmdAsync(MessageType.Log, $"穿透协议 | 映射关系(公网=>内网)", cancellationToken);
Thread.Sleep(300); Thread.Sleep(300);
if (requet.Webs != null && requet.Webs.Any()) if (requet.Webs != null && requet.Webs.Any())
@ -89,12 +89,11 @@ namespace FastTunnel.Core.Handlers.Server
if (item.LocalPort == 22) if (item.LocalPort == 22)
tips.Add("您已将22端口暴露请确保您的PC密码足够安全。"); tips.Add("您已将22端口暴露请确保您的PC密码足够安全。");
ForwardInfo<ForwardHandlerArg> old; if (server.ForwardList.TryGetValue(item.RemotePort, out var old))
if (server.ForwardList.TryGetValue(item.RemotePort, out old))
{ {
logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}"); logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}");
old.Listener.Stop(); old.Listener.Stop();
server.ForwardList.TryRemove(item.RemotePort, out ForwardInfo<ForwardHandlerArg> _); server.ForwardList.TryRemove(item.RemotePort, out var _);
} }
// TODO: 客户端离线时销毁 // TODO: 客户端离线时销毁
@ -134,11 +133,10 @@ namespace FastTunnel.Core.Handlers.Server
await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.NoTunnel, CancellationToken.None); await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.NoTunnel, CancellationToken.None);
} }
public virtual async Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd) public virtual async Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd, CancellationToken cancellationToken)
{ {
var msg = JsonSerializer.Deserialize<LogInMassage>(lineCmd); var msg = JsonSerializer.Deserialize<LogInMassage>(lineCmd);
await HandleLoginAsync(fastTunnelServer, tunnelClient, msg); await HandleLoginAsync(fastTunnelServer, tunnelClient, msg, cancellationToken);
return NeedRecive; return NeedRecive;
} }
}
} }

View File

@ -4,22 +4,25 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE // https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using FastTunnel.Core.Client;
using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Protocol;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.IO.Pipelines;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; 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; } public WebSocket webSocket { get; private set; }
/// <summary> /// <summary>
@ -27,16 +30,20 @@ namespace FastTunnel.Core.Models
/// </summary> /// </summary>
public int ConnectionPort { get; set; } public int ConnectionPort { get; set; }
readonly FastTunnelServer fastTunnelServer; private readonly FastTunnelServer fastTunnelServer;
readonly ILoginHandler loginHandler; private readonly ILoginHandler loginHandler;
private readonly ILogger<TunnelClient> logger;
public IPAddress RemoteIpAddress { get; private set; } public IPAddress RemoteIpAddress { get; private set; }
readonly IList<WebInfo> webInfos = new List<WebInfo>(); private readonly IList<WebInfo> webInfos = new List<WebInfo>();
readonly IList<ForwardInfo<ForwardHandlerArg>> forwardInfos = new List<ForwardInfo<ForwardHandlerArg>>(); private readonly IList<ForwardInfo<ForwardHandlerArg>> forwardInfos = new List<ForwardInfo<ForwardHandlerArg>>();
public TunnelClient(WebSocket webSocket, FastTunnelServer fastTunnelServer, ILoginHandler loginHandler, IPAddress remoteIpAddress) public TunnelClient(
WebSocket webSocket, FastTunnelServer fastTunnelServer,
ILoginHandler loginHandler, IPAddress remoteIpAddress, ILogger<TunnelClient> logger)
{ {
this.logger = logger;
this.webSocket = webSocket; this.webSocket = webSocket;
this.fastTunnelServer = fastTunnelServer; this.fastTunnelServer = fastTunnelServer;
this.loginHandler = loginHandler; this.loginHandler = loginHandler;
@ -53,31 +60,29 @@ namespace FastTunnel.Core.Models
forwardInfos.Add(forwardInfo); forwardInfos.Add(forwardInfo);
} }
/// <summary>
/// 接收客户端的消息
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task ReviceAsync(CancellationToken cancellationToken) public async Task ReviceAsync(CancellationToken cancellationToken)
{ {
var buffer = new byte[FastTunnelConst.MAX_CMD_LENGTH]; var utility = new WebSocketUtility(webSocket, ProcessLine);
var tunnelProtocol = new TunnelProtocol(); await utility.ProcessLinesAsync(cancellationToken);
}
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) private async void ProcessLine(ReadOnlySequence<byte> line, CancellationToken cancellationToken)
{ {
if (!await HandleCmdAsync(this, item)) var cmd = Encoding.UTF8.GetString(line);
{ await HandleCmdAsync(this, cmd, cancellationToken);
return;
};
} }
}
} private async Task<bool> HandleCmdAsync(TunnelClient tunnelClient, string lineCmd, CancellationToken cancellationToken)
private async Task<bool> HandleCmdAsync(TunnelClient tunnelClient, string lineCmd)
{ {
try try
{ {
return await loginHandler.HandlerMsg(fastTunnelServer, tunnelClient, lineCmd.Substring(1)); return await loginHandler.HandlerMsg(fastTunnelServer, tunnelClient, lineCmd.Substring(1), cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -103,5 +108,4 @@ namespace FastTunnel.Core.Models
webSocket.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None); webSocket.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
} }
}
} }

View File

@ -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;
}
}
}
}

View File

@ -33,14 +33,12 @@ namespace FastTunnel.Core.Services
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
_fastTunnelClient.StartAsync(cancellationToken); await _fastTunnelClient.StartAsync(cancellationToken);
await Task.CompletedTask;
} }
public Task StopAsync(CancellationToken cancellationToken) public async Task StopAsync(CancellationToken cancellationToken)
{ {
_fastTunnelClient.Stop(cancellationToken); await _fastTunnelClient.StopAsync(cancellationToken);
return Task.CompletedTask;
} }
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)

View 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;
}
}

View File

@ -36,4 +36,6 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties config_4appsettings_1development_1json__JsonSchema="https://json.schemastore.org/appsettings.json" /></VisualStudio></ProjectExtensions>
</Project> </Project>

View File

@ -21,15 +21,11 @@ public class Program
public static IHostBuilder CreateHostBuilder(string[] args) => public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args) Host.CreateDefaultBuilder(args)
.UseSerilog((hostBuilderContext, services, loggerConfiguration) => .UseSerilog((context, services, configuration) => configuration
{ .ReadFrom.Configuration(context.Configuration)
var enableFileLog = (bool)(hostBuilderContext.Configuration.GetSection("EnableFileLog")?.Get(typeof(bool)) ?? false); .ReadFrom.Services(services)
loggerConfiguration.WriteTo.Console(); .Enrich.FromLogContext()
if (enableFileLog) .WriteTo.Console())
{
loggerConfiguration.WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7);
}
})
.UseWindowsService() .UseWindowsService()
.ConfigureWebHost(webHostBuilder => .ConfigureWebHost(webHostBuilder =>
{ {

View File

@ -1,12 +1,7 @@
{ {
"Logging": { "Serilog": {
"LogLevel": { "MinimumLevel": {
// Trace Debug Information Warning Error "Default": "Debug"
"Default": "Debug", }
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
} }
},
"AllowedHosts": "*",
"EnableFileLog": false
} }

View File

@ -1,12 +1,23 @@
{ {
"Logging": { "Serilog": {
"LogLevel": { "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
// Trace Debug Information Warning Error "MinimumLevel": {
"Default": "Information", "Default": "Information",
"Override": {
"Microsoft": "Warning", "Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information" "Microsoft.Hosting.Lifetime": "Information"
} }
}, },
"WriteTo": [
//{
// "Name": "File",
// "Args": {
// "path": "Logs/log-.log",
// "rollingInterval": 3
// }
//}
]
},
"AllowedHosts": "*", "AllowedHosts": "*",
// Http& // Http&
"urls": "http://*:1270", "urls": "http://*:1270",