1

I try to create proxy server with fiddlercore and when i use event BeforeResponse code work very slow. What i'm attempt to do is to insert script at the top of the current browsed page.

Why is below code so slow is it need to be inserted some kind of filter or what?

Fiddler.FiddlerApplication.BeforeResponse += delegate(Fiddler.Session oS)
        {
            Console.WriteLine("{0}:HTTP {1} for {2}", oS.id, oS.responseCode, oS.fullUrl);

            oS.utilDecodeResponse(); 
            string HTML = oS.GetResponseBodyAsString();

            HtmlDocument htmlDoc = new HtmlDocument();
            try
            {
                htmlDoc.LoadHtml(HTML);
                HtmlNode node = htmlDoc.CreateElement("script");
                node.SetAttributeValue("src", "https://somesite.com");
                HtmlNode Head = htmlDoc.DocumentNode.SelectSingleNode("//Head");

                if (Head != null)
                {
                    Head.AppendChild(node);
                    oS.utilSetResponseBody(htmlDoc.DocumentNode.OuterHtml);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }

            Console.WriteLine("URL: {0}", oS.url);
        };

Here is full code:

    /*
    * This demo program shows how to use the FiddlerCore library.
    *
    * Before compiling, ensure that the project's REFERENCES list points to the 
    * copy of FiddlerCore.dll included in this package.
    *
    * By default, the project is compiled without support for the SAZ File format.
    * If you want to add SAZ support, define the token SAZ_SUPPORT in the list of
    * Conditional Compilation symbols on the project's BUILD tab. You will also
    * need to add Ionic.Zip.Reduced.dll to your project's references.
    */

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using System.Threading;
    using Fiddler;
    using System.Diagnostics;
    using HtmlAgilityPack;
    using System.Security.Cryptography.X509Certificates;
    using System.Text.RegularExpressions;

    namespace Demo
    {
        class Program
        {
            static Proxy oSecureEndpoint;
            static string sSecureEndpointHostname = "localhost";
            static int iSecureEndpointPort = 7777;

            public static void WriteCommandResponse(string s)
            {
                ConsoleColor oldColor = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine(s);
                Console.ForegroundColor = oldColor;
            }

            public static void DoQuit()
            {
                WriteCommandResponse("Shutting down...");
                //  UninstallCertificate();
                //  Console.Write("\ncert: {0}", CertMaker.rootCertExists()); Console.ReadLine();
                //  if (null != oSecureEndpoint) oSecureEndpoint.Dispose();
                Fiddler.FiddlerApplication.Shutdown();

                Thread.Sleep(500);
            }
            private static string Ellipsize(string s, int iLen)
            {
                if (s.Length <= iLen) return s;
                return s.Substring(0, iLen - 3) + "...";
            }

    #if SAZ_SUPPORT
            private static void ReadSessions(List<Fiddler.Session> oAllSessions)
            {
                Session[] oLoaded = Utilities.ReadSessionArchive(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) 
                                                               + Path.DirectorySeparatorChar + "ToLoad.saz", false);

                if ((oLoaded != null) && (oLoaded.Length > 0))
                {
                    oAllSessions.AddRange(oLoaded);
                    WriteCommandResponse("Loaded: " + oLoaded.Length + " sessions.");
                }
            }

            private static void SaveSessionsToDesktop(List<Fiddler.Session> oAllSessions)
            {
                bool bSuccess = false;
                string sFilename = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
                                 + Path.DirectorySeparatorChar + DateTime.Now.ToString("hh-mm-ss") + ".saz";
                try
                {
                    try
                    {
                        Monitor.Enter(oAllSessions);

                        string sPassword = null;
                        Console.WriteLine("Password Protect this Archive (Y/N)?");
                        ConsoleKeyInfo oCKI = Console.ReadKey();
                        if ((oCKI.KeyChar == 'y') || (oCKI.KeyChar == 'Y'))
                        {
                            Console.WriteLine("\nEnter the password:");
                            sPassword = Console.ReadLine();
                            Console.WriteLine(String.Format("\nEncrypting with Password: '{0}'", sPassword));
                        }
                        Console.WriteLine();

                        bSuccess = Utilities.WriteSessionArchive(sFilename, oAllSessions.ToArray(), sPassword, false);
                    }
                    finally
                    {
                        Monitor.Exit(oAllSessions);
                    }

                    WriteCommandResponse( bSuccess ? ("Wrote: " + sFilename) : ("Failed to save: " + sFilename) );
                }
                catch (Exception eX)
                {
                    Console.WriteLine("Save failed: " + eX.Message);
                }
            }
    #endif
            public static bool InstallCertificate()
            {
                if (!CertMaker.rootCertExists())
                {
                    if (!CertMaker.createRootCert())
                        return false;

                    if (!CertMaker.trustRootCert())
                        return false;
                }

                return true;
            }

            public static bool UninstallCertificate()
            {
                if (CertMaker.rootCertExists())
                {
                    if (!CertMaker.removeFiddlerGeneratedCerts(true))
                        return false;
                }
                return true;
            }

            private static void WriteSessionList(List<Fiddler.Session> oAllSessions)
            {
                ConsoleColor oldColor = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Session list contains...");
                try
                {
                    Monitor.Enter(oAllSessions);
                    foreach (Session oS in oAllSessions)
                    {
                        Console.Write(String.Format("{0} {1} {2}\n{3} {4}\n\n", oS.id, oS.oRequest.headers.HTTPMethod, Ellipsize(oS.fullUrl, 60), oS.responseCode, oS.oResponse.MIMEType));
                    }
                }
                finally
                {
                    Monitor.Exit(oAllSessions);
                }
                Console.WriteLine();
                Console.ForegroundColor = oldColor;
            }

            private static bool setMachineTrust(X509Certificate2 oRootCert)
            {
                try
                {
                    // X509Certificate2 cert = X509Certificate2("C:\Path\my.pfx", "password");

                    X509Store certStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
                    certStore.Open(OpenFlags.ReadWrite);

                    try
                    {
                        certStore.Add(oRootCert);
                    }
                    finally
                    {
                        certStore.Close();
                    }
                    return true;
                }
                catch (Exception eX)
                {
                    return false;
                }
            }

            static void Main(string[] args)
            {
                List<Fiddler.Session> oAllSessions = new List<Fiddler.Session>();

                // <-- Personalize for your Application, 64 chars or fewer
                Fiddler.FiddlerApplication.SetAppDisplayName("FiddlerCoreDemoApp");
                #region AttachEventListeners

                // Simply echo notifications to the console.  Because Fiddler.CONFIG.QuietMode=true 
                // by default, we must handle notifying the user ourselves.
                Fiddler.FiddlerApplication.OnNotification += delegate(object sender, NotificationEventArgs oNEA) { Console.WriteLine("** NotifyUser: " + oNEA.NotifyString); };
                Fiddler.FiddlerApplication.Log.OnLogString += delegate(object sender, LogEventArgs oLEA) { Console.WriteLine("** LogString: " + oLEA.LogString); };

                Fiddler.FiddlerApplication.BeforeRequest += delegate(Fiddler.Session oS)
                {
                    oS.bBufferResponse = true;

                    //Monitor.Enter(oAllSessions);
                    //oAllSessions.Add(oS);
                    //Monitor.Exit(oAllSessions);
                    //oS["X-AutoAuth"] = "(default)";

                    //if ((oS.oRequest.pipeClient.LocalPort == iSecureEndpointPort) && (oS.hostname == sSecureEndpointHostname))
                    {
                        // oSession.oRequest["NewHeaderName"] = "New header value";
                        // oS.oResponse.headers.Add("headerName", "\nThis is new header!!!\n").ToString()

                        //oS.utilCreateResponseAndBypassServer();
                        //oS.oResponse.headers.Add("headerName", "This is new header!!!\n");

                        //oS.oResponse.headers.SetStatus(200, "Ok");
                        //oS.oResponse["Content-Type"] = "text/html; charset=UTF-8";
                        //oS.oResponse["Cache-Control"] = "private, max-age=0";

                        // oS.utilSetResponseBody("fazlija ");

                        //oS.utilSetResponseBody("<html><body>Response for https://" + sSecureEndpointHostname + ":" + iSecureEndpointPort.ToString() + " received. Your response was:<br /><plaintext>" + oS.oRequest.headers.ToString());
                        // oS.oResponse["NewHeaderName"] = "\nNew header value";
                        // oSession.oResponse.headers.Remove("Set-Cookie");
                    //}
                };

                // Fiddler.FiddlerApplication.OnReadResponseBuffer += new EventHandler<RawReadEventArgs>(FiddlerApplication_OnReadResponseBuffer);
                Fiddler.FiddlerApplication.BeforeResponse += delegate(Fiddler.Session oS)
                {
                    Console.WriteLine("{0}:HTTP {1} for {2}", oS.id, oS.responseCode, oS.fullUrl);
                     oS.utilDecodeResponse(); oS.utilReplaceInResponse("Amazon", "Something");
                };

                Fiddler.FiddlerApplication.AfterSessionComplete += delegate(Fiddler.Session oS)
                {
                    //Console.WriteLine("Finished session:\t" + oS.fullUrl); 
                    Console.Title = ("Session list contains: " + oAllSessions.Count.ToString() + " sessions");
                };

                Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);
                #endregion AttachEventListeners

                string sSAZInfo = "NoSAZ";

                Console.WriteLine(String.Format("Starting {0} ({1})...", Fiddler.FiddlerApplication.GetVersionString(), sSAZInfo));

                Fiddler.CONFIG.IgnoreServerCertErrors = false;

                FiddlerApplication.Prefs.SetBoolPref("fiddler.network.streaming.abortifclientaborts", true);

                // For forward-compatibility with updated FiddlerCore libraries, it is strongly recommended that you 
                // start with the DEFAULT options and manually disable specific unwanted options.
                FiddlerCoreStartupFlags oFCSF = FiddlerCoreStartupFlags.Default;

                // NOTE: In the next line, you can pass 0 for the port (instead of 8877) to have FiddlerCore auto-select an available port
                int iPort = 8879;
                Fiddler.FiddlerApplication.Startup(iPort, oFCSF);

                FiddlerApplication.Log.LogFormat("Created endpoint listening on port {0}", iPort);

                FiddlerApplication.Log.LogFormat("Starting with settings: [{0}]", oFCSF);
                FiddlerApplication.Log.LogFormat("Gateway: {0}", CONFIG.UpstreamGateway.ToString());

                Console.WriteLine("Hit CTRL+C to end session.");

                // We'll also create a HTTPS listener, useful for when FiddlerCore is masquerading as a HTTPS server
                // instead of acting as a normal CERN-style proxy server.
                oSecureEndpoint = FiddlerApplication.CreateProxyEndpoint(iSecureEndpointPort, true, sSecureEndpointHostname);
                if (null != oSecureEndpoint)
                {
                    FiddlerApplication.Log.LogFormat("Created secure endpoint listening on port {0}, using a HTTPS certificate for '{1}'", iSecureEndpointPort, sSecureEndpointHostname);
                }

                bool bDone = false;
                do
                {

                } while (!bDone);
            }


            /// <summary>
            /// This callback allows your code to evaluate the certificate for a site and optionally override default validation behavior for that certificate.
            /// You should not implement this method unless you understand why it is a security risk.
            /// </summary>
            static void CheckCert(object sender, ValidateServerCertificateEventArgs e)
            {
                if (null != e.ServerCertificate)
                {
                    Console.WriteLine("Certificate for " + e.ExpectedCN + " was for site " + e.ServerCertificate.Subject + " and errors were " + e.CertificatePolicyErrors.ToString());

                    if (e.ServerCertificate.Subject.Contains("fiddler2.com"))
                    {
                        Console.WriteLine("Got a certificate for fiddler2.com. We'll say this is also good for any other site, like https://fiddlertool.com.");
                        e.ValidityState = CertificateValidity.ForceValid;
                    }
                }
            }


            /*
            // This event handler is called on every socket read for the HTTP Response. You almost certainly don't want
            // to add a handler for this event, but the code below shows how you can use it to mess up your HTTP traffic.
            static void FiddlerApplication_OnReadResponseBuffer(object sender, RawReadEventArgs e)
            {
                // NOTE: arrDataBuffer is a fixed-size array. Only bytes 0 to iCountOfBytes should be read/manipulated.
                //
                // Just for kicks, lowercase every byte. Note that this will obviously break any binary content.
                for (int i = 0; i < e.iCountOfBytes; i++)
                {
                    if ((e.arrDataBuffer[i] > 0x40) && (e.arrDataBuffer[i] < 0x5b))
                    {
                        e.arrDataBuffer[i] = (byte)(e.arrDataBuffer[i] + (byte)0x20);
                    }
                }
                Console.WriteLine(String.Format("Read {0} response bytes for session {1}", e.iCountOfBytes, e.sessionOwner.id));
            }
            */

            /// <summary>
            /// When the user hits CTRL+C, this event fires.  We use this to shut down and unregister our FiddlerCore.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
            {
                DoQuit();
            }

            /*
            public static bool InstallCertificate()
            {
                if (!CertMaker.rootCertExists())
                {
                    if (!CertMaker.createRootCert())
                        return false;

                    if (!CertMaker.trustRootCert())
                        return false;



                    App.Configuration.UrlCapture.Cert =
                        FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.bc.cert", null);
                    App.Configuration.UrlCapture.Key =
                        FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.bc.key", null);
                }

                return true;
            }
            public static bool UninstallCertificate()
            {
                if (CertMaker.rootCertExists())
                {
                    if (!CertMaker.removeFiddlerGeneratedCerts(true))
                        return false;
                }
                App.Configuration.UrlCapture.Cert = null;
                App.Configuration.UrlCapture.Key = null;
                return true;
            }

             */
        }
    }
tonni
  • 1,155
  • 3
  • 16
  • 34

1 Answers1

2

Your current code is decompressing every single HTTP response and trying to treat it as a HTML document. That will obviously be slow and fail for everything that isn't HTML (images, etc).

Instead, you should only do your processing if oS.oResponse.headers.ExistsAndContains("text/html"). You don't need to manually call utilDecodeResponse as the GetResponseBodyAsString will handle that for you. And rather than using whatever HtmlDocument is, you might instead consider performing a string replacement, e.g. replacing </head> with <script src=whatever.js /></head>.

Lord Zsolt
  • 6,086
  • 9
  • 41
  • 68
EricLaw
  • 54,427
  • 7
  • 140
  • 182
  • thnx i try that but still is very slow ... i use your "SampleApp" -> Demo project & just uncoment FiddlerCoreStartupFlags to Default and run -> and it very slow! (Fiddler work very speed but fiddlercore are slow -> should some setting need to be configure, to work speed?) – tonni Oct 26 '14 at 09:43
  • "slow" doesn't give me anything to go on. What *specifically* is "slow"? What sort of timings are you encountering? – EricLaw Oct 27 '14 at 14:57
  • it take about 10 seconds to load amazon.com -> specifically i just Uncomenent FiddlerCoreStartupFlags.Default and in beforerequest set oS.bBufferResponse = true; – tonni Oct 27 '14 at 15:26
  • Your code still tries to decode everything and treat it as HTML, and it still causes buffering of every response. Why? – EricLaw Oct 27 '14 at 20:25
  • sorry it's seems that i put old code (take a look now i put new one -> which seems to work fine, thnx), what exact 2 parameters take ExistsAndContains method? – tonni Oct 27 '14 at 22:07
  • 1
    ExistsAndContains takes a header name, and the string you are attempting to find within that header. You can find this information, among other places, in the FiddlerScript Editor's Class View sidebar. – EricLaw Oct 28 '14 at 14:08