myio_chunk.c

最終更新:2009/11/8

myio_chunk.c

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: }
Copyright (C) KAKU PROJECT (2009)KAKU PROJECT (2009)