VMODを作る上で何かしらの後処理や前処理を、ユーザの処理前にやりたいケースが存在します。
たとえば僕が以前作ったvmod_redirectの場合です。
Varnishのリダイレクトはかなりめんどくさくて以下のように記述する必要があります。
sub vcl_recv { if (req.http.user-agent ~ "iP(hone|od)") { error 750 "Moved Temporarily"; } } sub vcl_error { if (obj.status == 750) { set obj.http.Location = "http://www.example.com/iphoneversion/"; set obj.status = 302; return(deliver); } }
このように一回エラー関数でvcl_errorに飛ばして判断する必要があります。
しかしvmod_redirectではvcl_errorに記述することなくredirectを実現しています。
import redirect; sub vcl_recv { if (req.http.user-agent ~ "iP(hone|od)") { error(redirect.location(302,"http://www.example.com/iphoneversion/") , "Moved Temporarily"); } }
これはvcl_errorの関数ポインタをフックすることにより
オリジナルのvcl_errorが呼び出される前に独自の関数を呼び出すためです。
ではフックするためのポインタは何処に定義されているでしょうか?
vmodで使うsturct sess構造体(sp)に含まれています。
vcl_errorの場合 sp->vcl->error_func vcl_deliverの場合 sp->vcl->deliver_func ...
ここのポインタを書き換えることでVarnishが呼び出すアクションを変更することが可能です。
そのことでオリジナルのアクションを呼び出す前後で任意の処理を行うことができ非常に便利です。
実際に書き換える場合はこのようなコードで行います
static vcl_func_f *vmod_redirect_Hook_vcl_error = NULL; static pthread_mutex_t vmod_redirect_mutex = PTHREAD_MUTEX_INITIALIZER; static unsigned hook_done = 0; static int vmod_Hook_vcl_error(struct sess *sp){ ....//任意の処理を書く //オリジナルのvcl_errorを呼び出してリターン return(vmod_redirect_Hook_vcl_error(sp)); } int vmod_location(struct sess *sp, int status, const char*p,...) { //VCLのreloadでフックが外れた場合の検知 if(hook_done == 1 && sp->vcl->error_func != vmod_redirect_Hook_vcl_error ) hook_done = 0; if(hook_done == 0){ //別のスレッドが同時に書き込みをしないようにロックする AZ(pthread_mutex_lock(&vmod_redirect_mutex)); if(hook_done == 0) { //退避先にオリジナルのポインタを保存 vmod_redirect_Hook_vcl_error = sp->vcl->error_func; //フックする sp->vcl->error_func = vmod_Hook_vcl_error; hook_done = 1; } //ロック開放 AZ(pthread_mutex_unlock(&vmod_redirect_mutex)); } .... return (status); }
ポイントとして初回のvmod関数呼び出し時にフックします。
これはvmod_Initではフックをするために必要な構造体へのアクセス方法が無いためです。
またmutexを利用してロックしていますが、これは複数のスレッドが同時にフックを行おうとしてフック関数でループするのを防ぐためです
ロックを行わないと、上図の(6)でオリジナルのポインタとおもって、フック後のポインタを取得してしまいます。
二回0チェックしているのは、ロックをかける間に他のスレッドが書き込んでいないことを確認するためです。
このようにフックを使うことでよりVMODの表現が増えるんじゃないかなと思います。
参考になれば幸いです
注意
varnishadmのvcl.useとvcl.discardを使わないでください。
vcl.useの場合は直前のVCLのメソッドが呼ばれ、vcl.discardはセグフォを起こす危険性があります
—-
2012-08-02追記
VCLのリロードを行った時にフックが外れるので検知用コードを追加(hook_done)