mirror of
https://github.com/FastTunnel/FastTunnel.git
synced 2025-02-08 10:59:31 +08:00
代码清理
This commit is contained in:
parent
ea8072ac6e
commit
593f12d925
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 =====");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, "[全局异常]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user