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