/* myio_chunk.c * チャンク形式のデコーダ。MYIO 版 */ /* -- 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 * */ #include "myio_chunk.h" int myhextoi (const char *str) { int ret = 0; const char *p = str; if (!p) { return -1; } for (; *p; p++) { if (*p >= '0' && *p <= '9') { ret = ret * 16 + *p - '0'; } else if (*p >= 'a' && *p <= 'f') { ret = ret * 16 + *p - 'a' + 10; } else if (*p >= 'A' && *p <= 'F') { ret = ret * 16 + *p - 'A' + 10; } else { break; } } return ret; } /* 概要:chunk-size と CRLF の読み出し * 返り値: * 成功時:chunk-size (0以上) * 失敗時:-1 * メモ:chunk-extension は無視する。 */ int MYIO_read_chunk_size_CRLF ( MYIO *in ) { int chunk_size = 0; char line[MYIO_BUF_SIZE]; if (MYIO_gets(in, line, sizeof(line)) < 0) { return -1; } chunk_size = myhextoi(line); if (chunk_size < 0) { return -1; } return chunk_size; } /* 概要:chunk-data と CRLF の読み出し * 返り値: * 成功時:読み出した chunk-data のバイト数(0以上)。 * 失敗時:-1 */ int MYIO_read_chunk_data_CRLF( MYIO *in , int size , MYIO *out ) { int length = 0; char line[MYIO_BUF_SIZE]; if ((length = MYIO_move (in, size, out)) < 0) { return -1; } if (size != length) { return -1; } if (MYIO_gets (in, line, sizeof(line)) < 0) { return -1; } if (line[0] != '\0') { return -1; } return length; } /* 概要:entity-header と CRLF の読み出し * 返り値: * 成功時:1 * 失敗時:0 最後の CRLF を見つけられなかった時 * メモ:読み出した entity-header は f に渡される * void f (const char *line, void *ctx); */ int MYIO_read_entity_headers_CRLF_do( MYIO *in /* 入力ストリーム */ , void *f , void *ctx ) { void (*func)(char *, void *) = f; char line[MYIO_BUF_SIZE]; while (MYIO_gets(in, line, sizeof(line)) >= 0) { if (line[0] == '\0') { return 1; } /* 取り出した entity-header の処理 */ if (func) (*func)(line, ctx); } return 0; } #define MYIO_read_trailer_CRLF(b) \ MYIO_read_entity_headers_CRLF_do((b),((void*)0),((void*)0)) /* 概要:チャンク形式データのデコード * 返り値:コンテンツの長さ(0以上)。デコード失敗時は -1 * メモ:読み出した entity-header はコールバック関数 f に渡される * void f (const char *entity_header, void *ctx); */ int MYIO_decode_chunked_body_do ( MYIO *in /* IN: 入力ストリーム*/ , MYIO *out /* OUT: 出力ストリーム */ , void *f /* IN: コールバック関数 */ , void *ctx /* IN: コールバック関数用コンテクストデータ */ ) { int length = 0; int chunk_size=0; while ((chunk_size = MYIO_read_chunk_size_CRLF(in)) > 0) { if (MYIO_read_chunk_data_CRLF(in, chunk_size, out) < 0) { return -1; } length += chunk_size; } if (chunk_size < 0) { /* last-chunk がなかった */ return -1; } /* trailer と最後の CRLF の読み出し */ if (MYIO_read_entity_headers_CRLF_do(in, f, ctx) == 0) { return -1; } return length; } /* 概要:チャンク形式データのデコード * 返り値:コンテンツの長さ(0以上)。デコード失敗時は -1 * メモ:読み出した entity-header は無視される */ int MYIO_decode_chunked_body ( MYIO *in /* IN: 入力ストリーム*/ , MYIO *out /* OUT: 出力ストリーム */ ) { return MYIO_decode_chunked_body_do (in, out, (void*)0, (void*)0); } /* * チャンク形式データをデコードする MYIO フィルタ */ #define MYIO_DECHUNK_BUF_SIZE 1024 typedef struct myio_dechunk_ctx_st { MYIO *in; /* read していない残りのチャンクサイズ */ int chunk_remain; /* バッファに保持しているデータサイズ */ int size; /* バッファ中の現在の位置 */ int pos; /* バッファ */ char buf[MYIO_DECHUNK_BUF_SIZE]; } myio_dechunk_ctx; int myio_dechunk_read ( MYIO *in , char * buf , int size ) { int read_size = 0; myio_dechunk_ctx *ctx = (myio_dechunk_ctx*)(in->ptr); #ifdef DEBUG if(0)puts("#myio_dechunk_read(1)"); #endif if (!ctx) { return -1; } if (!ctx->in) { return -1; } /* バッファが空か、すでに読み出し済みの時。 * 最初に呼ばれる時: ctx->pos==0 ctx->size==0 * 読み出し済みの時: ctx->pos >= ctx->size */ if (ctx->pos >= ctx->size) { #ifdef DEBUG if(0)puts("#myio_dechunk_read(2)"); #endif /* 現在読み出し中のチャンクがもうない時 */ if (ctx->chunk_remain <= 0) { ctx->chunk_remain = MYIO_read_chunk_size_CRLF(ctx->in); if (ctx->chunk_remain < 0) { #ifdef DEBUG if(0)puts("#myio_dechunk_read(3)"); #endif return ctx->chunk_remain; } /* ラストチャンクの時、trailer と最後の CRLF の読み出し */ if (ctx->chunk_remain == 0) { if (MYIO_read_entity_headers_CRLF_do(ctx->in, (void*)0, (void*)0) == 0) { #ifdef DEBUG if(0)puts("#myio_dechunk_read(4)"); #endif return -1; } } } /* チャンクをバッファのサイズの範囲内で読み出し */ ctx->pos = 0; { int nbytes = sizeof(ctx->buf); if (ctx->chunk_remain < nbytes) { nbytes = ctx->chunk_remain; } ctx->size = MYIO_read(ctx->in, ctx->buf, nbytes); } if (ctx->size <= 0) { #ifdef DEBUG if(0)puts("#myio_dechunk_read(5)"); #endif return ctx->size; } ctx->chunk_remain -= ctx->size; if (ctx->chunk_remain == 0) { char line[3]; /* チャンクの後の CRLF 読み出し */ if (MYIO_gets(ctx->in, line, sizeof(line)) < 0) { return -1; } if (line[0] != '\0') { return -1; } } #ifdef DEBUG if(0)puts("#myio_dechunk_read(5.1)"); #endif } while (ctx->pos < ctx->size) { #ifdef DEBUG if(0)printf("."); #endif if (read_size >= size) { break; } buf[read_size++] = ctx->buf[ctx->pos++]; } #ifdef DEBUG if(0)puts("#myio_dechunk_read(7)"); #endif return read_size; } #define MYIO_METHOD_NAME_DECHUNK_FILTER "_DECHUNK_FILTER_" MYIO_METHOD myio_dechunk_method = { MYIO_METHOD_NAME_DECHUNK_FILTER , (void*)0 , myio_dechunk_read , (void*)0 , (void*)0 }; /* * チャンク形式データ用デコーダのフィルタ * alloc_func はメモリアロケーションの関数 * void * alloc_func(void *alloc_func_ctx, int alloc_size) */ MYIO *MYIO_dechunk_filter_new ( MYIO *in /* デコード対象ストリーム */ , void *alloc_func /* メモリアロケーションの関数 */ , void *alloc_func_ctx /* メモリアロケーションの関数のコンテクストデータ */ ) { MYIO *myio; myio_dechunk_ctx *ctx; void * (*alloc_f)(void*,int) = alloc_func; if (alloc_f == (void*)0) { return (void*)0; } ctx = (myio_dechunk_ctx *)alloc_f(alloc_func_ctx, sizeof(myio_dechunk_ctx)); ctx->in = in; ctx->chunk_remain = 0; ctx->size = 0; ctx->pos = 0; myio = (MYIO*)alloc_f(alloc_func_ctx, sizeof(MYIO)); return MYIO_make(myio, &myio_dechunk_method, 1, 0, ctx); } /* * デコード対象ストリームの取得 */ MYIO *MYIO_buf_dechunk_filter_MYIO ( MYIO *myio ) { if (!myio) { return (void*)0; } if (myio->ptr) { if (myio->method) { if (mystreq(myio->method->name, MYIO_METHOD_NAME_DECHUNK_FILTER)) { myio_dechunk_ctx *ctx = myio->ptr; return ctx->in; } } } return (void*)0; } /* チャンク形式データ用デコーダのフィルタの解放 * ・対象ストリームの解放は行わない。 * ・free_func は領域の解放を行う関数。void free_func(free_func_ctx, data); */ void MYIO_dechunk_filter_free( MYIO *myio , void *free_func , void *free_func_ctx ) { void (*free_f)(void*,void*) = free_func; if (!myio || !free_func) { return; } if (myio->ptr) { if (myio->method) { if (mystreq(myio->method->name, MYIO_METHOD_NAME_DECHUNK_FILTER)) { free_f(free_func_ctx, myio->ptr); free_f(free_func_ctx, myio); } } } }