优化离线后的响应报文

This commit is contained in:
SpringHgui 2021-08-22 00:55:28 +08:00
parent 970b5ca8de
commit e47c97da0e
5 changed files with 212 additions and 183 deletions

View File

@ -1,6 +1,7 @@
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;
@ -48,12 +49,13 @@ namespace FastTunnel.Core.Forwarder
}
}
public async ValueTask<Stream> proxyAsync(string host, CancellationToken cancellation)
public async ValueTask<Stream> proxyAsync(string host, SocketsHttpConnectionContext context, CancellationToken cancellation)
{
WebInfo web;
if (!_fastTunnelServer.WebList.TryGetValue(host, out web))
{
throw new Exception($"站点未登录:{host}");
// 客户端已离线
return await OfflinePage(host, context);
}
try
@ -82,5 +84,13 @@ namespace FastTunnel.Core.Forwarder
throw;
}
}
private async ValueTask<Stream> OfflinePage(string host, SocketsHttpConnectionContext context)
{
var bytes = Encoding.UTF8.GetBytes(
$"HTTP/1.1 200 OK\r\nContent-Type:text/html; charset=utf-8\r\n\r\n{TunnelResource.Page_Offline}\r\n");
return await Task.FromResult(new ResponseStream(bytes));
}
}
}

View File

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
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(); }
MemoryStream m_Stream;
public ResponseStream(byte[] bytes)
{
m_Stream = new MemoryStream(bytes);
}
public override void Flush()
{
throw new NotImplementedException();
}
bool complete = false;
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;
}
}
}

View File

@ -1,157 +0,0 @@
using FastTunnel.Core.Dispatchers;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Sockets
{
/// <summary>
/// 异步数据交换
/// 存在大文件下载时,导致栈溢出的问题
/// </summary>
public class AsyncSocketSwap : ISocketSwap
{
private Socket m_sockt1;
private Socket m_sockt2;
bool m_swaping = false;
byte[] m_buffer = new byte[1024];
SocketAsyncEventArgs e1;
SocketAsyncEventArgs e2;
public AsyncSocketSwap(Socket sockt1, Socket sockt2)
{
m_sockt1 = sockt1;
m_sockt2 = sockt2;
e1 = new SocketAsyncEventArgs();
e2 = new SocketAsyncEventArgs();
e1.Completed += IO_Completed;
e2.Completed += IO_Completed;
e1.UserToken = new SwapUserToken { Reciver = m_sockt1, Sender = m_sockt2 };
e2.UserToken = new SwapUserToken { Reciver = m_sockt2, Sender = m_sockt1 };
e1.SetBuffer(m_buffer, 0, 512);
e2.SetBuffer(m_buffer, 512, 512);
}
public ISocketSwap BeforeSwap(Action fun)
{
if (m_swaping)
throw new Exception("BeforeSwap must be invoked before StartSwap!");
fun?.Invoke();
return this;
}
public Task StartSwapAsync()
{
try
{
Console.WriteLine("StartSwapAsync");
m_swaping = true;
if (!m_sockt1.ReceiveAsync(e1))
{
ProcessReceive(e1);
}
if (!m_sockt2.ReceiveAsync(e2))
{
ProcessReceive(e2);
}
return Task.CompletedTask;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
ExceptionDispatchInfo.Capture(ex).Throw();
return Task.CompletedTask;
}
}
private void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
private void ProcessReceive(SocketAsyncEventArgs e)
{
var token = e.UserToken as SwapUserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
e.SetBuffer(e.Offset, 512);
if (!token.Sender.SendAsync(e))
{
ProcessSend(e);
}
}
else
{
// close
CloseSocket(token.Reciver);
}
}
private void ProcessSend(SocketAsyncEventArgs e)
{
var token = e.UserToken as SwapUserToken;
if (e.SocketError == SocketError.Success)
{
Console.WriteLine("ProcessSend:" + e.BytesTransferred);
if (!token.Reciver.ReceiveAsync(e))
{
e.SetBuffer(e.Offset, 512);
ProcessReceive(e);
}
}
else
{
CloseSocket(token.Sender);
}
}
private void CloseSocket(Socket socket)
{
try
{
Console.WriteLine("CloseSocket");
try
{
socket.Shutdown(SocketShutdown.Send);
}
catch (Exception) { }
socket.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public class SwapUserToken
{
public Socket Reciver { get; set; }
public Socket Sender { get; set; }
}
}
}

View File

@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
@ -13,12 +13,12 @@ namespace FastTunnel.Core {
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
@ -33,7 +33,7 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
@ -47,8 +47,8 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
@ -61,7 +61,7 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 查找类似 服务端禁用了Forward 的本地化字符串。
/// Looks up a localized string similar to 服务端禁用了Forward.
/// </summary>
internal static string ForwardDisabled {
get {
@ -70,7 +70,7 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 查找类似 您尚未创建任何隧道请登录https://suidao.io 创建后重试。 的本地化字符串。
/// Looks up a localized string similar to 您尚未创建任何隧道请登录https://suidao.io 创建后重试。.
/// </summary>
internal static string NoTunnel {
get {
@ -79,7 +79,7 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 查找类似 &lt;!DOCTYPE html&gt;
/// Looks up a localized string similar to &lt;!DOCTYPE html&gt;
///&lt;html lang=&quot;en&quot;&gt;
///
///&lt;head&gt;
@ -99,7 +99,7 @@ namespace FastTunnel.Core {
///
/// .btn-primary {
/// color: #fff;
/// background-color [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// background-color [rest of string was truncated]&quot;;.
/// </summary>
internal static string Page_HostRequired {
get {
@ -108,7 +108,7 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 查找类似 &lt;!DOCTYPE html&gt;
/// Looks up a localized string similar to &lt;!DOCTYPE html&gt;
///&lt;html lang=&quot;en&quot;&gt;
///&lt;head&gt;
/// &lt;meta charset=&quot;utf-8&quot; /&gt;
@ -127,7 +127,7 @@ namespace FastTunnel.Core {
///
/// .btn-primary {
/// color: #fff;
/// background-color: [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// background-color: [rest of string was truncated]&quot;;.
/// </summary>
internal static string Page_NoSite {
get {
@ -136,7 +136,7 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 查找类似 &lt;!DOCTYPE html&gt;
/// Looks up a localized string similar to &lt;!DOCTYPE html&gt;
///&lt;html lang=&quot;en&quot;&gt;
///
///&lt;head&gt;
@ -156,7 +156,7 @@ namespace FastTunnel.Core {
///
/// .btn-primary {
/// color: #fff;
/// background-color [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// background-color [rest of string was truncated]&quot;;.
/// </summary>
internal static string Page_NotAccessIps {
get {
@ -165,7 +165,7 @@ namespace FastTunnel.Core {
}
/// <summary>
/// 查找类似 &lt;!DOCTYPE html&gt;
/// Looks up a localized string similar to &lt;!DOCTYPE html&gt;
///&lt;html lang=&quot;en&quot;&gt;
///
///&lt;head&gt;
@ -185,16 +185,42 @@ namespace FastTunnel.Core {
///
/// .btn-primary {
/// color: #fff;
/// background-color [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// background-color [rest of string was truncated]&quot;;.
/// </summary>
internal static string Page_NoTunnel {
internal static string Page_Offline {
get {
return ResourceManager.GetString("Page_NoTunnel", resourceCulture);
}
}
/// <summary>
/// 查找类似 \n=====隧道已建立成功,现在可以通过以下方式访问您的内网服务了=====\n{0}\n 的本地化字符串。
/// Looks up a localized string similar to HTTP/1.1 200 OK
///Date: Sat, 21 Aug 2021 15:14:17 GMT
///Content-Type: text/html; charset=utf-8
///Server: Microsoft-IIS/10.0
///X-Powered-By: ASP.NET
///Content-Length: 3026
///
///&lt;!DOCTYPE html&gt;
///&lt;html lang=&quot;en&quot;&gt;
///&lt;head&gt;
/// &lt;meta charset=&quot;utf-8&quot; /&gt;
/// &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
/// &lt;title&gt; - BX.SignalR&lt;/title&gt;
/// &lt;link rel=&quot;stylesheet&quot; href=&quot;/lib/bootstrap/dist/css/bootstrap.min.css&quot; /&gt;
/// &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/site.css&quot; /&gt;
///&lt;/head&gt;
///&lt;body&gt;
/// &lt;head [rest of string was truncated]&quot;;.
/// </summary>
internal static string Test {
get {
return ResourceManager.GetString("Test", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to \n=====隧道已建立成功,现在可以通过以下方式访问您的内网服务了=====\n{0}\n.
/// </summary>
internal static string TunnelLlist {
get {

View File

@ -588,6 +588,93 @@
&lt;/html&gt;</value>
<comment>客户端未登录</comment>
</data>
<data name="Test" xml:space="preserve">
<value>HTTP/1.1 200 OK
Date: Sat, 21 Aug 2021 15:14:17 GMT
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Content-Length: 3026
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="utf-8" /&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
&lt;title&gt; - BX.SignalR&lt;/title&gt;
&lt;link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" /&gt;
&lt;link rel="stylesheet" href="/css/site.css" /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;header&gt;
&lt;nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"&gt;
&lt;div class="container"&gt;
&lt;a class="navbar-brand" href="/"&gt;BX.SignalR&lt;/a&gt;
&lt;button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation"&gt;
&lt;span class="navbar-toggler-icon"&gt;&lt;/span&gt;
&lt;/button&gt;
&lt;div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"&gt;
&lt;ul class="navbar-nav flex-grow-1"&gt;
&lt;li class="nav-item"&gt;
&lt;a class="nav-link text-dark" href="/"&gt;Home&lt;/a&gt;
&lt;/li&gt;
&lt;li class="nav-item"&gt;
&lt;a class="nav-link text-dark" href="/Privacy"&gt;Privacy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/nav&gt;
&lt;/header&gt;
&lt;div class="container"&gt;
&lt;main role="main" class="pb-3"&gt;
&lt;div class="container"&gt;
&lt;div class="row"&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class="row"&gt;
&lt;div class="col-2"&gt;User&lt;/div&gt;
&lt;div class="col-4"&gt;&lt;input type="text" id="userInput" /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="row"&gt;
&lt;div class="col-2"&gt;Message&lt;/div&gt;
&lt;div class="col-4"&gt;&lt;input type="text" id="messageInput" /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="row"&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class="row"&gt;
&lt;div class="col-6"&gt;
&lt;input type="button" id="sendButton" value="Send Message" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="row"&gt;
&lt;div class="col-12"&gt;
&lt;hr /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="row"&gt;
&lt;div class="col-6"&gt;
&lt;ul id="messagesList"&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;script src="/js/signalr/dist/browser/signalr.js"&gt;&lt;/script&gt;
&lt;script src="/js/chat.js"&gt;&lt;/script&gt;
&lt;/main&gt;
&lt;/div&gt;
&lt;footer class="border-top footer text-muted"&gt;
&lt;div class="container"&gt;
&amp;copy; 2020 - BX.SignalR - &lt;a href="/Privacy"&gt;Privacy&lt;/a&gt;
&lt;/div&gt;
&lt;/footer&gt;
&lt;script src="/lib/jquery/dist/jquery.min.js"&gt;&lt;/script&gt;
&lt;script src="/lib/bootstrap/dist/js/bootstrap.bundle.min.js"&gt;&lt;/script&gt;
&lt;script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"&gt;&lt;/script&gt;
&lt;script src="/_framework/aspnetcore-browser-refresh.js"&gt;&lt;/script&gt;&lt;/body&gt;
&lt;/html&gt;</value>
</data>
<data name="TunnelLlist" xml:space="preserve">
<value>\n=====隧道已建立成功,现在可以通过以下方式访问您的内网服务了=====\n{0}\n</value>
</data>