清理代码

This commit is contained in:
Gui.H 2022-05-10 23:31:59 +08:00
parent 7624d71a2b
commit 0ab538cc58
35 changed files with 97 additions and 951 deletions

View File

@ -5,7 +5,7 @@
// Copyright (c) 2019 Gui.H
using FastTunnel.Api.Models;
using FastTunnel.Core.Client;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;

View File

@ -12,8 +12,8 @@ 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.Protocol;
using FastTunnel.Core.Utilitys;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -80,8 +80,8 @@ public class FastTunnelClient : IFastTunnelClient
// 连接到的目标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);
socket.Options.SetRequestHeader(ProtocolConst.FASTTUNNEL_VERSION, AssemblyUtility.GetVersion().ToString());
socket.Options.SetRequestHeader(ProtocolConst.FASTTUNNEL_TOKEN, ClientConfig.Token);
_logger.LogInformation($"正在连接服务端 {Server.ServerAddr}:{Server.ServerPort}");
await socket.ConnectAsync(
@ -110,7 +110,7 @@ public class FastTunnelClient : IFastTunnelClient
private async Task ReceiveServerAsync(CancellationToken cancellationToken)
{
var buffer = new byte[FastTunnelConst.MAX_CMD_LENGTH];
var buffer = new byte[ProtocolConst.MAX_CMD_LENGTH];
while (!cancellationToken.IsCancellationRequested)
{
var res = await socket.ReceiveAsync(buffer, cancellationToken);
@ -144,7 +144,7 @@ public class FastTunnelClient : IFastTunnelClient
}
catch (Exception ex)
{
_logger.LogError(ex);
_logger.LogError(ex, "HandleServerRequest Error");
}
}

View File

@ -7,11 +7,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Services;
namespace FastTunnel.Core.Client;
public class ServiceFastTunnelClient : IHostedService
{

View File

@ -1,17 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System;
namespace FastTunnel.Core.Exceptions;
public class APIErrorException : Exception
{
public APIErrorException(string message)
: base(message)
{
}
}

View File

@ -4,8 +4,8 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using FastTunnel.Core.Client;
using FastTunnel.Core.Forwarder.Kestrel;
using FastTunnel.Core.Forwarder.Kestrel.MiddleWare;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -14,15 +14,19 @@ namespace FastTunnel.Core.Extensions;
public static class ListenOptionsSwapExtensions
{
/// <summary>
/// 使用FastTunnel中间件
/// </summary>
/// <param name="listenOptions"></param>
/// <returns></returns>
public static ListenOptions UseConnectionFastTunnel(this ListenOptions listenOptions)
{
var fastTunnelServer = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<FastTunnelServer>();
var loggerFactory = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<SwapConnectionMiddleware>();
var loggerClient = loggerFactory.CreateLogger<ClientConnectionMiddleware>();
var loggerHttp = loggerFactory.CreateLogger<HandleHttpConnectionMiddleware>();
var fastTunnelServer = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<FastTunnelServer>();
var loggerHttp = loggerFactory.CreateLogger<FastTunnelConnectionMiddleware>();
listenOptions.Use(next => new HandleHttpConnectionMiddleware(next, loggerHttp, fastTunnelServer).OnConnectionAsync);
listenOptions.Use(next => new FastTunnelConnectionMiddleware(next, loggerHttp, fastTunnelServer).OnConnectionAsync);
listenOptions.Use(next => new SwapConnectionMiddleware(next, logger, fastTunnelServer).OnConnectionAsync);
// 登录频率低,放在后面

View File

@ -1,18 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Extensions;
public static class LoggerExtentions
{
public static void LogError(this ILogger logger, Exception ex)
{
logger.LogError(ex, string.Empty);
}
}

View File

@ -9,7 +9,7 @@ using FastTunnel.Core.Config;
using FastTunnel.Core.Forwarder.MiddleWare;
using FastTunnel.Core.Handlers.Client;
using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Services;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -46,7 +46,6 @@ public static class ServicesExtensions
services.Configure<DefaultServerConfig>(configurationSection)
.AddTransient<ILoginHandler, LoginHandler>()
.AddSingleton<FastTunnelClientHandler>()
.AddSingleton<FastTunnelSwapHandler>()
.AddSingleton<FastTunnelServer>();
}

View File

@ -1,21 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System.Net.Sockets;
using System.Text;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
namespace FastTunnel.Core.Extensions;
public static class SocketExtensions
{
public static void SendCmd<T>(this Socket socket, Message<T> message)
where T : TunnelMassage
{
socket.Send(Encoding.UTF8.GetBytes(message.ToJson() + "\n"));
}
}

View File

@ -10,7 +10,8 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Exceptions;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
using FastTunnel.Core.Protocol;
namespace FastTunnel.Core.Extensions;
@ -24,7 +25,7 @@ public static class WebSocketExtensions
}
var buffer = Encoding.UTF8.GetBytes($"{(char)type}{content}\n");
if (type != MessageType.LogIn && buffer.Length > FastTunnelConst.MAX_CMD_LENGTH)
if (type != MessageType.LogIn && buffer.Length > ProtocolConst.MAX_CMD_LENGTH)
throw new ArgumentOutOfRangeException(nameof(content));
await socket.SendAsync(buffer, WebSocketMessageType.Binary, false, cancellationToken);

View File

@ -1,37 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using FastTunnel.Core.Extensions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Filters;
public class FastTunnelExceptionFilter : IExceptionFilter
{
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly ILogger<FastTunnelExceptionFilter> logger;
public FastTunnelExceptionFilter(
ILogger<FastTunnelExceptionFilter> logger,
IWebHostEnvironment hostingEnvironment)
{
this.logger = logger;
_hostingEnvironment = hostingEnvironment;
}
public void OnException(ExceptionContext context)
{
if (!_hostingEnvironment.IsDevelopment())
{
return;
}
logger.LogError(context.Exception, "[全局异常]");
}
}

View File

@ -1,100 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System;
using System.Buffers;
using System.Text;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using Microsoft.AspNetCore.Connections;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.Kestrel;
internal class ClientConnectionMiddleware
{
private readonly ConnectionDelegate next;
private readonly ILogger<ClientConnectionMiddleware> logger;
private readonly FastTunnelServer fastTunnelServer;
public ClientConnectionMiddleware(ConnectionDelegate next, ILogger<ClientConnectionMiddleware> logger, FastTunnelServer fastTunnelServer)
{
this.next = next;
this.logger = logger;
this.fastTunnelServer = fastTunnelServer;
}
internal async Task OnConnectionAsync(ConnectionContext context)
{
if (!await ReadPipeAsync(context))
{
await next(context);
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <returns>is for FastTunnel</returns>
private async Task<bool> ReadPipeAsync(ConnectionContext context)
{
var reader = context.Transport.Input;
var isProxy = false;
while (true)
{
var result = await reader.ReadAsync();
var buffer = result.Buffer;
SequencePosition? position = null;
do
{
position = buffer.PositionOf((byte)'\n');
if (position != null)
{
isProxy = ProcessProxyLine(buffer.Slice(0, position.Value));
if (isProxy)
{
await Login(buffer, position.Value, context);
return true;
}
else
{
context.Transport.Input.AdvanceTo(buffer.Start, buffer.Start);
return false;
}
}
}
while (position != null);
if (result.IsCompleted)
{
break;
}
}
return false;
}
private async Task Login(ReadOnlySequence<byte> buffer, SequencePosition position, ConnectionContext context)
{
}
/// <summary>
///
/// </summary>
/// <param name="readOnlySequence"></param>
private bool ProcessProxyLine(ReadOnlySequence<byte> readOnlySequence)
{
var str = Encoding.UTF8.GetString(readOnlySequence);
return str.StartsWith("LOGIN");
}
}

View File

@ -10,8 +10,8 @@ using System.Collections.Generic;
using System.IO.Pipelines;
using System.Text;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
@ -38,7 +38,7 @@ internal class FastTunnelConnectionContext : ConnectionContext
public override IDictionary<object, object> Items { get => _inner.Items; set => _inner.Items = value; }
public bool IsFastTunnel => Method == "PROXY" || MatchWeb != null;
public bool IsFastTunnel => Method == ProtocolConst.HTTP_METHOD_SWAP || MatchWeb != null;
public WebInfo MatchWeb { get; private set; }
@ -47,13 +47,10 @@ internal class FastTunnelConnectionContext : ConnectionContext
return _inner.DisposeAsync();
}
private readonly ReadOnlySequence<byte> readableBuffer;
/// <summary>
/// 解析FastTunnel协议
/// </summary>
/// <returns>is for FastTunnel</returns>
internal async Task<bool> TryAnalysisPipeAsync()
internal async Task TryAnalysisPipeAsync()
{
var reader = Transport.Input;
@ -73,21 +70,22 @@ internal class FastTunnelConnectionContext : ConnectionContext
if (position != null)
{
var readedPosition = readableBuffer.GetPosition(1, position.Value);
if (ProcessLine(tempBuffer.Slice(0, position.Value)))
{
if (Method == "PROXY")
if (Method == ProtocolConst.HTTP_METHOD_SWAP)
{
reader.AdvanceTo(position.Value, position.Value);
reader.AdvanceTo(readedPosition, readedPosition);
}
else
{
reader.AdvanceTo(readableBuffer.Start, readableBuffer.Start);
}
return false;
return;
}
tempBuffer = tempBuffer.Slice(readableBuffer.GetPosition(1, position.Value));
tempBuffer = tempBuffer.Slice(readedPosition);
}
}
while (position != null && !readableBuffer.IsEmpty);
@ -99,15 +97,16 @@ internal class FastTunnelConnectionContext : ConnectionContext
}
}
return false;
return;
}
public string Method;
public string Host = null;
public string MessageId;
private bool complete = false;
private bool isFirstLine = true;
public IList<string> HasReadLInes { get; private set; } = new List<string>();
/// <summary>
///
/// GET / HTTP/1.1
@ -121,17 +120,19 @@ internal class FastTunnelConnectionContext : ConnectionContext
///
/// </summary>
/// <param name="readOnlySequence"></param>
/// <returns>Header读取完毕</returns>
private bool ProcessLine(ReadOnlySequence<byte> readOnlySequence)
{
var lineStr = Encoding.UTF8.GetString(readOnlySequence);
Console.WriteLine($"[HandleLien] {lineStr}");
HasReadLInes.Add(lineStr);
if (isFirstLine)
{
Method = lineStr.Substring(0, lineStr.IndexOf(" ")).ToUpper();
switch (Method)
{
case "PROXY":
case ProtocolConst.HTTP_METHOD_SWAP:
// 客户端发起消息互转
var endIndex = lineStr.IndexOf(" ", 7);
MessageId = lineStr.Substring(7, endIndex - 7);
@ -147,9 +148,11 @@ internal class FastTunnelConnectionContext : ConnectionContext
{
if (lineStr.Equals("\r"))
{
complete = true;
if (Method != "PROXY")
if (Method == ProtocolConst.HTTP_METHOD_SWAP)
{
// do nothing
}
else
{
// 匹配Host
if (fastTunnelServer.TryGetWebProxyByHost(Host, out var web))
@ -163,9 +166,8 @@ internal class FastTunnelConnectionContext : ConnectionContext
switch (Method)
{
case "PROXY":
// 找msgid
case ProtocolConst.HTTP_METHOD_SWAP:
// do nothing
break;
default:
// 检查Host决定是否进行代理

View File

@ -5,19 +5,23 @@
// Copyright (c) 2019 Gui.H
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Forwarder.Kestrel;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Connections;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.Kestrel;
namespace FastTunnel.Core.Forwarder.Kestrel.MiddleWare;
internal class HandleHttpConnectionMiddleware
/// <summary>
/// 预处理中间件
/// </summary>
internal class FastTunnelConnectionMiddleware
{
private readonly ConnectionDelegate next;
private readonly ILogger<HandleHttpConnectionMiddleware> logger;
private readonly ILogger<FastTunnelConnectionMiddleware> logger;
private readonly FastTunnelServer fastTunnelServer;
public HandleHttpConnectionMiddleware(ConnectionDelegate next, ILogger<HandleHttpConnectionMiddleware> logger, FastTunnelServer fastTunnelServer)
public FastTunnelConnectionMiddleware(ConnectionDelegate next, ILogger<FastTunnelConnectionMiddleware> logger, FastTunnelServer fastTunnelServer)
{
this.next = next;
this.logger = logger;

View File

@ -10,16 +10,21 @@ using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Exceptions;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Forwarder.Kestrel;
using FastTunnel.Core.Forwarder.MiddleWare;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.Kestrel;
namespace FastTunnel.Core.Forwarder.Kestrel.MiddleWare;
/// <summary>
/// 核心逻辑处理中间件
/// </summary>
internal class SwapConnectionMiddleware
{
private readonly ConnectionDelegate next;
@ -38,7 +43,7 @@ internal class SwapConnectionMiddleware
var ctx = context as FastTunnelConnectionContext;
if (ctx != null && ctx.IsFastTunnel)
{
if (ctx.Method == "PROXY")
if (ctx.Method == ProtocolConst.HTTP_METHOD_SWAP)
{
await doSwap(ctx);
}
@ -50,8 +55,6 @@ internal class SwapConnectionMiddleware
{
throw new NotSupportedException();
}
}
else
{
@ -82,7 +85,7 @@ internal class SwapConnectionMiddleware
web.LogOut();
// 通讯异常,返回客户端离线
throw new Exception("客户端离线");
throw new ClienOffLineException("客户端离线");
}
using var res = await tcs.Task;
@ -142,50 +145,4 @@ internal class SwapConnectionMiddleware
logger.LogInformation($"=====================Swap End:{requestId}================== ");
}
}
private async Task Swap(ReadOnlySequence<byte> buffer, SequencePosition position, ConnectionContext context)
{
var firstLineBuffer = buffer.Slice(0, position);
var firstLine = Encoding.UTF8.GetString(firstLineBuffer);
// SWAP /c74eb488a0f54d888e63d85c67428b52 HTTP/1.1
var endIndex = firstLine.IndexOf(" ", 6);
var requestId = firstLine.Substring(6, endIndex - 6);
Console.WriteLine($"[开始进行Swap操作] {requestId}");
context.Transport.Input.AdvanceTo(buffer.GetPosition(1, position), buffer.GetPosition(1, position));
if (!fastTunnelServer.ResponseTasks.TryRemove(requestId, out var responseForYarp))
{
logger.LogError($"[PROXY]:RequestId不存在 {requestId}");
return;
};
using var reverseConnection = new DuplexPipeStream(context.Transport.Input, context.Transport.Output, true);
responseForYarp.TrySetResult(reverseConnection);
var lifetime = context.Features.Get<IConnectionLifetimeFeature>();
var closedAwaiter = new TaskCompletionSource<object>();
lifetime.ConnectionClosed.Register((task) =>
{
(task as TaskCompletionSource<object>).SetResult(null);
}, closedAwaiter);
try
{
await closedAwaiter.Task;
}
catch (Exception ex)
{
logger.LogError(ex, "");
}
finally
{
context.Transport.Input.Complete();
context.Transport.Output.Complete();
logger.LogInformation($"=====================Swap End:{requestId}================== ");
}
}
}

View File

@ -9,7 +9,7 @@ using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder.MiddleWare
namespace FastTunnel.Core.Forwarder.Kestrel.MiddleWare
{
internal static class TaskToApm
{
@ -21,7 +21,7 @@ namespace FastTunnel.Core.Forwarder.MiddleWare
/// <param name="callback">The callback to be invoked upon completion.</param>
/// <param name="state">The state to be stored in the IAsyncResult.</param>
/// <returns>An IAsyncResult to represent the task's asynchronous operation.</returns>
public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state) =>
public static IAsyncResult Begin(Task task, AsyncCallback callback, object state) =>
new TaskAsyncResult(task, state, callback);
/// <summary>Processes an IAsyncResult returned by Begin.</summary>
@ -60,13 +60,13 @@ namespace FastTunnel.Core.Forwarder.MiddleWare
/// <summary>The wrapped Task.</summary>
internal readonly Task _task;
/// <summary>Callback to invoke when the wrapped task completes.</summary>
private readonly AsyncCallback? _callback;
private readonly AsyncCallback _callback;
/// <summary>Initializes the IAsyncResult with the Task to wrap and the associated object state.</summary>
/// <param name="task">The Task to wrap.</param>
/// <param name="state">The new AsyncState value.</param>
/// <param name="callback">Callback to invoke when the wrapped task completes.</param>
internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback)
internal TaskAsyncResult(Task task, object state, AsyncCallback callback)
{
Debug.Assert(task != null);
_task = task;
@ -99,7 +99,7 @@ namespace FastTunnel.Core.Forwarder.MiddleWare
}
/// <summary>Gets a user-defined object that qualifies or contains information about an asynchronous operation.</summary>
public object? AsyncState { get; }
public object AsyncState { get; }
/// <summary>Gets a value that indicates whether the asynchronous operation completed synchronously.</summary>
/// <remarks>This is set lazily based on whether the <see cref="_task"/> has completed by the time this object is created.</remarks>
public bool CompletedSynchronously { get; }

View File

@ -9,6 +9,7 @@ using System.Buffers;
using System.IO;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Extensions;
@ -139,6 +140,7 @@ internal class DuplexPipeStream : Stream
// buffer.Count is int
var count = (int)Math.Min(readableBuffer.Length, destination.Length);
readableBuffer = readableBuffer.Slice(0, count);
Console.WriteLine($"[{this.GetHashCode()}读取]{Encoding.UTF8.GetString(readableBuffer)}");
readableBuffer.CopyTo(destination.Span);
return count;
}

View File

@ -8,10 +8,12 @@ using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
using FastTunnel.Core.Protocol;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@ -38,7 +40,7 @@ public class FastTunnelClientHandler
{
try
{
if (!context.WebSockets.IsWebSocketRequest || !context.Request.Headers.TryGetValue(FastTunnelConst.FASTTUNNEL_VERSION, out var version))
if (!context.WebSockets.IsWebSocketRequest || !context.Request.Headers.TryGetValue(ProtocolConst.FASTTUNNEL_VERSION, out var version))
{
await next();
return;
@ -48,7 +50,7 @@ public class FastTunnelClientHandler
}
catch (Exception ex)
{
logger.LogError(ex);
logger.LogError(ex, "处理登录异常");
}
}
@ -103,7 +105,7 @@ public class FastTunnelClientHandler
return true;
// 客户端未携带token登录失败
if (!context.Request.Headers.TryGetValue(FastTunnelConst.FASTTUNNEL_TOKEN, out var token))
if (!context.Request.Headers.TryGetValue(ProtocolConst.FASTTUNNEL_TOKEN, out var token))
return false;
if (fastTunnelServer.ServerOption.CurrentValue.Tokens?.Contains(token) ?? false)

View File

@ -1,73 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.MiddleWare;
public class FastTunnelSwapHandler
{
private readonly ILogger<FastTunnelClientHandler> logger;
private readonly FastTunnelServer fastTunnelServer;
public FastTunnelSwapHandler(ILogger<FastTunnelClientHandler> logger, FastTunnelServer fastTunnelServer)
{
this.logger = logger;
this.fastTunnelServer = fastTunnelServer;
}
public async Task Handle(HttpContext context, Func<Task> next)
{
try
{
if (context.Request.Method != "PROXY")
{
await next();
return;
}
var requestId = context.Request.Path.Value.Trim('/');
logger.LogDebug($"[PROXY]:Start {requestId}");
if (!fastTunnelServer.ResponseTasks.TryRemove(requestId, out var responseAwaiter))
{
logger.LogError($"[PROXY]:RequestId不存在 {requestId}");
return;
};
var lifetime = context.Features.Get<IConnectionLifetimeFeature>();
var transport = context.Features.Get<IConnectionTransportFeature>();
if (lifetime == null || transport == null)
{
return;
}
using var reverseConnection = new WebSocketStream(lifetime, transport);
responseAwaiter.TrySetResult(reverseConnection);
var closedAwaiter = new TaskCompletionSource<object>();
lifetime.ConnectionClosed.Register((task) =>
{
(task as TaskCompletionSource<object>).SetResult(null);
}, closedAwaiter);
await closedAwaiter.Task;
logger.LogDebug($"[PROXY]:Closed {requestId}");
}
catch (Exception ex)
{
logger.LogError(ex);
}
}
}

View File

@ -1,233 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.MiddleWare;
internal sealed class LoggingStream : Stream
{
private readonly Stream _inner;
private readonly ILogger _logger;
public LoggingStream(Stream inner, ILogger logger)
{
_inner = inner;
_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)
{
var read = _inner.Read(buffer, offset, count);
Log("[Read]", new ReadOnlySpan<byte>(buffer, offset, read));
return read;
}
public override int Read(Span<byte> destination)
{
var read = _inner.Read(destination);
Log("[Read]", destination.Slice(0, read));
return read;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var read = await _inner.ReadAsync(buffer.AsMemory(offset, count), cancellationToken);
Log("[ReadAsync]", new ReadOnlySpan<byte>(buffer, offset, read));
return read;
}
public override async ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default)
{
var read = await _inner.ReadAsync(destination, cancellationToken);
Log("[ReadAsync]", destination.Span.Slice(0, read));
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
return _inner.Seek(offset, origin);
}
public override void SetLength(long value)
{
_inner.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
Log("[Write]", new ReadOnlySpan<byte>(buffer, offset, count));
_inner.Write(buffer, offset, count);
}
public override void Write(ReadOnlySpan<byte> source)
{
Log("[Write]", source);
_inner.Write(source);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
Log("WriteAsync", new ReadOnlySpan<byte>(buffer, offset, count));
return _inner.WriteAsync(buffer, offset, count, cancellationToken);
}
public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default)
{
Log("WriteAsync", source.Span);
return _inner.WriteAsync(source, cancellationToken);
}
private void Log(string method, ReadOnlySpan<byte> buffer)
{
var builder = new StringBuilder();
builder.Append(method);
builder.Append('[');
builder.Append(buffer.Length);
builder.Append(']');
if (buffer.Length > 0)
{
builder.AppendLine();
}
var charBuilder = new StringBuilder();
// Write the hex
for (var i = 0; i < buffer.Length; i++)
{
builder.Append(buffer[i].ToString("X2", CultureInfo.InvariantCulture));
builder.Append(' ');
var bufferChar = (char)buffer[i];
if (char.IsControl(bufferChar))
{
charBuilder.Append('.');
}
else
{
charBuilder.Append(bufferChar);
}
if ((i + 1) % 16 == 0)
{
builder.Append(" ");
builder.Append(charBuilder);
if (i != buffer.Length - 1)
{
builder.AppendLine();
}
charBuilder.Clear();
}
else if ((i + 1) % 8 == 0)
{
builder.Append(' ');
charBuilder.Append(' ');
}
}
// Different than charBuffer.Length since charBuffer contains an extra " " after the 8th byte.
var numBytesInLastLine = buffer.Length % 16;
if (numBytesInLastLine > 0)
{
// 2 (between hex and char blocks) + num bytes left (3 per byte)
var padLength = 2 + (3 * (16 - numBytesInLastLine));
// extra for space after 8th byte
if (numBytesInLastLine < 8)
{
padLength++;
}
builder.Append(new string(' ', padLength));
builder.Append(charBuilder);
}
_logger.LogInformation(builder.ToString());
}
// The below APM methods call the underlying Read/WriteAsync methods which will still be logged.
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state);
}
public override int EndRead(IAsyncResult asyncResult)
{
return TaskToApm.End<int>(asyncResult);
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state);
}
public override void EndWrite(IAsyncResult asyncResult)
{
TaskToApm.End(asyncResult);
}
}

View File

@ -1,82 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder;
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)
{
m_Stream = new MemoryStream(bytes);
}
public override void Flush()
{
throw new NotImplementedException();
}
private bool complete;
public override int Read(byte[] buffer, int offset, int count)
{
if (!complete)
{
return 0;
};
var len = m_Stream.Read(buffer, offset, count);
return len;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
Console.Write(Encoding.UTF8.GetString(buffer, offset, count));
complete = true;
}
protected override void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
m_Stream.Dispose();
}
public override ValueTask DisposeAsync()
{
Dispose(true);
return ValueTask.CompletedTask;
}
}

View File

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

View File

@ -1,112 +0,0 @@
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections.Features;
namespace FastTunnel.Core.Forwarder;
internal sealed class WebSocketStream : Stream
{
private readonly Stream readStream;
private readonly Stream wirteStream;
private readonly IConnectionLifetimeFeature lifetimeFeature;
public WebSocketStream(IConnectionLifetimeFeature lifetimeFeature, IConnectionTransportFeature transportFeature)
{
this.readStream = transportFeature.Transport.Input.AsStream();
this.wirteStream = transportFeature.Transport.Output.AsStream();
this.lifetimeFeature = lifetimeFeature;
}
public WebSocketStream(Stream stream)
{
this.readStream = stream;
this.wirteStream = stream;
this.lifetimeFeature = null;
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override void Flush()
{
this.wirteStream.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return this.wirteStream.FlushAsync(cancellationToken);
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
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 Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return this.readStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override void Write(ReadOnlySpan<byte> buffer)
{
this.wirteStream.Write(buffer);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return this.wirteStream.WriteAsync(buffer, offset, count, cancellationToken);
}
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
await this.wirteStream.WriteAsync(buffer, cancellationToken);
}
protected override void Dispose(bool disposing)
{
this.lifetimeFeature?.Abort();
}
public override ValueTask DisposeAsync()
{
this.lifetimeFeature?.Abort();
return ValueTask.CompletedTask;
}
}

View File

@ -10,10 +10,11 @@ using System.Net.Sockets;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Exceptions;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
using FastTunnel.Core.Server;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Handlers.Server;

View File

@ -6,7 +6,7 @@
using System.Net.WebSockets;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Server;
namespace FastTunnel.Core.Handlers.Server;

View File

@ -5,8 +5,8 @@
// Copyright (c) 2019 Gui.H
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using FastTunnel.Core.Server;
namespace FastTunnel.Core.Handlers.Server;

View File

@ -9,11 +9,11 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Listener;
using FastTunnel.Core.Models;
using FastTunnel.Core.Models.Massage;
using FastTunnel.Core.Server;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Handlers.Server;

View File

@ -4,14 +4,8 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
namespace FastTunnel.Core.Models;
namespace FastTunnel.Core.Models.Massage;
public struct Message<T>
{
public MessageType MessageType { get; set; }
public T Content { get; set; }
}
public enum MessageType : byte
{

View File

@ -10,9 +10,9 @@ using System.Net;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using FastTunnel.Core.Client;
using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Protocol;
using FastTunnel.Core.Server;
namespace FastTunnel.Core.Models;
@ -53,7 +53,7 @@ public class TunnelClient
public async Task ReviceAsync(CancellationToken cancellationToken)
{
var buffer = new byte[FastTunnelConst.MAX_CMD_LENGTH];
var buffer = new byte[ProtocolConst.MAX_CMD_LENGTH];
var tunnelProtocol = new TunnelProtocol();
while (true)
@ -71,6 +71,7 @@ public class TunnelClient
}
}
}
private async Task<bool> HandleCmdAsync(TunnelClient tunnelClient, string lineCmd)
{
try

View File

@ -18,6 +18,5 @@ public class WebInfo
internal void LogOut()
{
// TODO:退出登录
throw new NotImplementedException();
}
}

View File

@ -4,13 +4,15 @@
// https://github.com/FastTunnel/FastTunnel/edit/v2/LICENSE
// Copyright (c) 2019 Gui.H
namespace FastTunnel.Core;
namespace FastTunnel.Core.Protocol;
public class FastTunnelConst
public class ProtocolConst
{
public const string FASTTUNNEL_VERSION = "FT_VERSION";
public const string FASTTUNNEL_MSGID = "FT_MSGID";
public const string FASTTUNNEL_TOKEN = "FT_TOKEN";
public const int MAX_CMD_LENGTH = 100;
public const string HTTP_METHOD_SWAP = "PROXY";
}

View File

@ -15,7 +15,7 @@ using FastTunnel.Core.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace FastTunnel.Core.Client;
namespace FastTunnel.Core.Server;
public class FastTunnelServer
{
@ -38,7 +38,7 @@ public class FastTunnelServer
public FastTunnelServer(ILogger<FastTunnelServer> logger, IOptionsMonitor<DefaultServerConfig> serverSettings)
{
this.logger = logger;
this.ServerOption = serverSettings;
ServerOption = serverSettings;
}
/// <summary>

View File

@ -8,8 +8,15 @@ using System;
namespace FastTunnel.Core.Utilitys;
/// <summary>
/// Assembly工具集
/// </summary>
public static class AssemblyUtility
{
/// <summary>
/// 获取版本号
/// </summary>
/// <returns></returns>
public static Version GetVersion()
{
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;

View File

@ -43,8 +43,5 @@
<None Update="uninstall.bat">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="install.bat">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -1,12 +0,0 @@
CHCP 65001
@echo off
color 0e
@echo ==================================
@echo 提醒:请右键本文件,用管理员方式打开。
@echo ==================================
@echo Start Install FastTunnel.Server
sc create FastTunnel.Server binPath=%~dp0\FastTunnel.Server.exe start= auto
sc description FastTunnel.Server "FastTunnel-开源内网穿透服务仓库地址https://github.com/SpringHgui/FastTunnel star项目以支持作者"
Net Start FastTunnel.Server
pause

View File

@ -1,11 +0,0 @@
CHCP 65001
@echo off
color 0e
@echo ==================================
@echo 提醒:请右键本文件,用管理员方式打开。
@echo ==================================
@echo Start Remove FastTunnel.Server
Net stop FastTunnel.Server
sc delete FastTunnel.Server
pause