Skip to content

Instantly share code, notes, and snippets.

@kewisch
Created October 2, 2014 14:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kewisch/cc447124497c4b9a4ca4 to your computer and use it in GitHub Desktop.
Save kewisch/cc447124497c4b9a4ca4 to your computer and use it in GitHub Desktop.
Monitor all http(s) network requests using the Mozilla Platform
/* 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;
}
@Noitidart
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment