搭建简单http代理的方式
Posted
技术标签:
【中文标题】搭建简单http代理的方式【英文标题】:The way of building a simple http proxy 【发布时间】:2019-08-19 17:27:46 【问题描述】:我正在尝试构建一个简单的 http 代理,它做了四件非常基本的事情:
接受来自网络浏览器的连接(使用 TcpClient/TcpListener)。 从其流中读取请求。 读取主机名并启动与主机的连接。 从网页加载内容并将其转发回客户端。我遇到的麻烦:
有时页面根本无法加载。 有时浏览器会给我一个错误“内容加密错误”(在 Firefox 中)。 我很少能看到内容损坏(纯文本而不是 html)。我做了什么:
HttpListener 类,包含用于侦听传入请求和调用事件 OnNewRequestReceived 的方法: public void Listen()
Listener.Start();
while (true)
var client = Listener.AcceptTcpClient();
Task.Run(() => StartReceivingData(client));
public void StartReceivingData(TcpClient client)
NetworkStream clientStream = client.GetStream();
var buffer = new byte[16000];
while (true)
try
if (!clientStream.CanRead)
return;
//connection is closed
if (clientStream.Read(buffer).Equals(0))
return;
OnNewRequestReceived?.Invoke(this, new RequestReceivedEventArgs() User = client, Request = buffer );
// when clientStream is disposed, exception is thrown.
catch return;
HttpClient 类,它基本上包含一个订阅上述事件的方法:
private void Listener_OnNewConnectionReceived(object sender, RequestReceivedEventArgs e)
string hostname = HttpQueryParser.GetHostName(e.Request);
NetworkStream proxyClientStream = e.User.GetStream();
try
if (firewall.CheckIfBlocked(hostname))
//send error page
e.User.GetStream().Write(Encoding.ASCII.GetBytes("<html><body style=\"padding:0; margin:0;\"><img style=\"padding:0; margin:0; width:100%; height:100%;\" src=\"https://www.hostinger.co.id/tutorial/wp-content/uploads/sites/11/2017/08/what-is-403-forbidden-error-and-how-to-fix-it.jpg\"</body></html>"));
return;
var targetServer = new TcpClient(hostname, 80);
NetworkStream targetServerStream = targetServer.GetStream();
targetServerStream.Write(e.Request);
var responseBuffer = new byte[32];
for (int offsetCounter = 0; true; ++offsetCounter)
var bytesRead = targetServerStream.Read(responseBuffer, 0, responseBuffer.Length);
// Console.WriteLine($"Read bytesRead from hostname.");
if (bytesRead.Equals(0))
return;
proxyClientStream.Write(responseBuffer, 0, responseBuffer.Length);
if (offsetCounter.Equals(0))
var headers = Encoding.UTF8.GetString(responseBuffer).Split("\r\n");
logger.Log(new HttpRequestEntry()
ResponseCode = headers[0].Substring(headers[0].IndexOf(" ") + 1),
Hostname = hostname
);
catch return;
finally proxyClientStream.Dispose();
所以,我猜我的缓冲区大小有问题,但将其更改为更高的值实际上并没有改变任何东西。
【问题讨论】:
【参考方案1】:好的,所以我不知道我的字节数组有什么问题,但是我使用 Stream.CopyTo 让它工作了,我对此感到非常惊讶 - 它适用于两个 NetworkStreams。 如果您好奇,这里是工作方法:
private void Listener_OnNewConnectionReceived(object sender, RequestReceivedEventArgs e)
string hostname = HttpQueryParser.GetHostName(e.Request);
NetworkStream proxyClientStream = e.User.GetStream();
try
if (firewall.CheckIfBlocked(hostname))
//send error page
e.User.GetStream().Write(Encoding.ASCII.GetBytes("<html><body style=\"padding:0; margin:0;\"><img style=\"padding:0; margin:0; width:100%; height:100%;\" src=\"https://www.hostinger.co.id/tutorial/wp-content/uploads/sites/11/2017/08/what-is-403-forbidden-error-and-how-to-fix-it.jpg\"</body></html>"));
return;
var targetServer = new TcpClient(hostname, 80);
NetworkStream targetServerStream = targetServer.GetStream();
targetServerStream.Write(e.Request);
var responseBuffer = new byte[32];
//this is to capture status of http request and log it.
targetServerStream.Read(responseBuffer, 0, responseBuffer.Length);
proxyClientStream.Write(responseBuffer, 0, responseBuffer.Length);
var headers = Encoding.UTF8.GetString(responseBuffer).Split("\r\n");
logger.Log(new HttpRequestEntry()
ResponseCode = headers[0].Substring(headers[0].IndexOf(" ") + 1),
Hostname = hostname
);
targetServerStream.CopyTo(proxyClientStream);
catch return;
finally proxyClientStream.Dispose();
【讨论】:
以上是关于搭建简单http代理的方式的主要内容,如果未能解决你的问题,请参考以下文章