爲何不用FiddlerCore?
說到FiddlerCore你們可能會比較陌生,那麼它哥Fiddler就比較熟悉了;抓包、模擬低帶寬、修改請求我平時比較經常使用。Fiddler的本質就是一個HTTP代理服務器。
FiddlerCore是Fiddler去除了UI的核心組件,能夠用於二次開發。以下圖所示:
html
Fiddler主要有如下幾點感受很彆扭:git
- API命名不規範、屬性/字段混用。
.NET中廣泛採用Pascal命名規範,而Fiddler的命名就像個大雜燴。例如:public成員用字段,CONFIG類名,oSession參數名 - 不支持異步。全部的回調都是同步。
- 框架設計不合理,不支持多實例,大量使用了靜態方法。全部方法都堆到了Session類中不易於擴展,起碼Request/Response也得分開啊。
單就以上幾點我感受這框架徹底沒有設計可言,若是沒有別的選擇FiddlerCore我或許就將就用了,好在Github上已經有人先一步從新實現了FiddlerCoregithub
Titanium-Web-Proxy介紹
一個跨平臺、輕量級、低內存、高性能的HTTP(S)代理服務器,開發語言爲C#web
https://github.com/justcoding121/Titanium-Web-Proxywindows
功能特性
- 支持HTTP(S)與HTTP 1.1的大部分功能
- 支持redirect/block/update 請求
- 支持更新Response
- 支持HTTP承載的WebSocket
- Support mutual SSL authentication
- 徹底異步的代理
- 支持代理受權與自動代理檢測
- Kerberos/NTLM authentication over HTTP protocols for windows domain
使用
安裝NuGet包服務器
Install-Package Titanium.Web.Proxy
框架
支持dom
- .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();
簡單的請求與響應處理async
//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協議