Titanium Web Proxy

    A light weight HTTP(S) proxy server written in C#

    Build Status Join the chat at https://gitter.im/Titanium-Web-Proxy/Lobby

    Kindly report only issues/bugs here . For programming help or questions use StackOverflow with the tag Titanium-Web-Proxy.

    • API Documentation
    • Wiki & Contribution guidelines

    Console example application screenshot

    alt tag

    GUI example application screenshot

    alt tag

    Features

    • Multithreaded & fully asynchronous proxy
    • Supports HTTP(S) and most features of HTTP 1.1
    • Supports redirect/block/update requests and modifying responses
    • Safely relays Web Socket requests over HTTP
    • Supports mutual SSL authentication
    • Supports proxy authentication & automatic proxy detection
    • Kerberos/NTLM authentication over HTTP protocols for windows domain

    Usage

    Refer the HTTP Proxy Server library in your project, look up Test project to learn usage.

    Install by nuget

    For beta releases on beta branch

    Install-Package Titanium.Web.Proxy -Pre
    

    For stable releases on stable branch

    Install-Package Titanium.Web.Proxy
    

    Supports

    • .Net Standard 2.0 or above
    • .Net Framework 4.5 or above

    Setup HTTP proxy:

    var proxyServer = new ProxyServer();
    
    //locally trust root certificate used by this proxy 
    proxyServer.CertificateManager.TrustRootCertificate = true;
    
    //optionally set the Certificate Engine
    //Under Mono only BouncyCastle will be supported
    //proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
    
    proxyServer.BeforeRequest += OnRequest;
    proxyServer.BeforeResponse += OnResponse;
    proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
    proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
    
    
    var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
    {
    //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")
    };
    
    //Fired when a CONNECT request is received
    explicitEndPoint.BeforeTunnelConnect += OnBeforeTunnelConnect;
    
    //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
    explicitEndPoint.BeforeTunnelConnect -= OnBeforeTunnelConnect;
    proxyServer.BeforeRequest -= OnRequest;
    proxyServer.BeforeResponse -= OnResponse;
    proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
    proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
    
    proxyServer.Stop();
    

    Sample request and response event handlers

    
    //To access requestBody from OnResponse handler
    private IDictionary<Guid, string> requestBodyHistory 
            = new ConcurrentDictionary<Guid, string>();
    
    private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
    {
        string hostname = e.WebSession.Request.RequestUri.Host;
    
        if (hostname.Contains("dropbox.com"))
        {
             //Exclude Https addresses you don't want to proxy
             //Useful for clients that use certificate pinning
             //for example dropbox.com
             e.DecryptSsl = false;
        }
    }
    
    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"))
        {
        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"))
        {
        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);
    }
    

    Note to contributors

    Roadmap

    • Support HTTP 2.0

    Collaborators

    • honfika
    Back to top Generated by DocFX