zoukankan      html  css  js  c++  java
  • 推荐一个比FiddlerCore好用的HTTP(S)代理服务器

    为什么不用FiddlerCore?

    说到FiddlerCore大家可能会比较陌生,那么它哥Fiddler就比较熟悉了;抓包、模拟低带宽、修改请求我平时比较常用。Fiddler的本质就是一个HTTP代理服务器。
    FiddlerCore是Fiddler去除了UI的核心组件,可以用于二次开发。如下图所示:

    Fiddler主要有以下几点感觉很别扭:

    1. API命名不规范、属性/字段混用。
      .NET中普遍采用Pascal命名规范,而Fiddler的命名就像个大杂烩。例如:public成员用字段,CONFIG类名,oSession参数名
    2. 不支持异步。所有的回调都是同步。
    3. 框架设计不合理,不支持多实例,大量使用了静态方法。所有方法都堆到了Session类中不易于扩展,起码Request/Response也得分开啊。

    单就以上几点我感觉这框架完全没有设计可言,如果没有别的选择FiddlerCore我或许就将就用了,好在Github上已经有人先一步重新实现了FiddlerCore

    Titanium-Web-Proxy介绍

    一个跨平台、轻量级、低内存、高性能的HTTP(S)代理服务器,开发语言为C#

    https://github.com/justcoding121/Titanium-Web-Proxy

    功能特性

    1. 支持HTTP(S)与HTTP 1.1的大部分功能
    2. 支持redirect/block/update 请求
    3. 支持更新Response
    4. 支持HTTP承载的WebSocket
    5. Support mutual SSL authentication
    6. 完全异步的代理
    7. 支持代理授权与自动代理检测
    8. Kerberos/NTLM authentication over HTTP protocols for windows domain

    使用

    安装NuGet包

    Install-Package Titanium.Web.Proxy

    支持

    • .Net Standard 1.6或更高
    • .Net Framework 4.5或更高

    设置HTTP代理

    var proxyServer = new ProxyServer();
    
    //locally trust root certificate used by this proxy 
    proxyServer.TrustRootCertificate = true;
    
    //optionally set the Certificate Engine
    //Under Mono only BouncyCastle will be supported
    //proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle;
    
    proxyServer.BeforeRequest += OnRequest;
    proxyServer.BeforeResponse += OnResponse;
    proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
    proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
    
    
    var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
    {
    //Exclude HTTPS addresses you don't want to proxy
    //Useful for clients that use certificate pinning
    //for example dropbox.com
    // ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
    
    //Use self-issued generic certificate on all HTTPS requests
    //Optimizes performance by not creating a certificate for each HTTPS-enabled domain
    //Useful when certificate trust is not required by proxy clients
    // GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
    };
    
    //An explicit endpoint is where the client knows about the existence of a proxy
    //So client sends request in a proxy friendly manner
    proxyServer.AddEndPoint(explicitEndPoint);
    proxyServer.Start();
    
    //Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy)
    //A transparent endpoint usually requires a network router port forwarding HTTP(S) packets or DNS
    //to send data to this endPoint
    var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true)
    {
    	//Generic Certificate hostname to use
    	//when SNI is disabled by client
    	GenericCertificateName = "google.com"
    };
    
    proxyServer.AddEndPoint(transparentEndPoint);
    
    //proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
    //proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
    
    foreach (var endPoint in proxyServer.ProxyEndPoints)
    Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
        endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
    
    //Only explicit proxies can be set as system proxy!
    proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
    proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
    
    //wait here (You can use something else as a wait function, I am using this as a demo)
    Console.Read();
    
    //Unsubscribe & Quit
    proxyServer.BeforeRequest -= OnRequest;
    proxyServer.BeforeResponse -= OnResponse;
    proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
    proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
    
    proxyServer.Stop();
    

    简单的请求与响应处理

    //To access requestBody from OnResponse handler
    private IDictionary<Guid, string> requestBodyHistory 
            = new ConcurrentDictionary<Guid, string>();
    
    public async Task OnRequest(object sender, SessionEventArgs e)
    {
        Console.WriteLine(e.WebSession.Request.Url);
    
        ////read request headers
        var requestHeaders = e.WebSession.Request.RequestHeaders;
    
        var method = e.WebSession.Request.Method.ToUpper();
        if ((method == "POST" || method == "PUT" || method == "PATCH"))
        {
    	//Get/Set request body bytes
    	byte[] bodyBytes = await e.GetRequestBody();
    	await e.SetRequestBody(bodyBytes);
    
    	//Get/Set request body as string
    	string bodyString = await e.GetRequestBodyAsString();
    	await e.SetRequestBodyString(bodyString);
    	
    	//store request Body/request headers etc with request Id as key
    	//so that you can find it from response handler using request Id
      	requestBodyHistory[e.Id] = bodyString;
        }
    
        //To cancel a request with a custom HTML content
        //Filter URL
        if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
        {
    	await e.Ok("<!DOCTYPE html>" +
    	      "<html><body><h1>" +
    	      "Website Blocked" +
    	      "</h1>" +
    	      "<p>Blocked by titanium web proxy.</p>" +
    	      "</body>" +
    	      "</html>");
        }
        //Redirect example
        if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
        {
    	await e.Redirect("https://www.paypal.com");
        }
    }
    
    //Modify response
    public async Task OnResponse(object sender, SessionEventArgs e)
    {
        //read response headers
        var responseHeaders = e.WebSession.Response.ResponseHeaders;
    
        //if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
        if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST")
        {
    	if (e.WebSession.Response.ResponseStatusCode == "200")
    	{
    	    if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
    	    {
    		byte[] bodyBytes = await e.GetResponseBody();
    		await e.SetResponseBody(bodyBytes);
    
    		string body = await e.GetResponseBodyAsString();
    		await e.SetResponseBodyString(body);
    	    }
    	}
        }
        
        //access request body/request headers etc by looking up using requestId
        if(requestBodyHistory.ContainsKey(e.Id))
        {
    	var requestBody = requestBodyHistory[e.Id];
        }
    }
    
    /// Allows overriding default certificate validation logic
    public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
    {
        //set IsValid to true/false based on Certificate Errors
        if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
    	e.IsValid = true;
    
        return Task.FromResult(0);
    }
    
    /// Allows overriding default client certificate selection logic during mutual authentication
    public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
    {
        //set e.clientCertificate to override
        return Task.FromResult(0);
    }
    

    未来路线图

    • 支持HTTP 2.0
    • 支持Socks协议
  • 相关阅读:
    Eclipse插件大全 (下)
    Eclipse插件大全 (上)
    Struts2学习笔记
    DisplayTag应用指南
    JFreeChart 2
    JFreeChart 1
    一对一直播app源码开发,多媒体消息发送优化方案
    仿比心视频聊天源码开发,网络节点数量和时延的关系
    一对一直播源码开发,保证实时性要从降低延迟下手
    小视频app源码凭什么成功出圈,守“江山”有多难?
  • 原文地址:https://www.cnblogs.com/mondol/p/7577587.html
Copyright © 2011-2022 走看看