myio_chunk.c
最終更新:2009/11/8
001: /* myio_chunk.c 002: * チャンク形式のデコーダ。MYIO 版 003: */ 004: 005: /* -- MEMO -- 006: * From RFC 2616 Section 3.6.1 007: * Chunked-Body = *chunk 008: * last-chunk 009: * trailer 010: * CRLF 011: * 012: * chunk = chunk-size [ chunk-extension ] CRLF 013: * chunk-data CRLF 014: * chunk-size = 1*HEX 015: * last-chunk = 1*("0") [ chunk-extension ] CRLF 016: * 017: * chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) 018: * chunk-ext-name = token 019: * chunk-ext-val = token | quoted-string 020: * chunk-data = chunk-size(OCTET) 021: * trailer = *(entity-header CRLF) 022: * 023: * From RFC 2616 Section 19.4.6 024: * length := 0 025: * read chunk-size, chunk-extension (if any) and CRLF 026: * while (chunk-size > 0) { 027: * read chunk-data and CRLF 028: * append chunk-data to entity-body 029: * length := length + chunk-size 030: * read chunk-size and CRLF 031: * } 032: * read entity-header 033: * while (entity-header not empty) { 034: * append entity-header to existing header fields 035: * read entity-header 036: * } 037: * Content-Length := length 038: * Remove "chunked" from Transfer-Encoding 039: * 040: */ 041: 042: #include "myio_chunk.h" 043: 044: int myhextoi (const char *str) { 045: int ret = 0; 046: 047: const char *p = str; 048: if (!p) { 049: return -1; 050: } 051: for (; *p; p++) { 052: if (*p >= '0' && *p <= '9') { 053: ret = ret * 16 + *p - '0'; 054: } else if (*p >= 'a' && *p <= 'f') { 055: ret = ret * 16 + *p - 'a' + 10; 056: } else if (*p >= 'A' && *p <= 'F') { 057: ret = ret * 16 + *p - 'A' + 10; 058: } else { 059: break; 060: } 061: } 062: return ret; 063: } 064: 065: /* 概要:chunk-size と CRLF の読み出し 066: * 返り値: 067: * 成功時:chunk-size (0以上) 068: * 失敗時:-1 069: * メモ:chunk-extension は無視する。 070: */ 071: 072: int MYIO_read_chunk_size_CRLF ( 073: MYIO *in 074: ) { 075: int chunk_size = 0; 076: 077: char line[MYIO_BUF_SIZE]; 078: 079: if (MYIO_gets(in, line, sizeof(line)) < 0) { 080: return -1; 081: } 082: chunk_size = myhextoi(line); 083: 084: if (chunk_size < 0) { 085: return -1; 086: } 087: return chunk_size; 088: } 089: 090: /* 概要:chunk-data と CRLF の読み出し 091: * 返り値: 092: * 成功時:読み出した chunk-data のバイト数(0以上)。 093: * 失敗時:-1 094: */ 095: 096: int MYIO_read_chunk_data_CRLF( 097: MYIO *in 098: , int size 099: , MYIO *out 100: ) { 101: int length = 0; 102: char line[MYIO_BUF_SIZE]; 103: 104: if ((length = MYIO_move (in, size, out)) < 0) { 105: return -1; 106: } 107: if (size != length) { 108: return -1; 109: } 110: 111: if (MYIO_gets (in, line, sizeof(line)) < 0) { 112: return -1; 113: } 114: 115: if (line[0] != '\0') { 116: return -1; 117: } 118: return length; 119: } 120: 121: /* 概要:entity-header と CRLF の読み出し 122: * 返り値: 123: * 成功時:1 124: * 失敗時:0 最後の CRLF を見つけられなかった時 125: * メモ:読み出した entity-header は f に渡される 126: * void f (const char *line, void *ctx); 127: */ 128: 129: int MYIO_read_entity_headers_CRLF_do( 130: MYIO *in /* 入力ストリーム */ 131: , void *f 132: , void *ctx 133: ) { 134: void (*func)(char *, void *) = f; 135: char line[MYIO_BUF_SIZE]; 136: while (MYIO_gets(in, line, sizeof(line)) >= 0) { 137: if (line[0] == '\0') { 138: return 1; 139: } 140: /* 取り出した entity-header の処理 */ 141: 142: if (func) 143: (*func)(line, ctx); 144: } 145: return 0; 146: } 147: 148: #define MYIO_read_trailer_CRLF(b) \ 149: MYIO_read_entity_headers_CRLF_do((b),((void*)0),((void*)0)) 150: 151: /* 概要:チャンク形式データのデコード 152: * 返り値:コンテンツの長さ(0以上)。デコード失敗時は -1 153: * メモ:読み出した entity-header はコールバック関数 f に渡される 154: * void f (const char *entity_header, void *ctx); 155: */ 156: 157: int MYIO_decode_chunked_body_do ( 158: MYIO *in /* IN: 入力ストリーム*/ 159: , MYIO *out /* OUT: 出力ストリーム */ 160: , void *f /* IN: コールバック関数 */ 161: , void *ctx /* IN: コールバック関数用コンテクストデータ */ 162: ) { 163: int length = 0; 164: int chunk_size=0; 165: 166: while ((chunk_size = MYIO_read_chunk_size_CRLF(in)) > 0) { 167: if (MYIO_read_chunk_data_CRLF(in, chunk_size, out) < 0) { 168: return -1; 169: } 170: length += chunk_size; 171: } 172: 173: if (chunk_size < 0) { /* last-chunk がなかった */ 174: return -1; 175: } 176: 177: /* trailer と最後の CRLF の読み出し */ 178: if (MYIO_read_entity_headers_CRLF_do(in, f, ctx) == 0) { 179: return -1; 180: } 181: 182: return length; 183: } 184: 185: /* 概要:チャンク形式データのデコード 186: * 返り値:コンテンツの長さ(0以上)。デコード失敗時は -1 187: * メモ:読み出した entity-header は無視される 188: */ 189: 190: int MYIO_decode_chunked_body ( 191: MYIO *in /* IN: 入力ストリーム*/ 192: , MYIO *out /* OUT: 出力ストリーム */ 193: ) { 194: return MYIO_decode_chunked_body_do (in, out, (void*)0, (void*)0); 195: } 196: 197: /* 198: * チャンク形式データをデコードする MYIO フィルタ 199: */ 200: 201: #define MYIO_DECHUNK_BUF_SIZE 1024 202: 203: typedef struct myio_dechunk_ctx_st { 204: MYIO *in; 205: /* read していない残りのチャンクサイズ */ 206: int chunk_remain; 207: 208: /* バッファに保持しているデータサイズ */ 209: int size; 210: 211: /* バッファ中の現在の位置 */ 212: int pos; 213: 214: /* バッファ */ 215: char buf[MYIO_DECHUNK_BUF_SIZE]; 216: } myio_dechunk_ctx; 217: 218: 219: int myio_dechunk_read ( 220: MYIO *in 221: , char * buf 222: , int size 223: ) { 224: int read_size = 0; 225: myio_dechunk_ctx *ctx = (myio_dechunk_ctx*)(in->ptr); 226: 227: #ifdef DEBUG 228: if(0)puts("#myio_dechunk_read(1)"); 229: #endif 230: if (!ctx) { 231: return -1; 232: } 233: 234: if (!ctx->in) { 235: return -1; 236: } 237: 238: /* バッファが空か、すでに読み出し済みの時。 239: * 最初に呼ばれる時: ctx->pos==0 ctx->size==0 240: * 読み出し済みの時: ctx->pos >= ctx->size 241: */ 242: if (ctx->pos >= ctx->size) { 243: #ifdef DEBUG 244: if(0)puts("#myio_dechunk_read(2)"); 245: #endif 246: 247: /* 現在読み出し中のチャンクがもうない時 */ 248: 249: if (ctx->chunk_remain <= 0) { 250: ctx->chunk_remain = MYIO_read_chunk_size_CRLF(ctx->in); 251: if (ctx->chunk_remain < 0) { 252: 253: #ifdef DEBUG 254: if(0)puts("#myio_dechunk_read(3)"); 255: #endif 256: 257: return ctx->chunk_remain; 258: } 259: /* ラストチャンクの時、trailer と最後の CRLF の読み出し */ 260: if (ctx->chunk_remain == 0) { 261: if (MYIO_read_entity_headers_CRLF_do(ctx->in, (void*)0, (void*)0) == 0) { 262: 263: #ifdef DEBUG 264: if(0)puts("#myio_dechunk_read(4)"); 265: #endif 266: return -1; 267: } 268: } 269: } 270: 271: /* チャンクをバッファのサイズの範囲内で読み出し */ 272: ctx->pos = 0; 273: { 274: int nbytes = sizeof(ctx->buf); 275: if (ctx->chunk_remain < nbytes) { 276: nbytes = ctx->chunk_remain; 277: } 278: ctx->size = MYIO_read(ctx->in, ctx->buf, nbytes); 279: } 280: if (ctx->size <= 0) { 281: 282: #ifdef DEBUG 283: if(0)puts("#myio_dechunk_read(5)"); 284: #endif 285: 286: return ctx->size; 287: } 288: ctx->chunk_remain -= ctx->size; 289: 290: if (ctx->chunk_remain == 0) { 291: char line[3]; 292: /* チャンクの後の CRLF 読み出し */ 293: if (MYIO_gets(ctx->in, line, sizeof(line)) < 0) { 294: return -1; 295: } 296: if (line[0] != '\0') { 297: return -1; 298: } 299: } 300: 301: 302: #ifdef DEBUG 303: if(0)puts("#myio_dechunk_read(5.1)"); 304: #endif 305: } 306: 307: while (ctx->pos < ctx->size) { 308: #ifdef DEBUG 309: if(0)printf("."); 310: #endif 311: if (read_size >= size) { 312: break; 313: } 314: buf[read_size++] = ctx->buf[ctx->pos++]; 315: } 316: 317: #ifdef DEBUG 318: if(0)puts("#myio_dechunk_read(7)"); 319: #endif 320: 321: return read_size; 322: } 323: 324: #define MYIO_METHOD_NAME_DECHUNK_FILTER "_DECHUNK_FILTER_" 325: 326: MYIO_METHOD myio_dechunk_method = { 327: MYIO_METHOD_NAME_DECHUNK_FILTER 328: , (void*)0 329: , myio_dechunk_read 330: , (void*)0 331: , (void*)0 332: }; 333: 334: /* 335: * チャンク形式データ用デコーダのフィルタ 336: * alloc_func はメモリアロケーションの関数 337: * void * alloc_func(void *alloc_func_ctx, int alloc_size) 338: */ 339: 340: MYIO *MYIO_dechunk_filter_new ( 341: MYIO *in /* デコード対象ストリーム */ 342: , void *alloc_func /* メモリアロケーションの関数 */ 343: , void *alloc_func_ctx /* メモリアロケーションの関数のコンテクストデータ */ 344: ) { 345: MYIO *myio; 346: myio_dechunk_ctx *ctx; 347: void * (*alloc_f)(void*,int) = alloc_func; 348: 349: if (alloc_f == (void*)0) { 350: return (void*)0; 351: } 352: ctx = (myio_dechunk_ctx *)alloc_f(alloc_func_ctx, sizeof(myio_dechunk_ctx)); 353: 354: ctx->in = in; 355: ctx->chunk_remain = 0; 356: ctx->size = 0; 357: ctx->pos = 0; 358: 359: myio = (MYIO*)alloc_f(alloc_func_ctx, sizeof(MYIO)); 360: 361: return MYIO_make(myio, &myio_dechunk_method, 1, 0, ctx); 362: } 363: 364: 365: /* 366: * デコード対象ストリームの取得 367: */ 368: 369: MYIO *MYIO_buf_dechunk_filter_MYIO ( 370: MYIO *myio 371: ) { 372: 373: if (!myio) { 374: return (void*)0; 375: } 376: 377: if (myio->ptr) { 378: if (myio->method) { 379: if (mystreq(myio->method->name, MYIO_METHOD_NAME_DECHUNK_FILTER)) { 380: myio_dechunk_ctx *ctx = myio->ptr; 381: return ctx->in; 382: } 383: } 384: } 385: return (void*)0; 386: } 387: 388: /* チャンク形式データ用デコーダのフィルタの解放 389: * ・対象ストリームの解放は行わない。 390: * ・free_func は領域の解放を行う関数。void free_func(free_func_ctx, data); 391: */ 392: 393: void MYIO_dechunk_filter_free( 394: MYIO *myio 395: , void *free_func 396: , void *free_func_ctx 397: ) { 398: void (*free_f)(void*,void*) = free_func; 399: 400: if (!myio || !free_func) { 401: return; 402: } 403: 404: if (myio->ptr) { 405: if (myio->method) { 406: if (mystreq(myio->method->name, MYIO_METHOD_NAME_DECHUNK_FILTER)) { 407: free_f(free_func_ctx, myio->ptr); 408: free_f(free_func_ctx, myio); 409: } 410: } 411: } 412: }
KAKU PROJECT (2009) |