Created
October 2, 2014 14:20
-
-
Save kewisch/cc447124497c4b9a4ca4 to your computer and use it in GitHub Desktop.
Monitor all http(s) network requests using the Mozilla Platform
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This Source Code Form is subject to the terms of the Mozilla Public | |
* License, v. 2.0. If a copy of the MPL was not distributed with this | |
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
var allRequests = []; | |
/** | |
* Add the following function as a request observer: | |
* Services.obs.addObserver(httpObserver, "http-on-examine-response", false); | |
* | |
* When done listening on requests: | |
* dump(allRequests.join("\n===\n")); // print them | |
* dump(JSON.stringify(allRequests, null, " ")) // jsonify them | |
*/ | |
function httpObserver(aSubject, aTopic, aData) { | |
if (aSubject instanceof Components.interfaces.nsITraceableChannel) { | |
let request = new TracedRequest(aSubject); | |
request._next = aSubject.setNewListener(request); | |
allRequests.push(request); | |
} | |
} | |
/** | |
* This is the object that represents a request/response and also collects the data for it | |
* | |
* @param aSubject The channel from the response observer. | |
*/ | |
function TracedRequest(aSubject) { | |
let httpchannel = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel); | |
let self = this; | |
this.requestHeaders = Object.create(null); | |
httpchannel.visitRequestHeaders({ | |
visitHeader: function(k, v) { | |
self.requestHeaders[k] = v; | |
} | |
}); | |
this.responseHeaders = Object.create(null); | |
httpchannel.visitResponseHeaders({ | |
visitHeader: function(k, v) { | |
self.responseHeaders[k] = v; | |
} | |
}); | |
this.uri = aSubject.URI.spec; | |
this.method = httpchannel.requestMethod; | |
this.requestBody = readRequestBody(aSubject); | |
this.responseStatus = httpchannel.responseStatus; | |
this.responseStatusText = httpchannel.responseStatusText; | |
this._chunks = []; | |
} | |
TracedRequest.prototype = { | |
uri: null, | |
method: null, | |
requestBody: null, | |
requestHeaders: null, | |
responseStatus: null, | |
responseStatusText: null, | |
responseHeaders: null, | |
responseBody: null, | |
toJSON: function() { | |
let j = Object.create(null); | |
for (let m of Object.keys(this)) { | |
if (typeof this[m] != "function" && m[0] != "_") { | |
j[m] = this[m]; | |
} | |
} | |
return j; | |
}, | |
onStartRequest: function(aRequest, aContext) this._next.onStartRequest(aRequest, aContext), | |
onStopRequest: function(aRequest, aContext, aStatusCode) { | |
this.responseBody = this._chunks.join(""); | |
this._chunks = null; | |
this._next.onStopRequest(aRequest, aContext, aStatusCode); | |
this._next = null; | |
}, | |
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) { | |
let binaryInputStream = Components.classes["@mozilla.org/binaryinputstream;1"] | |
.createInstance(Components.interfaces.nsIBinaryInputStream); | |
let storageStream = Components.classes["@mozilla.org/storagestream;1"] | |
.createInstance(Components.interfaces.nsIStorageStream); | |
let outStream = Components.classes["@mozilla.org/binaryoutputstream;1"] | |
.createInstance(Components.interfaces.nsIBinaryOutputStream); | |
binaryInputStream.setInputStream(aStream); | |
storageStream.init(8192, aCount, null); | |
outStream.setOutputStream(storageStream.getOutputStream(0)); | |
let data = binaryInputStream.readBytes(aCount); | |
this._chunks.push(data); | |
outStream.writeBytes(data, aCount); | |
this._next.onDataAvailable(aRequest, aContext, | |
storageStream.newInputStream(0), | |
aOffset, aCount); | |
}, | |
toString: function() { | |
let str = this.method + " " + this.uri; | |
for (let hdr of Object.keys(this.requestHeaders)) { | |
str += hdr + ": " + this.requestHeaders[hdr] + "\n"; | |
} | |
if (this.requestBody) { | |
str += "\r\n" + this.requestBody + "\n"; | |
} | |
str += "\n" + this.responseStatus + " " + this.responseStatusText | |
if (this.responseBody) { | |
str += "\r\n" + this.responseBody + "\n"; | |
} | |
return str; | |
} | |
}; | |
// Taken from: | |
// http://hg.mozilla.org/mozilla-central/file/2399d1ae89e9/toolkit/devtools/webconsole/network-helper.js#l120 | |
function readRequestBody(aRequest, aCharset="UTF-8") { | |
let text = null; | |
if (aRequest instanceof Ci.nsIUploadChannel) { | |
let iStream = aRequest.uploadStream; | |
let isSeekableStream = false; | |
if (iStream instanceof Ci.nsISeekableStream) { | |
isSeekableStream = true; | |
} | |
let prevOffset; | |
if (isSeekableStream) { | |
prevOffset = iStream.tell(); | |
iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); | |
} | |
// Read data from the stream. | |
try { | |
let rawtext = NetUtil.readInputStreamToString(iStream, iStream.available()) | |
let conv = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] | |
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter); | |
conv.charset = aCharset; | |
text = conv.ConvertToUnicode(rawtext); | |
} catch (err) { | |
} | |
// Seek locks the file, so seek to the beginning only if necko hasn't | |
// read it yet, since necko doesn't eek to 0 before reading (at lest | |
// not till 459384 is fixed). | |
if (isSeekableStream && prevOffset == 0) { | |
iStream.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0); | |
} | |
} | |
return text; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I wrote a slightly simpler version here: https://gist.github.com/Noitidart/d0b08629ee2804538ad9#file-_ff-addon-snippet-copyofrequestsource-js-L16
It uses promise so devuser can set up what to do on response ready without having to edit inside
onStopRequest