代码清理

This commit is contained in:
Gui.H 2022-05-08 21:38:59 +08:00
parent ea8072ac6e
commit 593f12d925
65 changed files with 1682 additions and 2229 deletions

View File

@ -6,7 +6,6 @@
using FastTunnel.Api.Models; using FastTunnel.Api.Models;
using FastTunnel.Core.Config; using FastTunnel.Core.Config;
using FastTunnel.Server.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;

View File

@ -4,7 +4,7 @@
// 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.Server.Models; using FastTunnel.Api.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;

View File

@ -4,8 +4,8 @@
// 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.Api.Models;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Server.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System; using System;

View File

@ -4,7 +4,7 @@
// 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.Server.Models; using FastTunnel.Api.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -4,7 +4,7 @@
// 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
namespace FastTunnel.Server.Models namespace FastTunnel.Api.Models
{ {
public class ApiResponse public class ApiResponse
{ {

View File

@ -4,153 +4,152 @@
// 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.Threading.Tasks;
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.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;
private readonly SwapHandler _newCustomerHandler;
private readonly LogHandler _logHandler;
public DefaultClientConfig ClientConfig { get; private set; }
public SuiDaoServer Server { get; protected set; }
public FastTunnelClient(
ILogger<FastTunnelClient> logger,
SwapHandler newCustomerHandler,
LogHandler logHandler,
IOptionsMonitor<DefaultClientConfig> configuration)
{ {
private ClientWebSocket socket; _logger = logger;
_newCustomerHandler = newCustomerHandler;
_logHandler = logHandler;
ClientConfig = configuration.CurrentValue;
Server = ClientConfig.Server;
}
protected readonly ILogger<FastTunnelClient> _logger; /// <summary>
private readonly SwapHandler _newCustomerHandler; /// 启动客户端
private readonly LogHandler _logHandler; /// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="customLoginMsg">自定义登录信息,可进行扩展业务</param>
public async void StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("===== FastTunnel Client Start =====");
public DefaultClientConfig ClientConfig { get; private set; } while (!cancellationToken.IsCancellationRequested)
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)
{ {
try try
{ {
var logMsg = GetLoginMsg(cancellationToken); await loginAsync(cancellationToken);
await ReceiveServerAsync(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);
} }
catch (Exception ex) 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)
{
try
{ {
_logger.LogInformation("===== FastTunnel Client Stoping ====="); 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)
{
var 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);
}
catch (Exception ex)
{
_logger.LogError(ex);
}
}
public void Stop(CancellationToken cancellationToken)
{
_logger.LogInformation("===== FastTunnel Client Stoping =====");
}
} }

View File

@ -4,73 +4,69 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Config; using FastTunnel.Core.Config;
using FastTunnel.Core.Models; using FastTunnel.Core.Models;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using System;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.IO;
using Yarp.ReverseProxy.Configuration;
using System.Collections.Generic;
namespace FastTunnel.Core.Client namespace FastTunnel.Core.Client;
public class FastTunnelServer
{ {
public class FastTunnelServer public int ConnectedClientCount;
public readonly IOptionsMonitor<DefaultServerConfig> ServerOption;
private readonly ILogger<FastTunnelServer> logger;
public ConcurrentDictionary<string, TaskCompletionSource<Stream>> ResponseTasks { get; } = new();
public ConcurrentDictionary<string, WebInfo> WebList { get; private set; } = new();
public ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>> ForwardList { get; private set; }
= new ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>>();
/// <summary>
/// 在线客户端列表
/// </summary>
public IList<TunnelClient> Clients = new List<TunnelClient>();
public FastTunnelServer(ILogger<FastTunnelServer> logger, IOptionsMonitor<DefaultServerConfig> serverSettings)
{ {
public int ConnectedClientCount; this.logger = logger;
public readonly IOptionsMonitor<DefaultServerConfig> ServerOption; this.ServerOption = serverSettings;
public IProxyConfigProvider proxyConfig; }
readonly ILogger<FastTunnelServer> logger;
public ConcurrentDictionary<string, TaskCompletionSource<Stream>> ResponseTasks { get; } = new(); /// <summary>
/// 客户端登录
/// </summary>
/// <param name="client"></param>
internal void OnClientLogin(TunnelClient client)
{
Interlocked.Increment(ref ConnectedClientCount);
logger.LogInformation($"客户端连接 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");
Clients.Add(client);
}
public ConcurrentDictionary<string, WebInfo> WebList { get; private set; } = new(); /// <summary>
/// 客户端退出
/// </summary>
/// <param name="client"></param>
/// <exception cref="NotImplementedException"></exception>
internal void OnClientLogout(TunnelClient client)
{
Interlocked.Decrement(ref ConnectedClientCount);
logger.LogInformation($"客户端关闭 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");
Clients.Remove(client);
client.Logout();
}
public ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>> ForwardList { get; private set; } internal bool TryGetWebProxyByHost(string host, out WebInfo web)
= new ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>>(); {
return WebList.TryGetValue(host, out web);
/// <summary>
/// 在线客户端列表
/// </summary>
public IList<TunnelClient> Clients = new List<TunnelClient>();
public FastTunnelServer(ILogger<FastTunnelServer> logger, IProxyConfigProvider proxyConfig, IOptionsMonitor<DefaultServerConfig> serverSettings)
{
this.logger = logger;
this.ServerOption = serverSettings;
this.proxyConfig = proxyConfig;
}
/// <summary>
/// 客户端登录
/// </summary>
/// <param name="client"></param>
internal void OnClientLogin(TunnelClient client)
{
Interlocked.Increment(ref ConnectedClientCount);
logger.LogInformation($"客户端连接 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");
Clients.Add(client);
}
/// <summary>
/// 客户端退出
/// </summary>
/// <param name="client"></param>
/// <exception cref="NotImplementedException"></exception>
internal void OnClientLogout(TunnelClient client)
{
Interlocked.Decrement(ref ConnectedClientCount);
logger.LogInformation($"客户端关闭 {client.RemoteIpAddress} 当前在线数:{ConnectedClientCount}");
Clients.Remove(client);
client.Logout();
}
internal bool TryGetWebProxyByHost(string host, out WebInfo web)
{
return WebList.TryGetValue(host, out web);
}
} }
} }

View File

@ -1,4 +1,4 @@
// 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
@ -6,12 +6,11 @@
using System.Threading; using System.Threading;
namespace FastTunnel.Core.Client namespace FastTunnel.Core.Client;
{
public interface IFastTunnelClient
{
void StartAsync(CancellationToken cancellationToken);
void Stop(CancellationToken cancellationToken); public interface IFastTunnelClient
} {
void StartAsync(CancellationToken cancellationToken);
void Stop(CancellationToken cancellationToken);
} }

View File

@ -1,22 +1,21 @@
// 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 FastTunnel.Core.Models;
using System.Collections.Generic; using System.Collections.Generic;
using FastTunnel.Core.Models;
namespace FastTunnel.Core.Config namespace FastTunnel.Core.Config;
public class DefaultClientConfig : IClientConfig
{ {
public class DefaultClientConfig : IClientConfig public SuiDaoServer Server { get; set; }
{
public SuiDaoServer Server { get; set; }
public string Token { get; set; } public string Token { get; set; }
public IEnumerable<WebConfig> Webs { get; set; } public IEnumerable<WebConfig> Webs { get; set; }
public IEnumerable<ForwardConfig> Forwards { get; set; } public IEnumerable<ForwardConfig> Forwards { get; set; }
} }
}

View File

@ -1,4 +1,4 @@
// 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
@ -7,48 +7,47 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace FastTunnel.Core.Config namespace FastTunnel.Core.Config;
public class DefaultServerConfig : IServerConfig
{ {
public class DefaultServerConfig : IServerConfig public string WebDomain { get; set; }
public string[] WebAllowAccessIps { get; set; }
public bool EnableForward { get; set; }
[Obsolete("由Tokens替换")]
public string Token { get; set; }
public List<string> Tokens { get; set; }
public ApiOptions Api { get; set; }
public class ApiOptions
{ {
public string WebDomain { get; set; } public JWTOptions JWT { get; set; }
public string[] WebAllowAccessIps { get; set; } public Account[] Accounts { get; set; }
}
public bool EnableForward { get; set; } public class JWTOptions
{
public int ClockSkew { get; set; }
[Obsolete("由Tokens替换")] public string ValidAudience { get; set; }
public string Token { get; set; }
public List<string> Tokens { get; set; } public string ValidIssuer { get; set; }
public ApiOptions Api { get; set; } public string IssuerSigningKey { get; set; }
public class ApiOptions public int Expires { get; set; }
{ }
public JWTOptions JWT { get; set; }
public Account[] Accounts { get; set; } public class Account
} {
public string Name { get; set; }
public class JWTOptions public string Password { get; set; }
{
public int ClockSkew { get; set; }
public string ValidAudience { get; set; }
public string ValidIssuer { get; set; }
public string IssuerSigningKey { get; set; }
public int Expires { get; set; }
}
public class Account
{
public string Name { get; set; }
public string Password { get; set; }
}
} }
} }

View File

@ -1,29 +1,28 @@
// 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 FastTunnel.Core.Models;
using System.Collections.Generic; using System.Collections.Generic;
using FastTunnel.Core.Models;
namespace FastTunnel.Core.Config namespace FastTunnel.Core.Config;
public interface IClientConfig
{ {
public interface IClientConfig public SuiDaoServer Server { get; set; }
{
public SuiDaoServer Server { get; set; }
public IEnumerable<WebConfig> Webs { get; set; } public IEnumerable<WebConfig> Webs { get; set; }
public IEnumerable<ForwardConfig> Forwards { get; set; } public IEnumerable<ForwardConfig> Forwards { get; set; }
} }
public class SuiDaoServer public class SuiDaoServer
{ {
public string Protocol { get; set; } = "ws"; public string Protocol { get; set; } = "ws";
public string ServerAddr { get; set; } public string ServerAddr { get; set; }
public int ServerPort { get; set; } public int ServerPort { get; set; }
}
} }

View File

@ -1,17 +1,16 @@
// 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
namespace FastTunnel.Core.Config namespace FastTunnel.Core.Config;
public interface IServerConfig
{ {
public interface IServerConfig string WebDomain { get; set; }
{
string WebDomain { get; set; }
string[] WebAllowAccessIps { get; set; } string[] WebAllowAccessIps { get; set; }
bool EnableForward { get; set; } bool EnableForward { get; set; }
}
} }

View File

@ -5,16 +5,13 @@
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace FastTunnel.Core.Exceptions namespace FastTunnel.Core.Exceptions;
public class APIErrorException : Exception
{ {
public class APIErrorException : Exception public APIErrorException(string message)
: base(message)
{ {
public APIErrorException(string message)
: base(message)
{
}
} }
} }

View File

@ -6,13 +6,12 @@
using System; using System;
namespace FastTunnel.Core.Exceptions namespace FastTunnel.Core.Exceptions;
public class ClienOffLineException : Exception
{ {
public class ClienOffLineException : Exception public ClienOffLineException(string message)
: base(message)
{ {
public ClienOffLineException(string message)
: base(message)
{
}
} }
} }

View File

@ -5,17 +5,12 @@
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Exceptions namespace FastTunnel.Core.Exceptions;
public class SocketClosedException : Exception
{ {
public class SocketClosedException : Exception public SocketClosedException(string msg) : base(msg)
{ {
public SocketClosedException(string msg) : base(msg)
{
}
} }
} }

View File

@ -1,4 +1,4 @@
// 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
@ -6,13 +6,12 @@
using System.Text; using System.Text;
namespace FastTunnel.Core.Extensions namespace FastTunnel.Core.Extensions;
public static class ByteArrayExtensions
{ {
public static class ByteArrayExtensions public static string GetString(this byte[] buffer, int offset, int count)
{ {
public static string GetString(this byte[] buffer, int offset, int count) return Encoding.UTF8.GetString(buffer, offset, count);
{
return Encoding.UTF8.GetString(buffer, offset, count);
}
} }
} }

View File

@ -4,11 +4,6 @@
// 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;
using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Core.Forwarder.Kestrel; using FastTunnel.Core.Forwarder.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;

View File

@ -4,16 +4,15 @@
// 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 Microsoft.Extensions.Logging;
using System; using System;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Extensions namespace FastTunnel.Core.Extensions;
public static class LoggerExtentions
{ {
public static class LoggerExtentions public static void LogError(this ILogger logger, Exception ex)
{ {
public static void LogError(this ILogger logger, Exception ex) logger.LogError(ex, string.Empty);
{
logger.LogError(ex, string.Empty);
}
} }
} }

View File

@ -6,19 +6,18 @@
using System.Text.Json; using System.Text.Json;
namespace FastTunnel.Core.Extensions namespace FastTunnel.Core.Extensions;
{
public static class ObjectExtensions
{
public static string ToJson(this object message)
{
if (message == null)
{
return null;
}
var jsonOptions = new JsonSerializerOptions { WriteIndented = false }; public static class ObjectExtensions
return JsonSerializer.Serialize(message, message.GetType(), jsonOptions); {
public static string ToJson(this object message)
{
if (message == null)
{
return null;
} }
var jsonOptions = new JsonSerializerOptions { WriteIndented = false };
return JsonSerializer.Serialize(message, message.GetType(), jsonOptions);
} }
} }

View File

@ -7,94 +7,59 @@
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Core.Config; using FastTunnel.Core.Config;
using FastTunnel.Core.Forwarder.MiddleWare; using FastTunnel.Core.Forwarder.MiddleWare;
using FastTunnel.Core.Forwarder;
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.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 System.Threading.Tasks;
using System.Threading;
using Microsoft.AspNetCore.Http;
namespace FastTunnel.Core.Extensions 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> services.Configure<DefaultClientConfig>(configurationSection);
/// 客户端依赖及HostedService services.AddFastTunnelClient();
/// </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) public static void AddFastTunnelClient(this IServiceCollection services)
{ {
services.AddTransient<IFastTunnelClient, FastTunnelClient>() services.AddTransient<IFastTunnelClient, FastTunnelClient>()
.AddSingleton<IExceptionFilter, FastTunnelExceptionFilter>() .AddSingleton<LogHandler>()
.AddSingleton<LogHandler>() .AddSingleton<SwapHandler>();
.AddSingleton<SwapHandler>();
services.AddHostedService<ServiceFastTunnelClient>(); services.AddHostedService<ServiceFastTunnelClient>();
} }
/// <summary> /// <summary>
/// 添加服务端后台进程 /// 添加服务端后台进程
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
public static void AddFastTunnelServer(this IServiceCollection services, IConfigurationSection configurationSection) public static void AddFastTunnelServer(this IServiceCollection services, IConfigurationSection configurationSection)
{ {
services.AddReverseProxy().LoadFromMemory(); services.Configure<DefaultServerConfig>(configurationSection)
services.AddSingleton<IForwarderHttpClientFactory, FastTunnelForwarderHttpClientFactory>(); .AddTransient<ILoginHandler, LoginHandler>()
.AddSingleton<FastTunnelClientHandler>()
.AddSingleton<FastTunnelSwapHandler>()
.AddSingleton<FastTunnelServer>();
}
services.Configure<DefaultServerConfig>(configurationSection) /// <summary>
.AddSingleton<IExceptionFilter, FastTunnelExceptionFilter>() /// 服务端中间件
.AddTransient<ILoginHandler, LoginHandler>() /// </summary>
.AddSingleton<FastTunnelClientHandler>() /// <param name="app"></param>
.AddSingleton<FastTunnelSwapHandler>() public static void UseFastTunnelServer(this IApplicationBuilder app)
.AddSingleton<FastTunnelServer>(); {
} app.UseWebSockets();
/// <summary> // var swapHandler = app.ApplicationServices.GetRequiredService<FastTunnelSwapHandler>();
/// 服务端中间件 var clientHandler = app.ApplicationServices.GetRequiredService<FastTunnelClientHandler>();
/// </summary> app.Use(clientHandler.Handle);
/// <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);
}
//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))
// {
// context.Response.StatusCode = 404;
// return Task.CompletedTask;
// }
// context.Response.StatusCode = 200;
// context.Response.WriteAsync(TunnelResource.Page_NotFound, CancellationToken.None);
// return Task.CompletedTask;
// });
//}
} }
} }

View File

@ -4,21 +4,18 @@
// 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.Models;
using FastTunnel.Core.Models.Massage;
using System;
using System.Collections.Generic;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
namespace FastTunnel.Core.Extensions namespace FastTunnel.Core.Extensions;
public static class SocketExtensions
{ {
public static class SocketExtensions public static void SendCmd<T>(this Socket socket, Message<T> message)
where T : TunnelMassage
{ {
public static void SendCmd<T>(this Socket socket, Message<T> message) socket.Send(Encoding.UTF8.GetBytes(message.ToJson() + "\n"));
where T : TunnelMassage
{
socket.Send(Encoding.UTF8.GetBytes(message.ToJson() + "\n"));
}
} }
} }

View File

@ -1,31 +1,27 @@
// 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; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastTunnel.Core.Extensions namespace FastTunnel.Core.Extensions;
{
public static class TaskCompletionSourceExtensions
{
public static void SetTimeOut<T>(this TaskCompletionSource<T> tcs, int timeoutMs, Action? action)
{
var ct = new CancellationTokenSource(timeoutMs);
ct.Token.Register(() =>
{
if (tcs.Task.IsCompleted)
return;
tcs.TrySetCanceled(); public static class TaskCompletionSourceExtensions
action?.Invoke(); {
}, useSynchronizationContext: false); public static void SetTimeOut<T>(this TaskCompletionSource<T> tcs, int timeoutMs, Action? action)
} {
var ct = new CancellationTokenSource(timeoutMs);
ct.Token.Register(() =>
{
if (tcs.Task.IsCompleted)
return;
tcs.TrySetCanceled();
action?.Invoke();
}, useSynchronizationContext: false);
} }
} }

View File

@ -4,48 +4,43 @@
// 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.IO.Pipelines; using System.IO.Pipelines;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastTunnel.Core.Extensions namespace FastTunnel.Core.Extensions;
{
internal static class ValueTaskExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task GetAsTask(this in ValueTask<FlushResult> valueTask)
{
// Try to avoid the allocation from AsTask
if (valueTask.IsCompletedSuccessfully)
{
// Signal consumption to the IValueTaskSource
valueTask.GetAwaiter().GetResult();
return Task.CompletedTask;
}
else
{
return valueTask.AsTask();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] internal static class ValueTaskExtensions
public static ValueTask GetAsValueTask(this in ValueTask<FlushResult> valueTask) {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task GetAsTask(this in ValueTask<FlushResult> valueTask)
{
// Try to avoid the allocation from AsTask
if (valueTask.IsCompletedSuccessfully)
{ {
// Try to avoid the allocation from AsTask // Signal consumption to the IValueTaskSource
if (valueTask.IsCompletedSuccessfully) valueTask.GetAwaiter().GetResult();
{ return Task.CompletedTask;
// Signal consumption to the IValueTaskSource }
valueTask.GetAwaiter().GetResult(); else
return default; {
} return valueTask.AsTask();
else }
{ }
return new ValueTask(valueTask.AsTask());
} [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ValueTask GetAsValueTask(this in ValueTask<FlushResult> valueTask)
{
// Try to avoid the allocation from AsTask
if (valueTask.IsCompletedSuccessfully)
{
// Signal consumption to the IValueTaskSource
valueTask.GetAwaiter().GetResult();
return default;
}
else
{
return new ValueTask(valueTask.AsTask());
} }
} }
} }

View File

@ -4,33 +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 FastTunnel.Core.Exceptions;
using FastTunnel.Core.Models;
using System; using System;
using System.Collections.Generic;
using System.Linq;
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.Exceptions;
using FastTunnel.Core.Models;
namespace FastTunnel.Core.Extensions namespace FastTunnel.Core.Extensions;
public static class WebSocketExtensions
{ {
public static class WebSocketExtensions public static async Task SendCmdAsync(this WebSocket socket, MessageType type, string content, CancellationToken cancellationToken)
{ {
public static async Task SendCmdAsync(this WebSocket socket, MessageType type, string content, CancellationToken cancellationToken) if (socket.State == WebSocketState.Closed || socket.State == WebSocketState.Aborted)
{ {
if (socket.State == WebSocketState.Closed || socket.State == WebSocketState.Aborted) throw new SocketClosedException(socket.State.ToString());
{
throw new SocketClosedException(socket.State.ToString());
}
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);
} }
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);
} }
} }

View File

@ -1,43 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks> <TargetFrameworks>net6.0</TargetFrameworks>
<Version>2.1.0</Version> <Version>2.1.0</Version>
<PackageProjectUrl>https://github.com/SpringHgui/FastTunnel</PackageProjectUrl> <PackageProjectUrl>https://github.com/SpringHgui/FastTunnel</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<Copyright>FastTunnel</Copyright> <Copyright>FastTunnel</Copyright>
<Description>expose a local server behind a NAT or firewall to the internet like ngrok and frp</Description> <Description>expose a local server behind a NAT or firewall to the internet like ngrok and frp</Description>
<Authors>Gui.H</Authors> <Authors>Gui.H</Authors>
<Company>FastTunnel</Company> <Company>FastTunnel</Company>
<Product>FastTunnel</Product> <Product>FastTunnel</Product>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<RepositoryUrl>https://github.com/SpringHgui/FastTunnel</RepositoryUrl> <RepositoryUrl>https://github.com/SpringHgui/FastTunnel</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageTags>FastTunnel.Core</PackageTags> <PackageTags>FastTunnel.Core</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReleaseNotes>FastTunnel.Core</PackageReleaseNotes> <PackageReleaseNotes>FastTunnel.Core</PackageReleaseNotes>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Core" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0-preview.3.22175.4" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0-preview.3.22175.4" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0-preview.3.22175.4" /> <Compile Update="TunnelResource.Designer.cs">
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0-preview.3.22175.4" /> <DesignTime>True</DesignTime>
<PackageReference Include="Yarp.ReverseProxy" Version="1.1.0-rc.1.22211.2" /> <AutoGen>True</AutoGen>
</ItemGroup> <DependentUpon>TunnelResource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="TunnelResource.Designer.cs"> <EmbeddedResource Update="TunnelResource.resx">
<DesignTime>True</DesignTime> <Generator>PublicResXFileCodeGenerator</Generator>
<AutoGen>True</AutoGen> <LastGenOutput>TunnelResource.Designer.cs</LastGenOutput>
<DependentUpon>TunnelResource.resx</DependentUpon> </EmbeddedResource>
</Compile> </ItemGroup>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="TunnelResource.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>TunnelResource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project> </Project>

View File

@ -4,20 +4,13 @@
// 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; namespace FastTunnel.Core;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core public class FastTunnelConst
{ {
public class FastTunnelConst public const string FASTTUNNEL_VERSION = "FT_VERSION";
{ public const string FASTTUNNEL_MSGID = "FT_MSGID";
public const string FASTTUNNEL_VERSION = "FT_VERSION"; public const string FASTTUNNEL_TOKEN = "FT_TOKEN";
public const string FASTTUNNEL_MSGID = "FT_MSGID";
public const string FASTTUNNEL_TOKEN = "FT_TOKEN";
public const int MAX_CMD_LENGTH = 100; public const int MAX_CMD_LENGTH = 100;
}
} }

View File

@ -1,34 +1,37 @@
// 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 Microsoft.AspNetCore.Mvc.Filters; using FastTunnel.Core.Extensions;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using FastTunnel.Core.Extensions;
namespace FastTunnel.Core.Filters namespace FastTunnel.Core.Filters;
public class FastTunnelExceptionFilter : IExceptionFilter
{ {
public class FastTunnelExceptionFilter : IExceptionFilter private readonly IWebHostEnvironment _hostingEnvironment;
private readonly ILogger<FastTunnelExceptionFilter> logger;
public FastTunnelExceptionFilter(
ILogger<FastTunnelExceptionFilter> logger,
IWebHostEnvironment hostingEnvironment)
{ {
private readonly IWebHostEnvironment _hostingEnvironment; this.logger = logger;
private readonly ILogger<FastTunnelExceptionFilter> logger; _hostingEnvironment = hostingEnvironment;
}
public FastTunnelExceptionFilter( public void OnException(ExceptionContext context)
ILogger<FastTunnelExceptionFilter> logger, {
IWebHostEnvironment hostingEnvironment) if (!_hostingEnvironment.IsDevelopment())
{ {
this.logger = logger; return;
_hostingEnvironment = hostingEnvironment;
} }
public void OnException(ExceptionContext context) logger.LogError(context.Exception, "[全局异常]");
{
if (!_hostingEnvironment.IsDevelopment())
{
return;
}
logger.LogError(context.Exception, "[全局异常]");
}
} }
} }

View File

@ -1,108 +0,0 @@
// 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
using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models;
using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Yarp.ReverseProxy.Forwarder;
namespace FastTunnel.Core.Forwarder
{
public class FastTunnelForwarderHttpClientFactory : ForwarderHttpClientFactory
{
readonly ILogger<FastTunnelForwarderHttpClientFactory> logger;
readonly FastTunnelServer fastTunnelServer;
public FastTunnelForwarderHttpClientFactory(ILogger<FastTunnelForwarderHttpClientFactory> logger, FastTunnelServer fastTunnelServer)
{
this.fastTunnelServer = fastTunnelServer;
this.logger = logger;
}
protected override void ConfigureHandler(ForwarderHttpClientContext context, SocketsHttpHandler handler)
{
base.ConfigureHandler(context, handler);
handler.ConnectCallback = ConnectCallback;
}
private async ValueTask<Stream> ConnectCallback(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
{
var host = context.InitialRequestMessage.RequestUri.Host;
try
{
var res = await proxyAsync(host, context, cancellationToken);
return res;
}
catch (Exception ex)
{
logger.LogError(ex, "ConnectCallback Error");
throw;
}
}
public async ValueTask<Stream> proxyAsync(string host, SocketsHttpConnectionContext context, CancellationToken cancellation)
{
WebInfo web;
if (!fastTunnelServer.WebList.TryGetValue(host, out web))
{
// 客户端已离线
return await OfflinePage(host, context);
}
var msgId = Guid.NewGuid().ToString().Replace("-", "");
TaskCompletionSource<Stream> tcs = new(cancellation);
logger.LogDebug($"[Http]Swap开始 {msgId}|{host}=>{web.WebConfig.LocalIp}:{web.WebConfig.LocalPort}");
tcs.SetTimeOut(10000, () => { logger.LogDebug($"[Proxy TimeOut]:{msgId}"); });
fastTunnelServer.ResponseTasks.TryAdd(msgId, tcs);
try
{
// 发送指令给客户端,等待建立隧道
await web.Socket.SendCmdAsync(MessageType.SwapMsg, $"{msgId}|{web.WebConfig.LocalIp}:{web.WebConfig.LocalPort}", cancellation);
var res = await tcs.Task;
logger.LogDebug($"[Http]Swap OK {msgId}");
return res;
}
catch (WebSocketException)
{
// 通讯异常,返回客户端离线
return await OfflinePage(host, context);
}
catch (Exception)
{
throw;
}
finally
{
fastTunnelServer.ResponseTasks.TryRemove(msgId, out _);
}
}
private async ValueTask<Stream> OfflinePage(string host, SocketsHttpConnectionContext context)
{
var bytes = Encoding.UTF8.GetBytes(
$"HTTP/1.1 200 OK\r\nContent-Type:text/html; charset=utf-8\r\n\r\n{TunnelResource.Page_Offline}\r\n");
return await Task.FromResult(new ResponseStream(bytes));
}
}
}

View File

@ -1,34 +0,0 @@
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Yarp.ReverseProxy.Configuration;
namespace FastTunnel.Core.Forwarder
{
public class FastTunnelProxyConfig : IProxyConfig
{
public FastTunnelProxyConfig()
: this(Array.Empty<RouteConfig>(), Array.Empty<ClusterConfig>())
{
}
public FastTunnelProxyConfig(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
{
this.Routes = routes;
this.Clusters = clusters;
this.ChangeToken = new CancellationChangeToken(cancellationToken.Token);
}
public IReadOnlyList<RouteConfig> Routes { get; }
public IReadOnlyList<ClusterConfig> Clusters { get; }
public IChangeToken ChangeToken { get; }
private readonly CancellationTokenSource cancellationToken = new();
}
}

View File

@ -1,107 +0,0 @@
// 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
using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models;
using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Yarp.ReverseProxy.Forwarder;
namespace FastTunnel.Core.Forwarder
{
public class ForwarderClientFactory : ForwarderHttpClientFactory
{
readonly ILogger<ForwarderClientFactory> logger;
readonly FastTunnelServer fastTunnelServer;
public ForwarderClientFactory(ILogger<ForwarderClientFactory> logger, FastTunnelServer fastTunnelServer)
{
this.fastTunnelServer = fastTunnelServer;
this.logger = logger;
}
protected override void ConfigureHandler(ForwarderHttpClientContext context, SocketsHttpHandler handler)
{
base.ConfigureHandler(context, handler);
handler.ConnectCallback = ConnectCallback;
}
private async ValueTask<Stream> ConnectCallback(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
{
var host = context.InitialRequestMessage.RequestUri.Host;
try
{
var res = await proxyAsync(host, context, cancellationToken);
return res;
}
catch (Exception)
{
throw;
}
}
public async ValueTask<Stream> proxyAsync(string host, SocketsHttpConnectionContext context, CancellationToken cancellation)
{
WebInfo web;
if (!fastTunnelServer.WebList.TryGetValue(host, out web))
{
// 客户端已离线
return await OfflinePage(host, context);
}
var msgId = Guid.NewGuid().ToString().Replace("-", "");
TaskCompletionSource<Stream> tcs = new();
logger.LogDebug($"[Http]Swap开始 {msgId}|{host}=>{web.WebConfig.LocalIp}:{web.WebConfig.LocalPort}");
tcs.SetTimeOut(10000, () => { logger.LogDebug($"[Proxy TimeOut]:{msgId}"); });
fastTunnelServer.ResponseTasks.TryAdd(msgId, tcs);
try
{
// 发送指令给客户端,等待建立隧道
await web.Socket.SendCmdAsync(MessageType.SwapMsg, $"{msgId}|{web.WebConfig.LocalIp}:{web.WebConfig.LocalPort}", cancellation);
var res = await tcs.Task;
logger.LogDebug($"[Http]Swap OK {msgId}");
return res;
}
catch (WebSocketException)
{
// 通讯异常,返回客户端离线
return await OfflinePage(host, context);
}
catch (Exception)
{
throw;
}
finally
{
fastTunnelServer.ResponseTasks.TryRemove(msgId, out _);
}
}
private async ValueTask<Stream> OfflinePage(string host, SocketsHttpConnectionContext context)
{
var bytes = Encoding.UTF8.GetBytes(
$"HTTP/1.1 200 OK\r\nContent-Type:text/html; charset=utf-8\r\n\r\n{TunnelResource.Page_Offline}\r\n");
return await Task.FromResult(new ResponseStream(bytes));
}
}
}

View File

@ -1,127 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Yarp.ReverseProxy.Configuration;
using System.Linq;
namespace FastTunnel.Core.Forwarder
{
/// <summary>
/// Extends the IReverseProxyBuilder to support the InMemoryConfigProvider
/// </summary>
public static class InMemoryConfigProviderExtensions
{
public static IReverseProxyBuilder LoadFromMemory(this IReverseProxyBuilder builder)
{
builder.Services.AddSingleton<IProxyConfigProvider>(
new InMemoryConfigProvider(Array.Empty<RouteConfig>(), Array.Empty<ClusterConfig>()));
return builder;
}
}
/// <summary>
/// Provides an implementation of IProxyConfigProvider to support config being generated by code.
/// </summary>
public class InMemoryConfigProvider : IProxyConfigProvider
{
// Marked as volatile so that updates are atomic
private volatile InMemoryConfig _config;
private object locker = new object();
public InMemoryConfigProvider(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
{
_config = new InMemoryConfig(routes, clusters);
}
/// <summary>
/// Implementation of the IProxyConfigProvider.GetConfig method to supply the current snapshot of configuration
/// </summary>
/// <returns>An immutable snapshot of the current configuration state</returns>
public IProxyConfig GetConfig() => _config;
/// <summary>
/// Swaps the config state with a new snapshot of the configuration, then signals the change
/// </summary>
public void Update(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
{
var oldConfig = _config;
_config = new InMemoryConfig(routes, clusters);
oldConfig.SignalChange();
}
public void AddWeb(string hostName)
{
lock (locker)
{
var oldConfig = _config;
if (oldConfig.Routes.Any(x => x.ClusterId == hostName))
{
return;
}
var newRoutes = oldConfig.Routes.ToList();
newRoutes.Add(new RouteConfig
{
ClusterId = hostName,
RouteId = hostName,
Match = new RouteMatch { Hosts = new string[] { hostName } }
});
var newClusters = oldConfig.Clusters.ToList();
newClusters.Add(new ClusterConfig
{
ClusterId = hostName,
Destinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase)
{
{ "default", new DestinationConfig() {Address = $"http://{hostName}",} }
}
});
_config = new InMemoryConfig(newRoutes, newClusters);
oldConfig.SignalChange();
}
}
/// <summary>
/// Implementation of IProxyConfig which is a snapshot of the current config state. The data for this class should be immutable.
/// </summary>
private class InMemoryConfig : IProxyConfig
{
// Used to implement the change token for the state
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
public InMemoryConfig(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
{
Routes = routes;
Clusters = clusters;
ChangeToken = new CancellationChangeToken(_cts.Token);
}
/// <summary>
/// A snapshot of the list of routes for the proxy
/// </summary>
public IReadOnlyList<RouteConfig> Routes { get; }
/// <summary>
/// A snapshot of the list of Clusters which are collections of interchangable destination endpoints
/// </summary>
public IReadOnlyList<ClusterConfig> Clusters { get; }
/// <summary>
/// Fired to indicate the the proxy state has changed, and that this snapshot is now stale
/// </summary>
public IChangeToken ChangeToken { get; }
internal void SignalChange()
{
_cts.Cancel();
}
}
}
}

View File

@ -6,22 +6,18 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.Kestrel; namespace FastTunnel.Core.Forwarder.Kestrel;
internal class ClientConnectionMiddleware internal class ClientConnectionMiddleware
{ {
readonly ConnectionDelegate next; private readonly ConnectionDelegate next;
readonly ILogger<ClientConnectionMiddleware> logger; private readonly ILogger<ClientConnectionMiddleware> logger;
FastTunnelServer fastTunnelServer; private readonly FastTunnelServer fastTunnelServer;
public ClientConnectionMiddleware(ConnectionDelegate next, ILogger<ClientConnectionMiddleware> logger, FastTunnelServer fastTunnelServer) public ClientConnectionMiddleware(ConnectionDelegate next, ILogger<ClientConnectionMiddleware> logger, FastTunnelServer fastTunnelServer)
{ {
@ -43,7 +39,7 @@ internal class ClientConnectionMiddleware
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context"></param>
/// <returns>is for FastTunnel</returns> /// <returns>is for FastTunnel</returns>
async Task<bool> ReadPipeAsync(ConnectionContext context) private async Task<bool> ReadPipeAsync(ConnectionContext context)
{ {
var reader = context.Transport.Input; var reader = context.Transport.Input;

View File

@ -8,7 +8,6 @@ using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
@ -20,9 +19,9 @@ using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.Kestrel; namespace FastTunnel.Core.Forwarder.Kestrel;
internal class FastTunnelConnectionContext : ConnectionContext internal class FastTunnelConnectionContext : ConnectionContext
{ {
private ConnectionContext _inner; private readonly ConnectionContext _inner;
FastTunnelServer fastTunnelServer; private readonly FastTunnelServer fastTunnelServer;
ILogger _logger; private readonly ILogger _logger;
public FastTunnelConnectionContext(ConnectionContext context, FastTunnelServer fastTunnelServer, ILogger logger) public FastTunnelConnectionContext(ConnectionContext context, FastTunnelServer fastTunnelServer, ILogger logger)
{ {
@ -48,7 +47,7 @@ internal class FastTunnelConnectionContext : ConnectionContext
return _inner.DisposeAsync(); return _inner.DisposeAsync();
} }
ReadOnlySequence<byte> readableBuffer; private readonly ReadOnlySequence<byte> readableBuffer;
/// <summary> /// <summary>
/// 解析FastTunnel协议 /// 解析FastTunnel协议
@ -106,9 +105,8 @@ internal class FastTunnelConnectionContext : ConnectionContext
public string Method; public string Method;
public string Host = null; public string Host = null;
public string MessageId; public string MessageId;
private bool complete = false;
bool complete = false; private bool isFirstLine = true;
bool isFirstLine = true;
/// <summary> /// <summary>
/// ///
@ -154,7 +152,7 @@ internal class FastTunnelConnectionContext : ConnectionContext
if (Method != "PROXY") if (Method != "PROXY")
{ {
// 匹配Host // 匹配Host
if (fastTunnelServer.TryGetWebProxyByHost(Host, out WebInfo web)) if (fastTunnelServer.TryGetWebProxyByHost(Host, out var web))
{ {
MatchWeb = web; MatchWeb = web;
} }

View File

@ -4,11 +4,6 @@
// 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.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
@ -18,9 +13,9 @@ namespace FastTunnel.Core.Forwarder.Kestrel;
internal class HandleHttpConnectionMiddleware internal class HandleHttpConnectionMiddleware
{ {
readonly ConnectionDelegate next; private readonly ConnectionDelegate next;
readonly ILogger<HandleHttpConnectionMiddleware> logger; private readonly ILogger<HandleHttpConnectionMiddleware> logger;
FastTunnelServer fastTunnelServer; private readonly FastTunnelServer fastTunnelServer;
public HandleHttpConnectionMiddleware(ConnectionDelegate next, ILogger<HandleHttpConnectionMiddleware> logger, FastTunnelServer fastTunnelServer) public HandleHttpConnectionMiddleware(ConnectionDelegate next, ILogger<HandleHttpConnectionMiddleware> logger, FastTunnelServer fastTunnelServer)
{ {

View File

@ -6,10 +6,7 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -25,9 +22,9 @@ namespace FastTunnel.Core.Forwarder.Kestrel;
internal class SwapConnectionMiddleware internal class SwapConnectionMiddleware
{ {
readonly ConnectionDelegate next; private readonly ConnectionDelegate next;
readonly ILogger<SwapConnectionMiddleware> logger; private readonly ILogger<SwapConnectionMiddleware> logger;
FastTunnelServer fastTunnelServer; private readonly FastTunnelServer fastTunnelServer;
public SwapConnectionMiddleware(ConnectionDelegate next, ILogger<SwapConnectionMiddleware> logger, FastTunnelServer fastTunnelServer) public SwapConnectionMiddleware(ConnectionDelegate next, ILogger<SwapConnectionMiddleware> logger, FastTunnelServer fastTunnelServer)
{ {

View File

@ -6,12 +6,9 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FastTunnel.Core.Extensions; using FastTunnel.Core.Extensions;
@ -76,7 +73,7 @@ internal class DuplexPipeStream : Stream
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
ValueTask<int> vt = ReadAsyncInternal(new Memory<byte>(buffer, offset, count), default); var vt = ReadAsyncInternal(new Memory<byte>(buffer, offset, count), default);
return vt.IsCompleted ? return vt.IsCompleted ?
vt.Result : vt.Result :
vt.AsTask().GetAwaiter().GetResult(); vt.AsTask().GetAwaiter().GetResult();

View File

@ -1,113 +1,114 @@
// Copyright (c) 2019-2022 Gui.H. https://github.com/FastTunnel/FastTunnel // Licensed under the Apache License, Version 2.0 (the "License").
// The FastTunnel licenses this file to you under the Apache License Version 2.0. // You may not use this file except in compliance with the License.
// For more details,You may obtain License file at: https://github.com/FastTunnel/FastTunnel/blob/v2/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.Net.WebSockets;
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.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.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder.MiddleWare namespace FastTunnel.Core.Forwarder.MiddleWare;
public class FastTunnelClientHandler
{ {
public class FastTunnelClientHandler private readonly ILogger<FastTunnelClientHandler> logger;
private readonly FastTunnelServer fastTunnelServer;
private readonly Version serverVersion;
private readonly ILoginHandler loginHandler;
public FastTunnelClientHandler(
ILogger<FastTunnelClientHandler> logger, FastTunnelServer fastTunnelServer, ILoginHandler loginHandler)
{ {
readonly ILogger<FastTunnelClientHandler> logger; this.logger = logger;
readonly FastTunnelServer fastTunnelServer; this.fastTunnelServer = fastTunnelServer;
readonly Version serverVersion; this.loginHandler = loginHandler;
readonly ILoginHandler loginHandler;
public FastTunnelClientHandler( serverVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
ILogger<FastTunnelClientHandler> logger, FastTunnelServer fastTunnelServer, ILoginHandler loginHandler) }
public async Task Handle(HttpContext context, Func<Task> next)
{
try
{ {
this.logger = logger; if (!context.WebSockets.IsWebSocketRequest || !context.Request.Headers.TryGetValue(FastTunnelConst.FASTTUNNEL_VERSION, out var version))
this.fastTunnelServer = fastTunnelServer;
this.loginHandler = loginHandler;
serverVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
}
public async Task Handle(HttpContext context, Func<Task> next)
{
try
{ {
if (!context.WebSockets.IsWebSocketRequest || !context.Request.Headers.TryGetValue(FastTunnelConst.FASTTUNNEL_VERSION, out var version)) await next();
{
await next();
return;
};
await handleClient(context, version);
}
catch (Exception ex)
{
logger.LogError(ex);
}
}
private async Task handleClient(HttpContext context, string clientVersion)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
if (Version.Parse(clientVersion).Major != serverVersion.Major)
{
await Close(webSocket, $"客户端版本{clientVersion}与服务端版本{serverVersion}不兼容,请升级。");
return; return;
} };
if (!checkToken(context)) await handleClient(context, version);
{
await Close(webSocket, "Token验证失败");
return;
}
var client = new TunnelClient(webSocket, fastTunnelServer, loginHandler, context.Connection.RemoteIpAddress);
client.ConnectionPort = context.Connection.LocalPort;
try
{
fastTunnelServer.OnClientLogin(client);
await client.ReviceAsync(CancellationToken.None);
fastTunnelServer.OnClientLogout(client);
}
catch (Exception)
{
fastTunnelServer.OnClientLogout(client);
}
} }
catch (Exception ex)
private static async Task Close(WebSocket webSocket, string reason)
{ {
await webSocket.SendCmdAsync(MessageType.Log, reason, CancellationToken.None); logger.LogError(ex);
await webSocket.CloseAsync(WebSocketCloseStatus.Empty, string.Empty, CancellationToken.None); }
}
private async Task handleClient(HttpContext context, string clientVersion)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
if (Version.Parse(clientVersion).Major != serverVersion.Major)
{
await Close(webSocket, $"客户端版本{clientVersion}与服务端版本{serverVersion}不兼容,请升级。");
return; return;
} }
private bool checkToken(HttpContext context) if (!checkToken(context))
{ {
var checkToken = false; await Close(webSocket, "Token验证失败");
if (fastTunnelServer.ServerOption.CurrentValue.Tokens != null && fastTunnelServer.ServerOption.CurrentValue.Tokens.Count != 0) return;
{ }
checkToken = true;
}
if (!checkToken) var client = new TunnelClient(webSocket, fastTunnelServer, loginHandler, context.Connection.RemoteIpAddress);
return true; client.ConnectionPort = context.Connection.LocalPort;
// 客户端未携带token登录失败 try
if (!context.Request.Headers.TryGetValue(FastTunnelConst.FASTTUNNEL_TOKEN, out var token)) {
return false; fastTunnelServer.OnClientLogin(client);
await client.ReviceAsync(CancellationToken.None);
if (fastTunnelServer.ServerOption.CurrentValue.Tokens?.Contains(token) ?? false) fastTunnelServer.OnClientLogout(client);
return true; }
catch (Exception)
return false; {
fastTunnelServer.OnClientLogout(client);
} }
} }
private static async Task Close(WebSocket webSocket, string reason)
{
await webSocket.SendCmdAsync(MessageType.Log, reason, CancellationToken.None);
await webSocket.CloseAsync(WebSocketCloseStatus.Empty, string.Empty, CancellationToken.None);
return;
}
private bool checkToken(HttpContext context)
{
var checkToken = false;
if (fastTunnelServer.ServerOption.CurrentValue.Tokens != null && fastTunnelServer.ServerOption.CurrentValue.Tokens.Count != 0)
{
checkToken = true;
}
if (!checkToken)
return true;
// 客户端未携带token登录失败
if (!context.Request.Headers.TryGetValue(FastTunnelConst.FASTTUNNEL_TOKEN, out var token))
return false;
if (fastTunnelServer.ServerOption.CurrentValue.Tokens?.Contains(token) ?? false)
return true;
return false;
}
} }

View File

@ -1,71 +1,73 @@
// 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.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions; using FastTunnel.Core.Extensions;
using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder.MiddleWare namespace FastTunnel.Core.Forwarder.MiddleWare;
public class FastTunnelSwapHandler
{ {
public class FastTunnelSwapHandler private readonly ILogger<FastTunnelClientHandler> logger;
private readonly FastTunnelServer fastTunnelServer;
public FastTunnelSwapHandler(ILogger<FastTunnelClientHandler> logger, FastTunnelServer fastTunnelServer)
{ {
ILogger<FastTunnelClientHandler> logger; this.logger = logger;
FastTunnelServer fastTunnelServer; this.fastTunnelServer = fastTunnelServer;
}
public FastTunnelSwapHandler(ILogger<FastTunnelClientHandler> logger, FastTunnelServer fastTunnelServer) public async Task Handle(HttpContext context, Func<Task> next)
{
try
{ {
this.logger = logger; if (context.Request.Method != "PROXY")
this.fastTunnelServer = fastTunnelServer; {
await next();
return;
}
var requestId = context.Request.Path.Value.Trim('/');
logger.LogDebug($"[PROXY]:Start {requestId}");
if (!fastTunnelServer.ResponseTasks.TryRemove(requestId, out var responseAwaiter))
{
logger.LogError($"[PROXY]:RequestId不存在 {requestId}");
return;
};
var lifetime = context.Features.Get<IConnectionLifetimeFeature>();
var transport = context.Features.Get<IConnectionTransportFeature>();
if (lifetime == null || transport == null)
{
return;
}
using var reverseConnection = new WebSocketStream(lifetime, transport);
responseAwaiter.TrySetResult(reverseConnection);
var closedAwaiter = new TaskCompletionSource<object>();
lifetime.ConnectionClosed.Register((task) =>
{
(task as TaskCompletionSource<object>).SetResult(null);
}, closedAwaiter);
await closedAwaiter.Task;
logger.LogDebug($"[PROXY]:Closed {requestId}");
} }
catch (Exception ex)
public async Task Handle(HttpContext context, Func<Task> next)
{ {
try logger.LogError(ex);
{
if (context.Request.Method != "PROXY")
{
await next();
return;
}
var requestId = context.Request.Path.Value.Trim('/');
logger.LogDebug($"[PROXY]:Start {requestId}");
if (!fastTunnelServer.ResponseTasks.TryRemove(requestId, out var responseAwaiter))
{
logger.LogError($"[PROXY]:RequestId不存在 {requestId}");
return;
};
var lifetime = context.Features.Get<IConnectionLifetimeFeature>();
var transport = context.Features.Get<IConnectionTransportFeature>();
if (lifetime == null || transport == null)
{
return;
}
using var reverseConnection = new WebSocketStream(lifetime, transport);
responseAwaiter.TrySetResult(reverseConnection);
var closedAwaiter = new TaskCompletionSource<object>();
lifetime.ConnectionClosed.Register((task) =>
{
(task as TaskCompletionSource<object>).SetResult(null);
}, closedAwaiter);
await closedAwaiter.Task;
logger.LogDebug($"[PROXY]:Closed {requestId}");
}
catch (Exception ex)
{
logger.LogError(ex);
}
} }
} }
} }

View File

@ -5,232 +5,229 @@
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.MiddleWare namespace FastTunnel.Core.Forwarder.MiddleWare;
internal sealed class LoggingStream : Stream
{ {
internal sealed class LoggingStream : Stream private readonly Stream _inner;
private readonly ILogger _logger;
public LoggingStream(Stream inner, ILogger logger)
{ {
private readonly Stream _inner; _inner = inner;
private readonly ILogger _logger; _logger = logger;
}
public LoggingStream(Stream inner, ILogger logger) public override bool CanRead
{
get
{ {
_inner = inner; return _inner.CanRead;
_logger = logger;
}
public override bool CanRead
{
get
{
return _inner.CanRead;
}
}
public override bool CanSeek
{
get
{
return _inner.CanSeek;
}
}
public override bool CanWrite
{
get
{
return _inner.CanWrite;
}
}
public override long Length
{
get
{
return _inner.Length;
}
}
public override long Position
{
get
{
return _inner.Position;
}
set
{
_inner.Position = value;
}
}
public override void Flush()
{
_inner.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return _inner.FlushAsync(cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count)
{
int read = _inner.Read(buffer, offset, count);
Log("[Read]", new ReadOnlySpan<byte>(buffer, offset, read));
return read;
}
public override int Read(Span<byte> destination)
{
int read = _inner.Read(destination);
Log("[Read]", destination.Slice(0, read));
return read;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
int read = await _inner.ReadAsync(buffer.AsMemory(offset, count), cancellationToken);
Log("[ReadAsync]", new ReadOnlySpan<byte>(buffer, offset, read));
return read;
}
public override async ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default)
{
int read = await _inner.ReadAsync(destination, cancellationToken);
Log("[ReadAsync]", destination.Span.Slice(0, read));
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
return _inner.Seek(offset, origin);
}
public override void SetLength(long value)
{
_inner.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
Log("[Write]", new ReadOnlySpan<byte>(buffer, offset, count));
_inner.Write(buffer, offset, count);
}
public override void Write(ReadOnlySpan<byte> source)
{
Log("[Write]", source);
_inner.Write(source);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
Log("WriteAsync", new ReadOnlySpan<byte>(buffer, offset, count));
return _inner.WriteAsync(buffer, offset, count, cancellationToken);
}
public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default)
{
Log("WriteAsync", source.Span);
return _inner.WriteAsync(source, cancellationToken);
}
private void Log(string method, ReadOnlySpan<byte> buffer)
{
var builder = new StringBuilder();
builder.Append(method);
builder.Append('[');
builder.Append(buffer.Length);
builder.Append(']');
if (buffer.Length > 0)
{
builder.AppendLine();
}
var charBuilder = new StringBuilder();
// Write the hex
for (int i = 0; i < buffer.Length; i++)
{
builder.Append(buffer[i].ToString("X2", CultureInfo.InvariantCulture));
builder.Append(' ');
var bufferChar = (char)buffer[i];
if (char.IsControl(bufferChar))
{
charBuilder.Append('.');
}
else
{
charBuilder.Append(bufferChar);
}
if ((i + 1) % 16 == 0)
{
builder.Append(" ");
builder.Append(charBuilder);
if (i != buffer.Length - 1)
{
builder.AppendLine();
}
charBuilder.Clear();
}
else if ((i + 1) % 8 == 0)
{
builder.Append(' ');
charBuilder.Append(' ');
}
}
// Different than charBuffer.Length since charBuffer contains an extra " " after the 8th byte.
var numBytesInLastLine = buffer.Length % 16;
if (numBytesInLastLine > 0)
{
// 2 (between hex and char blocks) + num bytes left (3 per byte)
var padLength = 2 + (3 * (16 - numBytesInLastLine));
// extra for space after 8th byte
if (numBytesInLastLine < 8)
{
padLength++;
}
builder.Append(new string(' ', padLength));
builder.Append(charBuilder);
}
_logger.LogInformation(builder.ToString());
}
// The below APM methods call the underlying Read/WriteAsync methods which will still be logged.
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state);
}
public override int EndRead(IAsyncResult asyncResult)
{
return TaskToApm.End<int>(asyncResult);
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state);
}
public override void EndWrite(IAsyncResult asyncResult)
{
TaskToApm.End(asyncResult);
} }
} }
public override bool CanSeek
{
get
{
return _inner.CanSeek;
}
}
public override bool CanWrite
{
get
{
return _inner.CanWrite;
}
}
public override long Length
{
get
{
return _inner.Length;
}
}
public override long Position
{
get
{
return _inner.Position;
}
set
{
_inner.Position = value;
}
}
public override void Flush()
{
_inner.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return _inner.FlushAsync(cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count)
{
var read = _inner.Read(buffer, offset, count);
Log("[Read]", new ReadOnlySpan<byte>(buffer, offset, read));
return read;
}
public override int Read(Span<byte> destination)
{
var read = _inner.Read(destination);
Log("[Read]", destination.Slice(0, read));
return read;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var read = await _inner.ReadAsync(buffer.AsMemory(offset, count), cancellationToken);
Log("[ReadAsync]", new ReadOnlySpan<byte>(buffer, offset, read));
return read;
}
public override async ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default)
{
var read = await _inner.ReadAsync(destination, cancellationToken);
Log("[ReadAsync]", destination.Span.Slice(0, read));
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
return _inner.Seek(offset, origin);
}
public override void SetLength(long value)
{
_inner.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
Log("[Write]", new ReadOnlySpan<byte>(buffer, offset, count));
_inner.Write(buffer, offset, count);
}
public override void Write(ReadOnlySpan<byte> source)
{
Log("[Write]", source);
_inner.Write(source);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
Log("WriteAsync", new ReadOnlySpan<byte>(buffer, offset, count));
return _inner.WriteAsync(buffer, offset, count, cancellationToken);
}
public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default)
{
Log("WriteAsync", source.Span);
return _inner.WriteAsync(source, cancellationToken);
}
private void Log(string method, ReadOnlySpan<byte> buffer)
{
var builder = new StringBuilder();
builder.Append(method);
builder.Append('[');
builder.Append(buffer.Length);
builder.Append(']');
if (buffer.Length > 0)
{
builder.AppendLine();
}
var charBuilder = new StringBuilder();
// Write the hex
for (var i = 0; i < buffer.Length; i++)
{
builder.Append(buffer[i].ToString("X2", CultureInfo.InvariantCulture));
builder.Append(' ');
var bufferChar = (char)buffer[i];
if (char.IsControl(bufferChar))
{
charBuilder.Append('.');
}
else
{
charBuilder.Append(bufferChar);
}
if ((i + 1) % 16 == 0)
{
builder.Append(" ");
builder.Append(charBuilder);
if (i != buffer.Length - 1)
{
builder.AppendLine();
}
charBuilder.Clear();
}
else if ((i + 1) % 8 == 0)
{
builder.Append(' ');
charBuilder.Append(' ');
}
}
// Different than charBuffer.Length since charBuffer contains an extra " " after the 8th byte.
var numBytesInLastLine = buffer.Length % 16;
if (numBytesInLastLine > 0)
{
// 2 (between hex and char blocks) + num bytes left (3 per byte)
var padLength = 2 + (3 * (16 - numBytesInLastLine));
// extra for space after 8th byte
if (numBytesInLastLine < 8)
{
padLength++;
}
builder.Append(new string(' ', padLength));
builder.Append(charBuilder);
}
_logger.LogInformation(builder.ToString());
}
// The below APM methods call the underlying Read/WriteAsync methods which will still be logged.
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state);
}
public override int EndRead(IAsyncResult asyncResult)
{
return TaskToApm.End<int>(asyncResult);
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state);
}
public override void EndWrite(IAsyncResult asyncResult)
{
TaskToApm.End(asyncResult);
}
} }

View File

@ -5,10 +5,7 @@
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@ -9,75 +9,74 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder namespace FastTunnel.Core.Forwarder;
public class ResponseStream : Stream
{ {
public class ResponseStream : Stream public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => throw new NotImplementedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
private readonly MemoryStream m_Stream;
public ResponseStream(byte[] bytes)
{ {
public override bool CanRead => true; m_Stream = new MemoryStream(bytes);
}
public override bool CanSeek => false; public override void Flush()
{
throw new NotImplementedException();
}
public override bool CanWrite => true; private bool complete;
public override long Length => throw new NotImplementedException(); public override int Read(byte[] buffer, int offset, int count)
{
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } if (!complete)
readonly MemoryStream m_Stream;
public ResponseStream(byte[] bytes)
{ {
m_Stream = new MemoryStream(bytes); return 0;
};
var len = m_Stream.Read(buffer, offset, count);
return len;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
Console.Write(Encoding.UTF8.GetString(buffer, offset, count));
complete = true;
}
protected override void Dispose(bool disposing)
{
if (!disposing)
{
return;
} }
public override void Flush() m_Stream.Dispose();
{ }
throw new NotImplementedException();
}
bool complete; public override ValueTask DisposeAsync()
{
public override int Read(byte[] buffer, int offset, int count) Dispose(true);
{ return ValueTask.CompletedTask;
if (!complete)
{
return 0;
};
var len = m_Stream.Read(buffer, offset, count);
return len;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
Console.Write(Encoding.UTF8.GetString(buffer, offset, count));
complete = true;
}
protected override void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
m_Stream.Dispose();
}
public override ValueTask DisposeAsync()
{
Dispose(true);
return ValueTask.CompletedTask;
}
} }
} }

View File

@ -1,111 +1,109 @@
// Copyright (c) 2019-2022 Gui.H. https://github.com/FastTunnel/FastTunnel // Licensed under the Apache License, Version 2.0 (the "License").
// The FastTunnel licenses this file to you under the Apache License Version 2.0. // You may not use this file except in compliance with the License.
// For more details,You may obtain License file at: https://github.com/FastTunnel/FastTunnel/blob/v2/LICENSE // You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using Microsoft.AspNetCore.Http;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace FastTunnel.Core.Forwarder namespace FastTunnel.Core.Forwarder;
public class TranStream : Stream
{ {
public class TranStream : Stream private readonly Stream readStream;
private readonly Stream wirteStream;
public TranStream(HttpContext context)
{ {
private readonly Stream readStream; this.readStream = context.Request.BodyReader.AsStream();
private readonly Stream wirteStream; this.wirteStream = context.Response.BodyWriter.AsStream();
}
public override bool CanRead => true;
public TranStream(HttpContext context) public override bool CanSeek => false;
{
this.readStream = context.Request.BodyReader.AsStream();
this.wirteStream = context.Response.BodyWriter.AsStream();
}
public override bool CanRead => true;
public override bool CanSeek => false; public override bool CanWrite => true;
public override bool CanWrite => true; public override long Length => throw new NotSupportedException();
public override long Length => throw new NotSupportedException(); public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override long Position public override void Flush()
{ {
get => throw new NotSupportedException(); this.wirteStream.Flush();
set => throw new NotSupportedException(); }
}
public override void Flush() public override Task FlushAsync(CancellationToken cancellationToken)
{ {
this.wirteStream.Flush(); return this.wirteStream.FlushAsync(cancellationToken);
} }
public override Task FlushAsync(CancellationToken cancellationToken) public override long Seek(long offset, SeekOrigin origin)
{ {
return this.wirteStream.FlushAsync(cancellationToken); throw new NotSupportedException();
} }
public override long Seek(long offset, SeekOrigin origin) public override void SetLength(long value)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public override void SetLength(long value) public override int Read(byte[] buffer, int offset, int count)
{ {
throw new NotSupportedException(); return this.readStream.Read(buffer, offset, count);
} }
public override void Write(byte[] buffer, int offset, int count)
{
this.wirteStream.Write(buffer, offset, count);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return this.readStream.ReadAsync(buffer, cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count) public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
return this.readStream.Read(buffer, offset, count); var len = await this.readStream.ReadAsync(buffer, offset, count, cancellationToken);
} if (len == 0) { Console.WriteLine("==========ReadAsync END=========="); }
public override void Write(byte[] buffer, int offset, int count) return len;
{ }
this.wirteStream.Write(buffer, offset, count);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return this.readStream.ReadAsync(buffer, cancellationToken);
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public override void Write(ReadOnlySpan<byte> buffer)
{ {
var len = await this.readStream.ReadAsync(buffer, offset, count, cancellationToken); this.wirteStream.Write(buffer);
if (len == 0) { Console.WriteLine("==========ReadAsync END=========="); } }
return len;
}
public override void Write(ReadOnlySpan<byte> buffer) public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
this.wirteStream.Write(buffer); return this.wirteStream.WriteAsync(buffer, offset, count, cancellationToken);
} }
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{ {
return this.wirteStream.WriteAsync(buffer, offset, count, cancellationToken); await this.wirteStream.WriteAsync(buffer, cancellationToken);
} }
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) protected override void Dispose(bool disposing)
{ {
await this.wirteStream.WriteAsync(buffer, cancellationToken); Console.WriteLine("========Dispose=========");
} }
protected override void Dispose(bool disposing) public override ValueTask DisposeAsync()
{ {
Console.WriteLine("========Dispose========="); return ValueTask.CompletedTask;
} }
public override ValueTask DisposeAsync() public override void Close()
{ {
return ValueTask.CompletedTask; Console.WriteLine("========Close=========");
} base.Close();
public override void Close()
{
Console.WriteLine("========Close=========");
base.Close();
}
} }
} }

View File

@ -1,112 +1,112 @@
using Microsoft; // Licensed under the Apache License, Version 2.0 (the "License").
using Microsoft.AspNetCore.Connections.Features; // 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;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections.Features;
namespace FastTunnel.Core.Forwarder namespace FastTunnel.Core.Forwarder;
internal sealed class WebSocketStream : Stream
{ {
sealed class WebSocketStream : Stream private readonly Stream readStream;
private readonly Stream wirteStream;
private readonly IConnectionLifetimeFeature lifetimeFeature;
public WebSocketStream(IConnectionLifetimeFeature lifetimeFeature, IConnectionTransportFeature transportFeature)
{ {
private readonly Stream readStream; this.readStream = transportFeature.Transport.Input.AsStream();
private readonly Stream wirteStream; this.wirteStream = transportFeature.Transport.Output.AsStream();
private readonly IConnectionLifetimeFeature lifetimeFeature; this.lifetimeFeature = lifetimeFeature;
}
public WebSocketStream(IConnectionLifetimeFeature lifetimeFeature, IConnectionTransportFeature transportFeature) public WebSocketStream(Stream stream)
{ {
this.readStream = transportFeature.Transport.Input.AsStream(); this.readStream = stream;
this.wirteStream = transportFeature.Transport.Output.AsStream(); this.wirteStream = stream;
this.lifetimeFeature = lifetimeFeature; this.lifetimeFeature = null;
} }
public WebSocketStream(Stream stream) public override bool CanRead => true;
{
this.readStream = stream;
this.wirteStream = stream;
this.lifetimeFeature = null;
}
public override bool CanRead => true; public override bool CanSeek => false;
public override bool CanSeek => false; public override bool CanWrite => true;
public override bool CanWrite => true; public override long Length => throw new NotSupportedException();
public override long Length => throw new NotSupportedException(); public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override long Position public override void Flush()
{ {
get => throw new NotSupportedException(); this.wirteStream.Flush();
set => throw new NotSupportedException(); }
}
public override void Flush() public override Task FlushAsync(CancellationToken cancellationToken)
{ {
this.wirteStream.Flush(); return this.wirteStream.FlushAsync(cancellationToken);
} }
public override Task FlushAsync(CancellationToken cancellationToken) public override long Seek(long offset, SeekOrigin origin)
{ {
return this.wirteStream.FlushAsync(cancellationToken); throw new NotSupportedException();
} }
public override long Seek(long offset, SeekOrigin origin) public override void SetLength(long value)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public override void SetLength(long value) public override int Read(byte[] buffer, int offset, int count)
{ {
throw new NotSupportedException(); return this.readStream.Read(buffer, offset, count);
} }
public override void Write(byte[] buffer, int offset, int count)
{
this.wirteStream.Write(buffer, offset, count);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return this.readStream.ReadAsync(buffer, cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count) public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
return this.readStream.Read(buffer, offset, count); return this.readStream.ReadAsync(buffer, offset, count, cancellationToken);
} }
public override void Write(byte[] buffer, int offset, int count)
{
this.wirteStream.Write(buffer, offset, count);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return this.readStream.ReadAsync(buffer, cancellationToken);
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public override void Write(ReadOnlySpan<byte> buffer)
{ {
return this.readStream.ReadAsync(buffer, offset, count, cancellationToken); this.wirteStream.Write(buffer);
} }
public override void Write(ReadOnlySpan<byte> buffer) public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
this.wirteStream.Write(buffer); return this.wirteStream.WriteAsync(buffer, offset, count, cancellationToken);
} }
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{ {
return this.wirteStream.WriteAsync(buffer, offset, count, cancellationToken); await this.wirteStream.WriteAsync(buffer, cancellationToken);
} }
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) protected override void Dispose(bool disposing)
{ {
await this.wirteStream.WriteAsync(buffer, cancellationToken); this.lifetimeFeature?.Abort();
} }
protected override void Dispose(bool disposing) public override ValueTask DisposeAsync()
{ {
this.lifetimeFeature?.Abort(); this.lifetimeFeature?.Abort();
} return ValueTask.CompletedTask;
public override ValueTask DisposeAsync()
{
this.lifetimeFeature?.Abort();
return ValueTask.CompletedTask;
}
} }
} }

View File

@ -4,19 +4,13 @@
// 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.Client;
using FastTunnel.Core.Models;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
namespace FastTunnel.Core.Handlers.Client namespace FastTunnel.Core.Handlers.Client;
public interface IClientHandler
{ {
public interface IClientHandler Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken);
{
Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken);
}
} }

View File

@ -4,30 +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.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;
public LogHandler(ILogger<LogHandler> logger)
{ {
ILogger<LogHandler> _logger; _logger = logger;
}
public LogHandler(ILogger<LogHandler> logger) public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken)
{ {
_logger = logger; _logger.LogInformation(msg.Replace("\n", string.Empty));
} await Task.CompletedTask;
public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken)
{
_logger.LogInformation(msg.Replace("\n", string.Empty));
await Task.CompletedTask;
}
} }
} }

View File

@ -1,86 +1,87 @@
// Copyright (c) 2019-2022 Gui.H. https://github.com/FastTunnel/FastTunnel // Licensed under the Apache License, Version 2.0 (the "License").
// The FastTunnel licenses this file to you under the Apache License Version 2.0. // You may not use this file except in compliance with the License.
// For more details,You may obtain License file at: https://github.com/FastTunnel/FastTunnel/blob/v2/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.Client;
using System; using System;
using System.IO; using System.IO;
using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
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.Sockets; using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Net.Security;
namespace FastTunnel.Core.Handlers.Client namespace FastTunnel.Core.Handlers.Client;
public class SwapHandler : IClientHandler
{ {
public class SwapHandler : IClientHandler private readonly ILogger<SwapHandler> _logger;
public SwapHandler(ILogger<SwapHandler> logger)
{ {
readonly ILogger<SwapHandler> _logger; _logger = logger;
}
public SwapHandler(ILogger<SwapHandler> logger) public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken)
{
var msgs = msg.Split('|');
var requestId = msgs[0];
var address = msgs[1];
try
{ {
_logger = logger; using (var serverStream = await createRemote(requestId, cleint, cancellationToken))
} using (var localStream = await createLocal(requestId, address, cancellationToken))
public async Task HandlerMsgAsync(FastTunnelClient cleint, string msg, CancellationToken cancellationToken)
{
var msgs = msg.Split('|');
var requestId = msgs[0];
var address = msgs[1];
try
{ {
using (Stream serverStream = await createRemote(requestId, cleint, cancellationToken)) var taskX = serverStream.CopyToAsync(localStream, cancellationToken);
using (Stream localStream = await createLocal(requestId, address, cancellationToken)) var taskY = localStream.CopyToAsync(serverStream, cancellationToken);
try
{
await Task.WhenAll(taskX, taskY);
}
catch (Exception)
{ {
var taskX = serverStream.CopyToAsync(localStream, cancellationToken);
var taskY = localStream.CopyToAsync(serverStream, cancellationToken);
try }
{ finally
await Task.WhenAll(taskX, taskY); {
} _logger.LogError($"=====================Swap End:{requestId}================== ");
catch (Exception ex)
{
}
finally
{
_logger.LogError($"=====================Swap End:{requestId}================== ");
}
} }
} }
catch (Exception ex)
{
_logger.LogError(ex, $"Swap error {requestId}");
}
} }
catch (Exception ex)
private async Task<Stream> createLocal(string requestId, string localhost, CancellationToken cancellationToken)
{ {
var socket = await DnsSocketFactory.ConnectAsync(localhost.Split(":")[0], int.Parse(localhost.Split(":")[1])); _logger.LogError(ex, $"Swap error {requestId}");
return new NetworkStream(socket, true) { ReadTimeout = 1000 * 60 * 10 };
}
private async Task<Stream> createRemote(string requestId, FastTunnelClient cleint, CancellationToken cancellationToken)
{
var socket = await DnsSocketFactory.ConnectAsync(cleint.Server.ServerAddr, cleint.Server.ServerPort);
Stream serverStream = new NetworkStream(socket, true) { ReadTimeout = 1000 * 60 * 10 };
if (cleint.Server.Protocol == "wss")
{
var sslStream = new SslStream(serverStream, false, delegate { return true; });
await sslStream.AuthenticateAsClientAsync(cleint.Server.ServerAddr);
serverStream = sslStream;
}
var reverse = $"PROXY /{requestId} HTTP/1.1\r\nHost: {cleint.Server.ServerAddr}:{cleint.Server.ServerPort}\r\n\r\n";
var requestMsg = Encoding.UTF8.GetBytes(reverse);
await serverStream.WriteAsync(requestMsg, cancellationToken);
return serverStream;
} }
} }
private async Task<Stream> createLocal(string requestId, string localhost, CancellationToken cancellationToken)
{
var socket = await DnsSocketFactory.ConnectAsync(localhost.Split(":")[0], int.Parse(localhost.Split(":")[1]));
return new NetworkStream(socket, true) { ReadTimeout = 1000 * 60 * 10 };
}
private async Task<Stream> createRemote(string requestId, FastTunnelClient cleint, CancellationToken cancellationToken)
{
var socket = await DnsSocketFactory.ConnectAsync(cleint.Server.ServerAddr, cleint.Server.ServerPort);
Stream serverStream = new NetworkStream(socket, true) { ReadTimeout = 1000 * 60 * 10 };
if (cleint.Server.Protocol == "wss")
{
var sslStream = new SslStream(serverStream, false, delegate { return true; });
await sslStream.AuthenticateAsClientAsync(cleint.Server.ServerAddr);
serverStream = sslStream;
}
var reverse = $"PROXY /{requestId} HTTP/1.1\r\nHost: {cleint.Server.ServerAddr}:{cleint.Server.ServerPort}\r\n\r\n";
var requestMsg = Encoding.UTF8.GetBytes(reverse);
await serverStream.WriteAsync(requestMsg, cancellationToken);
return serverStream;
}
} }

View File

@ -4,109 +4,104 @@
// 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.IO;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Core.Exceptions; using FastTunnel.Core.Exceptions;
using FastTunnel.Core.Extensions; using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models; using FastTunnel.Core.Models;
using FastTunnel.Core.Sockets;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Handlers.Server namespace FastTunnel.Core.Handlers.Server;
public class ForwardDispatcher
{ {
public class ForwardDispatcher private readonly FastTunnelServer _server;
private readonly ForwardConfig _config;
private readonly ILogger logger;
public ForwardDispatcher(ILogger logger, FastTunnelServer server, ForwardConfig config)
{ {
private FastTunnelServer _server; this.logger = logger;
private ForwardConfig _config; _server = server;
ILogger logger; _config = config;
}
public ForwardDispatcher(ILogger logger, FastTunnelServer server, ForwardConfig config) /// <summary>
{ ///
this.logger = logger; /// </summary>
_server = server; /// <param name="_socket">用户请求</param>
_config = config; /// <param name="client">FastTunnel客户端</param>
} /// <returns></returns>
public async Task DispatchAsync(Socket _socket, WebSocket client)
{
var msgId = Guid.NewGuid().ToString().Replace("-", "");
/// <summary> try
///
/// </summary>
/// <param name="_socket">用户请求</param>
/// <param name="client">FastTunnel客户端</param>
/// <returns></returns>
public async Task DispatchAsync(Socket _socket, WebSocket client)
{ {
var msgId = Guid.NewGuid().ToString().Replace("-", ""); await Task.Yield();
logger.LogDebug($"[Forward]Swap开始 {msgId}|{_config.RemotePort}=>{_config.LocalIp}:{_config.LocalPort}");
var tcs = new TaskCompletionSource<Stream>();
tcs.SetTimeOut(10000, () => { logger.LogDebug($"[Dispatch TimeOut]:{msgId}"); });
_server.ResponseTasks.TryAdd(msgId, tcs);
try try
{ {
await Task.Yield(); await client.SendCmdAsync(MessageType.Forward, $"{msgId}|{_config.LocalIp}:{_config.LocalPort}", CancellationToken.None);
logger.LogDebug($"[Forward]Swap开始 {msgId}|{_config.RemotePort}=>{_config.LocalIp}:{_config.LocalPort}"); }
catch (SocketClosedException sex)
var tcs = new TaskCompletionSource<Stream>(); {
tcs.SetTimeOut(10000, () => { logger.LogDebug($"[Dispatch TimeOut]:{msgId}"); }); // TODO:客户端已掉线,但是没有移除对端口的监听
logger.LogError($"[Forward]Swap 客户端已离线 {sex.Message}");
_server.ResponseTasks.TryAdd(msgId, tcs); tcs.TrySetCanceled();
Close(_socket);
try return;
{
await client.SendCmdAsync(MessageType.Forward, $"{msgId}|{_config.LocalIp}:{_config.LocalPort}", CancellationToken.None);
}
catch (SocketClosedException sex)
{
// TODO:客户端已掉线,但是没有移除对端口的监听
logger.LogError($"[Forward]Swap 客户端已离线 {sex.Message}");
tcs.TrySetCanceled();
Close(_socket);
return;
}
catch (Exception ex)
{
// 网络不稳定
logger.LogError(ex, $"[Forward]Swap Exception");
tcs.TrySetCanceled();
Close(_socket);
return;
}
using (var stream1 = await tcs.Task)
using (var stream2 = new NetworkStream(_socket, true) { ReadTimeout = 1000 * 60 * 10 })
{
await Task.WhenAll(stream1.CopyToAsync(stream2), stream2.CopyToAsync(stream1));
}
logger.LogDebug($"[Forward]Swap OK {msgId}");
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogDebug($"[Forward]Swap Error {msgId}" + ex.Message); // 网络不稳定
logger.LogError(ex, $"[Forward]Swap Exception");
tcs.TrySetCanceled();
Close(_socket);
return;
} }
finally
{
_server.ResponseTasks.TryRemove(msgId, out _);
}
}
private void Close(Socket socket) using (var stream1 = await tcs.Task)
using (var stream2 = new NetworkStream(_socket, true) { ReadTimeout = 1000 * 60 * 10 })
{
await Task.WhenAll(stream1.CopyToAsync(stream2), stream2.CopyToAsync(stream1));
}
logger.LogDebug($"[Forward]Swap OK {msgId}");
}
catch (Exception ex)
{ {
try logger.LogDebug($"[Forward]Swap Error {msgId}" + ex.Message);
{ }
socket.Shutdown(SocketShutdown.Both); finally
} {
catch (Exception) _server.ResponseTasks.TryRemove(msgId, out _);
{ }
} }
finally
{ private void Close(Socket socket)
socket.Close(); {
} try
{
socket.Shutdown(SocketShutdown.Both);
}
catch (Exception)
{
}
finally
{
socket.Close();
} }
} }
} }

View File

@ -4,21 +4,15 @@
// 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.Models;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FastTunnel.Core.Client;
namespace FastTunnel.Core.Handlers.Server namespace FastTunnel.Core.Handlers.Server;
public interface IClientMessageHandler
{ {
public interface IClientMessageHandler bool NeedRecive { get; }
{
bool NeedRecive { get; }
Task<bool> HandlerMsg(FastTunnelServer server, WebSocket client, string msg); Task<bool> HandlerMsg(FastTunnelServer server, WebSocket client, string msg);
}
} }

View File

@ -4,14 +4,13 @@
// 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.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);
{
Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd);
}
} }

View File

@ -4,127 +4,119 @@
// 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.Extensions;
using FastTunnel.Core.Forwarder;
using FastTunnel.Core.Listener;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Yarp.ReverseProxy.Configuration; using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Listener;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Handlers.Server namespace FastTunnel.Core.Handlers.Server;
public class LoginHandler : ILoginHandler
{ {
public class LoginHandler : ILoginHandler private readonly ILogger logger;
{ public const bool NeedRecive = true;
readonly ILogger logger;
readonly IProxyConfigProvider proxyConfig;
public const bool NeedRecive = true;
public LoginHandler(ILogger<LoginHandler> logger, IProxyConfigProvider proxyConfig) public LoginHandler(ILogger<LoginHandler> logger)
{
this.logger = logger;
}
protected async Task HandleLoginAsync(FastTunnelServer server, TunnelClient client, LogInMassage requet)
{
var hasTunnel = false;
await client.webSocket.SendCmdAsync(MessageType.Log, $"穿透协议 | 映射关系(公网=>内网)", CancellationToken.None);
Thread.Sleep(300);
if (requet.Webs != null && requet.Webs.Any())
{ {
this.proxyConfig = proxyConfig; hasTunnel = true;
this.logger = logger; 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; });
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; });
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; if (server.ServerOption.CurrentValue.EnableForward)
await client.webSocket.SendCmdAsync(MessageType.Log, $"穿透协议 | 映射关系(公网=>内网)", CancellationToken.None);
Thread.Sleep(300);
if (requet.Webs != null && requet.Webs.Any())
{ {
hasTunnel = true; hasTunnel = true;
foreach (var item in requet.Webs)
foreach (var item in requet.Forwards)
{ {
var hostName = $"{item.SubDomain}.{server.ServerOption.CurrentValue.WebDomain}".Trim().ToLower(); try
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 InMemoryConfigProvider).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) if (server.ForwardList.TryGetValue(item.RemotePort, out var old))
{ {
// TODO:validateDomain logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}");
hostName = www.Trim().ToLower(); old.Listener.Stop();
server.WebList.AddOrUpdate(www, info, (key, oldInfo) => { return info; }); server.ForwardList.TryRemove(item.RemotePort, out var _);
(proxyConfig as InMemoryConfigProvider).AddWeb(www);
await client.webSocket.SendCmdAsync(MessageType.Log, $" HTTP | http://{www}:{client.ConnectionPort} => {item.LocalIp}:{item.LocalPort}", CancellationToken.None);
client.AddWeb(info);
} }
// 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
if (requet.Forwards != null && requet.Forwards.Any())
{ {
if (server.ServerOption.CurrentValue.EnableForward) await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.ForwardDisabled, CancellationToken.None);
{
hasTunnel = true;
foreach (var item in requet.Forwards)
{
try
{
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);
}
} }
if (!hasTunnel)
await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.NoTunnel, CancellationToken.None);
} }
public virtual async Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd) if (!hasTunnel)
{ await client.webSocket.SendCmdAsync(MessageType.Log, TunnelResource.NoTunnel, CancellationToken.None);
var msg = JsonSerializer.Deserialize<LogInMassage>(lineCmd); }
await HandleLoginAsync(fastTunnelServer, tunnelClient, msg);
return NeedRecive; public virtual async Task<bool> HandlerMsg(FastTunnelServer fastTunnelServer, TunnelClient tunnelClient, string lineCmd)
} {
var msg = JsonSerializer.Deserialize<LogInMassage>(lineCmd);
await HandleLoginAsync(fastTunnelServer, tunnelClient, msg);
return NeedRecive;
} }
} }

View File

@ -4,124 +4,122 @@
// 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.Handlers.Server;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading; using System.Threading;
using FastTunnel.Core.Handlers.Server;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Listener namespace FastTunnel.Core.Listener;
public class PortProxyListener
{ {
public class PortProxyListener private readonly ILogger _logerr;
public string ListenIp { get; set; }
public int ListenPort { get; set; }
private int m_numConnectedSockets;
private bool shutdown;
private ForwardDispatcher _requestDispatcher;
private readonly Socket listenSocket;
private readonly WebSocket client;
public PortProxyListener(string ip, int port, ILogger logerr, WebSocket client)
{ {
readonly ILogger _logerr; this.client = client;
_logerr = logerr;
this.ListenIp = ip;
this.ListenPort = port;
public string ListenIp { get; set; } var ipa = IPAddress.Parse(ListenIp);
var localEndPoint = new IPEndPoint(ipa, ListenPort);
public int ListenPort { get; set; } listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(localEndPoint);
}
int m_numConnectedSockets; public void Start(ForwardDispatcher requestDispatcher)
{
shutdown = false;
_requestDispatcher = requestDispatcher;
bool shutdown; listenSocket.Listen();
ForwardDispatcher _requestDispatcher;
readonly Socket listenSocket;
readonly WebSocket client;
public PortProxyListener(string ip, int port, ILogger logerr, WebSocket client) StartAccept(null);
}
private void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
_logerr.LogDebug($"【{ListenIp}:{ListenPort}】: StartAccept");
if (acceptEventArg == null)
{ {
this.client = client; acceptEventArg = new SocketAsyncEventArgs();
_logerr = logerr; acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
this.ListenIp = ip; }
this.ListenPort = port; else
{
IPAddress ipa = IPAddress.Parse(ListenIp); // socket must be cleared since the context object is being reused
IPEndPoint localEndPoint = new IPEndPoint(ipa, ListenPort); acceptEventArg.AcceptSocket = null;
listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(localEndPoint);
} }
public void Start(ForwardDispatcher requestDispatcher) var willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{ {
shutdown = false; ProcessAcceptAsync(acceptEventArg);
_requestDispatcher = requestDispatcher;
listenSocket.Listen();
StartAccept(null);
} }
}
private void StartAccept(SocketAsyncEventArgs acceptEventArg) private void ProcessAcceptAsync(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{ {
_logerr.LogDebug($"【{ListenIp}:{ListenPort}】: StartAccept"); var accept = e.AcceptSocket;
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
}
else
{
// socket must be cleared since the context object is being reused
acceptEventArg.AcceptSocket = null;
}
bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg); Interlocked.Increment(ref m_numConnectedSockets);
if (!willRaiseEvent)
_logerr.LogInformation($"【{ListenIp}:{ListenPort}】Accepted. There are {{0}} clients connected to the port",
m_numConnectedSockets);
// 将此客户端交由Dispatcher进行管理
_requestDispatcher.DispatchAsync(accept, client);
// Accept the next connection request
StartAccept(e);
}
else
{
Stop();
}
}
private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
ProcessAcceptAsync(e);
}
public void Stop()
{
if (shutdown)
return;
try
{
if (listenSocket.Connected)
{ {
ProcessAcceptAsync(acceptEventArg); listenSocket.Shutdown(SocketShutdown.Both);
} }
} }
catch (Exception)
private void ProcessAcceptAsync(SocketAsyncEventArgs e)
{ {
if (e.SocketError == SocketError.Success)
{
var accept = e.AcceptSocket;
Interlocked.Increment(ref m_numConnectedSockets);
_logerr.LogInformation($"【{ListenIp}:{ListenPort}】Accepted. There are {{0}} clients connected to the port",
m_numConnectedSockets);
// 将此客户端交由Dispatcher进行管理
_requestDispatcher.DispatchAsync(accept, client);
// Accept the next connection request
StartAccept(e);
}
else
{
Stop();
}
} }
finally
private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
{ {
ProcessAcceptAsync(e); shutdown = true;
} listenSocket.Close();
public void Stop()
{
if (shutdown)
return;
try
{
if (listenSocket.Connected)
{
listenSocket.Shutdown(SocketShutdown.Both);
}
}
catch (Exception)
{
}
finally
{
shutdown = true;
listenSocket.Close();
}
} }
} }
} }

View File

@ -4,35 +4,34 @@
// 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
namespace FastTunnel.Core.Models namespace FastTunnel.Core.Models;
public class ForwardConfig
{ {
public class ForwardConfig /// <summary>
{ /// 局域网IP地址
/// <summary> /// </summary>
/// 局域网IP地址 public string LocalIp { get; set; }
/// </summary>
public string LocalIp { get; set; }
/// <summary> /// <summary>
/// 局域网ssh端口号 /// 局域网ssh端口号
/// </summary> /// </summary>
public int LocalPort { get; set; } = 22; public int LocalPort { get; set; } = 22;
/// <summary> /// <summary>
/// 服务端监听的端口号 1~65535 /// 服务端监听的端口号 1~65535
/// </summary> /// </summary>
public int RemotePort { get; set; } public int RemotePort { get; set; }
/// <summary> /// <summary>
/// 协议,内网服务监听的协议 /// 协议,内网服务监听的协议
/// </summary> /// </summary>
public ProtocolEnum Protocol { get; set; } public ProtocolEnum Protocol { get; set; }
} }
public enum ProtocolEnum public enum ProtocolEnum
{ {
TCP = 0, TCP = 0,
UDP = 1, UDP = 1,
}
} }

View File

@ -4,17 +4,13 @@
// 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.Net.Sockets; using System.Net.Sockets;
using System.Text;
namespace FastTunnel.Core.Models namespace FastTunnel.Core.Models;
public class ForwardHandlerArg
{ {
public class ForwardHandlerArg public ForwardConfig SSHConfig { get; set; }
{
public ForwardConfig SSHConfig { get; set; }
public Socket LocalClient { get; set; } public Socket LocalClient { get; set; }
}
} }

View File

@ -4,21 +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.Listener;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using FastTunnel.Core.Listener;
namespace FastTunnel.Core.Models namespace FastTunnel.Core.Models;
public class ForwardInfo<T>
{ {
public class ForwardInfo<T> public WebSocket Socket { get; set; }
{
public WebSocket Socket { get; set; }
public ForwardConfig SSHConfig { get; set; } public ForwardConfig SSHConfig { get; set; }
public PortProxyListener Listener { get; set; } public PortProxyListener Listener { get; set; }
}
} }

View File

@ -6,18 +6,17 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace FastTunnel.Core.Models.Massage namespace FastTunnel.Core.Models.Massage;
{
public class LogInMassage : TunnelMassage
{
/// <summary>
/// web穿透隧道列表
/// </summary>
public IEnumerable<WebConfig> Webs { get; set; }
/// <summary> public class LogInMassage : TunnelMassage
/// 端口转发隧道列表 {
/// </summary> /// <summary>
public IEnumerable<ForwardConfig> Forwards { get; set; } /// web穿透隧道列表
} /// </summary>
public IEnumerable<WebConfig> Webs { get; set; }
/// <summary>
/// 端口转发隧道列表
/// </summary>
public IEnumerable<ForwardConfig> Forwards { get; set; }
} }

View File

@ -4,9 +4,8 @@
// 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
namespace FastTunnel.Core.Models.Massage namespace FastTunnel.Core.Models.Massage;
public class TunnelMassage
{ {
public class TunnelMassage
{
}
} }

View File

@ -4,24 +4,19 @@
// 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; namespace FastTunnel.Core.Models;
using System.Collections.Generic;
using System.Text;
namespace FastTunnel.Core.Models public struct Message<T>
{ {
public struct Message<T> public MessageType MessageType { get; set; }
{
public MessageType MessageType { get; set; }
public T Content { get; set; } public T Content { get; set; }
} }
public enum MessageType : byte public enum MessageType : byte
{ {
LogIn = 1, // client LogIn = 1, // client
SwapMsg = 2, SwapMsg = 2,
Forward = 3, Forward = 3,
Log = 4, Log = 4,
}
} }

View File

@ -4,104 +4,101 @@
// 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.Net;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client; using FastTunnel.Core.Client;
using FastTunnel.Core.Handlers.Server; using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Protocol; using FastTunnel.Core.Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
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;
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)
{ {
public WebSocket webSocket { get; private set; } this.webSocket = webSocket;
this.fastTunnelServer = fastTunnelServer;
this.loginHandler = loginHandler;
this.RemoteIpAddress = remoteIpAddress;
}
/// <summary> internal void AddWeb(WebInfo info)
/// 服务端端口号 {
/// </summary> webInfos.Add(info);
public int ConnectionPort { get; set; } }
readonly FastTunnelServer fastTunnelServer; internal void AddForward(ForwardInfo<ForwardHandlerArg> forwardInfo)
readonly ILoginHandler loginHandler; {
forwardInfos.Add(forwardInfo);
}
public IPAddress RemoteIpAddress { get; private set; } public async Task ReviceAsync(CancellationToken cancellationToken)
{
var buffer = new byte[FastTunnelConst.MAX_CMD_LENGTH];
var tunnelProtocol = new TunnelProtocol();
readonly IList<WebInfo> webInfos = new List<WebInfo>(); while (true)
readonly IList<ForwardInfo<ForwardHandlerArg>> forwardInfos = new List<ForwardInfo<ForwardHandlerArg>>();
public TunnelClient(WebSocket webSocket, FastTunnelServer fastTunnelServer, ILoginHandler loginHandler, IPAddress remoteIpAddress)
{ {
this.webSocket = webSocket; var res = await webSocket.ReceiveAsync(buffer, cancellationToken);
this.fastTunnelServer = fastTunnelServer; var cmds = tunnelProtocol.HandleBuffer(buffer, 0, res.Count);
this.loginHandler = loginHandler; if (cmds == null) continue;
this.RemoteIpAddress = remoteIpAddress;
}
internal void AddWeb(WebInfo info) foreach (var item in cmds)
{
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); if (!await HandleCmdAsync(this, item))
var cmds = tunnelProtocol.HandleBuffer(buffer, 0, res.Count);
if (cmds == null) continue;
foreach (var item in cmds)
{ {
if (!await HandleCmdAsync(this, item)) return;
{ };
return;
};
}
} }
} }
private async Task<bool> HandleCmdAsync(TunnelClient tunnelClient, string lineCmd) }
{ 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));
catch (Exception ex) }
{ catch (Exception ex)
Console.WriteLine($"处理客户端消息失败cmd={lineCmd} {ex}"); {
return false; Console.WriteLine($"处理客户端消息失败cmd={lineCmd} {ex}");
} return false;
} }
}
internal void Logout()
{ internal void Logout()
// forward监听终止 {
if (forwardInfos != null) // forward监听终止
{ if (forwardInfos != null)
foreach (var item in forwardInfos) {
{ foreach (var item in forwardInfos)
try {
{ try
item.Listener.Stop(); {
} item.Listener.Stop();
catch { } }
} catch { }
} }
}
webSocket.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
} webSocket.CloseAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
} }
} }

View File

@ -4,32 +4,27 @@
// 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; namespace FastTunnel.Core.Models;
using System.Collections.Generic;
using System.Text;
namespace FastTunnel.Core.Models public class WebConfig
{ {
public class WebConfig /// <summary>
{ /// 子域名
/// <summary> /// </summary>
/// 子域名 public string SubDomain { get; set; }
/// </summary>
public string SubDomain { get; set; }
/// <summary> /// <summary>
/// 本地IP /// 本地IP
/// </summary> /// </summary>
public string LocalIp { get; set; } public string LocalIp { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public int LocalPort { get; set; } public int LocalPort { get; set; }
/// <summary> /// <summary>
/// 个人域名 /// 个人域名
/// </summary> /// </summary>
public string[] WWW { get; set; } public string[] WWW { get; set; }
}
} }

View File

@ -5,23 +5,19 @@
// Copyright (c) 2019 Gui.H // Copyright (c) 2019 Gui.H
using System; using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text;
namespace FastTunnel.Core.Models namespace FastTunnel.Core.Models;
public class WebInfo
{ {
public class WebInfo public WebSocket Socket { get; set; }
public WebConfig WebConfig { get; set; }
internal void LogOut()
{ {
public WebSocket Socket { get; set; } // TODO:退出登录
throw new NotImplementedException();
public WebConfig WebConfig { get; set; }
internal void LogOut()
{
// TODO:退出登录
throw new NotImplementedException();
}
} }
} }

View File

@ -4,41 +4,40 @@
// 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.Extensions;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using FastTunnel.Core.Extensions;
namespace FastTunnel.Core.Protocol namespace FastTunnel.Core.Protocol;
public class TunnelProtocol
{ {
public class TunnelProtocol private string massgeTemp;
private readonly string m_sectionFlag = "\n";
public IEnumerable<string> HandleBuffer(byte[] buffer, int offset, int count)
{ {
string massgeTemp; var words = buffer.GetString(offset, count);
string m_sectionFlag = "\n"; var sum = massgeTemp + words;
public IEnumerable<string> HandleBuffer(byte[] buffer, int offset, int count) if (sum.Contains(m_sectionFlag))
{ {
var words = buffer.GetString(offset, count); var array = (sum).Split(m_sectionFlag);
var sum = massgeTemp + words; massgeTemp = null;
var fullMsg = words.EndsWith(m_sectionFlag);
if (sum.Contains(m_sectionFlag)) if (!fullMsg)
{ {
var array = (sum).Split(m_sectionFlag); massgeTemp = array[array.Length - 1];
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;
} }
return array.Take(array.Length - 1);
}
else
{
massgeTemp = sum;
return null;
} }
} }
} }

View File

@ -4,56 +4,50 @@
// 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.Client;
using FastTunnel.Core.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Runtime.ExceptionServices; using FastTunnel.Core.Client;
using System.IO; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Services namespace FastTunnel.Core.Services;
public class ServiceFastTunnelClient : IHostedService
{ {
public class ServiceFastTunnelClient : IHostedService private readonly ILogger<ServiceFastTunnelClient> _logger;
private readonly IFastTunnelClient _fastTunnelClient;
public ServiceFastTunnelClient(ILogger<ServiceFastTunnelClient> logger, IFastTunnelClient fastTunnelClient)
{ {
readonly ILogger<ServiceFastTunnelClient> _logger; _logger = logger;
readonly IFastTunnelClient _fastTunnelClient; _fastTunnelClient = fastTunnelClient;
public ServiceFastTunnelClient(ILogger<ServiceFastTunnelClient> logger, IFastTunnelClient fastTunnelClient) AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_fastTunnelClient.StartAsync(cancellationToken);
await Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_fastTunnelClient.Stop(cancellationToken);
return Task.CompletedTask;
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{ {
_logger = logger; _logger.LogError("【UnhandledException】" + e.ExceptionObject);
_fastTunnelClient = fastTunnelClient; var type = e.ExceptionObject.GetType();
_logger.LogError("ExceptionObject GetType " + type);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
} }
catch
public async Task StartAsync(CancellationToken cancellationToken)
{ {
_fastTunnelClient.StartAsync(cancellationToken);
await Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_fastTunnelClient.Stop(cancellationToken);
return Task.CompletedTask;
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
_logger.LogError("【UnhandledException】" + e.ExceptionObject);
var type = e.ExceptionObject.GetType();
_logger.LogError("ExceptionObject GetType " + type);
}
catch
{
}
} }
} }
} }

View File

@ -4,25 +4,19 @@
// 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.Extensions;
using FastTunnel.Core.Models;
using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastTunnel.Core.Sockets namespace FastTunnel.Core.Sockets;
public class DnsSocketFactory
{ {
public class DnsSocketFactory public static async Task<Socket> ConnectAsync(string host, int port)
{ {
public static async Task<Socket> ConnectAsync(string host, int port) var Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
{ var dnsEndPoint = new DnsEndPoint(host, port);
var Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await Socket.ConnectAsync(dnsEndPoint);
DnsEndPoint dnsEndPoint = new DnsEndPoint(host, port); return Socket;
await Socket.ConnectAsync(dnsEndPoint);
return Socket;
}
} }
} }

View File

@ -6,13 +6,12 @@
using System; using System;
namespace FastTunnel.Core.Utilitys namespace FastTunnel.Core.Utilitys;
public static class AssemblyUtility
{ {
public static class AssemblyUtility public static Version GetVersion()
{ {
public static Version GetVersion() return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
{
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
}
} }
} }

View File

@ -18,6 +18,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig .editorconfig = .editorconfig
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastTunnel.Hosting", "FastTunnel.Hosting\FastTunnel.Hosting.csproj", "{D7F07110-E85C-4F0E-B479-AEC9760CD2A7}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -40,6 +42,10 @@ Global
{7D560A9A-E480-40F4-AAF7-398447438255}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D560A9A-E480-40F4-AAF7-398447438255}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D560A9A-E480-40F4-AAF7-398447438255}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D560A9A-E480-40F4-AAF7-398447438255}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D560A9A-E480-40F4-AAF7-398447438255}.Release|Any CPU.Build.0 = Release|Any CPU {7D560A9A-E480-40F4-AAF7-398447438255}.Release|Any CPU.Build.0 = Release|Any CPU
{D7F07110-E85C-4F0E-B479-AEC9760CD2A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7F07110-E85C-4F0E-B479-AEC9760CD2A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7F07110-E85C-4F0E-B479-AEC9760CD2A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7F07110-E85C-4F0E-B479-AEC9760CD2A7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -47,6 +53,7 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{C8ADFEB1-59DB-4CE3-8D04-5B547107BCCB} = {0E2A9DA2-26AE-4657-B4C5-3A913E2F5A3C} {C8ADFEB1-59DB-4CE3-8D04-5B547107BCCB} = {0E2A9DA2-26AE-4657-B4C5-3A913E2F5A3C}
{7D560A9A-E480-40F4-AAF7-398447438255} = {0E2A9DA2-26AE-4657-B4C5-3A913E2F5A3C} {7D560A9A-E480-40F4-AAF7-398447438255} = {0E2A9DA2-26AE-4657-B4C5-3A913E2F5A3C}
{D7F07110-E85C-4F0E-B479-AEC9760CD2A7} = {0E2A9DA2-26AE-4657-B4C5-3A913E2F5A3C}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3D9C6B44-6706-4EE8-9043-802BBE474A2E} SolutionGuid = {3D9C6B44-6706-4EE8-9043-802BBE474A2E}