フィルタの勉強

モジュールの宣言

  1|module AP_MODULE_DECLARE_DATA foo_module = {
  2|  STANDARD20_MODULE_STUFF,
  3|    foo_dir_config_creater,       /* dir config creater */
  4|    NULL,                         /* dir merger --- default is to override */
  5|    NULL,                         /* server config */
  6|    NULL,                         /* merge server config */
  7|    foo_command_table,            /* command table */
  8|    foo_register_hooks            /* register hooks */
  9|};

いわゆる「おまじない」。
ソースコードの上の方で

module AP_MODULE_DECLARE_DATA foo_module;

と宣言しているものもあった。意味はまだわからない。

3行目。初期化を行う関数のポインタを渡すっぽい。
7行目。独自のディレクティブの設定を行う配列のポインタを渡すっぽい。
8行目。ハンドラやフィルタの登録を行う関数のポインタを渡す。

初期化関数

  1|static void *foo_dir_config_creater(apr_pool_t *p, char *dir) {
  2|  struct Foo *f = (Foo *) apr_palloc(p, sizeof(struct Foo));
  3|
  4|  return (void *) f;
  5|}

ここで確保したメモリ領域を返すと「ap_filter_t::ctx」に保持されるっぽい。解放はApacheがやってくれるのかな?

フィルタの主処理でメモリ領域を取得するには

static apr_status_t foo_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
  struct Foo *soo = f->ctx;

とするらしい。

コマンドテーブル

  1|static const command_rec foo_command_table[] = {
  2|  AP_INIT_FLAG("FooCommand", ...),
  3|  ...,
  4|  {NULL}
  5|};

AP_INIT_FLAGとか、AP_INIT_TAKE1などのマクロを使って、独自のディレクティブを登録するみたい。詳細はこれから調査。マクロはこのへんに載ってる。
最後は「{NULL}」で終わり?

主処理

こちらからほぼ転載。

  1|apr_status_t foo_filter(ap_filter_t *f, apr_bucket_brigade *in_bb) {
  2|  const request_rec* const r = f->r;
  3|
  4|  if (APR_BRIGADE_EMPTY(in_bb)) {
  5|    return APR_SUCCESS;
  6|  }
  7|
  8|  apr_bucket_brigade* const out_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
  9|
 10|  while (!APR_BRIGADE_EMPTY(in_bb)) {
 11|    apr_bucket* const e = APR_BRIGADE_FIRST(in_bb);
 12|
 13|    if (APR_BUCKET_IS_EOS(e)) {
 14|      APR_BUCKET_REMOVE(e);
 15|      APR_BRIGADE_INSERT_TAIL(out_bb, e);
 16|      break;
 17|    }
 18|
 19|    if (APR_BUCKET_IS_FLUSH(e)) {
 20|      apr_bucket_delete(e);
 21|
 22|      apr_bucket* const bkt = apr_bucket_flush_create(f->c->bucket_alloc);
 23|      APR_BRIGADE_INSERT_TAIL(out_bb, bkt);
 24|
 25|      const apr_status_t rv = ap_pass_brigade(f->next, out_bb);
 26|
 27|      if (rv != APR_SUCCESS) {
 28|        return rv;
 29|      }
 30|
 31|      continue;
 32|    }
 33|
 34|    apr_size_t len;
 35|    const char* data;
 36|    apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
 37|
 38|    char* const buf = (char*)apr_palloc(r->pool, len);
 39|    memcpy(buf, data, len);
 40|
 41|    apr_bucket* const b = apr_bucket_pool_create(buf, len, r->pool, f->c->bucket_alloc);
 42|    APR_BRIGADE_INSERT_TAIL(out_bb, b);
 43|    apr_bucket_delete(e);
 44|  }
 45|
 46|  ap_remove_output_filter(f);
 47|  apr_brigade_cleanup(in_bb);
 48|  return ap_pass_brigade(f->next, out_bb);
 49|}

処理の流れ。

  1. 「while (!APR_BRIGADE_EMPTY(in_bb))」でバケットブリッジをぐるぐるまわす。
  2. 「APR_BRIGADE_FIRST(in_bb)」でバケットブリッジからバケットを取り出す。
  3. 「apr_bucket_read()」でデータを読み込んで、新しいバケットにコピー。
  4. 新しいバケットを新しいバケットブリッジに追加。
  5. 新しいバケットブリッジを次に渡す。

ブラウザにキャッシュがあるときは「APR_BUCKET_IS_EOS()」に引っかかって、その後の処理が実行されないみたい。
APR_BUCKET_IS_FLUSH()の詳細はこれから調査。
APR_BUCKET_REMOVE()とapr_bucket_delete()の違いも不明。
apr_brigade_cleanup()を呼んでないものもあったきがするなぁ。
メモリ領域の確保は基本的に「r->pool」を介して行うものらしい。