Recommend a better HTTP(S) proxy server than fiddlecore

Original text: Recommend a better HTTP(S) proxy server than fiddlecore

Why not Fiddler core?

When it comes to Fiddler core, you may not be familiar with it, so its brother Fiddler is familiar with it. I usually use it to grab packets, simulate low bandwidth, and modify requests. Fiddler is essentially an HTTP proxy server.
FiddlerCore is the core component of Fiddler's UI, which can be used for secondary development. As shown in the figure below:


Fiddler feels uncomfortable in the following aspects:

  1. API naming is not standardized and property / field is mixed.
    Pascal is widely used in. NET, and Fiddler's naming is like a hodgepodge. For example: public member field, CONFIG class name, oSession parameter name
  2. Asynchronous is not supported. All callbacks are synchronous.
  3. The frame design is unreasonable, does not support multiple instances, and uses a lot of static methods. It's not easy to expand all the methods piled into the Session class. At least Request/Response should be separated.

As for the above points, I feel that this framework has no design at all. If there is no other choice, Fiddler core may be used. Fortunately, on Github, someone has implemented Fiddler core step by step

Introduction to titanium web proxy

A cross platform, lightweight, low memory, high-performance HTTP(S) proxy server, development language C#

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

Functional features

  1. Supports most functions of HTTP(S) and HTTP 1.1
  2. Support for redirect/block/update requests
  3. Support update Response
  4. Support WebSocket hosted by HTTP
  5. Support mutual SSL authentication
  6. Fully asynchronous agent
  7. Support agent authorization and automatic agent detection
  8. Kerberos/NTLM authentication over HTTP protocols for windows domain

Use

Install NuGet package

Install-Package Titanium.Web.Proxy

Support

  • . Net Standard 1.6 or higher
  • . Net Framework 4.5 or higher

Set up HTTP proxy

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();

Simple request and response processing

//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);
}

Future Roadmap

  • Support for HTTP 2.0
  • Support for Socks protocol

Keywords: Google github network Session

Added by Valdhor on Tue, 28 Apr 2020 12:22:48 +0300