- fix inclusion of zlib.h
[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 struct {
88 int init;
89 int flags;
90 http_encoding_stream stream;
91 } HTTP_FILTER_BUFFER(gzip);
92 #endif /* HTTP_HAVE_ZLIB */
93
94
95 static HTTP_FILTER_FUNCTION(chunked_decode)
96 {
97 int out_avail = 0;
98 php_stream_bucket *ptr, *nxt;
99 HTTP_FILTER_BUFFER(chunked_decode) *buffer = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
100
101 if (bytes_consumed) {
102 *bytes_consumed = 0;
103 }
104
105 /* new data available? */
106 if (buckets_in->head) {
107
108 /* fetch available bucket data */
109 for (ptr = buckets_in->head; ptr; ptr = nxt) {
110 nxt = ptr->next;
111 if (bytes_consumed) {
112 *bytes_consumed += ptr->buflen;
113 }
114
115 if ((size_t) -1 == phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen)) {
116 return PSFS_ERR_FATAL;
117 }
118
119 php_stream_bucket_unlink(ptr TSRMLS_CC);
120 php_stream_bucket_delref(ptr TSRMLS_CC);
121 }
122 }
123 if (!phpstr_fix(PHPSTR(buffer))) {
124 return PSFS_ERR_FATAL;
125 }
126
127 /* we have data in our buffer */
128 while (PHPSTR_LEN(buffer)) {
129
130 /* we already know the size of the chunk and are waiting for data */
131 if (buffer->hexlen) {
132
133 /* not enough data buffered */
134 if (PHPSTR_LEN(buffer) < buffer->hexlen) {
135
136 /* flush anyway? */
137 if (flags == PSFS_FLAG_FLUSH_INC) {
138
139 /* flush all data (should only be chunk data) */
140 out_avail = 1;
141 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
142
143 /* waiting for less data now */
144 buffer->hexlen -= PHPSTR_LEN(buffer);
145 /* no more buffered data */
146 phpstr_reset(PHPSTR(buffer));
147 /* break */
148 }
149
150 /* we have too less data and don't need to flush */
151 else {
152 break;
153 }
154 }
155
156 /* we seem to have all data of the chunk */
157 else {
158 out_avail = 1;
159 NEW_BUCKET(PHPSTR_VAL(buffer), buffer->hexlen);
160
161 /* remove outgoing data from the buffer */
162 phpstr_cut(PHPSTR(buffer), 0, buffer->hexlen);
163 /* reset hexlen */
164 buffer->hexlen = 0;
165 /* continue */
166 }
167 }
168
169 /* we don't know the length of the chunk yet */
170 else {
171 size_t off = 0;
172
173 /* ignore preceeding CRLFs (too loose?) */
174 while (off < PHPSTR_LEN(buffer) && (
175 PHPSTR_VAL(buffer)[off] == 0xa ||
176 PHPSTR_VAL(buffer)[off] == 0xd)) {
177 ++off;
178 }
179 if (off) {
180 phpstr_cut(PHPSTR(buffer), 0, off);
181 }
182
183 /* still data there? */
184 if (PHPSTR_LEN(buffer)) {
185 int eollen;
186 const char *eolstr;
187
188 /* we need eol, so we can be sure we have all hex digits */
189 phpstr_fix(PHPSTR(buffer));
190 if ((eolstr = http_locate_eol(PHPSTR_VAL(buffer), &eollen))) {
191 char *stop = NULL;
192
193 /* read in chunk size */
194 buffer->hexlen = strtoul(PHPSTR_VAL(buffer), &stop, 16);
195
196 /* if strtoul() stops at the beginning of the buffered data
197 there's domething oddly wrong, i.e. bad input */
198 if (stop == PHPSTR_VAL(buffer)) {
199 return PSFS_ERR_FATAL;
200 }
201
202 /* cut out <chunk size hex><chunk extension><eol> */
203 phpstr_cut(PHPSTR(buffer), 0, eolstr + eollen - PHPSTR_VAL(buffer));
204 /* buffer->hexlen is 0 now or contains the size of the next chunk */
205 /* continue */
206 } else {
207 /* we have not enough data buffered to read in chunk size */
208 break;
209 }
210 }
211 /* break */
212 }
213 }
214
215 /* flush before close, but only if we are already waiting for more data */
216 if (flags == PSFS_FLAG_FLUSH_CLOSE && buffer->hexlen && PHPSTR_LEN(buffer)) {
217 out_avail = 1;
218 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
219 phpstr_reset(PHPSTR(buffer));
220 buffer->hexlen = 0;
221 }
222
223 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
224 }
225
226 static HTTP_FILTER_DESTRUCTOR(chunked_decode)
227 {
228 HTTP_FILTER_BUFFER(chunked_decode) *b = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
229
230 phpstr_dtor(PHPSTR(b));
231 pefree(b, this->is_persistent);
232 }
233
234 static HTTP_FILTER_FUNCTION(chunked_encode)
235 {
236 int out_avail = 0;
237 php_stream_bucket *ptr, *nxt;
238
239 if (bytes_consumed) {
240 *bytes_consumed = 0;
241 }
242
243 /* new data available? */
244 if (buckets_in->head) {
245 phpstr buf;
246 out_avail = 1;
247
248 phpstr_init(&buf);
249
250 /* fetch available bucket data */
251 for (ptr = buckets_in->head; ptr; ptr = nxt) {
252 nxt = ptr->next;
253 if (bytes_consumed) {
254 *bytes_consumed += ptr->buflen;
255 }
256
257 phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen);
258 phpstr_append(&buf, ptr->buf, ptr->buflen);
259 phpstr_appends(&buf, HTTP_CRLF);
260
261 /* pass through */
262 NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf));
263 /* reset */
264 phpstr_reset(&buf);
265
266 php_stream_bucket_unlink(ptr TSRMLS_CC);
267 php_stream_bucket_delref(ptr TSRMLS_CC);
268 }
269
270 /* free buffer */
271 phpstr_dtor(&buf);
272 }
273
274 /* terminate with "0" */
275 if (flags == PSFS_FLAG_FLUSH_CLOSE) {
276 out_avail = 1;
277 NEW_BUCKET("0" HTTP_CRLF, lenof("0" HTTP_CRLF));
278 }
279
280 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
281 }
282
283 static HTTP_FILTER_OPS(chunked_decode) = {
284 HTTP_FILTER_FUNC(chunked_decode),
285 HTTP_FILTER_DTOR(chunked_decode),
286 "http.chunked_decode"
287 };
288
289 static HTTP_FILTER_OPS(chunked_encode) = {
290 HTTP_FILTER_FUNC(chunked_encode),
291 NULL,
292 "http.chunked_encode"
293 };
294
295 #ifdef HTTP_HAVE_ZLIB
296 static HTTP_FILTER_FUNCTION(gzip)
297 {
298 int out_avail = 0;
299 php_stream_bucket *ptr, *nxt;
300 HTTP_FILTER_BUFFER(gzip) *buffer = (HTTP_FILTER_BUFFER(gzip) *) this->abstract;
301
302 if (bytes_consumed) {
303 *bytes_consumed = 0;
304 }
305
306 /* first round */
307 if (!buffer->init) {
308 char *encoded = NULL;
309 size_t encoded_len = 0;
310
311 buffer->init = 1;
312 http_encoding_stream_init(&buffer->stream, buffer->flags, -1, &encoded, &encoded_len);
313
314 if (encoded) {
315 out_avail = 1;
316 NEW_BUCKET(encoded, encoded_len);
317 pefree(encoded, this->is_persistent);
318 }
319 }
320
321 /* new data available? */
322 if (buckets_in->head) {
323
324 /* fetch available bucket data */
325 for (ptr = buckets_in->head; ptr; ptr = nxt) {
326 char *encoded = NULL;
327 size_t encoded_len = 0;
328
329 nxt = ptr->next;
330 if (bytes_consumed) {
331 *bytes_consumed += ptr->buflen;
332 }
333
334 /* this is actually flushing implicitly */
335 http_encoding_stream_update(&buffer->stream, ptr->buf, ptr->buflen, &encoded, &encoded_len);
336 if (encoded) {
337 out_avail = 1;
338 NEW_BUCKET(encoded, encoded_len);
339 pefree(encoded, this->is_persistent);
340 }
341
342 php_stream_bucket_unlink(ptr TSRMLS_CC);
343 php_stream_bucket_delref(ptr TSRMLS_CC);
344 }
345 }
346
347 /* flush & close */
348 if (flags == PSFS_FLAG_FLUSH_CLOSE) {
349 char *encoded = NULL;
350 size_t encoded_len = 0;
351
352 http_encoding_stream_finish(&buffer->stream, &encoded, &encoded_len);
353 if (encoded) {
354 out_avail = 1;
355 NEW_BUCKET(encoded, encoded_len);
356 }
357 }
358
359 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
360 }
361
362 static HTTP_FILTER_DESTRUCTOR(gzip)
363 {
364 HTTP_FILTER_BUFFER(gzip) *buffer = (HTTP_FILTER_BUFFER(gzip) *) this->abstract;
365
366 pefree(buffer, this->is_persistent);
367 }
368
369 static HTTP_FILTER_OPS(gzencode) = {
370 HTTP_FILTER_FUNC(gzip),
371 HTTP_FILTER_DTOR(gzip),
372 "http.gzencode"
373 };
374
375 static HTTP_FILTER_OPS(deflate) = {
376 HTTP_FILTER_FUNC(gzip),
377 HTTP_FILTER_DTOR(gzip),
378 "http.deflate"
379 };
380 #endif /* HTTP_HAVE_ZLIB */
381
382 static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
383 {
384 php_stream_filter *f = NULL;
385
386 if (!strcasecmp(name, "http.chunked_decode")) {
387 HTTP_FILTER_BUFFER(chunked_decode) *b = NULL;
388
389 if ((b = pecalloc(1, sizeof(HTTP_FILTER_BUFFER(chunked_decode)), p))) {
390 phpstr_init_ex(PHPSTR(b), 4096, p ? PHPSTR_INIT_PERSISTENT : 0);
391 if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_decode), b, p))) {
392 pefree(b, p);
393 }
394 }
395 } else
396
397 if (!strcasecmp(name, "http.chunked_encode")) {
398 f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_encode), NULL, p);
399 #ifdef HTTP_HAVE_ZLIB
400 } else
401
402 if (!strcasecmp(name, "http.gzencode")) {
403 HTTP_FILTER_BUFFER(gzip) *b = NULL;
404
405 if ((b = pecalloc(1, sizeof(HTTP_FILTER_BUFFER(gzip)), p))) {
406 b->flags = HTTP_ENCODING_STREAM_GZIP_HEADER;
407 if (p) {
408 b->flags |= HTTP_ENCODING_STREAM_PERSISTENT;
409 }
410 if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(gzencode), b, p))) {
411 pefree(b, p);
412 }
413 }
414 } else
415
416 if (!strcasecmp(name, "http.deflate")) {
417 HTTP_FILTER_BUFFER(gzip) *b = NULL;
418
419 if ((b = pecalloc(1, sizeof(HTTP_FILTER_BUFFER(gzip)), p))) {
420 if (p) {
421 b->flags |= HTTP_ENCODING_STREAM_PERSISTENT;
422 }
423 if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(deflate), b, p))) {
424 pefree(b, p);
425 }
426 }
427 #endif /* HTTP_HAVE_ZLIB */
428 }
429
430 return f;
431 }
432
433 php_stream_filter_factory http_filter_factory = {
434 http_filter_create
435 };
436
437 #endif /* ZEND_ENGINE_2 */
438
439 /*
440 * Local variables:
441 * tab-width: 4
442 * c-basic-offset: 4
443 * End:
444 * vim600: noet sw=4 ts=4 fdm=marker
445 * vim<600: noet sw=4 ts=4
446 */