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) |