宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。
以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
FireMobileSimulatorでのローカルプロキシ実装の試みを見て、UxUでデバッグ用ローカルプロキシみたいな事をできるようにしてみたい、と思った。
ただ、HTTPのことはこれっぽっちも分からない。ソケット通信も、一応独自プロトコルっぽいものを使って別プロファイルで動作中のFirefoxからテスト結果を受け取るということはできるようになったけど、それ以上の事は分かってないまま。なので、まじめにローカルプロキシを立てる以外の方法で、「特定のURIにアクセスしようとした時だけ、あらかじめ定義しておいたルールに従って別のリソースを返す」ということをできるようにしてみようと考えた。
僕が現在把握している方法としては、以下の物がある。
http-on-modify-request
イベントのタイミングでリダイレクトするやり方。shouldLoad()
の中でリダイレクトするやり方。1はthorikawaさんが頑張っておいでなので、それに期待している(ソースがGPL互換ならUxUにそのまま入れられるので)。他人のフンドシ。他力本願。ここでは後の2つについての挫折の経緯だけ書き留めておく。
結論を先に言っておくと、2も3も実装上の制限により全滅っぽい。やはり、ローカルプロキシをちゃんと実装するしか完全な解決策はないようだ。thorikawaさん期待age。
http-on-modify-request
イベントを使うやり方これは、現在FireMobileSimulator等ですでに使われている。nsHttpChannelはリクエストを送信する前にnsIObserverServiceを通じてhttp-on-modify-request
イベントを、レスポンスが返ってきた後にhttp-on-examine-response
とかhttp-on-examine-merged-response
とかhttp-on-examine-cached-response
とかのイベントを通知する(SubjectはnsHttpChannel自身)ので、このタイミングでリダイレクトをしようという話。
FireMobileSimulatorの場合、nsHttpChannelの現在の通信をキャンセルした上で、nsHttpChannelからnsIWebNavigationを引っ張ってきてloadURI()
で新しく通信を始めてる。
var redirected = 'http://www.example.com/';
var observer = {
observe : function(aSubject, aTopic, aData) {
if (aTopic == 'http-on-modify-request') {
var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel)
.QueryInterface(Ci.nsIRequest);
// ここでキャンセル
httpChannel.cancel(Components.results.NS_ERROR_FAILURE);
// リクエストし直し
httpChannel.notificationCallbacks
.getInterface(Ci.nsIWebNavigation)
.loadURI(redirected, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
}
};
Cc['@mozilla.org/observer-service;1']
.getService(Ci.nsIObserverService)
.addObserver(observer, 'http-on-modify-request', false);
通常のbrowserやiframeの場合はこれでいいんだけど、img要素やXMLHttpRequestからの通信では失敗する。具体的にはhttpChannel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
の時点でエラーになる。例えばXMLHttpRequestによる通信なら、httpChannel.notificationCallbacks.getInterface(Ci.nsIXMLHttpRequest)
とすれば元のリクエストを取得できるので、こんな感じで場合に応じて再リクエストの方法を振り分けてやる必要がある。(XMLHttpRequestの場合については、どうやれば再リクエストできるのかまではたどり着けてない。もしかしたら無理なのかも。)
spec
を書き換える場合nsIChannelのURI
プロパティはnsIURIインターフェースの読み取り専用プロパティなので、通信先のURIを書き換えることはできない……ように見える。でも実はnsIURIのspec
プロパティの方は書き換え可能なので、ここに新しいURIを代入することででもリダイレクトできてしまう。
observe : function(aSubject, aTopic, aData) {
if (aTopic == 'http-on-modify-request') {
var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
httpChannel.URI.spec = redirected;
}
とはいえこの方法は全くお勧めできない。動く場合もあるけど、動かない場合もある、という感じで実に不安定だ。nsHttpChannelの実装を見れば分かるけど、http-on-modify-request
が通知された段階ですでにその時のリクエスト先URIに基づいた初期化処理がいくつか終わってしまっているので、それと矛盾するURIを設定する(例えば、HTTPのリクエストだった物をFile URLにリダイレクトするとか)と、この後の内部処理でエラーが起こるっぽい。
http-on-examine-response
の方ではnsIHttpChannelのsetResponseHeader()
を利用できるので、Location
ヘッダにURIを設定してみたんだけど、ダメだった。残念ながらヘッダを解釈してくれなかった。
nsHttpChannelの実装を見たら、HTTPのステータスコードが301とか302とかの時だけLocation
ヘッダを見るようになってた。ステータスコードを保持しているプロパティは読み取り専用なので、強制的にLocation
ヘッダを読ませるということはできそうにない。
shouldLoad()
を使うやり方これはURNサポートなどで実際に使っている。詳しい方法は2007年当時のエントリに書いてあるけど、要約するとこういうことだ。
やってみると、一見上手く動いてくれてるように見えるんだけど、XPConnect特権がある実行コンテキストで作成したImageやnsIXMLHttpRequestのインスタンスからの通信が捕捉されなくて、UxUの用途では使えない感じだった。まあ仮に捕捉できたところで、元の通信を止めて別のURIで通信するようにさせる方法は分かってない(もしかしたら無いかもしれない)んだけど。
あと、shouldLoad()
の第2引数はnsIURIインターフェースなのでこれのspec
プロパティを書き換えたらどうだろう?と思ってやってみたけど、これもうまくいかなかった。browser要素等での読み込みの場合だと、nsDocShellのInternalLoad()
でエラーが発生する。実装を見た感じでは、nsIContentPolicyに処理が渡ってくるより前に元のURIに基づいてセキュリティ関係の機能が初期化されてるので、その後でURIを書き換えたのがいけなかったんじゃないかと思う。
とにかく、nsHttpChannelのインスタンスが作成された後からどうこうしようと思うのがそもそも手遅れくさい。それより前のステップでアクセス先のURIを書き換えようと思ったら、ローカルプロキシを立てる以外に手は無いようだ。
いいかげん諦めてMozilla Partyの発表資料作ることにします……
先日の僕のFirefoxアドオン(XPCOM)でHTTPプロクシを実装するの記事の発展系として、piroさんがローカルプロキシっぽいことをローカルプロキシを立てずにやろうとして挫折したことのまとめというすばらしくためになる記事を書かれています。 この記事の中でpiroさんは「特定
はじめまして。
Addon開発の初心者です。
最近 Add-on Builderで拡張機能を開発するようになりました。
ある特定のURL(パラメータを含む)のHTTPリクエストを少し書き換えたくてこのページを参考にさせていただきながら、URLを書き換える拡張機能を作っているところです。
基本的に URLの書き換えは成功しているようで次の3パターン
・ロケーションバーへの直接入力
・リンクを右クリック → リンクを新しいタブで開く
・リンクを右クリック → リンクを新しいウィンドウで開く
は正常に書き換え後のURLでページが表示されます。
しかし、リンクを左クリックした場合は書き換え後のURLでページが表示されません。
正確には、書き換え後のURLで HTTPリクエストは発行されるもののページが更新されないという状態です。
httpChannel.cancel(Components.results.NS_ERROR_FAILURE); でページ更新もキャンセルされてしまっているかもしれないと思い
httpChannel.cancel(Components.results.NS_OK); を試してみましたが、こちらはページの読み込みが完了しませんでした。
環境は Firefox 13.0.1 です。
お忙しいとは存じますが、問題解決のためのヒントを教えていただけないでしょうか。
どうぞ、よろしくお願いいたします。
の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2009-05-28_proxy.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。
writeback message: Ready to post a comment.