サーバに送っているHTTPリクエストのヘッダやレスポンスのヘッダが見られるFirefoxのextension livehttpheaders がどうやってこの機能を実現しているのか調べてみました。
HTTPリクエストを送るタイミングでイベントを捕まえることができるんだろうと予想して探してみるとLiveHTTPHeaders.jsの43行めからのaddToListener()に
function addToListener(obj)
{
... snip ...
// Should be a new version of Mozilla/Phoenix (after september 15, 2003)
var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
observerService.addObserver(obj, "http-on-modify-request", false);
observerService.addObserver(obj, "http-on-examine-response", false);
}
}
という記述がありました。
調べるとnsIObserverServiceでaddObserverを使ってnsIObserverのオブジェクトを登録しておくと、登録したオブジェクトのobserveメソッドでaddObserverの二番目の引数で指定したトピック(firefoxではnotificationの種類をtopicと呼ぶそうです)の通知をうけとることができるそうです。
network:offline-status-changed で試してみます。Execute JSで
var offlineObserver = {
observe: function (aSubject, aTopic, aState) {
Firebug.Console.log( [aSubject, aTopic, aState] );
}
};
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(offlineObserver, "network:offline-status-changed", false);
を実行すると、メニューの[ファイル][オフライン作業]でオンライン/オフラインを切り替えたときに

offlineObserverのobserveが呼び出されてtopicとonline/offlineのステータス情報を受け取ることができます。addObserverに通知を受け取るように登録するのはnsIOberverのオブジェクトと書きましたが、実際にはQueryInterfaceは呼ばれないようなのでobserveメソッドさえ持っていれば何でもかまいません。
observeメソッドの中でnotificationを受け取れるということがわかったのでlivehttpheadersのコードに戻ってきてobserveを探すと
// This is the observerService's observe listener.
observe: function(aSubject, aTopic, aData) {
if (aTopic == 'http-on-modify-request') {
aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
this.onModifyRequest(aSubject);
} else if (aTopic == 'http-on-examine-response') {
aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
this.onExamineResponse(aSubject);
}
},
となっていてtopicをifで場合分けして処理しています。Google Code Searchで類似のコードを探してみると、ひとつのインスタンスで複数のtopicを受け取るようにしてtopicに応じて処理を分けるのが一般的みたいです。
ここから先はちょっと複雑なのでHTTPリクエストを読み出しかただけまとめます。
コールバックでobserveが呼び出されるとき、aSubjectにnsIHttpChannelを持つオブジェクトが渡されます。リクエストヘッダ/レスポンスヘッダはこのオブジェクトを使ってvisitRequestHeaders/visitResponseHeadersで読み出します。
HTTPリクエストのメソッドがPOSTのときにpostする内容は、
channel.QueryInterface(Components.interfaces.nsIUploadChannel);
var us = channel.uploadStream;
us.QueryInterface(Components.interfaces.nsISeekableStream);
var ss = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces. nsIScriptableInputStream);
ss.init(us);
us.seek(0, 0);
var n = ss.available();
var data = ss.read(n);
ss.close();
us.close();
として読み出していました。HTTPレスポンスの内容も基本的には同様の方法で読み出しています。
というわけでlivehttpheadersはnsIObserverServiceを使ってnsIHttpChannelでリクエストが送られるときとレスポンスが帰ってきたときにnotifyされるhttp-on-modify-requestとhttp-on-examine-responseをobserveすることでHTTPリクエストの内容をのぞいていることがわかりました。
http-on-modify-requestとhttp-on-examine-responseはFirefoxでふつうにHTTPのリクエストを出すときに必ず使われるnsIHttpChannelがnotifyしてくれるものなので、これをobserveできるのは非常に強力です。
nsIHttpChannelは、ふつうにFirefoxがHTMLのページを表示したときに生じるHTTPリクエストだけでなく、Firefoxのextensionが生成するHTTPリクエストからも利用されています。その結果、このnotificationをobserveするとjavascriptから呼び出すことができるXMLHttpRequestやGM_xmlhttpRequestはもちろん、Google Toolbarのようなextensionがサーバに送っているリクエストも捕捉できますし、Flashのプラグインも中でnsIHttpChannelを使っているようでFlashがリモートのファイルをHTTPで読みこむのも捕捉することができます。
逆に捕捉されたくないときにはnsISocketTransportServiceを用いて自分でソケットを作成してHTTPリクエストを送ることも可能ですが、その場合FirefoxのProxy設定やキャッシュやクッキー、すべて自分で面倒を見る必要があります。
nsIHttpChannelに関するnotificationは2つですが、ほかにもnotifyしてくれることはないのかなーと思って Google Code Search で探してみるともうひとつありました。
mozilla/netwerk/protocol/http/public/nsIHttpProtocolHandler.idl - Google Code Search
の一番最後に
/**
* The observer of this topic is notified after the received HTTP response
* is merged with data from the browser cache. This means that, among other
* things, the Content-Type header will be set correctly.
*/
#define NS_HTTP_ON_EXAMINE_MERGED_RESPONSE_TOPIC "http-on-examine-merged-response"
というのが定義されています。
ブラウザキャッシュのデータとHTTPレスポンスがマージされるときに呼ばれるとコメントに書かれていますが、何のことかさっぱりわかりません。さらにGoogle Code Searchで調べるとmozilla/netwerk/protocol/http/src/nsHttpHandler.hの
OnExamineMergedResponseでしか参照されておらず、このメソッドは mozilla/netwerk/protocol/http/src/nsHttpChannel.cppの中で呼び出されていて、このメソッドのはじめに
// ok, we've just received a 206
//
// we need to stream whatever data is in the cache out first, and then
// pick up whatever data is on the wire, writing it into the cache.
と書かれていました。http-on-examine-merged-responseは 206 Partial Content を受け取ったときに呼ばれるものみたいです。
使い方のわからないメソッドやインターフェイスはGoogle Code Searchで調べるとわかることがあります。(少なくともC++でどう実装されているかは見ることはできます)
ソースコード検索エンジンにはほかにも Codase - Source Code Search Engine や Koders - Source Code Search Engine がありますが、以前に(半年くらい前)使ってみた感じでは Google Code Search がヒット数が多くレスポンスもよかったためGoogle Code Searchを利用しています。
トラックバック元エントリにこのエントリへのリンクがない場合はトラックバックを受け付けません。
http://labs.gmo.jp/mt/mt-tb.cgi/156
comments