# you better don't look inside
[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 #include "php_http.h"
20
21 #ifdef ZEND_ENGINE_2
22
23 #include "php_streams.h"
24 #include "php_http_api.h"
25 #include "php_http_filter_api.h"
26
27 PHP_MINIT_FUNCTION(http_filter)
28 {
29 php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC);
30 return SUCCESS;
31 }
32
33 /*
34 -
35 */
36
37 typedef struct {
38 phpstr buffer;
39 ulong hexlen;
40 } http_filter_buffer;
41
42 #define HTTP_FILTER_PARAMS \
43 php_stream *stream, \
44 php_stream_filter *this, \
45 php_stream_bucket_brigade *buckets_in, \
46 php_stream_bucket_brigade *buckets_out, \
47 size_t *bytes_consumed, int flags \
48 TSRMLS_DC
49 #define HTTP_FILTER_OP(filter) \
50 http_filter_op_ ##filter
51 #define HTTP_FILTER_OPS(filter) \
52 php_stream_filter_ops HTTP_FILTER_OP(filter)
53 #define HTTP_FILTER_DTOR(filter) \
54 http_filter_ ##filter## _dtor
55 #define HTTP_FILTER_DESTRUCTOR(filter) \
56 void HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC)
57 #define HTTP_FILTER_FUNC(filter) \
58 http_filter_ ##filter
59 #define HTTP_FILTER_FUNCTION(filter) \
60 php_stream_filter_status_t HTTP_FILTER_FUNC(filter)(HTTP_FILTER_PARAMS)
61
62 #define NEW_BUCKET(data, length) \
63 { \
64 char *__data; \
65 php_stream_bucket *__buck; \
66 \
67 __data = pemalloc(length, this->is_persistent); \
68 if (!__data) { \
69 return PSFS_ERR_FATAL; \
70 } \
71 memcpy(__data, data, length); \
72 \
73 __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
74 if (!__buck) { \
75 pefree(__data, this->is_persistent); \
76 return PSFS_ERR_FATAL; \
77 } \
78 \
79 php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
80 }
81
82 static HTTP_FILTER_FUNCTION(chunked_decode)
83 {
84 int out_avail = 0;
85 php_stream_bucket *ptr, *nxt;
86 http_filter_buffer *buffer = (http_filter_buffer *) (this->abstract);
87
88 if (bytes_consumed) {
89 *bytes_consumed = 0;
90 }
91
92 /* new data available? */
93 if (buckets_in->head) {
94
95 /* fetch available bucket data */
96 for (ptr = buckets_in->head; ptr; ptr = nxt) {
97 nxt = ptr->next;
98 if (bytes_consumed) {
99 *bytes_consumed += ptr->buflen;
100 }
101
102 if ((size_t) -1 == phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen)) {
103 return PSFS_ERR_FATAL;
104 }
105
106 php_stream_bucket_unlink(ptr TSRMLS_CC);
107 php_stream_bucket_delref(ptr TSRMLS_CC);
108 }
109 }
110 if (!phpstr_fix(PHPSTR(buffer))) {
111 return PSFS_ERR_FATAL;
112 }
113
114 /* we have data in our buffer */
115 while (PHPSTR_LEN(buffer)) {
116
117 /* we already know the size of the chunk and are waiting for data */
118 if (buffer->hexlen) {
119
120 /* not enough data buffered */
121 if (PHPSTR_LEN(buffer) < buffer->hexlen) {
122
123 /* flush anyway? */
124 if (flags == PSFS_FLAG_FLUSH_INC) {
125
126 /* flush all data (should only be chunk data) */
127 out_avail = 1;
128 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
129
130 /* waiting for less data now */
131 buffer->hexlen -= PHPSTR_LEN(buffer);
132 /* no more buffered data */
133 phpstr_reset(PHPSTR(buffer));
134 /* break */
135 }
136
137 /* we have too less data and don't need to flush */
138 else {
139 break;
140 }
141 }
142
143 /* we seem to have all data of the chunk */
144 else {
145 out_avail = 1;
146 NEW_BUCKET(PHPSTR_VAL(buffer), buffer->hexlen);
147
148 /* remove outgoing data from the buffer */
149 phpstr_cut(PHPSTR(buffer), 0, buffer->hexlen);
150 /* reset hexlen */
151 buffer->hexlen = 0;
152 /* continue */
153 }
154 }
155
156 /* we don't know the length of the chunk yet */
157 else {
158 size_t off = 0;
159
160 /* ignore preceeding CRLFs (too loose?) */
161 while (off < PHPSTR_LEN(buffer) && (
162 PHPSTR_VAL(buffer)[off] == 0xa ||
163 PHPSTR_VAL(buffer)[off] == 0xd)) {
164 ++off;
165 }
166 if (off) {
167 phpstr_cut(PHPSTR(buffer), 0, off);
168 }
169
170 /* still data there? */
171 if (PHPSTR_LEN(buffer)) {
172 int eollen;
173 const char *eolstr;
174
175 /* we need eol, so we can be sure we have all hex digits */
176 phpstr_fix(PHPSTR(buffer));
177 if ((eolstr = http_locate_eol(PHPSTR_VAL(buffer), &eollen))) {
178 char *stop = NULL;
179
180 /* read in chunk size */
181 buffer->hexlen = strtoul(PHPSTR_VAL(buffer), &stop, 16);
182
183 /* if strtoul() stops at the beginning of the buffered data
184 there's domething oddly wrong, i.e. bad input */
185 if (stop == PHPSTR_VAL(buffer)) {
186 return PSFS_ERR_FATAL;
187 }
188
189 /* cut out <chunk size hex><chunk extension><eol> */
190 phpstr_cut(PHPSTR(buffer), 0, eolstr + eollen - PHPSTR_VAL(buffer));
191 /* buffer->hexlen is 0 now or contains the size of the next chunk */
192 /* continue */
193 } else {
194 /* we have not enough data buffered to read in chunk size */
195 break;
196 }
197 }
198 /* break */
199 }
200 }
201
202 /* flush before close, but only if we are already waiting for more data */
203 if (flags == PSFS_FLAG_FLUSH_CLOSE && buffer->hexlen && PHPSTR_LEN(buffer)) {
204 out_avail = 1;
205 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
206 phpstr_reset(PHPSTR(buffer));
207 buffer->hexlen = 0;
208 }
209
210 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
211 }
212
213 static HTTP_FILTER_DESTRUCTOR(chunked_decode)
214 {
215 http_filter_buffer *b = (http_filter_buffer *) (this->abstract);
216
217 phpstr_dtor(PHPSTR(b));
218 pefree(b, this->is_persistent);
219 }
220
221 static HTTP_FILTER_FUNCTION(chunked_encode)
222 {
223 int out_avail = 0;
224 php_stream_bucket *ptr, *nxt;
225
226 if (bytes_consumed) {
227 *bytes_consumed = 0;
228 }
229
230 /* new data available? */
231 if (buckets_in->head) {
232 phpstr buf;
233 out_avail = 1;
234
235 phpstr_init(&buf);
236
237 /* fetch available bucket data */
238 for (ptr = buckets_in->head; ptr; ptr = nxt) {
239 nxt = ptr->next;
240 if (bytes_consumed) {
241 *bytes_consumed += ptr->buflen;
242 }
243
244 phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen);
245 phpstr_append(&buf, ptr->buf, ptr->buflen);
246 phpstr_appends(&buf, HTTP_CRLF);
247
248 /* pass through */
249 NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf));
250 /* reset */
251 phpstr_reset(&buf);
252
253 php_stream_bucket_unlink(ptr TSRMLS_CC);
254 php_stream_bucket_delref(ptr TSRMLS_CC);
255 }
256
257 /* free buffer */
258 phpstr_dtor(&buf);
259 }
260
261 /* terminate with "0" */
262 if (flags == PSFS_FLAG_FLUSH_CLOSE) {
263 out_avail = 1;
264 NEW_BUCKET("0" HTTP_CRLF, lenof("0" HTTP_CRLF));
265 }
266
267 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
268 }
269
270 static HTTP_FILTER_OPS(chunked_decode) = {
271 HTTP_FILTER_FUNC(chunked_decode),
272 HTTP_FILTER_DTOR(chunked_decode),
273 "http.chunked_decode"
274 };
275
276 static HTTP_FILTER_OPS(chunked_encode) = {
277 HTTP_FILTER_FUNC(chunked_encode),
278 NULL,
279 "http.chunked_encode"
280 };
281
282 static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
283 {
284 php_stream_filter *f = NULL;
285
286 if (!strcasecmp(name, "http.chunked_decode")) {
287 http_filter_buffer *b = NULL;
288
289 if ((b = pecalloc(1, sizeof(http_filter_buffer), p))) {
290 phpstr_init_ex(PHPSTR(b), 4096, p ? PHPSTR_INIT_PERSISTENT : 0);
291 if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_decode), b, p))) {
292 pefree(b, p);
293 }
294 }
295 } else
296
297 if (!strcasecmp(name, "http.chunked_encode")) {
298 f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_encode), NULL, p);
299 }
300
301 return f;
302 }
303
304 php_stream_filter_factory http_filter_factory = {
305 http_filter_create
306 };
307
308 #endif /* ZEND_ENGINE_2 */
309
310 /*
311 * Local variables:
312 * tab-width: 4
313 * c-basic-offset: 4
314 * End:
315 * vim600: noet sw=4 ts=4 fdm=marker
316 * vim<600: noet sw=4 ts=4
317 */