読者です 読者をやめる 読者になる 読者になる

愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

Plack::App::Proxy::Selectiveとか作ってみた

Perl

マクラ

どうも、大してPerlも書けないのに会社のローカルでは常に3つほどplackupでサーバ立ち上げて作業しているzentoooです。すいませんすいません。


企業で働いていると、関与してはいるけれど自分には権限がないので本番サーバあるいはそれに準ずるサーバにアップできないファイルに、よく遭遇しますよね。

そういった事態にあまり遭遇しない場合でも、本番サーバにアップする前に、本番環境で現在編集中のcssやらjsやらのファイルをテストしたい、テストできたらいいなあ、という需要はけっこうあったりなかったりするんではないでしょうか。

んで、僕の場合もそういったことがちょいちょいあったりするわけです。本番環境って、直にユーザにつながっているものなので権限があろうとなかろうと気軽にファイルをアップできるものではありません。


というようなことを考えたり考えなかったりしていると、id:ZIGOROuさんに「それHTTP::Proxy::Selectiveでできるよ!」と以下のページを教えてもらい、しばらくselective_proxyを使っていました。

http://tech.kayac.com/archive/local-proxy-by-perl-and-pac.html

HTTP::Proxy::Selectiveはこちら(CPAN

http://search.cpan.org/dist/HTTP-Proxy-Selective/lib/HTTP/Proxy/Selective.pm

一つ目のリンク先に書いてあるとおり、ちょっとした設定ファイルを書き換えるだけでリモートのサーバの特定のパスに置いてある静的ファイルをローカルのファイルに読み替えることができ、「おお、これ便利やんけ!」ってな感じにコーフンしておりました。


で、しばらく使っているうちにいくつか問題点が…

HTTP::Proxy::Selectiveの問題点というか気になる点

  • なぜかすぐ死んでしまう

そんなに検証しなかったのでどういう状況下で起こるのかはよく分かっていませんが、なぜか特定の状況下でローカルに立ててるプロキシサーバが何の断りもなく(スタックトレース等もなく)死んでしまうという致命的な問題点

  • 設定ファイルが.ini

一風変わったOSを使っている人以外にとっては馴染みの薄い形式

  • 設定に融通がきかない

パスの指定にワイルドカードやら正規表現がつかえない?(未検証)

なんでHTTP::Proxy::Selectiveすぐ死んでしまうん?

実は一番上の問題点以外は割とどうでもよかったりするのですが、特定の状況下でなぜか死ぬ、というのがなかなか面倒で、特に僕の場合は「本番サーバでは死なないが、本番の一段階前のサーバに使うと死ぬ」という「むしろ逆にしてくれよ!」みたいななんだかとても微妙な動作状況。

「すぐ死んでしまうホタルみたいなモジュールを直してられっかバッキャロウ!」ということで、「それPlackで(ry」と思ったのと、Plack先生といちゃいちゃする練習として、車輪の再発明感ましましながらもPlackベースで作ってみました。

Plack::App::Proxy::Selective

http://github.com/zentooo/p5-plack-app-proxy-selective

名前はまんまパクリ。Plack周りをいじったことのあるお方なら薄々お気づきかもしれませんが、内部でPlack::App::DirectoryとPlack::App::Proxyに振っているだけです。

使いかた

基本的にはこんな感じに使います。

use strict;
use warnings;

use Plack::App::Proxy::Selective;
use Path::Class;

Plack::App::Proxy::Selective->new(
    filter => +{
        'google.com' => +{
            '/style' => 'css',
        },
        'www.yahoo.co.jp' => +{
            'js' => 'script',
        },
    },
    base_dir => file(__FILE__)->dir,
)->to_app;

newの名前付き引数filterに設定値としてのハッシュリファレンスを、base_dirにローカルのドキュメントのルートとしてPath::Class::Dirのインスタンスを渡してやります。後は、このファイルをproxy.psgiとか適当な名前で保存して

plackup -p 8080 proxy.psgi

するだけ。

このローカルプロキシをhttp接続に用いるプロキシとしてブラウザに設定すると、あらふしぎ!指定したドメイン以下の指定したディレクトリ以下のファイルが、そっくりそのままローカルのファイルで置き換わります。ちなみに僕は、proxyのON/OFFはVimperatorのautoproxychanger.jsで切り替えています。


ちなみにパス名とかは正規表現に対応しているので、こんなこともできます。

    Plack::App::Proxy::Selective->new(
        filter => +{
            'google.com' => +{
                '/css/.*/' => '/style/',
                '/script/.*' => '/js/ext/',
                '/js/.*\.js' => '/js/ext/',
            },
        },
        base_dir => file(__FILE__)->dir,
    )->to_app;

上から順に、

  1. css/なんとか/ 以下のファイルを全部ローカルのカレントディレクトリ/style 以下のファイルに置き換える(css/ 直下は置き換わらない)
  2. /script/ 以下のファイルを$PWD/js/ext/ 以下のファイルに置き換える(これだと/script/って書くのと同じ)
  3. /js 以下の、jsファイルのみ$PWD/js/ext/ 以下のファイルに置き換える(/js/hoge.css とかは置き換わらない)


あと多分あんまやらないと思うけど、

    Plack::App::Proxy::Selective->new(
        filter => +{
            'google.com' => +{
                '.*\.js' => '/script/',
            },
        },
        base_dir => file(__FILE__)->dir,
    )->to_app;

という書き方をすると、当然ながらサーバ上の全jsファイルがローカルのscript以下のものに置き換えられる。

現状の問題点
  • コードが汚い

Perl慣れしてない&手探りで書いたのでコードがいけてない。というか、ハッシュリファレンスをイテレートするスマートなやり方がよくわからん。while ( my ($key, $value) = each %$hashref ) ってやると何かへんなことになるし。誰か教えてちょんまげ!


追記:
正確には while ( my ($key, $value) = each %{$self->filter} ) {} が動いていなかった。コメント欄参照。

  • hoge.js.bakとかは無理

「ファイル名とは"foo.bar"みたいな文字列である」と内部で勝手に決め込んでいるので、hoge.js.bakとか拡張子の2個以上ついたファイルに現状対応できていない。いらん気もするけど。

予想されるユースケースに関しては一応テスト書いたけど、正規表現によっては変なことになるかも。

そのうちつけましょう。

たぶん便利

psgiファイル書いてplackupすればいいだけなのですこし便利。あと、書いたのが自分なのでもし落ちちゃった場合もデバッグが楽。

4つめ

これで、会社のMacで立ち上げるPlackアプリが4つになりますね!