ウォンツテック

そでやまのーと

/server/util_filter.c
フィルターの実装を見てみる。

まずはフィルター名の格納方法から

typedef struct {
    int c;
    filter_trie_node *child;
} filter_trie_child_ptr;

struct filter_trie_node {
    ap_filter_rec_t *frec;
    filter_trie_child_ptr *children;
    int nchildren;
    int size;
};

trie構造で格納している。たとえばabcというフィルタ名ならまずaという文字の子がいるか検索し、いればその子にbという子がいるか検索、次はcというふうに1文字ずつ検索していく。
途中で文字が無ければそのフィルタは存在しない。
フィルタを新しく追加する場合もこの方法で検索していき、途中で文字が存在しなければそれように構造体を作る(trie_node_alloc)

  • trie_node_alloc
static filter_trie_node *trie_node_alloc(apr_pool_t *p,
                                         filter_trie_node *parent, char c)
{
    filter_trie_node *new_node;
    if (parent) {
        int i;
        for (i = 0; i < parent->nchildren; i++) {
            if (c == parent->children[i].c) {
                return parent->children[i].child;
            }
            else if (c < parent->children[i].c) {
                break;
            }
        }
        new_node =
            (filter_trie_node *)apr_palloc(p, sizeof(filter_trie_node));
        trie_node_link(p, parent, new_node, c);
    }
    else { /* No parent node */
        new_node = (filter_trie_node *)apr_palloc(p,
                                                  sizeof(filter_trie_node));
    }

    new_node->frec = NULL;
    new_node->nchildren = 0;
    new_node->size = TRIE_INITIAL_SIZE;
    new_node->children = (filter_trie_child_ptr *)apr_palloc(p,
                             new_node->size * sizeof(filter_trie_child_ptr));
    return new_node;
}

parentがNULLならば新しくノードを作成するだけで、存在すれば登録しようとしているフィルタ名の文字が存在するか検索。存在すればそのchildを返し、存在しなければノードを作成しparentにlinkする(trie_node_link)。

  • trie_node_link
static void trie_node_link(apr_pool_t *p, filter_trie_node *parent,
                           filter_trie_node *child, int c)
{
    int i, j;

    if (parent->nchildren == parent->size) {
        filter_trie_child_ptr *new;
        parent->size *= 2;
        new = (filter_trie_child_ptr *)apr_palloc(p, parent->size *
                                             sizeof(filter_trie_child_ptr));
        memcpy(new, parent->children, parent->nchildren *
               sizeof(filter_trie_child_ptr));
        parent->children = new;
    }

    for (i = 0; i < parent->nchildren; i++) {
        if (c == parent->children[i].c) {
            return;
        }
        else if (c < parent->children[i].c) {
            break;
        }
    }
    for (j = parent->nchildren; j > i; j--) {
        parent->children[j].c = parent->children[j - 1].c;
        parent->children[j].child = parent->children[j - 1].child;
    }
    parent->children[i].c = c;
    parent->children[i].child = child;

    parent->nchildren++;
}

まずparentのサイズがMAXかどうか調べ(parent->nchildren == parent->size)いっぱいであればサイズ2倍のメモリ領域を確保する。
次に小さい順に格納されているchildrenを調べ新しいnodeを入れるべき位置をしらべそこに新しいnodeを挿入する。

  • register_filter
static ap_filter_rec_t *register_filter(const char *name,
                            ap_filter_func filter_func,
                            ap_init_filter_func filter_init,
                            ap_filter_type ftype,
                            filter_trie_node **reg_filter_set)
{
...
    for (n = normalized_name; *n; n++) {
        filter_trie_node *child = trie_node_alloc(FILTER_POOL, node, *n);
        if (apr_isalpha(*n)) {
            trie_node_link(FILTER_POOL, node, child, apr_toupper(*n));
        }
        node = child;
    }
...
    frec->filter_func = filter_func;
    frec->filter_init_func = filter_init;
    frec->ftype = ftype;
...
}

filter登録の主関数でフィルタ名をtrie_node_allocとtrie_node_linkを使って登録する。
その後フィルタ関数(filter_func)などの登録をし、最後にapr_pool_cleanup_registerを呼び出し、cleanupの方法などを登録する。

  • ap_register_input_filter
  • ap_register_output_filter
  • ap_register_output_filter_protocol
AP_DECLARE(ap_filter_rec_t *) ap_register_input_filter(const char *name,
                                          ap_in_filter_func filter_func,
                                          ap_init_filter_func filter_init,
                                          ap_filter_type ftype)
{
    ap_filter_func f;
    f.in_func = filter_func;
    return register_filter(name, f, filter_init, ftype,
                           &registered_input_filters);
}

/* Prepare to make this a #define in 2.2 */
AP_DECLARE(ap_filter_rec_t *) ap_register_output_filter(const char *name,
                                           ap_out_filter_func filter_func,
                                           ap_init_filter_func filter_init,
                                           ap_filter_type ftype)
{
    return ap_register_output_filter_protocol(name, filter_func,
                                              filter_init, ftype, 0) ;
}
AP_DECLARE(ap_filter_rec_t *) ap_register_output_filter_protocol(
                                           const char *name,
                                           ap_out_filter_func filter_func,
                                           ap_init_filter_func filter_init,
                                           ap_filter_type ftype,
                                           unsigned int proto_flags)
{
    ap_filter_rec_t* ret ;
    ap_filter_func f;
    f.out_func = filter_func;
    ret = register_filter(name, f, filter_init, ftype,
                          &registered_output_filters);
    ret->proto_flags = proto_flags ;
    return ret ;
}

これらの関数は登録時に実際呼ばれる関数で、内部ではほぼregister_filterを呼び出しているだけ。

  • ap_add_input_filter
  • ap_add_input_filter_handle
  • ap_add_output_filter
  • ap_add_output_filter_handle

これらは登録したフィルターをrequest_recやconn_recに登録するAPIでフィルタ名とhandle名(ap_filter_rec_t)のどちらかで登録する方法が提供されている。
内部ではadd_any_filterかadd_any_fuilter_handleが呼ばれている。

  • add_any_filter_handle
static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,                                          request_rec *r, conn_rec *c,                                          ap_filter_t **r_filters,                                          ap_filter_t **p_filters,                                          ap_filter_t **c_filters)
{
}

新しく作るフィルタ構造体(ap_filter_t)をr_filters、p_filters、c_filtersいずれかのリンクリストに挿入するのが主要部。
どれに挿入するかはap_filter_rec_tのftypeの種別によって決められる。

typedef enum {
    /** These filters are used to alter the content 
     *  that is passed through
     *  them. Examples are SSI or PHP. */
    AP_FTYPE_RESOURCE     = 10,
    /** These filters are used to alter the content 
     *  as a whole, but after all
     *  AP_FTYPE_RESOURCE filters are executed. 
     *  These filters should not
     *  change the content-type.  An example is deflate.  */
    AP_FTYPE_CONTENT_SET  = 20,
    /** These filters are used to handle the protocol
     *  between server and
     *  client.  Examples are HTTP and POP. */
    AP_FTYPE_PROTOCOL     = 30,
    /** These filters implement transport encodings 
     * (e.g., chunking). */
    AP_FTYPE_TRANSCODE    = 40,
    /** These filters will alter the content, 
     *  but in ways that are
     *  more strongly associated with the connection.  
     *  Examples are
     *  splitting an HTTP connection into multiple requests and
     *  buffering HTTP responses across multiple requests.
     *
     *  It is important to note that these types of filters 
     *  are not allowed in a sub-request. 
     *  A sub-request's output can certainly be filtered 
     *  by ::AP_FTYPE_RESOURCE filters, but all of the "final
     *  processing" is determined by the main request. */
    AP_FTYPE_CONNECTION  = 50,
    /** These filters don't alter the content.  
     *  They are responsible for
     *  sending/receiving data to/from the client. */
    AP_FTYPE_NETWORK     = 60
} ap_filter_type;

どんな処理をするかによってフィルタを挿入する位置を変えている。

  • add_any_filter

フィルタ名からtrie構造に登録されているフィルタfilter_trie_nodeを探し出し、add_any_filter_handleを呼び出している

  • remove_any_filter
  • ap_remove_input_filter
  • ap_remove_output_filter

フィルタリストから指定したフィルタ(ap_filter_t)を削除している。

  • ap_pass_brigade
AP_DECLARE(apr_status_t) ap_pass_brigade(ap_filter_t *next,
                                    apr_bucket_brigade *bb)
{
    if (next) {
        apr_bucket *e;
        if ((e = APR_BRIGADE_LAST(bb)) && APR_BUCKET_IS_EOS(e) && next->r) {
            next->r->eos_sent = 1;

            if (next->r->prev) {
                request_rec *prev = next->r->prev;

                while (prev) {
                    prev->eos_sent = 1;
                    prev = prev->prev;
                }
            }
        }
        return next->frec->filter_func.out_func(next, bb);
    }
    return AP_NOBODY_WROTE;
}

出力フィルタ関数を呼び出すAPIapr_bucket_brigade *bbは出力データ。中身はnext->frec->filter_func.out_func(next, bb);を呼び出しているだけ。

  • ap_filter_flush
  • ap_fflush
  • ap_fputstrs
  • ap_fprintf

これらは内部でap_pass_brigadeを呼び出しているだけ

  • ap_get_brigade
AP_DECLARE(apr_status_t) ap_get_brigade(ap_filter_t *next,
                                    apr_bucket_brigade *bb,
                                    ap_input_mode_t mode,
                                    apr_read_type_e block,
                                    apr_off_t readbytes)
{
    if (next) {
        return next->frec->filter_func.in_func(next, bb,
                                               mode, block,
                                               readbytes);
    }
    return AP_NOBODY_READ;
}

入力フィルタ関数を呼び出すAPIでnext->frec->filter_func.in_funcを呼び出すだけ。