9dd0827d016b935cd5ec8c855d98433c6f9b2258
[m6w6/ext-http] / php_http_filter.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-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id: http_filter_api.c 292841 2009-12-31 08:48:57Z mike $ */
14
15 #include "php_http.h"
16
17 PHP_MINIT_FUNCTION(http_filter)
18 {
19 php_stream_filter_register_factory("http.*", &php_http_filter_factory TSRMLS_CC);
20 return SUCCESS;
21 }
22
23 #define PHP_HTTP_FILTER_PARAMS \
24 php_stream *stream, \
25 php_stream_filter *this, \
26 php_stream_bucket_brigade *buckets_in, \
27 php_stream_bucket_brigade *buckets_out, \
28 size_t *bytes_consumed, int flags \
29 TSRMLS_DC
30 #define PHP_HTTP_FILTER_OP(filter) \
31 http_filter_op_ ##filter
32 #define PHP_HTTP_FILTER_OPS(filter) \
33 php_stream_filter_ops PHP_HTTP_FILTER_OP(filter)
34 #define PHP_HTTP_FILTER_DTOR(filter) \
35 http_filter_ ##filter## _dtor
36 #define PHP_HTTP_FILTER_DESTRUCTOR(filter) \
37 void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC)
38 #define PHP_HTTP_FILTER_FUNC(filter) \
39 http_filter_ ##filter
40 #define PHP_HTTP_FILTER_FUNCTION(filter) \
41 php_stream_filter_status_t PHP_HTTP_FILTER_FUNC(filter)(PHP_HTTP_FILTER_PARAMS)
42 #define PHP_HTTP_FILTER_BUFFER(filter) \
43 http_filter_ ##filter## _buffer
44
45 #define NEW_BUCKET(data, length) \
46 { \
47 char *__data; \
48 php_stream_bucket *__buck; \
49 \
50 __data = pemalloc(length, this->is_persistent); \
51 if (!__data) { \
52 return PSFS_ERR_FATAL; \
53 } \
54 memcpy(__data, data, length); \
55 \
56 __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
57 if (!__buck) { \
58 pefree(__data, this->is_persistent); \
59 return PSFS_ERR_FATAL; \
60 } \
61 \
62 php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
63 }
64
65 typedef struct _http_chunked_decode_filter_buffer_t {
66 php_http_buffer_t buffer;
67 ulong hexlen;
68 } PHP_HTTP_FILTER_BUFFER(chunked_decode);
69
70 typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(zlib);
71
72 static PHP_HTTP_FILTER_FUNCTION(chunked_decode)
73 {
74 int out_avail = 0;
75 php_stream_bucket *ptr, *nxt;
76 PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
77
78 if (bytes_consumed) {
79 *bytes_consumed = 0;
80 }
81
82 /* new data available? */
83 if (buckets_in->head) {
84
85 /* fetch available bucket data */
86 for (ptr = buckets_in->head; ptr; ptr = nxt) {
87 nxt = ptr->next;
88 if (bytes_consumed) {
89 *bytes_consumed += ptr->buflen;
90 }
91
92 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(PHP_HTTP_BUFFER(buffer), ptr->buf, ptr->buflen)) {
93 return PSFS_ERR_FATAL;
94 }
95
96 php_stream_bucket_unlink(ptr TSRMLS_CC);
97 php_stream_bucket_delref(ptr TSRMLS_CC);
98 }
99 }
100 if (!php_http_buffer_fix(PHP_HTTP_BUFFER(buffer))) {
101 return PSFS_ERR_FATAL;
102 }
103
104 /* we have data in our buffer */
105 while (PHP_HTTP_BUFFER_LEN(buffer)) {
106
107 /* we already know the size of the chunk and are waiting for data */
108 if (buffer->hexlen) {
109
110 /* not enough data buffered */
111 if (PHP_HTTP_BUFFER_LEN(buffer) < buffer->hexlen) {
112
113 /* flush anyway? */
114 if (flags & PSFS_FLAG_FLUSH_INC) {
115
116 /* flush all data (should only be chunk data) */
117 out_avail = 1;
118 NEW_BUCKET(PHP_HTTP_BUFFER_VAL(buffer), PHP_HTTP_BUFFER_LEN(buffer));
119
120 /* waiting for less data now */
121 buffer->hexlen -= PHP_HTTP_BUFFER_LEN(buffer);
122 /* no more buffered data */
123 php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
124 /* break */
125 }
126
127 /* we have too less data and don't need to flush */
128 else {
129 break;
130 }
131 }
132
133 /* we seem to have all data of the chunk */
134 else {
135 out_avail = 1;
136 NEW_BUCKET(PHP_HTTP_BUFFER_VAL(buffer), buffer->hexlen);
137
138 /* remove outgoing data from the buffer */
139 php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, buffer->hexlen);
140 /* reset hexlen */
141 buffer->hexlen = 0;
142 /* continue */
143 }
144 }
145
146 /* we don't know the length of the chunk yet */
147 else {
148 size_t off = 0;
149
150 /* ignore preceeding CRLFs (too loose?) */
151 while (off < PHP_HTTP_BUFFER_LEN(buffer) && (
152 PHP_HTTP_BUFFER_VAL(buffer)[off] == '\n' ||
153 PHP_HTTP_BUFFER_VAL(buffer)[off] == '\r')) {
154 ++off;
155 }
156 if (off) {
157 php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, off);
158 }
159
160 /* still data there? */
161 if (PHP_HTTP_BUFFER_LEN(buffer)) {
162 int eollen;
163 const char *eolstr;
164
165 /* we need eol, so we can be sure we have all hex digits */
166 php_http_buffer_fix(PHP_HTTP_BUFFER(buffer));
167 if ((eolstr = php_http_locate_bin_eol(PHP_HTTP_BUFFER_VAL(buffer), PHP_HTTP_BUFFER_LEN(buffer), &eollen))) {
168 char *stop = NULL;
169
170 /* read in chunk size */
171 buffer->hexlen = strtoul(PHP_HTTP_BUFFER_VAL(buffer), &stop, 16);
172
173 /* if strtoul() stops at the beginning of the buffered data
174 there's domething oddly wrong, i.e. bad input */
175 if (stop == PHP_HTTP_BUFFER_VAL(buffer)) {
176 return PSFS_ERR_FATAL;
177 }
178
179 /* cut out <chunk size hex><chunk extension><eol> */
180 php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, eolstr + eollen - PHP_HTTP_BUFFER_VAL(buffer));
181 /* buffer->hexlen is 0 now or contains the size of the next chunk */
182 if (!buffer->hexlen) {
183 php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_COMPLETED, NULL, 0);
184 break;
185 }
186 /* continue */
187 } else {
188 /* we have not enough data buffered to read in chunk size */
189 break;
190 }
191 }
192 /* break */
193 }
194 }
195
196 /* flush before close, but only if we are already waiting for more data */
197 if ((flags & PSFS_FLAG_FLUSH_CLOSE) && buffer->hexlen && PHP_HTTP_BUFFER_LEN(buffer)) {
198 out_avail = 1;
199 NEW_BUCKET(PHP_HTTP_BUFFER_VAL(buffer), PHP_HTTP_BUFFER_LEN(buffer));
200 php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
201 buffer->hexlen = 0;
202 }
203
204 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
205 }
206
207 static PHP_HTTP_FILTER_DESTRUCTOR(chunked_decode)
208 {
209 PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
210
211 php_http_buffer_dtor(PHP_HTTP_BUFFER(b));
212 pefree(b, this->is_persistent);
213 }
214
215 static PHP_HTTP_FILTER_FUNCTION(chunked_encode)
216 {
217 int out_avail = 0;
218 php_stream_bucket *ptr, *nxt;
219
220 if (bytes_consumed) {
221 *bytes_consumed = 0;
222 }
223
224 /* new data available? */
225 if (buckets_in->head) {
226 php_http_buffer_t buf;
227 out_avail = 1;
228
229 php_http_buffer_init(&buf);
230
231 /* fetch available bucket data */
232 for (ptr = buckets_in->head; ptr; ptr = nxt) {
233 nxt = ptr->next;
234 if (bytes_consumed) {
235 *bytes_consumed += ptr->buflen;
236 }
237
238 php_http_buffer_appendf(&buf, "%lx" PHP_HTTP_CRLF, (long unsigned int) ptr->buflen);
239 php_http_buffer_append(&buf, ptr->buf, ptr->buflen);
240 php_http_buffer_appends(&buf, PHP_HTTP_CRLF);
241
242 /* pass through */
243 NEW_BUCKET(PHP_HTTP_BUFFER_VAL(&buf), PHP_HTTP_BUFFER_LEN(&buf));
244 /* reset */
245 php_http_buffer_reset(&buf);
246
247 php_stream_bucket_unlink(ptr TSRMLS_CC);
248 php_stream_bucket_delref(ptr TSRMLS_CC);
249 }
250
251 /* free buffer */
252 php_http_buffer_dtor(&buf);
253 }
254
255 /* terminate with "0" */
256 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
257 out_avail = 1;
258 NEW_BUCKET("0" PHP_HTTP_CRLF, lenof("0" PHP_HTTP_CRLF));
259 }
260
261 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
262 }
263
264 static PHP_HTTP_FILTER_OPS(chunked_decode) = {
265 PHP_HTTP_FILTER_FUNC(chunked_decode),
266 PHP_HTTP_FILTER_DTOR(chunked_decode),
267 "http.chunked_decode"
268 };
269
270 static PHP_HTTP_FILTER_OPS(chunked_encode) = {
271 PHP_HTTP_FILTER_FUNC(chunked_encode),
272 NULL,
273 "http.chunked_encode"
274 };
275
276 static PHP_HTTP_FILTER_FUNCTION(zlib)
277 {
278 int out_avail = 0;
279 php_stream_bucket *ptr, *nxt;
280 PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
281
282 if (bytes_consumed) {
283 *bytes_consumed = 0;
284 }
285
286 /* new data available? */
287 if (buckets_in->head) {
288
289 /* fetch available bucket data */
290 for (ptr = buckets_in->head; ptr; ptr = nxt) {
291 char *encoded = NULL;
292 size_t encoded_len = 0;
293
294 nxt = ptr->next;
295 if (bytes_consumed) {
296 *bytes_consumed += ptr->buflen;
297 }
298
299 if (ptr->buflen) {
300 php_http_encoding_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len);
301 if (encoded) {
302 if (encoded_len) {
303 out_avail = 1;
304 NEW_BUCKET(encoded, encoded_len);
305 }
306 efree(encoded);
307 }
308 }
309
310 php_stream_bucket_unlink(ptr TSRMLS_CC);
311 php_stream_bucket_delref(ptr TSRMLS_CC);
312 }
313 }
314
315 /* flush & close */
316 if (flags & PSFS_FLAG_FLUSH_INC) {
317 char *encoded = NULL;
318 size_t encoded_len = 0;
319
320 php_http_encoding_stream_flush(buffer, &encoded, &encoded_len);
321 if (encoded) {
322 if (encoded_len) {
323 out_avail = 1;
324 NEW_BUCKET(encoded, encoded_len);
325 }
326 efree(encoded);
327 }
328 }
329
330 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
331 char *encoded = NULL;
332 size_t encoded_len = 0;
333
334 php_http_encoding_stream_finish(buffer, &encoded, &encoded_len);
335 if (encoded) {
336 if (encoded_len) {
337 out_avail = 1;
338 NEW_BUCKET(encoded, encoded_len);
339 }
340 efree(encoded);
341 }
342 }
343
344 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
345 }
346 static PHP_HTTP_FILTER_DESTRUCTOR(zlib)
347 {
348 PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
349 php_http_encoding_stream_free(&buffer);
350 }
351
352 static PHP_HTTP_FILTER_OPS(deflate) = {
353 PHP_HTTP_FILTER_FUNC(zlib),
354 PHP_HTTP_FILTER_DTOR(zlib),
355 "http.deflate"
356 };
357
358 static PHP_HTTP_FILTER_OPS(inflate) = {
359 PHP_HTTP_FILTER_FUNC(zlib),
360 PHP_HTTP_FILTER_DTOR(zlib),
361 "http.inflate"
362 };
363
364 static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
365 {
366 zval **tmp = &params;
367 php_stream_filter *f = NULL;
368
369 if (!strcasecmp(name, "http.chunked_decode")) {
370 PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = NULL;
371
372 if ((b = pecalloc(1, sizeof(PHP_HTTP_FILTER_BUFFER(chunked_decode)), p))) {
373 php_http_buffer_init_ex(PHP_HTTP_BUFFER(b), 4096, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
374 if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_decode), b, p))) {
375 pefree(b, p);
376 }
377 }
378 } else
379
380 if (!strcasecmp(name, "http.chunked_encode")) {
381 f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_encode), NULL, p);
382 } else
383
384 if (!strcasecmp(name, "http.inflate")) {
385 int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0;
386 PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
387
388 if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags TSRMLS_CC))) {
389 if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) {
390 php_http_encoding_stream_free(&b);
391 }
392 }
393 } else
394
395 if (!strcasecmp(name, "http.deflate")) {
396 int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0;
397 PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
398
399 if (params) {
400 switch (Z_TYPE_P(params)) {
401 case IS_ARRAY:
402 case IS_OBJECT:
403 if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) {
404 break;
405 }
406 default:
407 {
408 zval *num = php_http_ztyp(IS_LONG, *tmp);
409
410 flags |= (Z_LVAL_P(num) & 0x0fffffff);
411 zval_ptr_dtor(&num);
412 }
413 }
414 }
415 if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags TSRMLS_CC))) {
416 if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) {
417 php_http_encoding_stream_free(&b);
418 }
419 }
420 }
421
422 return f;
423 }
424
425 php_stream_filter_factory php_http_filter_factory = {
426 http_filter_create
427 };
428
429
430 /*
431 * Local variables:
432 * tab-width: 4
433 * c-basic-offset: 4
434 * End:
435 * vim600: noet sw=4 ts=4 fdm=marker
436 * vim<600: noet sw=4 ts=4
437 */