プロキシサーバーを作る!
表示しているコンテンツを書き換えてみよう
HTTPProxyServerには、取得したコンテンツを書き換える機能が標準搭載されています。HTTPProxyServerの起動オプションであるProxyContentHandlerでHandlerを指定することで、そのHandler内で自由にコンテンツを書き換えることができます。
サンプルとしては地味ですが「Think IT」を「THINK IT」に書き換えるプロキシを作ってみます。サンプルファイルのproxy2.rbのようになります。
Handlerでは2つの引数を受け取ります。1つ目がリクエスト(WEBrick::HTTRequest)、2つ目がレスポンス(WEBrick::HTTPResponse)です。これは「第2回:基本機能の実装にチャレンジ!(http://www.thinkit.co.jp/article/117/2/)」のWEBrickを使ったHTTPサーバーと同じです。このHandlerが呼ばれた時には既にプロキシで取得したコンテンツがレスポンスに設定されています。
本文の文字列を書き換えたい場合は、レスポンスのbodyを書き換えることで実現できます。また今回、書き換えたいのはHTMLファイルだけなので、Content-Typeがtext/htmlで始まるものだけ書き換えるようにします。
以下を実行してみると、タイトルバーやコンテンツ上部の「Think IT」が「THINK IT」に書き換わっているのが分かると思います(図2の右)。
$ ruby proxy2.rb
このまま、Googleで「think it」を検索してみましょう(図2の左)。検索結果のタイトルも「Think IT」が「THINK IT」に置換されています。
同じようにYahoo! Japanでも「think it」も検索してみましょう(図2の中央)。今度は検索結果の「Think IT」がそのまま出ています。なぜか置換が行われていません。
これは、Yahoo! Japanのページではコンテンツが圧縮して送出されているためです。通常Webブラウザではそれを自動で解凍して表示しているのですが、プロキシの時点では圧縮された形式のままです。よって「Think IT」も圧縮されているため、そのままではプロキシで置換することができません。この対策には複数の手段がありますが、今回は扱いません。興味のある方は、ブログ「WEBrickで自作プロキシ」(http://rucila.s43.xrea.com/memo/?date=20041020#p02)「WEBRickプロキシでgzipに対応するCommentsAdd Star」(http://d.hatena.ne.jp/hayori/20080205/1202189655)などを参考にしてみてください。
別の書き方
ProxyContentHandlerを使うことで、コンテンツフィルターを容易に書くことができますが、プロキシでコンテンツを取得する前に処理を行いたいなどの、細かい設定は行うことができません。このようにもっと細かい処理を書くには、HTTPProxyServerを継承して新しいクラスを作り、メソッドを上書きしてカスタマイズします。
この方法でproxy2.rbと同じ処理をする場合は、サンプルファイルのproxy3.rbのように書きます。
OriginalHTTPProxyServerでは、HTTPProxyServerを継承して、proxy_serviceメソッドを上書きしています。proxy_serviceメソッドでは、その名の通りプロキシ先のコンテンツを取得する処理を行っています。ここを上書きし、まずsuperで元のproxy_serviceメソッドの処理を実行します。これで、目的のコンテンツがresに設定されます。
この後は、先のHandlerの時と同じコードを書くことで、取得したコンテンツの書き換えが行えます。
これを利用し、特定のWebサイトだけプロキシ経由でアクセスさせないようにすることができます。
例えば、2ちゃんねる(http://www.2ch.net/)をブロックするには、リクエストのホストが.2ch.netである場合にプロキシ処理を行わないようにします。その代わりに、レスポンスボディーに「blocked」と表示させるようにしましょう。サンプルファイルproxy4.rbのようになります。
proxy3.rbとは違い、superより前にリクエストのホスト(req.host)を見て、.2ch.netだった場合にはsuperを呼び出さずに、このメソッドでレスポンスボディー(res.body)にblockedを設定しています。ホストがそれ以外の場合には、superを呼び出すことでプロキシ処理を行うようにします。
今回はホスト名だけを見ていますが、パス(req.path)も見ることで、特定のディレクトリ以下のコンテンツのみをブロックすることなども容易に実装できます。ぜひ自分でいろいろ試してみてください。
次は仕事で使えるプロキシを作ってみましょう。