实现一个简单的Http代理服务器
2021-02-17 16:18
标签:建立 proc forward gif 告诉 应该 targe sequence 比较 昨天介绍了下微软的反向代理库YARP,今天准备实现一个简单的Http正向代理服务器玩下。首先还是介绍下背景知识: 普通代理(Http) 在Http的时代,大部分是走的RFC 7230中描述的普通代理。这种代理扮演的是「中间人」角色,对于连接到它的客户端来说,它是服务端;对于要连接的服务端来说,它是客户端。它就负责在两端之间来回传送 HTTP 报文。它的流程是: 这种代理服务器实现是比较简单的,基本上是原封不动的透传,主要是第2步,需要从header中识别目标主机地址。 隧道代理(Https) 到了Https时代,这种方式就有问题了,代理服务器是一个web服务器,它是影响了客户端和服务器的TLS加密连接的。此时主要使用RFC中定义的通过 Web 代理服务器用隧道方式传输基于 TCP 的协议的隧道代理方式,它的主要流程为: 这种模式下,和Sock5等代理协议非常类似了,代理服务器完全就是一个透传的管道了。只不过是通过http协议协商建立起管道而已。建立连接后,代理服务器只起转发的作用,理论上也适用于转发其它TCP协议。 功能实现 两种代理服务器实际上流程是大同小异的,主要是识别目标主机的指令不同,以及交互的方式有所差异,建立连接和完成第一次交互后,后面基本上都是透传。从0开始实现也基本上就几十行代码,实现带调试几个小时差不多可以搞定,如下是我的一个简单的实现,基于.net core 3.1。 简单的运行测试了一下,基本功能应该是完善的,稳定运行貌似没有什么大问题。虽然没有什么额外的功能,但还是考虑了一下性能的,用了内存池,解析http头的时候也尽量较少了内存的分配。考虑带代码的可读性,也没有太必要做到性能的极致。后面有空的话准备用它来写一个SS客户端,使之有更好的扩展性。 这种模式下,和Sock5等代理协议非常类似了,代理服务器完全就是一个透传的管道了。只不过是通过http协议协商建立起管道而已。建立连接后,代理服务器只起转发的作用,理论上也适用于转发其它TCP协议。 参考文章 实现一个简单的Http代理服务器 标签:建立 proc forward gif 告诉 应该 targe sequence 比较 原文地址:https://www.cnblogs.com/TianFang/p/12952375.html
static void Main(string[] args)
{
ProxyServer.Run();
Thread.Sleep(-1);
}
class ProxyServer
{
public static void Run()
{
TcpServer.Run(3000, async tcp =>
{
using var handlder = new ProxyHandler(tcp);
await handlder.Process();
});
}
}
class ProxyHandler : IDisposable
{
TcpClient _tcp;
TcpClient _remoteTcp;
public ProxyHandler(TcpClient tcp)
{
_tcp = tcp;
_buffer = MemoryPoolbyte>.Shared.Rent(1024 * 3);
}
IMemoryOwnerbyte> _buffer;
Memorybyte> _header;
public async Task Process()
{
var count = await _tcp.GetStream().ReadAsync(_buffer.Memory);
_header = _buffer.Memory[..count];
parseHeader(out var method, out var endPoint);
//Console.WriteLine(endPoint);
if (method.Equals("CONNECT", StringComparison.OrdinalIgnoreCase))
{
await createTunnel(endPoint);
}
else
{
await createProxy(endPoint);
}
await pipeStream(_tcp.GetStream(), _remoteTcp.GetStream());
}
static byte[] _tunnelReply = Encoding.UTF8.GetBytes("HTTP/1.0 200 Connection Established\r\n\r\n");
async ValueTask createTunnel(string endPoint)
{
var host = endPoint.Split(":");
_remoteTcp = new TcpClient();
await _remoteTcp.ConnectAsync(host[0], int.Parse(host[1]));
await _tcp.GetStream().WriteAsync(_tunnelReply);
}
async ValueTask createProxy(string endPoint)
{
var host = new Uri(endPoint);
_remoteTcp = new TcpClient();
await _remoteTcp.ConnectAsync(host.Host, host.Port);
await _remoteTcp.GetStream().WriteAsync(_header);
}
void parseHeader(out string method, out string endPoint)
{
var reader = new SequenceReaderbyte>(new ReadOnlySequencebyte>(_header));
method = readToSpace(ref reader);
endPoint = readToSpace(ref reader);
static string readToSpace(ref SequenceReaderbyte> r)
{
//读到下一个空格
r.TryReadTo(out ReadOnlySpanbyte> buf, (byte)‘ ‘);
return Encoding.UTF8.GetString(buf);
}
}
async Task pipeStream(Stream s1, Stream s2)
{
await Task.WhenAll(pipe(s1, s2), pipe(s2, s1));
static async Task pipe(Stream source, Stream target)
{
try
{
await source.CopyToAsync(target);
}
finally
{
target.Close();
source.Close();
}
}
}
public void Dispose()
{
_buffer?.Dispose();
_tcp?.Dispose();
_remoteTcp?.Dispose();
}
}
class TcpServer
{
public static async void Run(int port, Func