看到一个文章[Go] 不到 100 行代码实现一个支持 CONNECT 动词的 HTTP 服务器
原理图如下:
这里在NET8.0中实现反向代理服务器部分
新建MiniApi项目
编辑Program.cs
文件。
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
// 将HTTP请求通过协议升级机制转为远程TCP请求(WebSocket分支,Nginx支持)
app.Map("/http2tcp", async (context) =>
{
var upgradeFeature = context.Features.Get<Microsoft.AspNetCore.Http.Features.IHttpUpgradeFeature>();
if (upgradeFeature != null && upgradeFeature.IsUpgradableRequest)
{
context.Features.Get<Microsoft.AspNetCore.Http.Timeouts.IHttpRequestTimeoutFeature>()?.DisableTimeout();
context.Response.Headers.Connection = Microsoft.Net.Http.Headers.HeaderNames.Upgrade;
context.Response.Headers.Upgrade = "http2tcp/1.0";
Stream stream = await upgradeFeature.UpgradeAsync();
using System.Net.Sockets.TcpClient tcpClient = new System.Net.Sockets.TcpClient();
await tcpClient.ConnectAsync(System.Net.IPEndPoint.Parse("127.0.0.1:1000"));
using System.Net.Sockets.NetworkStream network = tcpClient.GetStream();
var taskX = network.CopyToAsync(stream);
var tsakY = stream.CopyToAsync(network);
Task.WaitAny(taskX, tsakY);
}
});
// 将HTTP请求通过CONNECT方法转为TCP请求(CONNECT分支)主流浏览器都支持 https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT
app.MapMethods("", new[] { HttpMethods.Connect }, async (context) =>
{
var auth = context.Request.Headers["Proxy-Authorization"];
await context.Response.Body.FlushAsync();
var socket = context.Features.Get<Microsoft.AspNetCore.Connections.Features.IConnectionSocketFeature>()!.Socket;
var stream = new System.Net.Sockets.NetworkStream(socket);
using var tcpClient = new System.Net.Sockets.TcpClient();
await tcpClient.ConnectAsync(System.Net.IPEndPoint.Parse(context.Request.Host.Value));
using var network = tcpClient.GetStream();
var taskX = network.CopyToAsync(stream);
var tsakY = stream.CopyToAsync(network);
Task.WaitAny(taskX, tsakY);
await socket.DisconnectAsync(true);
socket.Close();
});
app.Run();
正向代理服务器的HttpClient请求如下,将获取到的TcpStream和TcpListener的NetworkStream串联即可。
// HTTP Upgrade 客户端请求方式
GET http://127.0.0.1:5199/http2tcp HTTP/1.1
Connection: upgrade
Upgrade: example/1, foo/2
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:5199
HTTP CONNECT 客户端请求方式
CONNECT 127.0.0.1:1000 HTTP/1.1
Host: 127.0.0.1:1000
Proxy-Authorization: basic aGVsbG86d29ybGQ=
如下是正向代理服务器和目标服务器之间数据传输示意图。