FiddlerScript CookBook
You can add a lot of power to Fiddler by updating its JScript.NET CustomRules.js
file. To add rules to Fiddler, choose Customize Rules on Fiddler's
Rules menu. Enter code inside the suggested function and save the
file. Fiddler will automatically reload the rules.
Tips and Tricks
-
If you "break" your CustomRules.js file: delete the CustomRules.js
file in your \Documents\Fiddler2\Scripts\ folder. The next time Fiddler
starts, the default rules will be used to recreate your
CustomRules.js file. Fiddler's default rules are stored in \Program
Files\Fiddler2\Scripts\SampleRules.js.
-
You can change the JScript editor launched from the Rules menu.
Click Tools | Fiddler Options and edit the Editor
string. The Fiddler Script editor is a good choice.
Learn more...
-
You can use additional .NET assemblies in your script. Click
Tools | Fiddler Options and edit the References list on the
Extensions tab. You will
also need to
either register the assembly in the GAC or drop it in the Fiddler.exe folder.
Update the #import clause at the top of the JScript with the correct
namespaces in order to use the new assembly's functions without fully-qualifying
them.
Goal: Add custom columns to the UI See
UI Column Configuration.
Goal: Modify Request or Response
Add a request header |
oSession.oRequest["NewHeaderName"] = "New header value"; |
OnBeforeRequest |
Delete a response header |
oSession.oResponse.headers.Remove("Set-Cookie"); |
OnBeforeResponse |
Change a request for one page to a
different page on the same server |
if (oSession.PathAndQuery=="/version1.css") {
oSession.PathAndQuery="/version2.css";
} |
OnBeforeRequest |
Point all requests for one server to the
same port on a different server |
if (oSession.HostnameIs("www.bayden.com")) {
oSession.hostname="test.bayden.com";
} |
OnBeforeRequest |
Point all requests for one port to a
different port on a different server |
if (oSession.host=="www.bayden.com:8080") {
oSession.host="test.bayden.com:9090";
} |
OnBeforeRequest |
Point all requests for one server to a different server,
including HTTPS tunnels |
//
Redirect
traffic,
including
HTTPS
tunnels
if (oSession.HTTPMethodIs("CONNECT")
&& (oSession.PathAndQuery
==
"www.example.com:443"))
{
oSession.PathAndQuery
=
"beta.example.com:443";
}
if (oSession.HostnameIs("www.example.com"))
oSession.hostname
=
"beta.example.com";
|
OnBeforeRequest |
Simulate the Windows HOSTS file, by
pointing one Hostname to a different IP address.
(Retargets without changing the request's Host header) |
// All requests for subdomain.example.com should be directed to the
development server at 128.123.133.123
if
(oSession.HostnameIs("subdomain.example.com")){
oSession.bypassGateway
=
true;
// Prevent this request from going through an upstream proxy
oSession["x-overrideHost"]
=
"128.123.133.123";
// DNS name or IP address of target server
}
|
OnBeforeRequest |
Retarget requests for a single page to a
different page, potentially on a different server.
(Retargets by changing the request's Host header) |
if (oSession.url=="www.example.com/live.js") {
oSession.url = "dev.example.com/workinprogress.js";
} |
OnBeforeRequest |
Prevent upload of HTTP Cookies |
oSession.oRequest.headers.Remove("Cookie"); |
OnBeforeRequest |
Decompress and unchunk a HTTP response, updating
headers if needed |
// Remove any compression or chunking from the response in order to make
it easier to manipulate oSession.utilDecodeResponse(); |
OnBeforeResponse |
Search and replace in HTML. |
if (oSession.HostnameIs("www.bayden.com") && oSession.oResponse.headers.ExistsAndContains("Content-Type","text/html")){ oSession.utilDecodeResponse();
oSession.utilReplaceInResponse('<b>','<u>'); } |
OnBeforeResponse |
Case insensitive Search of response HTML. |
if (oSession.oResponse.headers.ExistsAndContains("Content-Type", "text/html")
&& oSession.utilFindInResponse("searchfor", false)>-1){
oSession["ui-color"] = "red"; } |
OnBeforeResponse |
Remove all DIV tags (and content inside
the DIV tag) |
//
If
content-type
is
HTML,
then
remove
all
DIV
tags
if
(oSession.oResponse.headers.ExistsAndContains("Content-Type",
"html")){
//
Remove
any
compression
or
chunking
oSession.utilDecodeResponse();
var oBody
= System.Text.Encoding.UTF8.GetString(oSession.responseBodyBytes);
//
Replace
all
instances
of
the
DIV
tag
with
an
empty
string
var oRegEx
=
/<div[^>]*>(.*?)<\/div>/gi;
oBody = oBody.replace(oRegEx,
"");
//
Set
the
response
body
to
the
div-less
string
oSession.utilSetResponseBody(oBody);
} |
onBeforeResponse |
Pretend your browser is the GoogleBot webcrawler |
oSession.oRequest["User-Agent"]="Googlebot/2.X
(+http://www.googlebot.com/bot.html)"; |
OnBeforeRequest |
Request Hebrew content |
oSession.oRequest["Accept-Language"]="he";
|
OnBeforeRequest |
Deny .CSS requests |
if (oSession.uriContains(".css")){
oSession["ui-color"]="orange"; oSession["ui-bold"]="true";
oSession.oRequest.FailSession(404, "Blocked", "Fiddler blocked CSS
file");
} |
OnBeforeRequest |
Simulate HTTP Basic authentication
(Requires user to enter a password before displaying web content.) |
if ((oSession.HostnameIs("www.example.com"))
&&
!oSession.oRequest.headers.Exists("Authorization"))
{
// Prevent IE's "Friendly Errors Messages" from hiding the error message
by making response body longer than 512 chars.
var oBody = "<html><body>[Fiddler] Authentication
Required.<BR>".PadRight(512, ' ') + "</body></html>";
oSession.utilSetResponseBody(oBody);
// Build up the headers
oSession.oResponse.headers.HTTPResponseCode
= 401;
oSession.oResponse.headers.HTTPResponseStatus
= "401 Auth Required";
oSession.oResponse["WWW-Authenticate"]
= "Basic realm=\"Fiddler (just hit Ok)\"";
oResponse.headers.Add("Content-Type", "text/html");
} |
OnBeforeResponse |
Respond to a request with
a file loaded from the \Captures\Responses folder |
if (oSession.PathAndQuery=="/version1.css") {
oSession["x-replywithfile"] ="version2.css";
} |
OnBeforeRequest
or
OnBeforeResponse |
Your rule here? |
Request or submit a new rule using the CONTACT link
above |
Thanks! |
Scenario: Performance-Testing
Simulate modem uploads |
// Delay sends by 300ms per KB uploaded.
oSession["request-trickle-delay"] = "300"; |
OnBeforeRequest |
Simulate modem downloads |
// Delay receives by 150ms per KB downloaded.
oSession["response-trickle-delay"] = "150"; |
OnBeforeResponse |
Flag content which isn't set to cache on the client. |
if (!(oSession.oResponse.headers.Exists("Expires")
|| (oSession.oResponse.headers.ExistsAndContains("Cache-Control",
"age")))
|| (oSession.oResponse.headers.Exists("Vary"))){ {
oSession["ui-color"]="brown"; // Use C# color strings
here. oSession["ui-italic"]="true";
} |
OnBeforeResponse |
Display in the "Custom Column" the number of
milliseconds from the moment of the request until the last byte was
received. |
oSession["ui-customcolumn"] =
oSession["X-TTLB"]; |
onBeforeResponse |
Display the # of milliseconds until the First Byte
was received from the server, followed by the # of ms until the Last
Byte. |
oSession["ui-customcolumn"] = "FB: " + oSession["X-TTFB"] + ";
LB: " + oSession["X-TTLB"]; |
onBeforeResponse |
Add a CopyTimers context menu item to the
Session List |
public
static
ContextAction("CopyTimers")
function
CopyTimers(oSessions:
Fiddler.Session[]){
if
(null
==
oSessions){
MessageBox.Show("Please
select
sessions
to
copy timers for.",
"Nothing
to
Do");
return;
}
var
s:
System.Text.StringBuilder
=
new
System.Text.StringBuilder();
for
(var
x
=
0;
x
<
oSessions.Length;
x++)
{
s.AppendFormat("{0},
{1},
{2},
{3},
{4},
{5},
{6},
{7}\r\n",
oSessions[x].Timers.ClientConnected,
oSessions[x].Timers.ClientDoneRequest,
oSessions[x].Timers.ServerConnected,
oSessions[x].Timers.ServerGotRequest,
oSessions[x].Timers.ServerBeginResponse,
oSessions[x].Timers.ServerDoneResponse,
oSessions[x].Timers.ClientBeginResponse,
oSessions[x].Timers.ClientDoneResponse
);
}
Utilities.CopyToClipboard(s.ToString());
MessageBox.Show("Done.");
}
|
Global scope |
Miscellaneous rules
Display in the "Custom Column" the time of the
original request |
oSession["ui-customcolumn"]
+=
DateTime.Now.ToString("h:mm:ss.ffff
"); |
onBeforeRequest |
Show any Set-Cookie headers in Custom column
in Session list. |
oSession["ui-customcolumn"] = oSession.oResponse["Set-Cookie"]; |
OnBeforeResponse |
Mark any requests which send cookies in red, and show
the value in the Custom column. Otherwise, mark request in
green. |
if (oSession.oRequest.headers.Exists("Cookie"))
{
oSession["ui-color"]="red";
oSession["ui-customcolumn"] = oSession.oRequest["Cookie"];
}
else
oSession["ui-color"]="green"; |
OnBeforeRequest |
Hide requests for .GIFs from the session list |
if (oSession.url.EndsWith(".gif")){
oSession["ui-hide"]="hiding image requests";
//String value not important
} |
OnBeforeRequest |
Hide completed responses which returned images |
if (oSession.oResponse.headers.ExistsAndContains("Content-Type",
"image/")) { oSession["ui-hide"] = "hiding
images"; // String value not important
} |
OnBeforeResponse |
Hide requests to domains except those I care about |
if (!oSession.HostnameIs("domainIcareabout.com")){
oSession["ui-hide"] = "hiding boring domains"; //
String value not important
} |
OnBeforeRequest |
Unhide any response which returned a 404 |
if (oSession.responseCode == 404){ oSession.oFlags.Remove("ui-hide");
}
|
OnBeforeResponse |
Play a sound when a file is missing. Note the name "Notify" is
a standard Windows system sound entry. |
if (oSession.responseCode == 404){
FiddlerObject.playSound("Notify");
oSession["uiikeout"]="true";
} |
OnBeforeResponse |
Show HTTP POST bodies in a messagebox |
var
oBodyString =
System.Text.Encoding.UTF8.GetString(oSession.requestBodyBytes);
if (oBodyString.Length
> 0)
FiddlerObject.alert(oBodyString); |
OnBeforeRequest |
Flag all pages in which the server sends a cookie |
if (oSession.oResponse.headers.Exists("Set-Cookie")
||
oSession.utilDecodeResponse(); oSession.utilFindInResponse("document.cookie", false)>-1 ||
oSession.utilFindInResponse('HTTP-EQUIV="Set-Cookie"', false)>-1){
oSession["ui-color"]="purple";
} |
OnBeforeResponse |
Pause all HTTP POSTs to allow hand-editing (the POST
verb is often used for submitting forms) |
if (oSession.HTTPMethodIs("POST")){
oSession["x-breakrequest"]="breaking for POST"; } |
OnBeforeRequest |
Pause all HTTP POSTs that contain thekeyword |
if
(oSession.HTTPMethodIs("POST")
&&
(oSession.utilFindInRequest("thekeyword",
true)
>
-1)){
oSession["x-breakrequest"]
=
"keyword";
}
|
OnBeforeRequest |
Pause a request for an XML file to allow hand-editing |
if (oSession.url.toLowerCase().indexOf(".xml")>-1){
oSession["x-breakrequest"]="reason_XML";
} |
OnBeforeRequest |
Pause a response containing JavaScript to allow hand-editing |
if (oSession.oResponse.headers.ExistsAndContains("Content-Type",
"javascript")){ oSession["x-breakresponse"]="reason
is JScript";
} |
OnBeforeResponse |
Crawl Sequential URLs |
public
static
ToolsAction("Crawl
Sequential
URLs")
function
doCrawl(){
var
sBase:
String;
var
sInt:
String;
sBase
=
FiddlerObject.prompt("Enter
base
URL
with
##
in
place
of
the
start
integer",
"http://www.example.com/img##.jpg");
sInt
=
FiddlerObject.prompt("Start
At",
"1");
var
iFirst
=
int.Parse(sInt);
sInt
=
FiddlerObject.prompt("End
At",
"12");
var
iLast
=
int.Parse(sInt);
for
(var
x=iFirst;
x<=iLast;
x++)
{
//Replace
's'
with
your
HTTP
Request.
Note:
\
is
a
special
character
in
JScript
//
If
you
want
to
represent
a
backslash
in
a
string
constant,
double
it
like
\\
var
s
=
"GET
"
+
sBase.Replace("##",
x.ToString())
+
"
HTTP/1.0\r\n\r\n";
var
b=false;
while(!b){
try{
FiddlerObject.utilIssueRequest(s);
b=true;
}
catch(e){
var
iT
=
Environment.TickCount
+
10000;
FiddlerObject.StatusText
=
"Waiting
10
sec
because
we
have
too
many
requests
outstanding...";
while
(iT
>
Environment.TickCount){
Application.DoEvents();
} }
} } }
|
Global scope |
Log sessions to a local database |
Note: The
following example relies upon OLEDB 4.0 which is not
available for 64bit processes. You can either select
another data provider (e.g. SQLServer) or you can
force
Fiddler to run in 32bit mode.
Add the following to the Rules file to
create a new menu item.
// Log the currently selected
sessions in the list to a database.
// Note: The DB must already exist and you must
have permissions to write to it.
public static ToolsAction("Log Selected
Sessions")
function DoLogSessions(oSessions:
Fiddler.Session[]){
if (null == oSessions || oSessions.Length < 1){
MessageBox.Show("Please select some sessions
first!");
return;
}
var strMDB = "C:\\log.mdb";
var cnn = null;
var sdr = null;
var cmd = null;
try
{
cnn = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
+ strMDB);
cnn.Open();
cmd = new OleDbCommand();
cmd.Connection = cnn;
for (var x = 0; x < oSessions.Length; x++){
var strSQL = "INSERT into
tblSessions ([ResponseCode],[URL])
Values (" +
oSessions[x].responseCode + ", '" +
oSessions[x].url + "')";
cmd.CommandText = strSQL;
cmd.ExecuteNonQuery();
}
}
catch (ex){
MessageBox.Show(ex);
}
finally
{
if (cnn != null ){
cnn.Close();
}
}
} |
Note: To use the Database
Objects in Fiddler 2.3.9 and below, you'll need to add
system.data to the References list
inside Tools | Fiddler Options |
Extensions | Scripting.
In 2.3.9.1 and later, this reference will occur
automatically.
Then,
list the new import at the top of your rules script:
import System.Data.OleDb; |
|
|
|
|
Global scope |
Search for a target string on a series of
successively named HTML pages (e.g. find first page containing "TargetString"
from 1.htm, 2.htm, 3.htm, 4.htm, etc). |
Add the following to the Rules file to
create a new menu item.
public static ToolsAction("Find
page containing search string")
function doGrab(){
var s = "GET /gallery/image1.htm HTTP/1.1\r\nHost:
www.example.com\r\nX-My-Num: 1\r\n\r\n";
try{
FiddlerObject.utilIssueRequest(s);
}
catch(e){
MessageBox.Show("send failed" + e.ToString());
}
} |
Now, this will make the first request. The trick is that you
really want to look at the ~result~ of that request.
This is sorta tricky because the utilIssueRequest call
is asynchronous, and so you don't get the response
directly.
But, remember that Fiddler rules run on each response,
so you can then put code in the OnBeforeResponse handler
that looks like...
if (oSession.oRequest.headers.Exists("X-My-Num")){
// This is a response to my Grab code...
if (oSession.utilFindInResponse("targetstring",
false) > -1){
// If the index of the target string is >-1, we found the search
string...
MessageBox.Show("Found target string!");
}
else
{
//didn't find the target string. increment
the number.
var n = int.parse(oSession.oRequest["X-My-Num"]);
n++;
var s = "GET
/gallery/image" + n.ToString() +
".htm HTTP/1.1\r\nHost:
http://www.example.com\r\nX-My-Num:
"+ n.ToString()
+ "\r\n\r\n";
try{ // Make a recursive HTTP request for the next item.
FiddlerObject.utilIssueRequest(s);
}
catch(e){
MessageBox.Show("send failed" + e.ToString());
}
}
} |
|
|
|
|
Global scope & onBeforeResponse |
Save selected response files to disk in the Fiddler
Captures folder (useful for copying online photo albums. |
public static ContextAction("Dump Files")
function DoDumpFiles(oSessions: Fiddler.Session[]){
if (null == oSessions){
MessageBox.Show("Please select sessions to dump.", "Nothing
to Do");
return;
}
for (var x = 0; x < oSessions.Length; x++){
oSessions[x].SaveResponseBody();
}
MessageBox.Show("Done.");
} |
Global scope |
Search all text responses for a list of strings. |
oSession.utilDecodeResponse();
// Create
a array
of strings
we're looking
for.
var oFindStrings
= new
Array(
"XMLHttp",
"onreadystatechange",
"readyState",
"responseBody",
"responseText",
"responseXML",
"statusText",
"abort",
"getAllResponseHeaders",
"getResponseHeader",
"setRequestHeader");
// For
each target
string, check
the response
to see
if it's
present.
var iEach=0;
oSession["ui-customcolumn"]=String.Empty;
for (iEach;
iEach<oFindStrings.length;
iEach++){
if
(oSession.utilFindInResponse(oFindStrings[iEach],
false)>0) {
oSession["ui-color"]="purple";
oSession["ui-customcolumn"]
+= oFindStrings[iEach]+";
";
} } |
OnBeforeRequest |
Bypass the upstream proxy for all requests to a
certain domain (E.g. Emulate the IE Proxy bypass list) |
if (oSession.HostnameIs("www.example.com")){
oSession.bypassGateway = true;
} |
OnBeforeRequest |
Show redirection target Location in Session List |
if
((oSession.responseCode
>
299)
&&
(oSession.responseCode
<
308)){
oSession["ui-customcolumn"]
=
oSession.oResponse["Location"];
}
|
OnBeforeResponse |
Add image size information in a column. Note: you
must add System.drawing.dll inside Tools > Fiddler Options > Extensions
> References |
public
static
BindUIColumn("ImageSize",
60)
function
FillImageSizeColumn(oS:
Session){
if
((oS.oResponse
!=
null)
&&
(oS.oResponse.headers
!=
null))
{
try{
if
(!oS.oResponse.headers.ExistsAndContains("Content-Type",
"image/"))
return
"NotAnImage";
var
oMS:
System.IO.MemoryStream
=
new
System.IO.MemoryStream(oS.responseBodyBytes);
var
i:System.Drawing.Bitmap
=
new
System.Drawing.Bitmap(oMS);
return
(i.Width
+
"
x
"
+
i.Height);
}
catch(e)
{
return
"err";
}
} return
String.Empty;
}
|
Global Scope |
Scenario: Working with menus
Add context-menu item to open currently selected URLs
using Firefox |
public static ContextAction("Open in Firefox")
function DoOpenInIE(oSessions: Fiddler.Session[]){
if (null == oSessions){
MessageBox.Show("Please choose at least 1 session.");
return;
}
for (var x = 0; x < oSessions.Length; x++){
System.Diagnostics.Process.Start("firefox.exe",
oSessions[x].url);
}
} |
Global scope |
Add a submenu to the Rules menu and
create an option in it |
public static RulesOption("Non-Exclusive-Test",
"User-Agent")
var m_UANONRad: boolean = true;
|
Global scope |
To build submenus with mutually exclusive
radio options |
public static
RulesOption("Spoof Netscape &3.0", "User-Agent", true)
var m_NS3: boolean = false;
public static RulesOption("Spoof IE &6.0", "User-Agent", true)
var m_IE6: boolean = false;
public static RulesOption("Spoof nothing", "User-Agent", true)
var m_UANONE: boolean = true; |
Global scope |
To build a submenu with
mutually exclusive radio options, that control a single string variable.
Offers a more compact syntax than the previous alternative. |
RulesString("&SubMenuName",
true)
RulesStringValue(0,"Option1Name",
"Option1Value")
RulesStringValue(1,"Option2Name",
"Option2Value")
RulesStringValue(2,"&Custom...",
"%CUSTOM%")
public
static
var
sTheOptionValue:
String
=
null;
|
Global scope |
Same as previous, but with a default
option pre-selected. |
RulesString("&SubMenuName",
true)
RulesStringValue(0,"Option1Name",
"Option1Value")
RulesStringValue(1,"Option2NameDEFAULT",
"DefaultVal",
true)
RulesStringValue(2,"&Custom...",
"%CUSTOM%")
public
static
var
sTheOptionValue:
String
=
null; |
Global scope |
Add a Tools menu option that resets the
script |
// Force a manual reload of the script file. Resets all
// RulesOption variables to their defaults.
public static ToolsAction("Reset Script")
function DoManualReload(){
FiddlerObject.ReloadScript();
}
|
Global scope |
Add a Tools menu option that clears
all WinINET/IE cookies and cache files |
public static ToolsAction("Reset IE"){
FiddlerObject.UI.actClearWinINETCache();
FiddlerObject.UI.actClearWinINETCookies();
} |
Global scope |
|