【问题标题】:The way of building a simple http proxy搭建简单http代理的方式
【发布时间】: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(); }


        }

所以,我猜我的缓冲区大小有问题,但将其更改为更高的值实际上并没有改变任何东西。

【问题讨论】:

    标签: c# http networking proxy


    【解决方案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(); }
    
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-18
      • 2023-01-26
      • 1970-01-01
      • 2013-12-19
      • 2011-05-23
      • 1970-01-01
      相关资源
      最近更新 更多