This commit is contained in:
Gui.H 2022-07-01 17:18:04 +08:00
parent 0b93061e2a
commit 98ecb29407
9 changed files with 127 additions and 64 deletions

View File

@ -27,7 +27,7 @@
// [] ip // [] ip
"LocalIp": "127.0.0.1", "LocalIp": "127.0.0.1",
// [] // []
"LocalPort": 8080, "LocalPort": 8090,
// [] , 访url http://${SubDomain}.${WebDomain}:${ServerPort} // [] , 访url http://${SubDomain}.${WebDomain}:${ServerPort}
"SubDomain": "test" "SubDomain": "test"

View File

@ -4,6 +4,9 @@
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="Forwarder\Kestrel\FastTunnelConnectionContext.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </ItemGroup>

View File

@ -8,59 +8,43 @@ 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.Reflection.PortableExecutable;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FastTunnel.Core.Models; using FastTunnel.Core.Models;
using FastTunnel.Core.Protocol; using FastTunnel.Core.Protocol;
using FastTunnel.Core.Server; using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Logging;
namespace FastTunnel.Core.Forwarder.Kestrel; namespace FastTunnel.Core.Forwarder.Kestrel.MiddleWare;
internal class FastTunnelConnectionContext : ConnectionContext
public class FastTunelProtocol
{ {
private readonly ConnectionContext _inner; public FastTunelProtocol(ConnectionContext context, FastTunnelServer fastTunnelServer)
private readonly FastTunnelServer fastTunnelServer;
private readonly ILogger _logger;
public FastTunnelConnectionContext(ConnectionContext context, FastTunnelServer fastTunnelServer, ILogger logger)
{ {
this.context = context;
this.fastTunnelServer = fastTunnelServer; this.fastTunnelServer = fastTunnelServer;
this._inner = context;
this._logger = logger;
} }
public override IDuplexPipe Transport { get => _inner.Transport; set => _inner.Transport = value; } bool IsFastTunnel => Method == ProtocolConst.HTTP_METHOD_SWAP || MatchWeb != null;
public override string ConnectionId { get => _inner.ConnectionId; set => _inner.ConnectionId = value; }
public override IFeatureCollection Features => _inner.Features;
public override IDictionary<object, object> Items { get => _inner.Items; set => _inner.Items = value; }
public bool IsFastTunnel => Method == ProtocolConst.HTTP_METHOD_SWAP || MatchWeb != null;
public WebInfo MatchWeb { get; private set; } public WebInfo MatchWeb { get; private set; }
public override ValueTask DisposeAsync() ConnectionContext context { get; }
{
return _inner.DisposeAsync(); IDuplexPipe Transport => context.Transport;
}
/// <summary>
/// 解析FastTunnel协议
/// </summary>
internal async Task TryAnalysisPipeAsync() internal async Task TryAnalysisPipeAsync()
{ {
var reader = Transport.Input; var _input = Transport.Input;
ReadResult result; ReadResult result;
ReadOnlySequence<byte> readableBuffer; ReadOnlySequence<byte> readableBuffer;
while (true) while (true)
{ {
result = await reader.ReadAsync(); result = await _input.ReadAsync();
var tempBuffer = readableBuffer = result.Buffer; var tempBuffer = readableBuffer = result.Buffer;
SequencePosition? position = null; SequencePosition? position = null;
@ -72,17 +56,28 @@ internal class FastTunnelConnectionContext : ConnectionContext
if (position != null) if (position != null)
{ {
var readedPosition = readableBuffer.GetPosition(1, position.Value); var readedPosition = readableBuffer.GetPosition(1, position.Value);
if (ProcessLine(tempBuffer.Slice(0, position.Value))) if (ProcessLine(tempBuffer.Slice(0, position.Value), out string line))
{ {
if (Method == ProtocolConst.HTTP_METHOD_SWAP) if (Method == ProtocolConst.HTTP_METHOD_SWAP)
{ {
reader.AdvanceTo(readedPosition, readedPosition); _input.AdvanceTo(readedPosition, readedPosition);
} }
else else
{ {
reader.AdvanceTo(readableBuffer.Start, readableBuffer.Start); _input.AdvanceTo(readableBuffer.Start, readableBuffer.Start);
} }
if (IsFastTunnel)
{
context.Features.Set<IFastTunnelFeature>(new FastTunnelFeature()
{
MatchWeb = MatchWeb,
HasReadLInes = HasReadLInes,
Method = Method,
Host = Host,
MessageId = MessageId,
});
}
return; return;
} }
@ -93,12 +88,10 @@ internal class FastTunnelConnectionContext : ConnectionContext
if (result.IsCompleted) if (result.IsCompleted)
{ {
reader.AdvanceTo(readableBuffer.End, readableBuffer.End); _input.AdvanceTo(readableBuffer.End, readableBuffer.End);
break; return;
} }
} }
return;
} }
public string Method; public string Method;
@ -107,6 +100,7 @@ internal class FastTunnelConnectionContext : ConnectionContext
private bool isFirstLine = true; private bool isFirstLine = true;
public IList<string> HasReadLInes { get; private set; } = new List<string>(); public IList<string> HasReadLInes { get; private set; } = new List<string>();
public FastTunnelServer fastTunnelServer { get; }
/// <summary> /// <summary>
/// ///
@ -122,9 +116,9 @@ internal class FastTunnelConnectionContext : ConnectionContext
/// </summary> /// </summary>
/// <param name="readOnlySequence"></param> /// <param name="readOnlySequence"></param>
/// <returns>Header读取完毕</returns> /// <returns>Header读取完毕</returns>
private bool ProcessLine(ReadOnlySequence<byte> readOnlySequence) private bool ProcessLine(ReadOnlySequence<byte> readOnlySequence, out string lineStr)
{ {
var lineStr = Encoding.UTF8.GetString(readOnlySequence); lineStr = Encoding.UTF8.GetString(readOnlySequence);
HasReadLInes.Add(lineStr); HasReadLInes.Add(lineStr);
if (isFirstLine) if (isFirstLine)

View File

@ -0,0 +1,24 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FastTunnel.Core.Models;
namespace FastTunnel.Core.Forwarder.Kestrel.MiddleWare;
public class FastTunnelFeature : IFastTunnelFeature
{
public WebInfo MatchWeb { get; set; }
public IList<string> HasReadLInes { get; set; }
public string Method { get; set; }
public string Host { get; set; }
public string MessageId { get; set; }
}

View File

@ -41,35 +41,45 @@ internal class ForwarderMiddleware
internal async Task OnConnectionAsync(ConnectionContext context) internal async Task OnConnectionAsync(ConnectionContext context)
{ {
var ctx = context as FastTunnelConnectionContext; logger.LogInformation("=========ForwarderMiddleware SART===========");
if (ctx != null && ctx.IsFastTunnel)
var feat = context.Features.Get<IFastTunnelFeature>();
if (feat == null)
{ {
if (ctx.Method == ProtocolConst.HTTP_METHOD_SWAP) logger.LogInformation("=========ForwarderMiddleware END===========");
// not fasttunnel request
await next(context);
return;
}
else
{
logger.LogInformation("=========Swap STRART===========");
if (feat.Method == ProtocolConst.HTTP_METHOD_SWAP)
{ {
await doSwap(ctx); await doSwap(context);
} }
else if (ctx.MatchWeb != null) else if (feat.MatchWeb != null)
{ {
await waitSwap(ctx); await waitSwap(context);
} }
else else
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
}
else logger.LogInformation("=========Swap END===========");
{ logger.LogInformation("=========ForwarderMiddleware END===========");
await next(context);
} }
} }
private async Task waitSwap(FastTunnelConnectionContext context) private async Task waitSwap(ConnectionContext context)
{ {
var feat = context.Features.Get<IFastTunnelFeature>();
var requestId = Guid.NewGuid().ToString().Replace("-", ""); var requestId = Guid.NewGuid().ToString().Replace("-", "");
var web = context.MatchWeb; var web = feat.MatchWeb;
TaskCompletionSource<Stream> tcs = new(); TaskCompletionSource<Stream> tcs = new();
logger.LogDebug($"[Http]Swap开始 {requestId}|{context.Host}=>{web.WebConfig.LocalIp}:{web.WebConfig.LocalPort}"); logger.LogDebug($"[Http]Swap开始 {requestId}|{feat.Host}=>{web.WebConfig.LocalIp}:{web.WebConfig.LocalPort}");
tcs.SetTimeOut(10000, () => { logger.LogDebug($"[Proxy TimeOut]:{requestId}"); }); tcs.SetTimeOut(10000, () => { logger.LogDebug($"[Proxy TimeOut]:{requestId}"); });
fastTunnelServer.ResponseTasks.TryAdd(requestId, tcs); fastTunnelServer.ResponseTasks.TryAdd(requestId, tcs);
@ -111,9 +121,10 @@ internal class ForwarderMiddleware
} }
} }
private async Task doSwap(FastTunnelConnectionContext context) private async Task doSwap(ConnectionContext context)
{ {
var requestId = context.MessageId; var feat = context.Features.Get<IFastTunnelFeature>();
var requestId = feat.MessageId;
if (!fastTunnelServer.ResponseTasks.TryRemove(requestId, out var responseStream)) if (!fastTunnelServer.ResponseTasks.TryRemove(requestId, out var responseStream))
{ {
throw new Exception($"[PROXY]:RequestId不存在 {requestId}"); throw new Exception($"[PROXY]:RequestId不存在 {requestId}");

View File

@ -0,0 +1,26 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FastTunnel.Core.Models;
using FastTunnel.Core.Protocol;
namespace FastTunnel.Core.Forwarder.Kestrel.MiddleWare;
internal interface IFastTunnelFeature
{
public WebInfo MatchWeb { get; set; }
public IList<string> HasReadLInes { get; set; }
public string Method { get; set; }
public string Host { get; set; }
public string MessageId { get; set; }
}

View File

@ -30,11 +30,10 @@ internal class InitializerMiddleware
internal async Task OnConnectionAsync(ConnectionContext context) internal async Task OnConnectionAsync(ConnectionContext context)
{ {
logger.LogInformation("=========OnConnectionAsync==========="); logger.LogInformation("=========TryAnalysisPipeAsync SART===========");
var ftContext = new FastTunnelConnectionContext(context, fastTunnelServer, logger); await new FastTunelProtocol(context, fastTunnelServer).TryAnalysisPipeAsync();
await ftContext.TryAnalysisPipeAsync();
logger.LogInformation("=========TryAnalysisPipeAsync END==========="); logger.LogInformation("=========TryAnalysisPipeAsync END===========");
await next(ftContext.IsFastTunnel ? ftContext : context);
await next(context);
} }
} }

View File

@ -31,9 +31,9 @@ public class FastTunnelHostingStartup : IHostingStartup
}); });
}); });
builder.Configure((webHostBuilderContext, app) => //builder.Configure((webHostBuilderContext, app) =>
{ //{
app.UseFastTunnelServer(); // app.UseFastTunnelServer();
}); //});
} }
} }

View File

@ -102,9 +102,15 @@ public class Startup
app.UseAuthorization(); app.UseAuthorization();
// --------------------- Custom UI ---------------- // --------------------- Custom UI ----------------
app.UseFastTunnelServer();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();
endpoints.MapFallback(async (HttpContext ctx) =>
{
await ctx.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("hello~"));
});
}); });
} }
} }