• 使用Beetle实现http代理服务


        之前介绍Beetle的应用都是基于自定义消息,那给人的感就是这个组件只能做这方面的用途.其实Beetle只是一个基础的Tcp组件,在它的基础上可以完成其他协议通讯上的工作.以下是通过Beetle简单地制作一个http代理服务器,为了验证这个代理服务器的功能编写这文章也是通过http代理服务器来提交.以下描述Beetle实现这个功能.

        首先Beetle并没有提供Http协议的封装,为了对http协议进行分析必须实现一个简单的http协议分析器.组件提供了Package基础类和其扩展的EofDataOfPackage和HeadSizeOfPackage;http是一种基于结束符的协方方式,可以直接从EofDataOfPackage派生下来.以下是一个http协议分析类的简单实现

    public class HttpPackage:Beetle.EofDataOfPackage
        {
            public HttpPackage(Beetle.TcpChannel channel)
                : base(channel)
            {
            }
            public static Beetle.ByteArrayPool BlockByteArrayPool = new Beetle.ByteArrayPool(200, Beetle.TcpUtils.ReceiveBufferLength);
            protected override Beetle.IMessage GetMessage(string name)
            {
                return new HttpMessage();
            }
            private HttpMessage httpMsg = null;
            private int mContentLength = 0;
            public override void Import(byte[] data, int start, int count)
            {
                int rcount = 0;
                if (httpMsg == null)
                {
                    httpMsg = new HttpMessage();
                    int httpendindex = ByteIndexOf(data, EofData);
                    if (httpendindex < 0)
                        throw Beetle.NetTcpException.ReadDataError(null);
                    rcount = httpendindex + 1;
                    loadHttpInfo(Encoding.ASCII.GetString(data, 0, rcount), httpMsg);
                   
                    OnReceiveMessage(httpMsg);
                    if (!httpMsg.HasBody && !httpMsg.IsGzip)
                        httpMsg = null;
    
                }
                if (rcount < count && mContentLength > 0)
                {
                    HttpBodyBlock block = new HttpBodyBlock();
                    block.Data = BlockByteArrayPool.Pop();
                    Buffer.BlockCopy(data, start + rcount, block.Data.Array, 0, count - rcount);
                    mContentLength = mContentLength - (count - rcount);
                    block.Data.SetInfo(0, count - rcount);
                    
                    OnReceiveMessage(block);
                    mContentLength = mContentLength - (count - rcount);
                    if (mContentLength == 0)
                        httpMsg = null;
                }
                else if (rcount < count)
                {
    
                    HttpBodyBlock block = new HttpBodyBlock();
                    block.Data = BlockByteArrayPool.Pop();
                    Buffer.BlockCopy(data, start + rcount, block.Data.Array, 0, count - rcount);
                    mContentLength = mContentLength - (count - rcount);
                    block.Data.SetInfo(0, count - rcount);
                    OnReceiveMessage(block);
                }
            }
            private void loadHttpInfo(string info, HttpMessage http)
            {
                string[] properties = info.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                http.HttpMethod = properties[0];
                mContentLength = 0;
                for (int i = 1; i < properties.Length; i++)
                {
                    string property = properties[i];
                    int index = property.IndexOf(':');
                    HttpMessage.Header header = new HttpMessage.Header();
                    header.Name = property.Substring(0, index);
                    header.Value = property.Substring(index + 1, (property.Length - index - 1));
                    http.Headers.Add(header);
                    if (header.Name == "Content-Length")
                    {
                        mContentLength = int.Parse(header.Value.Trim());
                        http.HasBody = true;
                    }
                    if (header.Name == "Host")
                    {
                        string[] values = header.Value.Split(':');
                        http.Host = values[0].Trim();
                        if (values.Length > 1)
                            http.Port = int.Parse(values[1].Trim());
    
                    }
                    if (header.Name == "Proxy-Connection")
                    {
                        header.Name = "Connection";
                    }
                    if (header.Name == "Content-Encoding")
                    {
                        if (header.Value.IndexOf("gzip", StringComparison.InvariantCultureIgnoreCase) >= 0)
                            http.IsGzip = true;
                    }
    
                }
                if (!string.IsNullOrEmpty(http.Host))
                    http.HttpMethod = http.HttpMethod.Replace("http://" + http.Host.Trim(), "");
            }
            public override void MessageWrite(Beetle.IMessage msg, Beetle.BufferWriter writer)
            {
                msg.Save(writer);
                if (msg is HttpBodyBlock)
                {
                    BlockByteArrayPool.Push(((HttpBodyBlock)msg).Data);
                }
            }
            private static byte[] mEofData = Encoding.ASCII.GetBytes("\r\n\r\n");
            protected override byte[] EofData
            {
                get { return mEofData; }
            }
    }

    消息分析比较简单,首先是看一下存不存在Http请求或应答信息,如果不存在则先获取http相关信息.后面就是根据ContentLength来获取内容数据,包括提交数据.不过对于应答来说有些gzip打开的情况下是不存在ContentLength的.大体上一个http协议的分析就完成了.

    协议分析完成后下面就是代理交互部分实现,当一个请求接入的时候就根据当前http的请求信息产生一个新目的端的连接

            private void onRequestReceive(Beetle.PacketRecieveMessagerArgs e)
            {
                SendToTarget(e.Message);
                if (e.Message is HttpPackage.HttpMessage)
                {
                    HttpPackage.HttpMessage http = (HttpPackage.HttpMessage)e.Message;
                    if (mTargetChannel == null)
                    {
                        createTarget(http);
                    }
                   
                }
            }
            private void createTarget(HttpPackage.HttpMessage http)
            {
    
                try
                {
                    System.Net.IPAddress[] ipaddress = System.Net.Dns.GetHostAddresses(http.Host);
                    Beetle.TcpServer.CreateClientAsync(ipaddress[0], http.Port, OnTargetCreate);
                }
                catch (Exception e_)
                {
                    Console.WriteLine(e_.Message);
                    Dispose();
    
                }
            }

    判断当前连接对应的目的端连接是否存在,如果不存在则创建连接.创建完成后就是两个连接数据转发交互了.

            private void onTargetReceive(Beetle.PacketRecieveMessagerArgs e)
            {
                if (e.Message is HttpPackage.HttpMessage)
                {
                    HttpPackage.HttpMessage http = (HttpPackage.HttpMessage)e.Message;
                    Console.WriteLine("{0} Response to {1}", mTargetChannel.EndPoint, mRequestChannel.EndPoint);
                    Console.Write(http);
                    ResponseHttps.Add(http);
                    mRequestChannel.Send(http);
    
                }
                else
                {
                    HttpPackage.HttpBodyBlock block = (HttpPackage.HttpBodyBlock)e.Message;
                    mRequestChannel.Send(block);
                    Console.WriteLine("{0} Response to {1}", mTargetChannel.EndPoint, mRequestChannel.EndPoint);
                    Console.WriteLine(" Response DataLength:" + block.Data.Count);
    
                }
            }

    到这里一个http代理服务器就已经完成了,这只是很普通的http代理功能,对于https不起作用.如果要做一个很完善的http代理服务器那首先要对http协议有个详细的了解,对没释放连处理,内存使用回收等.这里只是作为一个sample简单的制作.以下是通过这个代理服务查看www.163.com的效果,如果你想用他来FQ那不行:)对于http这此协议墙是一定会拦到的,如果要FQ那就做个client在本地获了http加密后提交给代理服务器,代理服务器解密处理转发:)

    下载完整代码:

    ProxyServer.rar (210.98 kb)

    访问Beetlex的Github
  • 相关阅读:
    JAVA网络编程-IP组播
    Centos7安装Node
    Android Studio解决support-annotations版本冲突
    Wordpresss建站笔记:英文模板出现中文如何解决?
    Win10系统下插入耳机前面板无声后面板有声的处理(二)
    webstorm编辑器html浏览器快捷浏览按键图标消失的处理
    近期的感想
    Octet string 解析
    SSH隧道:端口转发功能详解
    uint, not []uint8
  • 原文地址:https://www.cnblogs.com/smark/p/2457061.html
Copyright © 2020-2023  润新知