/* mybb.c * Bucket Brigade のサンプル * (1)ストリームでのデータ操作関数 * ・ストリーム間のバイト単位での移動 * ・ストリームからの行単位での切り出し * ・ストリームからファイルへの書き出し * (2)チャンク形式のデコーダ関数 */ #include #include "apr_general.h" #include "apr_buckets.h" #include "apr_file_io.h" #include "apr_strings.h" #include "mybb.h" #if 0 /* * ストリーム bb1 から size バイト分の buckets をバッファに移す。 * 返り値は実際に移したバイト数。 * size<=0 の時は何もしない。0が返る。 * ストリーム bb1 が空の時も何もしない。0が返る。 * 終了後は、ストリーム bb1 の先頭 size バイト分がなくなる。 */ int move_bytes_from_bb_to_buf ( apr_bucket_brigade *bb1 , apr_size_t size , char *buf ) { int ret=0; apr_size_t remain=size; apr_bucket *b=NULL; if (size <=0) { return 0; } if (APR_BRIGADE_EMPTY(bb1)) { return 0; } if(0)puts("#move_bytes_from_bb(1)"); /* for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BUCKET_NEXT(b)) { バケット b をストリーム bb1 から削除し、bb2 に挿入した場合に、APR_BUCKET_NEXT(b) は bb2 での 位置関係に依存したものとなるため、ループの書き方はいつもと少し違う。毎回先頭のバケットを チェックする。 */ for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BRIGADE_FIRST(bb1)) { if(0)printf("(1)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (remain <= 0) { break; } if(0)puts("#move_bytes_from_bb(2)"); if (b->length == 0) { /* 削除しておく */ APR_BUCKET_REMOVE(b); continue; } if(0)puts("#move_bytes_from_bb(3)"); if (APR_BUCKET_IS_SOCKET(b)) { char *buf = NULL; apr_size_t len=0; apr_bucket_read(b, &buf, &len, APR_BLOCK_READ); /* 読み飛ばすだけ。 */ /* バケットがソケットの場合には read しないとストリームにデータが積まれないらしい。b->length も UNKNOWN(-1)。 */ } if(0)puts("#move_bytes_from_bb(4)"); if(0)printf("(2)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (b->length > remain) { if(0)printf("(3)remain=[%d], b->length=[%ld], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); /* b を remain バイト分と b->length-remain バイト分に分割 */ apr_bucket_split(b, remain); /* remain バイト分を bb1 から削除 */ APR_BUCKET_REMOVE(b); /* remain バイト分をバッファコピー */ apr_bucket_read(b, buf+ret, remain, 1); /* 移動した分をカウント */ ret += remain; /* ループを抜ける */ break; } if(0)puts("#move_bytes_from_bb(8)"); /* b を bb1 から削除 */ APR_BUCKET_REMOVE(b); if(0)puts("#move_bytes_from_bb(9)"); /* b を bb2 に追加 */ apr_bucket_read(b, buf+ret, b->length, 1); if(0)puts("#move_bytes_from_bb(10)"); /* 移動した分をカウント */ ret += b->length; remain -= b->length; if(0)printf("remain=[%d]\n", remain); } if(0)puts("#move_bytes_from_bb(11)"); return ret; } #endif /* * ストリーム bb1 から size バイト分の buckets をストリーム bb2 に移す。 * 返り値は実際に移したバイト数。 * size<=0 の時は何もしない。0が返る。 * ストリーム bb1 が空の時も何もしない。0が返る。 * 終了後は、ストリーム bb1 の先頭 size バイト分がなくなる。ストリーム bb2 は後尾に size バイト分増えている。 */ int move_bytes_from_bb (apr_bucket_brigade *bb1, apr_size_t size, apr_bucket_brigade *bb2) { int ret=0; apr_size_t remain=size; apr_bucket *b=NULL; if (size <=0) { return 0; } if (APR_BRIGADE_EMPTY(bb1)) { return 0; } if(0)puts("#move_bytes_from_bb(1)"); /* for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BUCKET_NEXT(b)) { バケット b をストリーム bb1 から削除し、bb2 に挿入した場合に、APR_BUCKET_NEXT(b) は bb2 での 位置関係に依存したものとなるため、ループの書き方はいつもと少し違う。毎回先頭のバケットを チェックする。 */ for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BRIGADE_FIRST(bb1)) { if(0)printf("(1)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (remain <= 0) { break; } if(0)puts("#move_bytes_from_bb(2)"); if (b->length == 0) { /* 削除しておく */ APR_BUCKET_REMOVE(b); continue; } if(0)puts("#move_bytes_from_bb(3)"); if (APR_BUCKET_IS_SOCKET(b)) { const char *buf = NULL; apr_size_t len=0; apr_bucket_read(b, &buf, &len, APR_BLOCK_READ); /* 読み飛ばすだけ。 */ /* バケットがソケットの場合には read しないとストリームにデータが積まれないらしい。b->length も UNKNOWN(-1)。 */ } if(0)puts("#move_bytes_from_bb(4)"); if(0)printf("(2)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (b->length > remain) { if(0)printf("(3)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); /* b を remain バイト分と b->length-remain バイト分に分割 */ apr_bucket_split(b, remain); if(0)puts("#move_bytes_from_bb(5)"); /* remain バイト分を bb1 から削除 */ APR_BUCKET_REMOVE(b); if(0)puts("#move_bytes_from_bb(6)"); /* remain バイト分を bb2 に追加 */ APR_BRIGADE_INSERT_TAIL(bb2, b); if(0)puts("#move_bytes_from_bb(7)"); /* 移動した分をカウント */ ret += remain; /* ループを抜ける */ break; } if(0)puts("#move_bytes_from_bb(8)"); /* b を bb1 から削除 */ APR_BUCKET_REMOVE(b); if(0)puts("#move_bytes_from_bb(9)"); /* b を bb2 に追加 */ APR_BRIGADE_INSERT_TAIL(bb2, b); if(0)puts("#move_bytes_from_bb(10)"); /* 移動した分をカウント */ ret += b->length; remain -= b->length; if(0)printf("remain=[%d]\n", remain); } if(0)puts("#move_bytes_from_bb(11)"); return ret; } /* * LF で終わる一行分の文字列をストリーム bb から切り出す。 * 最後のLF もしくは CRLF はヌル文字で置き換えられる。 * 返り値の文字列は必ずヌル文字で終わる * 終了後は、ストリーム bb から先頭の LF で終わる一行分のデータがなくなる。 * apr_brigade_split_line でエラーになった場合やサイズが大きすぎる場合(MAX_LINE_SIZE 以上)は NULL が返る。 * 空行の場合は、"" が返る。 */ char *get_line_from_bb (apr_bucket_brigade *bb) { apr_bucket *b = NULL; char *ret; char *p = NULL; apr_size_t len=0; apr_bucket_brigade *tmp_bb=apr_brigade_create(bb->p, bb->bucket_alloc); if (APR_SUCCESS != apr_brigade_split_line(tmp_bb, bb, APR_BLOCK_READ, MAX_LINE_SIZE)) { return NULL; } if (APR_BRIGADE_EMPTY(tmp_bb)) { /* こういうケースはある? */ return NULL; } p = ret = apr_palloc(bb->p, MAX_LINE_SIZE); for (b = APR_BRIGADE_FIRST(tmp_bb); b != APR_BRIGADE_SENTINEL(tmp_bb); b = APR_BUCKET_NEXT(b)) { const char *buf = NULL; apr_size_t buf_len; if (0 == b->length) { continue; } apr_bucket_read(b, &buf, &buf_len, APR_BLOCK_READ); if (p-ret+buf_len >= MAX_LINE_SIZE) { return NULL; /* 長すぎる時も NULL を返す。が、apr_brigade_split_line で成功した場合はこういうケースはないのかもしれない */ } memcpy(p, buf, buf_len); p += buf_len; } *p = '\0'; /* 必ずヌル文字で終端 */ len = p-ret; if (len > 0 && *(p-1) == '\n') { /* 行末の LF はヌル文字に */ *(p-1) = '\0'; } if (len > 1 && *(p-2) == '\r') { /* 行末の CR はヌル文字に */ *(p-2) = '\0'; } apr_brigade_destroy(tmp_bb); /* 一応デストロイしておく。*/ return ret; } /* * 概要:ストリームの中身をファイルに書き込む。バイト数指定なし。 * ストリームの中身は変更なし。 */ void write_from_bb_to_fp (apr_bucket_brigade *bb, apr_file_t *fp) { apr_bucket *b = NULL; const char *buf = NULL; apr_size_t len=0; for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { if (0 == b->length) { continue; } apr_bucket_read(b, &buf, &len, APR_BLOCK_READ); if(0)printf("len=[%d], b->length=[%d], metadata=[%d]\n", len, b->length, APR_BUCKET_IS_METADATA(b)); apr_file_write(fp, buf, &len); } } /* * ストリーム bb1 から size バイト分の buckets をファイルに書き込む。 * 返り値は実際に移したバイト数。 * size<=0 の時は何もしない。0が返る。 * ストリーム bb1 が空の時も何もしない。0が返る。 * 終了後は、ストリーム bb1 の先頭 size バイト分がなくなる。 */ int move_bytes_from_bb_to_fp (apr_bucket_brigade *bb1, apr_size_t size, apr_file_t *fp) { int ret=0; apr_size_t remain=size; apr_bucket *b=NULL; if (size <=0) { return 0; } if (APR_BRIGADE_EMPTY(bb1)) { return 0; } /* for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BUCKET_NEXT(b)) { バケット b をストリーム bb1 から削除し、bb2 に挿入した場合に、APR_BUCKET_NEXT(b) は bb2 での 位置関係に依存したものとなるため、ループの書き方はいつもと少し違う。毎回先頭のバケットを チェックする。 */ for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BRIGADE_FIRST(bb1)) { const char *buf = NULL; apr_size_t len=0; if(0)printf("(1)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (remain <= 0) { break; } if(0)printf("(1.1)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (b->length == 0) { /* 削除しておく */ APR_BUCKET_REMOVE(b); continue; } if(0)printf("(1.2)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (APR_BUCKET_IS_SOCKET(b)) { apr_bucket_read(b, &buf, &len, APR_BLOCK_READ); /* 読み飛ばすだけ。 */ /* バケットがソケットの場合にはサイズが不明(b->length == (-1))。 * read しないと実際のデータが積まれず、サイズが確定しない。 */ } if(0)printf("(2)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (b->length > remain) { if(0)printf("(3)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); /* b を remain バイト分と b->length-remain バイト分に分割 */ apr_bucket_split(b, remain); } /* remain バイト分を bb1 から削除 */ APR_BUCKET_REMOVE(b); /* remain バイト分をファイルに書き出し */ apr_bucket_read(b, &buf, &len, APR_BLOCK_READ); apr_file_write(fp, buf, &len); /* 書き出した分をカウント */ ret += len; remain -= len; if(0)printf("remain=[%d]\n", remain); } return ret; } /* * 以下、チャンク形式のデコーダ関数 */ /* -- MEMO -- * From RFC 2616 Section 3.6.1 * Chunked-Body = *chunk * last-chunk * trailer * CRLF * * chunk = chunk-size [ chunk-extension ] CRLF * chunk-data CRLF * chunk-size = 1*HEX * last-chunk = 1*("0") [ chunk-extension ] CRLF * * chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) * chunk-ext-name = token * chunk-ext-val = token | quoted-string * chunk-data = chunk-size(OCTET) * trailer = *(entity-header CRLF) * * From RFC 2616 Section 19.4.6 * length := 0 * read chunk-size, chunk-extension (if any) and CRLF * while (chunk-size > 0) { * read chunk-data and CRLF * append chunk-data to entity-body * length := length + chunk-size * read chunk-size and CRLF * } * read entity-header * while (entity-header not empty) { * append entity-header to existing header fields * read entity-header * } * Content-Length := length * Remove "chunked" from Transfer-Encoding * */ /* 概要:bb1 からの、chunk-size と CRLF の読み出し * 返り値: * 成功時:移動した chunk-size (0以上) * 失敗時:-1 * メモ:chunk-extension は無視する。 */ apr_size_t read_chunk_size_CRLF (apr_bucket_brigade *bb) { apr_size_t chunk_size = 0; char *line = get_line_from_bb(bb); if (! line) return -1; if (line) { char *endptr=NULL; errno = 0; /* 16進数の文字列をパースする。 * ・strtol では、"0x" で始まる場合も許容されることに注意 * ・strtol では、16進数の文字の後に続く文字は無視されることにも注意。 * 従って、chunk-extension が存在しても無視される。 * * XXX * long int から apr_size_t への変換は危険かもしれない。 */ chunk_size = (apr_size_t)strtol(line, &endptr, 16); if (errno) { return -1; } } return chunk_size; } /* 概要:bb1 からの、chunk-data の bb2 への移動と CRLF の読み出し * 返り値: * 成功時:移動したバイト数(0以上)。 * 失敗時:-1 */ int read_chunk_data_CRLF(apr_bucket_brigade *bb1, apr_size_t size, apr_bucket_brigade *bb2) { int length = 0; char *line = NULL; if ((length = move_bytes_from_bb(bb1, size, bb2)) < 0) { return -1; } if (size != length) { return -1; } if ((line = get_line_from_bb(bb1)) == NULL) { return -1; } if (strcmp(line, "")) { return -1; } return length; } /* 概要:bb1 からの、entity-header と CRLF の読み出し * 返り値: * 成功時:1 * 失敗時:0 最後の CRLF を見つけられなかった時 * メモ:読み出した entity-header は無視する。 */ int read_entity_header_CRLF(apr_bucket_brigade *bb1) { char *line=NULL; while ((line = get_line_from_bb(bb1))!=NULL) { if (!strcmp(line, "")) { return 1; } /* * この位置で、見つけた entity-header に関する何らかの処理(パースして key/value をテーブル * に保存など)を行なう。ここでは無視する。 */ } return 0; } #define read_trailer_CRLF read_entity_header_CRLF /* 概要:チャンク形式データのデコード * パラメータ * IN bb1 : デコード対象のデータを持つストリーム * OUT bb2 : デコード後のデータを保管するストリーム * 返り値:コンテンツの長さ(0以上)。デコード失敗時は -1 */ int decode_chunked_body (apr_bucket_brigade *bb1, apr_bucket_brigade *bb2) { int length = 0; int chunk_size=0; while ((chunk_size = read_chunk_size_CRLF(bb1)) > 0) { if (read_chunk_data_CRLF(bb1, chunk_size, bb2) < 0) { return -1; } length += chunk_size; } if (chunk_size < 0) { /* last-chunk がなかった */ return -1; } if (read_trailer_CRLF(bb1) == 0) { /* trailer と最後の CRLF の読み出し */ return -1; } return length; } /* * ストリーム bb1 から size バイト分の buckets をソケットに書き込む。 * 返り値は実際に移したバイト数。 * size<=0 の時は何もしない。0が返る。 * ストリーム bb1 が空の時も何もしない。0が返る。 * 終了後は、ストリーム bb1 の先頭 size バイト分がなくなる。 */ int move_bytes_from_bb_to_s (apr_bucket_brigade *bb1, apr_size_t size, apr_socket_t *s) { int ret=0; apr_size_t remain=size; apr_bucket *b=NULL; if (size <=0) { return 0; } if (APR_BRIGADE_EMPTY(bb1)) { return 0; } /* for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BUCKET_NEXT(b)) { バケット b をストリーム bb1 から削除し、bb2 に挿入した場合に、APR_BUCKET_NEXT(b) は bb2 での 位置関係に依存したものとなるため、ループの書き方はいつもと少し違う。毎回先頭のバケットを チェックする。 */ for (b = APR_BRIGADE_FIRST(bb1); b != APR_BRIGADE_SENTINEL(bb1); b = APR_BRIGADE_FIRST(bb1)) { const char *buf = NULL; apr_size_t len=0; if(0)printf("(1)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (remain <= 0) { break; } if (b->length == 0) { /* 削除しておく */ APR_BUCKET_REMOVE(b); continue; } if (APR_BUCKET_IS_SOCKET(b)) { apr_bucket_read(b, &buf, &len, APR_BLOCK_READ); /* 読み飛ばすだけ。 */ /* バケットがソケットの場合にはサイズが不明(b->length == (-1))。 * read しないと実際のデータが積まれず、サイズが確定しない。 */ } if(0)printf("(2)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); if (b->length > remain) { if(0)printf("(3)remain=[%d], b->length=[%d], metadata=[%d]\n", remain, b->length, APR_BUCKET_IS_METADATA(b)); /* b を remain バイト分と b->length-remain バイト分に分割 */ apr_bucket_split(b, remain); } /* remain バイト分を bb1 から削除 */ APR_BUCKET_REMOVE(b); /* remain バイト分をファイルに書き出し */ apr_bucket_read(b, &buf, &len, APR_BLOCK_READ); if(0)fwrite(buf, len, 1, stdout); apr_socket_send(s, buf, &len); /* 書き出した分をカウント */ ret += len; remain -= len; if(0)printf("remain=[%d]\n", remain); } return ret; }