- finish work on encoding api
[m6w6/ext-http] / http_filter_api.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #define HTTP_WANT_ZLIB
20 #include "php_http.h"
21
22 #ifdef ZEND_ENGINE_2
23
24 #include "php_streams.h"
25 #include "php_http_api.h"
26 #include "php_http_encoding_api.h"
27 #include "php_http_filter_api.h"
28
29 PHP_MINIT_FUNCTION(http_filter)
30 {
31 php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC);
32 return SUCCESS;
33 }
34
35 /*
36 -
37 */
38
39 #define HTTP_FILTER_PARAMS \
40 php_stream *stream, \
41 php_stream_filter *this, \
42 php_stream_bucket_brigade *buckets_in, \
43 php_stream_bucket_brigade *buckets_out, \
44 size_t *bytes_consumed, int flags \
45 TSRMLS_DC
46 #define HTTP_FILTER_OP(filter) \
47 http_filter_op_ ##filter
48 #define HTTP_FILTER_OPS(filter) \
49 php_stream_filter_ops HTTP_FILTER_OP(filter)
50 #define HTTP_FILTER_DTOR(filter) \
51 http_filter_ ##filter## _dtor
52 #define HTTP_FILTER_DESTRUCTOR(filter) \
53 void HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC)
54 #define HTTP_FILTER_FUNC(filter) \
55 http_filter_ ##filter
56 #define HTTP_FILTER_FUNCTION(filter) \
57 php_stream_filter_status_t HTTP_FILTER_FUNC(filter)(HTTP_FILTER_PARAMS)
58 #define HTTP_FILTER_BUFFER(filter) \
59 http_filter_ ##filter## _buffer
60
61 #define NEW_BUCKET(data, length) \
62 { \
63 char *__data; \
64 php_stream_bucket *__buck; \
65 \
66 __data = pemalloc(length, this->is_persistent); \
67 if (!__data) { \
68 return PSFS_ERR_FATAL; \
69 } \
70 memcpy(__data, data, length); \
71 \
72 __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
73 if (!__buck) { \
74 pefree(__data, this->is_persistent); \
75 return PSFS_ERR_FATAL; \
76 } \
77 \
78 php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
79 }
80
81 typedef struct {
82 phpstr buffer;
83 ulong hexlen;
84 } HTTP_FILTER_BUFFER(chunked_decode);
85
86 #ifdef HTTP_HAVE_ZLIB
87 typedef http_encoding_stream HTTP_FILTER_BUFFER(deflate);
88 typedef http_encoding_stream HTTP_FILTER_BUFFER(inflate);
89 #endif /* HTTP_HAVE_ZLIB */
90
91
92 static HTTP_FILTER_FUNCTION(chunked_decode)
93 {
94 int out_avail = 0;
95 php_stream_bucket *ptr, *nxt;
96 HTTP_FILTER_BUFFER(chunked_decode) *buffer = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
97
98 if (bytes_consumed) {
99 *bytes_consumed = 0;
100 }
101
102 /* new data available? */
103 if (buckets_in->head) {
104
105 /* fetch available bucket data */
106 for (ptr = buckets_in->head; ptr; ptr = nxt) {
107 nxt = ptr->next;
108 if (bytes_consumed) {
109 *bytes_consumed += ptr->buflen;
110 }
111
112 if ((size_t) -1 == phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen)) {
113 return PSFS_ERR_FATAL;
114 }
115
116 php_stream_bucket_unlink(ptr TSRMLS_CC);
117 php_stream_bucket_delref(ptr TSRMLS_CC);
118 }
119 }
120 if (!phpstr_fix(PHPSTR(buffer))) {
121 return PSFS_ERR_FATAL;
122 }
123
124 /* we have data in our buffer */
125 while (PHPSTR_LEN(buffer)) {
126
127 /* we already know the size of the chunk and are waiting for data */
128 if (buffer->hexlen) {
129
130 /* not enough data buffered */
131 if (PHPSTR_LEN(buffer) < buffer->hexlen) {
132
133 /* flush anyway? */
134 if (flags & PSFS_FLAG_FLUSH_INC) {
135
136 /* flush all data (should only be chunk data) */
137 out_avail = 1;
138 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
139
140 /* waiting for less data now */
141 buffer->hexlen -= PHPSTR_LEN(buffer);
142 /* no more buffered data */
143 phpstr_reset(PHPSTR(buffer));
144 /* break */
145 }
146
147 /* we have too less data and don't need to flush */
148 else {
149 break;
150 }
151 }
152
153 /* we seem to have all data of the chunk */
154 else {
155 out_avail = 1;
156 NEW_BUCKET(PHPSTR_VAL(buffer), buffer->hexlen);
157
158 /* remove outgoing data from the buffer */
159 phpstr_cut(PHPSTR(buffer), 0, buffer->hexlen);
160 /* reset hexlen */
161 buffer->hexlen = 0;
162 /* continue */
163 }
164 }
165
166 /* we don't know the length of the chunk yet */
167 else {
168 size_t off = 0;
169
170 /* ignore preceeding CRLFs (too loose?) */
171 while (off < PHPSTR_LEN(buffer) && (
172 PHPSTR_VAL(buffer)[off] == 0xa ||
173 PHPSTR_VAL(buffer)[off] == 0xd)) {
174 ++off;
175 }
176 if (off) {
177 phpstr_cut(PHPSTR(buffer), 0, off);
178 }
179
180 /* still data there? */
181 if (PHPSTR_LEN(buffer)) {
182 int eollen;
183 const char *eolstr;
184
185 /* we need eol, so we can be sure we have all hex digits */
186 phpstr_fix(PHPSTR(buffer));
187 if ((eolstr = http_locate_eol(PHPSTR_VAL(buffer), &eollen))) {
188 char *stop = NULL;
189
190 /* read in chunk size */
191 buffer->hexlen = strtoul(PHPSTR_VAL(buffer), &stop, 16);
192
193 /* if strtoul() stops at the beginning of the buffered data
194 there's domething oddly wrong, i.e. bad input */
195 if (stop == PHPSTR_VAL(buffer)) {
196 return PSFS_ERR_FATAL;
197 }
198
199 /* cut out <chunk size hex><chunk extension><eol> */
200 phpstr_cut(PHPSTR(buffer), 0, eolstr + eollen - PHPSTR_VAL(buffer));
201 /* buffer->hexlen is 0 now or contains the size of the next chunk */
202 /* continue */
203 } else {
204 /* we have not enough data buffered to read in chunk size */
205 break;
206 }
207 }
208 /* break */
209 }
210 }
211
212 /* flush before close, but only if we are already waiting for more data */
213 if ((flags & PSFS_FLAG_FLUSH_CLOSE) && buffer->hexlen && PHPSTR_LEN(buffer)) {
214 out_avail = 1;
215 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
216 phpstr_reset(PHPSTR(buffer));
217 buffer->hexlen = 0;
218 }
219
220 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
221 }
222
223 static HTTP_FILTER_DESTRUCTOR(chunked_decode)
224 {
225 HTTP_FILTER_BUFFER(chunked_decode) *b = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
226
227 phpstr_dtor(PHPSTR(b));
228 pefree(b, this->is_persistent);
229 }
230
231 static HTTP_FILTER_FUNCTION(chunked_encode)
232 {
233 int out_avail = 0;
234 php_stream_bucket *ptr, *nxt;
235
236 if (bytes_consumed) {
237 *bytes_consumed = 0;
238 }
239
240 /* new data available? */
241 if (buckets_in->head) {
242 phpstr buf;
243 out_avail = 1;
244
245 phpstr_init(&buf);
246
247 /* fetch available bucket data */
248 for (ptr = buckets_in->head; ptr; ptr = nxt) {
249 nxt = ptr->next;
250 if (bytes_consumed) {
251 *bytes_consumed += ptr->buflen;
252 }
253
254 phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen);
255 phpstr_append(&buf, ptr->buf, ptr->buflen);
256 phpstr_appends(&buf, HTTP_CRLF);
257
258 /* pass through */
259 NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf));
260 /* reset */
261 phpstr_reset(&buf);
262
263 php_stream_bucket_unlink(ptr TSRMLS_CC);
264 php_stream_bucket_delref(ptr TSRMLS_CC);
265 }
266
267 /* free buffer */
268 phpstr_dtor(&buf);
269 }
270
271 /* terminate with "0" */
272 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
273 out_avail = 1;
274 NEW_BUCKET("0" HTTP_CRLF, lenof("0" HTTP_CRLF));
275 }
276
277 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
278 }
279
280 static HTTP_FILTER_OPS(chunked_decode) = {
281 HTTP_FILTER_FUNC(chunked_decode),
282 HTTP_FILTER_DTOR(chunked_decode),
283 "http.chunked_decode"
284 };
285
286 static HTTP_FILTER_OPS(chunked_encode) = {
287 HTTP_FILTER_FUNC(chunked_encode),
288 NULL,
289 "http.chunked_encode"
290 };
291
292 #ifdef HTTP_HAVE_ZLIB
293
294 static HTTP_FILTER_FUNCTION(deflate)
295 {
296 int out_avail = 0;
297 php_stream_bucket *ptr, *nxt;
298 HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) this->abstract;
299
300 if (bytes_consumed) {
301 *bytes_consumed = 0;
302 }
303
304 /* new data available? */
305 if (buckets_in->head) {
306
307 /* fetch available bucket data */
308 for (ptr = buckets_in->head; ptr; ptr = nxt) {
309 char *encoded = NULL;
310 size_t encoded_len = 0;
311
312 nxt = ptr->next;
313 if (bytes_consumed) {
314 *bytes_consumed += ptr->buflen;
315 }
316
317 if (ptr->buflen) {
318 http_encoding_deflate_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len);
319 if (encoded) {
320 out_avail = 1;
321 NEW_BUCKET(encoded, encoded_len);
322 efree(encoded);
323 }
324 }
325
326 php_stream_bucket_unlink(ptr TSRMLS_CC);
327 php_stream_bucket_delref(ptr TSRMLS_CC);
328 }
329 }
330
331 /* flush & close */
332 if (flags & PSFS_FLAG_FLUSH_INC) {
333 char *encoded = NULL;
334 size_t encoded_len = 0;
335
336 http_encoding_deflate_stream_flush(buffer, &encoded, &encoded_len);
337 if (encoded) {
338 out_avail = 1;
339 NEW_BUCKET(encoded, encoded_len);
340 efree(encoded);
341 }
342 }
343
344 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
345 char *encoded = NULL;
346 size_t encoded_len = 0;
347
348 http_encoding_deflate_stream_finish(buffer, &encoded, &encoded_len);
349 if (encoded) {
350 out_avail = 1;
351 NEW_BUCKET(encoded, encoded_len);
352 efree(encoded);
353 }
354 }
355
356 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
357 }
358
359 static HTTP_FILTER_FUNCTION(inflate)
360 {
361 int out_avail = 0;
362 php_stream_bucket *ptr, *nxt;
363 HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) this->abstract;
364
365 if (bytes_consumed) {
366 *bytes_consumed = 0;
367 }
368
369 /* new data available? */
370 if (buckets_in->head) {
371
372 /* fetch available bucket data */
373 for (ptr = buckets_in->head; ptr; ptr = nxt) {
374 char *decoded = NULL;
375 size_t decoded_len = 0;
376
377 nxt = ptr->next;
378 if (bytes_consumed) {
379 *bytes_consumed += ptr->buflen;
380 }
381
382 if (ptr->buflen) {
383 http_encoding_inflate_stream_update(buffer, ptr->buf, ptr->buflen, &decoded, &decoded_len);
384 if (decoded) {
385 out_avail = 1;
386 NEW_BUCKET(decoded, decoded_len);
387 efree(decoded);
388 }
389 }
390
391 php_stream_bucket_unlink(ptr TSRMLS_CC);
392 php_stream_bucket_delref(ptr TSRMLS_CC);
393 }
394 }
395
396 /* flush & close */
397 if (flags & PSFS_FLAG_FLUSH_INC) {
398 char *decoded = NULL;
399 size_t decoded_len = 0;
400
401 http_encoding_inflate_stream_flush(buffer, &decoded, &decoded_len);
402 if (decoded) {
403 out_avail = 1;
404 NEW_BUCKET(decoded, decoded_len);
405 efree(decoded);
406 }
407 }
408
409 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
410 char *decoded = NULL;
411 size_t decoded_len = 0;
412
413 http_encoding_inflate_stream_finish(buffer, &decoded, &decoded_len);
414 if (decoded) {
415 out_avail = 1;
416 NEW_BUCKET(decoded, decoded_len);
417 efree(decoded);
418 }
419 }
420
421 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
422 }
423
424 static HTTP_FILTER_DESTRUCTOR(deflate)
425 {
426 HTTP_FILTER_BUFFER(deflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) this->abstract;
427 http_encoding_deflate_stream_free(&buffer);
428 }
429
430 static HTTP_FILTER_DESTRUCTOR(inflate)
431 {
432 HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) this->abstract;
433 http_encoding_inflate_stream_free(&buffer);
434 }
435
436 static HTTP_FILTER_OPS(deflate) = {
437 HTTP_FILTER_FUNC(deflate),
438 HTTP_FILTER_DTOR(deflate),
439 "http.deflate"
440 };
441
442 static HTTP_FILTER_OPS(inflate) = {
443 HTTP_FILTER_FUNC(inflate),
444 HTTP_FILTER_DTOR(inflate),
445 "http.inflate"
446 };
447
448 #endif /* HTTP_HAVE_ZLIB */
449
450 static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
451 {
452 zval **tmp = &params;
453 php_stream_filter *f = NULL;
454
455 if (!strcasecmp(name, "http.chunked_decode")) {
456 HTTP_FILTER_BUFFER(chunked_decode) *b = NULL;
457
458 if ((b = pecalloc(1, sizeof(HTTP_FILTER_BUFFER(chunked_decode)), p))) {
459 phpstr_init_ex(PHPSTR(b), 4096, p ? PHPSTR_INIT_PERSISTENT : 0);
460 if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_decode), b, p))) {
461 pefree(b, p);
462 }
463 }
464 } else
465
466 if (!strcasecmp(name, "http.chunked_encode")) {
467 f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_encode), NULL, p);
468 #ifdef HTTP_HAVE_ZLIB
469 } else
470
471 if (!strcasecmp(name, "http.inflate")) {
472 int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0;
473 HTTP_FILTER_BUFFER(inflate) *b = NULL;
474
475 if ((b = http_encoding_inflate_stream_init(NULL, flags))) {
476 if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(inflate), b, p))) {
477 http_encoding_inflate_stream_free(&b);
478 }
479 }
480 } else
481
482 if (!strcasecmp(name, "http.deflate")) {
483 int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0;
484 HTTP_FILTER_BUFFER(deflate) *b = NULL;
485
486 if (params) {
487 switch (Z_TYPE_P(params))
488 {
489 case IS_ARRAY:
490 case IS_OBJECT:
491 if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void **) &tmp)) {
492 break;
493 }
494 default:
495 {
496 zval *orig = *tmp;
497
498 convert_to_long_ex(tmp);
499 flags |= (Z_LVAL_PP(tmp) & 0x0fffffff);
500 if (orig != *tmp) zval_ptr_dtor(tmp);
501 }
502 }
503 }
504 if ((b = http_encoding_deflate_stream_init(NULL, flags))) {
505 if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(deflate), b, p))) {
506 http_encoding_deflate_stream_free(&b);
507 }
508 }
509 #endif /* HTTP_HAVE_ZLIB */
510 }
511
512 return f;
513 }
514
515 php_stream_filter_factory http_filter_factory = {
516 http_filter_create
517 };
518
519 #endif /* ZEND_ENGINE_2 */
520
521 /*
522 * Local variables:
523 * tab-width: 4
524 * c-basic-offset: 4
525 * End:
526 * vim600: noet sw=4 ts=4 fdm=marker
527 * vim<600: noet sw=4 ts=4
528 */