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
"LocalIp": "127.0.0.1",
// []
"LocalPort": 8080,
"LocalPort": 8090,
// [] , 访url http://${SubDomain}.${WebDomain}:${ServerPort}
"SubDomain": "test"

View File

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

View File

@ -8,59 +8,43 @@ using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using FastTunnel.Core.Models;
using FastTunnel.Core.Protocol;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.FileSystemGlobbing;
namespace FastTunnel.Core.Forwarder.Kestrel;
internal class FastTunnelConnectionContext : ConnectionContext
{
private readonly ConnectionContext _inner;
private readonly FastTunnelServer fastTunnelServer;
private readonly ILogger _logger;
namespace FastTunnel.Core.Forwarder.Kestrel.MiddleWare;
public FastTunnelConnectionContext(ConnectionContext context, FastTunnelServer fastTunnelServer, ILogger logger)
public class FastTunelProtocol
{
public FastTunelProtocol(ConnectionContext context, FastTunnelServer fastTunnelServer)
{
this.context = context;
this.fastTunnelServer = fastTunnelServer;
this._inner = context;
this._logger = logger;
}
public override IDuplexPipe Transport { get => _inner.Transport; set => _inner.Transport = value; }
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;
bool IsFastTunnel => Method == ProtocolConst.HTTP_METHOD_SWAP || MatchWeb != null;
public WebInfo MatchWeb { get; private set; }
public override ValueTask DisposeAsync()
{
return _inner.DisposeAsync();
}
ConnectionContext context { get; }
IDuplexPipe Transport => context.Transport;
/// <summary>
/// 解析FastTunnel协议
/// </summary>
internal async Task TryAnalysisPipeAsync()
{
var reader = Transport.Input;
var _input = Transport.Input;
ReadResult result;
ReadOnlySequence<byte> readableBuffer;
while (true)
{
result = await reader.ReadAsync();
result = await _input.ReadAsync();
var tempBuffer = readableBuffer = result.Buffer;
SequencePosition? position = null;
@ -72,17 +56,28 @@ internal class FastTunnelConnectionContext : ConnectionContext
if (position != null)
{
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)
{
reader.AdvanceTo(readedPosition, readedPosition);
_input.AdvanceTo(readedPosition, readedPosition);
}
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;
}
@ -93,13 +88,11 @@ internal class FastTunnelConnectionContext : ConnectionContext
if (result.IsCompleted)
{
reader.AdvanceTo(readableBuffer.End, readableBuffer.End);
break;
}
}
_input.AdvanceTo(readableBuffer.End, readableBuffer.End);
return;
}
}
}
public string Method;
public string Host = null;
@ -107,6 +100,7 @@ internal class FastTunnelConnectionContext : ConnectionContext
private bool isFirstLine = true;
public IList<string> HasReadLInes { get; private set; } = new List<string>();
public FastTunnelServer fastTunnelServer { get; }
/// <summary>
///
@ -122,9 +116,9 @@ internal class FastTunnelConnectionContext : ConnectionContext
/// </summary>
/// <param name="readOnlySequence"></param>
/// <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);
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)
{
var ctx = context as FastTunnelConnectionContext;
if (ctx != null && ctx.IsFastTunnel)
logger.LogInformation("=========ForwarderMiddleware SART===========");
var feat = context.Features.Get<IFastTunnelFeature>();
if (feat == null)
{
if (ctx.Method == ProtocolConst.HTTP_METHOD_SWAP)
{
await doSwap(ctx);
logger.LogInformation("=========ForwarderMiddleware END===========");
// not fasttunnel request
await next(context);
return;
}
else if (ctx.MatchWeb != null)
else
{
await waitSwap(ctx);
logger.LogInformation("=========Swap STRART===========");
if (feat.Method == ProtocolConst.HTTP_METHOD_SWAP)
{
await doSwap(context);
}
else if (feat.MatchWeb != null)
{
await waitSwap(context);
}
else
{
throw new NotSupportedException();
}
}
else
{
await next(context);
logger.LogInformation("=========Swap END===========");
logger.LogInformation("=========ForwarderMiddleware END===========");
}
}
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 web = context.MatchWeb;
var web = feat.MatchWeb;
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}"); });
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))
{
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)
{
logger.LogInformation("=========OnConnectionAsync===========");
var ftContext = new FastTunnelConnectionContext(context, fastTunnelServer, logger);
await ftContext.TryAnalysisPipeAsync();
logger.LogInformation("=========TryAnalysisPipeAsync SART===========");
await new FastTunelProtocol(context, fastTunnelServer).TryAnalysisPipeAsync();
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) =>
{
app.UseFastTunnelServer();
});
//builder.Configure((webHostBuilderContext, app) =>
//{
// app.UseFastTunnelServer();
//});
}
}

View File

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