2014年12月8日月曜日

入力フィルタ: CORE_IN入力フィルタ

CORE_IN入力フィルタ: ap_core_input_filter


CORE_IN入力フィルタは、入力フィルタの最下層のネットワークフィルタになる。
この下には入力フィルタはないので、CIRE_IN入力フィルタでは、ネットワークからクライアントの送ってきたデータを受信し、処理して、上位の入力フィルタに引き渡す処理が行われる。
以前、入力フィルタの概要で少し見たが、今回はもう少し細かく全体を見ていく。

     94 apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
     95                                   ap_input_mode_t mode, apr_read_type_e block,
     96                                   apr_off_t readbytes)

引数は次の通り。

引数 説明
ap_filter_t *f 入力フィルタ情報。fはap_core_input_filterに関する情報を格納する。
apr_bucket_brigade *b 吸い上げたデータを格納する宛先のbucket brigade。
ap_input_mode_t mode 入力モード。
AP_MODE_READBYTES 入力フィルタは最大readbytesバイト読み込んでreturn する
AP_MODE_GETLINE 入力フィルタは、1行(CRLF)分のデータを読み込んでreturnする。(1行が長すぎたり、あるいは、CRLFがないような場合には部分的なデータを読み込んでreturnする可能性がある)
AP_MODE_EATCRLF 入力フィルタは、CRLFを見つけたら消費(除去)する
AP_MODE_SPECULATIVE 入力フィルタの読み込みは投機的なものとみなされる。読み込まれたデータは、以降の別のモードでの処理のために蓄積される。
AP_MODE_EXHAUSTIVE 入力フィルタの読み込みは完全なものとみなされる。それ以上読込できなくなるまで読込が行われる。このモードは特に注意して扱うこと。
AP_MODE_INIT 入力フィルタはコネクションを初期化する。NNTP over SSL やFTP over SSL等で必要。
apr_read_type_e block 読込タイプ
APR_BLOCK_READ ブロックモード
APR_NONBLOCK_READ 非ブロックモード
apr_off_t readbytes 入力データサイズ





     97 {
     98     apr_status_t rv;
     99     core_net_rec *net = f->ctx;

core_net_recはフィルタ情報に格納されているコンテキスト情報だ。
CORE_IN入力フィルタ、CORE出力フィルタの両方で利用される。
以下のように定義されている。

(httpd-2.4.9/include/http_core.h)

    699 typedef struct core_net_rec {
    700     /** Connection to the client */
    701     apr_socket_t *client_socket;
    702
    703     /** connection record */
    704     conn_rec *c;
    705
    706     core_output_filter_ctx_t *out_ctx;
    707     core_ctx_t *in_ctx;
    708 } core_net_rec;

    100     core_ctx_t *ctx = net->in_ctx;

core_net_rec情報に保持されているCOER_INフィルタコンテキスト情報は以下の構造体になっている(typedefで、core_ctx_t と定義されている)。

(httpd-2.4.9/server/core_filters.c)

     88 struct core_filter_ctx {
     89     apr_bucket_brigade *b;
     90     apr_bucket_brigade *tmpbb;
     91 };


bはデータ吸い上げのためのbucket brigade。通常はSOCKETバケットが保持されている。
tmpbbは一時的な保存用のbucket brigadeで、原則として空。

    101     const char *str;
    102     apr_size_t len;
    103
    104     if (mode == AP_MODE_INIT) {

CORE_IN入力フィルタはこのモードでの処理はないので、
このモードの場合、直ちに処理を終えている。

     :
    115         return APR_SUCCESS;
    116     }
    117
    118     if (!ctx)
    119     {

CORE_INコンテキスト情報がまだ作成されていない場合の処理。
このフィルタ処理の最初の処理で実行される。
CORE_INコンテキスト情報には bと tmpbbの2つのbucket brigadeがある。
いずれもconn_recのプール(transactionプール)から作成される。

    120         net->in_ctx = ctx = apr_palloc(f->c->pool, sizeof(*ctx));
    121         ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
    122         ctx->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
    123         /* seed the brigade with the client socket. */
    124         rv = ap_run_insert_network_bucket(f->c, ctx->b, net->client_socket);

ap_run_insert_network_bucketは、insert_network_bucketフック関数の実行処理だ。
core_insert_network_bucket()関数が実行される。
ここでは、net->client_socketからSOCKETデータバケットを作成し、ctx->bに追加している。
この時点ではctx->bは空だったので、これによりSOCKETデータバケットだけが格納されている状態になる。

(httpd-2.4.9/server/core.c)

   4805 static apr_status_t core_insert_network_bucket(conn_rec *c,
   4806                                                apr_bucket_brigade *bb,
   4807                                                apr_socket_t *socket)
   4808 {
   4809     apr_bucket *e = apr_bucket_socket_create(socket, c->bucket_alloc);
   4810     APR_BRIGADE_INSERT_TAIL(bb, e);
   4811     return APR_SUCCESS;
   4812 }

    125         if (rv != APR_SUCCESS)
    126             return rv;
    127     }
    128     else if (APR_BRIGADE_EMPTY(ctx->b)) {

このif文は、2度目以降の呼び出しで、ctx->bが空の場合の経路になる。
ctx->bにはすでにSOCKETデータバケットがないため、これ以上のデータ受信はできない。
また、未処理のデータも含まれていない。

EOFを返す。

    129         return APR_EOF;
    130     }
    131
    132     /* ### This is bad. */
    133     BRIGADE_NORMALIZE(ctx->b);

BRIGADE_NORMALIZEは、bucket brigade ctx->b の先頭から辿って、length==0のデータバケットの削除処理を行っている。

    134
    135     /* check for empty brigade again *AFTER* BRIGADE_NORMALIZE()
    136      * If we have lost our socket bucket (see above), we are EOF.
    137      *
    138      * Ideally, this should be returning SUCCESS with EOS bucket, but
    139      * some higher-up APIs (spec. read_request_line via ap_rgetline)
    140      * want an error code. */
    141     if (APR_BRIGADE_EMPTY(ctx->b)) {

BRIGADE_NORMALIZE()を行って、bucket brigadeが空になった場合、EOFを返す。
コメントによれが、本来であれば、EOSバケットをbucket brigade bに追加して、SUCCESSを返すべきだが、
例えば、ap_rgetline()から呼ばれるread_request_line()はエラーコードが戻ることを期待しているので、ここではエラーコード(EOF)を返している、とある。

    142         return APR_EOF;
    143     }
    144

ここからは入力モード別のコードとなっている

(1)AP_MODE_GETLINE入力モードの処理


    145     if (mode == AP_MODE_GETLINE) {
    146         /* we are reading a single LF line, e.g. the HTTP headers */

我々は、一行(LF)分(例えばHTTPヘッダ)を読み込む。

    147         rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);

apr_brigade_split_line()は bucket brigade ctx->bを先頭からLFまで読み込んで、bucket brigade bにbucketを移動、追加する。

(apr-1.5.1/include/apr_lib.h)
     51 /** A constant representing a 'large' string. */
     52 #define HUGE_STRING_LEN 8192

apr_brigade_split_lineは以下の通り。

(apr-util-1.5.4/buckets/apr_brigade.c)

    304 APU_DECLARE(apr_status_t) apr_brigade_split_line(apr_bucket_brigade *bbOut,
    305                                                  apr_bucket_brigade *bbIn,
    306                                                  apr_read_type_e block,
    307                                                  apr_off_t maxbytes)

bbIn(ctx->b)を読み込んで、LFを得るまで、bucketを bbOut(b)に移動する。

    308 {
    309     apr_off_t readbytes = 0;
    310

以下のwhile()文は、bbInが空になるまで繰り返される。

    311     while (!APR_BRIGADE_EMPTY(bbIn)) {
    312         const char *pos;
    313         const char *str;
    314         apr_size_t len;
    315         apr_status_t rv;
    316         apr_bucket *e;
    317
    318         e = APR_BRIGADE_FIRST(bbIn);

bucket brigade bbInの先頭bucketを取得する(bucket brigadeには繋がったまま)。

    319         rv = apr_bucket_read(e, &str, &len, block);

apr_bucket_read()でデータを読み取る。
strにbucketのデータへのアドレスが返る。

    320
    321         if (rv != APR_SUCCESS) {
    322             return rv;
    323         }
    324
    325         pos = memchr(str, APR_ASCII_LF, len);

データ内のLFを探す。

    326         /* We found a match. */
    327         if (pos != NULL) {

bucketのデータに LFが含まれた場合、
その位置でapr_bucket_split()でbucketを分割し、
前半にあたるbucket e を APR_BUCKET_REMOVEでbbInから切り離し、
APR_BRIGADE_INSERT_TAILでbbOutの末尾に追加し、
成功でreturnする。
ここが、この関数の成功時の経路。

    328             apr_bucket_split(e, pos - str + 1);
    329             APR_BUCKET_REMOVE(e);
    330             APR_BRIGADE_INSERT_TAIL(bbOut, e);
    331             return APR_SUCCESS;
    332         }

LFが見つからない場合は処理を続ける。

    333         APR_BUCKET_REMOVE(e);

APR_BUCKET_REMOVEで、bucket eを bucket brigade bbInから切り離す。
以下では bucket eのデータを bbOutに追加するが、その方法が2つある。

(a)bucket eがメタデータバケットか、 bucketに含むデータサイズがAPR_BUCKET_BUFF_SIZE/4 より大きい場合は
バケットを bucket brigade bbOutに追加する。
APR_BUCKET_BUFF_SIZEは8000バイトなので、2000バイトより大きい場合になる。

    334         if (APR_BUCKET_IS_METADATA(e) || len > APR_BUCKET_BUFF_SIZE/4) {
    335             APR_BRIGADE_INSERT_TAIL(bbOut, e);
    336         }

(b)それ以外の場合、bucketがデータを含むなら、apr_brigade_writeで、bbOutに、データを追加する(strからのlenバイト)。
apr_brigade_writeでは、bucket e内のデータをbbOutの末尾に追加する際、

  • bbOutの末尾のbucketがHEAPバケットだった場合で、
    • 末尾のHEAPバケットが参照するメモリ領域がほかのHEAPバケットから参照されていない
      かつ、
    • 末尾のHEAPバケットが参照するメモリ領域の未使用領域が、bucket e内のデータサイズを格納可能な場合

bbOutの末尾のHEAPバケットのデータ領域にデータをコピーする。

それができなかった場合、ここでは、第2引数(flush関数)がNULLなので、
strからのlenバイトをもとに新たなHEAPバケットを作成してデータをコピーする。
このHEAPバケットはbbOutの末尾に追加される。

この経路では、元のbucket eはapr_bucket_destroy()で破棄される。

    337         else {
    338             if (len > 0) {
    339                 rv = apr_brigade_write(bbOut, NULL, NULL, str, len);
    340                 if (rv != APR_SUCCESS) {
    341                     return rv;
    342                 }
    343             }
    344             apr_bucket_destroy(e);
    345         }
    346         readbytes += len;
    347         /* We didn't find an APR_ASCII_LF within the maximum line length. */
    348         if (readbytes >= maxbytes) {

読み込み済みデータ長が指定された最大読込サイズ(maxbytes)を越えた場合には、bucketの処理を終了(break)する。

    349             break;
    350         }
    351     }
    352
    353     return APR_SUCCESS;
    354 }

以上がapr_brigade_split_line関数の処理だ。

    148         /* We should treat EAGAIN here the same as we do for EOF (brigade is
    149          * empty).  We do this by returning whatever we have read.  This may
    150          * or may not be bogus, but is consistent (for now) with EOF logic.
    151          */
    152         if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) {

入力フィルタの読み込みが非ブロックモードの場合、読込可能なデータがなければこの経路になる。
apr_brigade_split_line()内部で行われる ap_bucket_read()でSOCKETバケットを読み込んだ場合に発生する可能性がある。
この経路では、それまでに読み込んだデータがあれば、それを成功で返す。

    153             rv = APR_SUCCESS;
    154         }
    155         return rv;
    156     }
    157

以上がAP_MODE_GETLINEモードの処理だ。
次は、

(2)AP_MODE_EATCRLF入力モードの処理


httpd-2.4.9のソースをgrepする限り、このモードは使用されていないようだ。
AP_MODE_GETLINEとAP_MODE_READBYTESが中心で、一部にAP_MODE_SPECULATIVEが使用されていた。

    158     /* ### AP_MODE_PEEK is a horrific name for this mode because we also
    159      * eat any CRLFs that we see.  That's not the obvious intention of
    160      * this mode.  Determine whether anyone actually uses this or not. */
    161     if (mode == AP_MODE_EATCRLF) {
    162         apr_bucket *e;
    163         const char *c;
    164
    165         /* The purpose of this loop is to ignore any CRLF (or LF) at the end
    166          * of a request.  Many browsers send extra lines at the end of POST
    167          * requests.  We use the PEEK method to determine if there is more
    168          * data on the socket, so that we know if we should delay sending the
    169          * end of one request until we have served the second request in a
    170          * pipelined situation.  We don't want to actually delay sending a
    171          * response if the server finds a CRLF (or LF), becuause that doesn't
    172          * mean that there is another request, just a blank line.
    173          */

コメントを訳してみる。

このループの目的はリクエストの末尾のCRLF(あるいはLF)を無視することだ。
多くのブラウザがPOSTリクエストの末尾に余分の行を送ってくる。

我々はソケットにさらにデータがあるかどうか知るために、PEEKメソッドを使用する。
それで、我々は、パイプラインされた状況で、2番目のリクエストを供給してしまうまで、
ひとつのリクエスト終了の送信を遅らせるべきかどうかを知ることができる。
我々は実際のところ、もしサーバがCRLR(またはLF)を見つけた場合にも、
そのCRLFは別のリクエストを意味せず、単なる空行かもしれないので、
レスポンスを返すのを遅らせたくはない。

よく分かった気はしない。訳が悪いんだろう。
CR+LFあるいは、LFを受信していても読み飛ばし、それ以外のデータがあった場合にSUCCESSを返すようになっている。

以下のwhile()文では、
(2.1)bucket brigade ctx->b の先頭を取得し(178行目)、
(2.2)bucketのデータを読み込み、
(2.3)CRLFを無視する処理をしている。
(2.4)処理対象のバケットがすべてCRLFで埋まっていた場合には、それを破棄している(198行目)。
(2.5)bucket brigadeが空になると、終了する(175行目)。
(141行目でctx->bが空かどうかはチェック済みなので、while()ループがいきなりこの条件にマッチすることはないので、2.5)
(2.1)に戻って、空でなければ、削除されたバケットの次にあった新しい先頭のバケットを取得する。


ここでは、CRLFのみのデータバケットがあった場合は削除されているが、そうでなければ、ctx->bはそのまま残っている。
SOCKETバケットの読み込みが実行され、もし、CRLF以外のデータが読み込まれていた場合には、直ちにSUCCESSで返ることになる。
SOCKETバケットを読んでもデータがない(読み込めない)場合は、EAGAINで返る、
SOCKETバケットがクライアント側でクローズさていた場合にも、EOFが返る。


つまり、bucket brigade は呼び出し元には返されない。CRLFでないデータが読み込めるかどうかが判断できるだけで、
実際にデータがあった場合には、別途読み込みを行う必要がある。

    174         while (1) {
    175             if (APR_BRIGADE_EMPTY(ctx->b))

(2.5) bucket brigadeが空の場合は終了(EOF)

    176                 return APR_EOF;
    177
    178             e = APR_BRIGADE_FIRST(ctx->b);

(2.1) bucket brigadeの先頭を取得

    179
    180             rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ);

(2.2) 読み込み済みのデータが、ctx->bになければ、
apr_bucket_read()は、SOCKETデータバケットを読み込むはずだが、
非ブロックモードなので、データがなければEAGAINで返ると考えられる。
実装は、socket_bucket_read()になる。これはすでに見た

    181
    182             if (rv != APR_SUCCESS)
    183                 return rv;
    184
    185             c = str;

(2.3) 以下のwhile()文でCRLFまたはLFを読み飛ばしている。

    186             while (c < str + len) {
    187                 if (*c == APR_ASCII_LF)
    188                     c++;
    189                 else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
    190                     c += 2;
    191                 else

改行以外のデータが読み込めた場合に、SUCCESSで返っている。

    192                     return APR_SUCCESS;
    193             }
    194
    195             /* If we reach here, we were a bucket just full of CRLFs, so
    196              * just toss the bucket. */
    197             /* FIXME: Is this the right thing to do in the core? */
    198             apr_bucket_delete(e);

(2.4) データバケットがすべてCRLFで埋まっていた場合、このバケットを削除する。

    199         }
    200         return APR_SUCCESS;
    201     }
    202

(3)AP_MODE_EXHAUSTIVE入力モードの処理

httpd-2.4.9のソースをgrepする限り、このモードは使用されていない。

    203     /* If mode is EXHAUSTIVE, we want to just read everything until the end
    204      * of the brigade, which in this case means the end of the socket.
    205      * To do this, we attach the brigade that has currently been setaside to
    206      * the brigade that was passed down, and send that brigade back.
    207      *
    208      * NOTE:  This is VERY dangerous to use, and should only be done with
    209      * extreme caution.  FWLIW, this would be needed by an MPM like Perchild;
    210      * such an MPM can easily request the socket and all data that has been
    211      * read, which means that it can pass it to the correct child process.
    212      */

これもコメントを訳してみる。

EXHAUSTIVEモードの場合、我々はbrigadeの終端まですべてを読み進むことを望んでいる。
それは、この場合、ソケットの終了を意味する。
これを行うために、我々は、現在setaside(保留)しているbrigadeを、
上流から渡されてきたbrigadeに追加し、その全体を上流に引き渡している。

注意: これ使用は*非常に*危険だ。その使用には特別な注意を払わなくてはならない。
Perchildのような、ソケットと読み込み済の全てのデータを簡単に要求できるようなMPMによってこれが必要とされるかもしれない。
これは、正しい子プロセスにそれを渡すことができることを意味する。

コメントの意味がよく分からない。
Perchild MPMは2.4には存在しない。2.0系の server/mpm/experimental にあったようだが、
現在は開発されていないようだ。
http://httpd.apache.org/docs/2.0/en/mod/perchild.html

以下のコードでは、
(3.1) ctx->bにあるSOCKETバケットごと上流に渡してしまうようだ。
つまり、ソケットと読み込み済みのデータをすべて上流に渡すことになる。
その後、ctx->bの方は空になってしまう。
(3.2) そのうしろにEOSメタデータバケットを作成して追加している。

    213     if (mode == AP_MODE_EXHAUSTIVE) {
    214         apr_bucket *e;
    215
    216         /* Tack on any buckets that were set aside. */
    217         APR_BRIGADE_CONCAT(b, ctx->b);

(3.1) APR_BRIGADE_CONCAT(a,b)はbucket brigade aの末尾にbucket brigade bを繋ぐ処理を行う。
その後、bucket brigade b は空になる。

    218
    219         /* Since we've just added all potential buckets (which will most
    220          * likely simply be the socket bucket) we know this is the end,
    221          * so tack on an EOS too. */
    222         /* We have read until the brigade was empty, so we know that we
    223          * must be EOS. */
    224         e = apr_bucket_eos_create(f->c->bucket_alloc);

(3.2) これは EOSメタデータバケットを生成している。

    225         APR_BRIGADE_INSERT_TAIL(b, e);

生成したEOSメタデータバケットを bucket brigade b の末尾に追加している。

    226         return APR_SUCCESS;
    227     }
    228

(4)AP_MODE_READBYTESおよびAP_MODE_SPECULATIVE入力モードの処理


このモードの処理では、指定したサイズまでのデータを読み込む。

(4.1) 指定したサイズ分のデータの存在を確認し、データがなければ、SOCKETバケットを読み込むことになる。
(4.2) それでも、不足する場合は、ブロックモードでSOCKETバケットを読込可能なだけ読み込む。
(4.3) AP_MODE_READBYTESの場合は、読み込んだデータを上流に引き渡す。
(4.4) AP_MODE_SPECULATIVEの場合は、読み込んだデータをコピーして、上流に引き渡す。

つまり、AP_MODE_SPECULATIVEの場合は、この処理後にCORE_IN入力フィルタをAP_MODE_READBYTESで実行すると
この処理で読み込んだデータを再び取得できる。

    229     /* read up to the amount they specified. */
    230     if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) {
    231         apr_bucket *e;
    232
    233         AP_DEBUG_ASSERT(readbytes > 0);
    234
    235         e = APR_BRIGADE_FIRST(ctx->b);
    236         rv = apr_bucket_read(e, &str, &len, block);

bucket brigade ctx->b の先頭を取得し、バケットのデータを読み込む

    237
    238         if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) {

先頭のデータがSOCKETバケットで(読み込み済みのデータがbucket brigade ctx->b にない)、読込タイプが非ブロックモードの場合、
データ読込の戻り値がEAGAINであれば(読込可能なデータがなかった場合)、空のbucket brigadeのままで、SUCCESSで返る。

    239             /* getting EAGAIN for a blocking read is an error; for a
    240              * non-blocking read, return an empty brigade. */
    241             return APR_SUCCESS;
    242         }
    243         else if (rv != APR_SUCCESS) {

それ以外の状況で、読込エラーの場合は、エラーで戻る。

    244             return rv;
    245         }
    246         else if (block == APR_BLOCK_READ && len == 0) {

読込タイプがブロックモードで、読み込めたデータ長が0の場合、
EOSとみなす。

    247             /* We wanted to read some bytes in blocking mode.  We read
    248              * 0 bytes.  Hence, we now assume we are EOS.
    249              *
    250              * When we are in normal mode, return an EOS bucket to the
    251              * caller.
    252              * When we are in speculative mode, leave ctx->b empty, so
    253              * that the next call returns an EOS bucket.
    254              */
    255             apr_bucket_delete(e);

apr_bucet_deleteはバケットを削除する。
SOCKETバケットも削除されるが、SOCKETバケットのdestroy()では処理は行われていない(apr_bucket_destroy_noop関数)。

    256
    257             if (mode == AP_MODE_READBYTES) {

REDBYTESモードの場合は、EOSメタデータバケットを生成し、bucket brigade bの末尾に追加して上流に渡す。
(SPECULATIVEの場合は、上流には何も返らない)

    258                 e = apr_bucket_eos_create(f->c->bucket_alloc);
    259                 APR_BRIGADE_INSERT_TAIL(b, e);
    260             }

SUCCESSで返る。

    261             return APR_SUCCESS;
    262         }
    263

以下はブロックモード/非ブロックモードに関わらず、データが読み込めた場合の処理となる。

    264         /* Have we read as much data as we wanted (be greedy)? */
    265         if (len < readbytes) {

(4.2) 読み込んだデータがreadbytes(読込を指定されたデータサイズ)に満たない場合の処理だ。

    266             apr_size_t bucket_len;
    267
    268             rv = APR_SUCCESS;
    269             /* We already registered the data in e in len */
    270             e = APR_BUCKET_NEXT(e);
    271             while ((len < readbytes) && (rv == APR_SUCCESS)
    272                    && (e != APR_BRIGADE_SENTINEL(ctx->b))) {
    273                 /* Check for the availability of buckets with known length */
    274                 if (e->length != -1) {

bucketのlengthが-1ではないので、格納されているデータサイズが分かる。
読み込み済みデータサイズにこれを加え、次のバケットを取得する。

    275                     len += e->length;
    276                     e = APR_BUCKET_NEXT(e);
    277                 }
    278                 else {

ここは e->length == -1 の場合なので、
この処理では、SOCKETデータバケットと考えられる。

    279                     /*
    280                      * Read from bucket, but non blocking. If there isn't any
    281                      * more data, well than this is fine as well, we will
    282                      * not wait for more since we already got some and we are
    283                      * only checking if there isn't more.
    284                      */
    285                     rv = apr_bucket_read(e, &str, &bucket_len,
    286                                          APR_NONBLOCK_READ);

SOCKETバケットを非ブロックモードで読み込む。
SOCKETバケットから読み込むが成功すると、現在のSOCKETバケットをHEAPバケットに入れ替えて、読み込んだデータをHEAPバケット内に蓄える。
元のSOCKETバケットはHEAPバケットの後ろに追加される。
読み込んだデータ長はbucket_lenにセットされ、HEAPバケット内に保持されているバッファのアドレスはstrにセットされている。

    287                     if (rv == APR_SUCCESS) {

成功だった場合には、読み込みデータ長を加算し、次のバケット(SOCKETバケット)を取得する。

    288                         len += bucket_len;
    289                         e = APR_BUCKET_NEXT(e);
    290                     }
    291                 }

271行目からのwhile()ループは、エラーが発生するか、lenがreadbytes以上になるか、バケットが終端になった場合に終了する

    292             }
    293         }
    294
    295         /* We can only return at most what we read. */
    296         if (len < readbytes) {

この時点でも読み込んだデータサイズが指定されたreadbytesを満たさない場合は、
readbytesに読み込んだデータサイズをセットする。

    297             readbytes = len;
    298         }
    299

以下の処理では、bucket brigade ctx->bから、readbytes文のデータを取り出し、
引数で渡ってきた bucket brigade bに追加する処理を行う

    300         rv = apr_brigade_partition(ctx->b, readbytes, &e);

apr_brigade_partition()は、bucket brigadeの先頭から指定の長さ(readbytes)分進めて、その次のbucketをeに返す。
指定の長さが、ひとつのデータbucketの途中だった場合、そのデータbucketを分割し、分割された後ろのbucketがeに返される。

    301         if (rv != APR_SUCCESS) {
    302             return rv;
    303         }
    304
    305         /* Must do move before CONCAT */
    306         ctx->tmpbb = apr_brigade_split_ex(ctx->b, e, ctx->tmpbb);

apr_brigade_split_exは、bucket brigade ctx->b を 指定したbucket eの前で分割する。
分割された eから後ろのbucket列は ctx->tmpbbに追加される。
注:ctx->tmpbbが空でない場合、追加する前に空にされる。

    307
    308         if (mode == AP_MODE_READBYTES) {

(4.3) READBYTESモードの場合は、ctx->b が、 引数で渡ってきている bucket brigade b に追加される。

    309             APR_BRIGADE_CONCAT(b, ctx->b);
    310         }
    311         else if (mode == AP_MODE_SPECULATIVE) {

(4.4) SPECULATIVEモードの場合は、bucket情報は複製された上で、引数で渡ってきている bucket brigade b に追加される。
ctx->bは空になる。

    312             apr_bucket *copy_bucket;
    313
    314             for (e = APR_BRIGADE_FIRST(ctx->b);
    315                  e != APR_BRIGADE_SENTINEL(ctx->b);
    316                  e = APR_BUCKET_NEXT(e))
    317             {
    318                 rv = apr_bucket_copy(e, &copy_bucket);
    319                 if (rv != APR_SUCCESS) {
    320                     return rv;
    321                 }
    322                 APR_BRIGADE_INSERT_TAIL(b, copy_bucket);
    323             }
    324         }
    325

最後に、readbytesで切り分けられた ctx->tmpbbのbucket列を ctx->bに追加する。

    326         /* Take what was originally there and place it back on ctx->b */
    327         APR_BRIGADE_CONCAT(ctx->b, ctx->tmpbb);
    328     }


    329     return APR_SUCCESS;
    330 }

CORE_IN入力フィルタはここまで。

1 件のコメント:

  1. Casino Games - DrmCD
    All games of 수원 출장마사지 chance! There's the biggest slot machines in the world, and the newest video slots 대전광역 출장마사지 are waiting for you. Play them for 밀양 출장마사지 FREE and win 시흥 출장마사지 REAL MONEY! 군포 출장안마

    返信削除