Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@Noitidart Noitidart commented Sep 21, 2015

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
You can’t perform that action at this time.