[Code] HTTP Proxy - WebProxySharp

  這是一個可以將HTTP請求做代理的類別,可以簡稱HTTP Proxy,因為是C Sharp版本,作者將它命名為WebProxySharp,同時它還可以擴充服務HTTPS版本,但必須經過一些憑證的設定,這個類別必須至少運作在Windows XP SP2 或 Windows Server 2003 作業系統的電腦以上的版本。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Net
{
    /// 
    /// 日期: 2012/11/12 ~ 2012/11/13
    /// 作者: Panther
    /// 
    public class WebProxySharp
    {
        public class HttpContextEventArgs : EventArgs
        {
            public HttpListenerContext Context;
            public string RequestUri;

            public HttpContextEventArgs(HttpListenerContext context, string requestUri)
            {
                Context = context;
                RequestUri = requestUri;
            }
        }
        /// 
        /// 當有新的請求時,會觸發這個事件。
        /// 
        public event EventHandler NewHttpContext;
        protected void OnNewHttpContext(HttpContextEventArgs e)
        {
            if (NewHttpContext != null)
                NewHttpContext(this, e);
        }

        #region Fileds

        internal HttpListener _listener;
        internal System.Threading.Thread _thread;
        internal bool _useIsLocalProtect;

        #endregion

        #region Properties

        public HttpListener Listener
        {
            get { return _listener; }
        }

        public bool UseIsLocalProtect
        {
            get { return _useIsLocalProtect; }
            set { _useIsLocalProtect = value; }
        }

        #endregion

        #region Consturctor

        public WebProxySharp()
        {
            Initialize();
        }

        public WebProxySharp(bool addDefualtPrefixes)
        {
            Initialize();
            AddDefualtHttpPrefixes();
        }

        #endregion

        #region Public Methods

        /// 
        /// 增加預設80 Port的處理前置詞。
        /// 
        public void AddDefualtHttpPrefixes()
        {
            _listener.Prefixes.Add("http://*:80/");
        }

        /// 
        /// 增加接受不同Port的處理前置詞。
        /// 
        /// 
        public void AddPrefixesPort(int port)
        {
            _listener.Prefixes.Add(string.Format("http://*:{0}/", port));
        }

        /// 
        /// 啟動HTTP監聽服務,並且使用非同步監聽請求來源。
        /// 
        public void Start()
        {
            if (!_listener.IsListening)
            {
                
                _listener.Start();
                
                _thread = new System.Threading.Thread(HandleMain);
                _thread.Start();
            }
        }

        /// 
        /// 停止HTTP監聽服務,並且結束副執行緒的處理。
        /// 
        public void Stop()
        {
            _listener.Close();

            if (_thread != null)
            {
                try
                {
                    _thread.Abort();
                }
                catch { }
                finally
                {
                    _thread = null;
                }
            }
        }

        #endregion

        #region Private Methods

        private void Initialize()
        {
            _listener = new HttpListener();
        }

        private void HandleMain()
        {
            while (true)
            {
                System.Threading.ThreadPool.QueueUserWorkItem(HandleContextCallBack, _listener.GetContext());
            }
        }

        private void HandleContextCallBack(object httpListenerContext)
        {
            var context = httpListenerContext as HttpListenerContext;

            // 正式服務時,應該要阻止本機迴圈攻擊。
            if (_useIsLocalProtect && context.Request.IsLocal)
            {
                context.Response.Close();
                return;
            }

            // 取得真正要請求的網址。
            var reqUrl = Uri.IsWellFormedUriString(context.Request.RawUrl, UriKind.Absolute) ?
                context.Request.RawUrl :
                "http://" + context.Request.UserHostName + context.Request.RawUrl;

            OnNewHttpContext(new HttpContextEventArgs(context, reqUrl));

            // 建立新的請求物件,並複製來源請求的部份Headers。
            var request = HttpWebRequest.Create(reqUrl) as HttpWebRequest;
            request.Method = context.Request.HttpMethod;
            request.Proxy = null;

            foreach (string key in context.Request.Headers)
            {
                var value = context.Request.Headers[key];

                switch (key)
                {
                    case "Host":
                    case "Proxy-Connection":
                    case "Content-Length":
                    case "Expect":
                        break;
                    case "Accept":
                        request.Accept = value;
                        break;
                    case "User-Agent":
                        request.UserAgent = value;
                        break;
                    case "Connection":
                        request.Connection = value;
                        break;
                    case "Referer":
                        request.Referer = value;
                        break;
                    case "Content-Type":
                        request.ContentType = value;
                        break;
                    default:
                        try
                        {
                            request.Headers.Add(key, value);
                        }
                        catch
                        {
                            System.Diagnostics.Debug.Print("Request Key: {0}", key);
                        }
                        break;
                }
            }

            if (context.Request.HasEntityBody)
            {
                using (var stream = request.GetRequestStream())
                {
                    context.Request.InputStream.CopyTo(stream);
                }
            }

            // 請求真正的目標網址,並將回覆返回給用戶來源端。
            using (var response = request.GetResponse() as HttpWebResponse)
            {
                context.Response.StatusCode = (int)response.StatusCode;
                context.Response.StatusDescription = response.StatusDescription;
                context.Response.ProtocolVersion = response.ProtocolVersion;
                foreach (string headerName in response.Headers)
                {
                    var value = response.Headers[headerName];
                    try
                    {
                        context.Response.AddHeader(headerName, value);
                    }
                    catch
                    {
                        Console.WriteLine("Request Key: {0}", headerName);
                    }
                }

                try
                {
                    response.GetResponseStream().CopyTo(context.Response.OutputStream);
                }
                catch
                {
                    context.Response.OutputStream.Write(new byte[0], 0, 0);
                }

                context.Response.Close();
            }

        }

        #endregion
    }
}
類別的使用也很簡單,例如:
static void Main(string[] args)
        {
            var webProxySharp = new WebProxySharp(true);
            webProxySharp.AddPrefixesPort(5678);
            webProxySharp.NewHttpContext += new EventHandler(webProxySharp_NewHttpContext);

            webProxySharp.Start();
            Console.WriteLine("Start Http Proxy, Welcome...");

            while (true)
            {
                Console.ReadKey();
                break;
            }

            webProxySharp.Stop();
        }

        static void webProxySharp_NewHttpContext(object sender, WebProxySharp.HttpContextEventArgs e)
        {
            Console.WriteLine(e.RequestUri);
        }

執行後,你只要在瀏覽器或自己撰寫工具設定Proxy即可。

沒有留言:

張貼留言