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, ©_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入力フィルタはここまで。
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! 군포 출장안마