3315f75841f216b7085042402648a90ff82d58a0
[m6w6/ext-http] / http_filter_api.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21 #include "php.h"
22
23 #include "php_http_std_defs.h"
24 #include "php_http_api.h"
25 #include "php_http_filter_api.h"
26
27 #include "phpstr/phpstr.h"
28
29 #include "php_streams.h"
30
31 /*
32 * TODO: allow use with persistent streams
33 */
34
35 typedef struct {
36 phpstr buffer;
37 ulong hexlen;
38 } http_filter_buffer;
39
40 #define PHP_STREAM_FILTER_OP_FILTER_PARAMS \
41 php_stream *stream, \
42 php_stream_filter *this, \
43 php_stream_bucket_brigade *buckets_in, \
44 php_stream_bucket_brigade *buckets_out, \
45 size_t *bytes_consumed, int flags \
46 TSRMLS_DC
47 #define PHP_STREAM_FILTER_OP_FILTER(function) \
48 static php_stream_filter_status_t function(PHP_STREAM_FILTER_OP_FILTER_PARAMS)
49
50 #define NEW_BUCKET(data, length) \
51 { \
52 char *__data; \
53 php_stream_bucket *__buck; \
54 \
55 __data = pemalloc(length, this->is_persistent); \
56 if (!__data) { \
57 return PSFS_ERR_FATAL; \
58 } \
59 memcpy(__data, data, length); \
60 \
61 __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
62 if (!__buck) { \
63 pefree(__data, this->is_persistent); \
64 return PSFS_ERR_FATAL; \
65 } \
66 \
67 php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
68 }
69
70 PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_decode)
71 {
72 int out_avail = 0;
73 php_stream_bucket *ptr, *nxt;
74 http_filter_buffer *buffer = (http_filter_buffer *) (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 phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen);
91 php_stream_bucket_unlink(ptr TSRMLS_CC);
92 php_stream_bucket_delref(ptr TSRMLS_CC);
93
94 }
95 }
96 phpstr_fix(PHPSTR(buffer));
97
98 /* we have data in our buffer */
99 while (PHPSTR_LEN(buffer)) {
100
101 /* we already know the size of the chunk and are waiting for data */
102 if (buffer->hexlen) {
103
104 /* not enough data buffered */
105 if (PHPSTR_LEN(buffer) < buffer->hexlen) {
106
107 /* flush anyway? */
108 if (flags == PSFS_FLAG_FLUSH_INC) {
109
110 /* flush all data (should only be chunk data) */
111 out_avail = 1;
112 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
113
114 /* waiting for less data now */
115 buffer->hexlen -= PHPSTR_LEN(buffer);
116 /* no more buffered data (breaks loop) */
117 phpstr_reset(PHPSTR(buffer));
118 }
119
120 /* we have too less data and don't need to flush */
121 else {
122 break;
123 }
124 }
125
126 /* we seem to have all data of the chunk */
127 else {
128 out_avail = 1;
129 NEW_BUCKET(PHPSTR_VAL(buffer), buffer->hexlen);
130
131 /* remove outgoing data from the buffer */
132 phpstr_cut(PHPSTR(buffer), 0, buffer->hexlen);
133 /* reset hexlen */
134 buffer->hexlen = 0;
135 }
136 }
137
138 /* we don't know the length of the chunk yet */
139 else {
140 size_t off = 0;
141
142 /* ignore preceeding CRLFs (too loose?) */
143 while (off < PHPSTR_LEN(buffer) && (
144 PHPSTR_VAL(buffer)[off] == 0xa ||
145 PHPSTR_VAL(buffer)[off] == 0xd)) {
146 ++off;
147 }
148 if (off) {
149 phpstr_cut(PHPSTR(buffer), 0, off);
150 }
151
152 /* still data there? */
153 if (PHPSTR_LEN(buffer)) {
154 int eollen;
155 const char *eolstr;
156
157 /* we need eol, so we can be sure we have all hex digits */
158 phpstr_fix(PHPSTR(buffer));
159 if (eolstr = http_locate_eol(PHPSTR_VAL(buffer), &eollen)) {
160 char *stop = NULL;
161
162 /* read in chunk size */
163 buffer->hexlen = strtoul(PHPSTR_VAL(buffer), &stop, 16);
164
165 /* if strtoul() stops at the beginning of the buffered data
166 there's domething oddly wrong, i.e. bad input */
167 if (stop == PHPSTR_VAL(buffer)) {
168 return PSFS_ERR_FATAL;
169 }
170
171 /* cut out <chunk size hex><chunk extension><eol> */
172 phpstr_cut(PHPSTR(buffer), 0, eolstr + eollen - PHPSTR_VAL(buffer));
173 /* buffer->hexlen is 0 now or contains the size of the next chunk */
174 /* continue */
175 }
176 }
177 /* break */
178 }
179 }
180
181 /* flush before close, but only if we are already waiting for more data */
182 if (flags == PSFS_FLAG_FLUSH_CLOSE && buffer->hexlen && PHPSTR_LEN(buffer)) {
183 out_avail = 1;
184 NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
185 phpstr_reset(PHPSTR(buffer));
186 buffer->hexlen = 0;
187 }
188
189 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
190 }
191
192 static void http_filter_chunked_decode_dtor(php_stream_filter *this TSRMLS_DC)
193 {
194 http_filter_buffer *b = (http_filter_buffer *) (this->abstract);
195
196 phpstr_dtor(PHPSTR(b));
197 pefree(b, this->is_persistent);
198 }
199
200 PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_encode)
201 {
202 int out_avail = 0;
203 php_stream_bucket *ptr, *nxt;
204
205 if (bytes_consumed) {
206 *bytes_consumed = 0;
207 }
208
209 /* new data available? */
210 if (buckets_in->head) {
211 phpstr buf;
212 out_avail = 1;
213
214 phpstr_init(&buf);
215
216 /* fetch available bucket data */
217 for (ptr = buckets_in->head; ptr; ptr = nxt) {
218 nxt = ptr->next;
219 if (bytes_consumed) {
220 *bytes_consumed += ptr->buflen;
221 }
222
223 phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen);
224 phpstr_append(&buf, ptr->buf, ptr->buflen);
225 phpstr_appends(&buf, HTTP_CRLF);
226
227 /* pass through */
228 NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf));
229 /* reset */
230 PHPSTR_LEN(&buf) = 0;
231
232 php_stream_bucket_unlink(ptr TSRMLS_CC);
233 php_stream_bucket_delref(ptr TSRMLS_CC);
234 }
235
236 /* free buffer */
237 phpstr_dtor(&buf);
238 }
239
240 /* terminate with "0" */
241 if (flags == PSFS_FLAG_FLUSH_CLOSE) {
242 out_avail = 1;
243 NEW_BUCKET("0" HTTP_CRLF, lenof("0" HTTP_CRLF));
244 }
245
246 return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
247 }
248
249 static php_stream_filter_ops http_filter_ops_chunked_decode = {
250 http_filter_chunked_decode,
251 http_filter_chunked_decode_dtor,
252 "http.chunked_decode"
253 };
254
255 static php_stream_filter_ops http_filter_ops_chunked_encode = {
256 http_filter_chunked_encode,
257 NULL,
258 "http.chunked_encode"
259 };
260
261 static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
262 {
263 php_stream_filter *f = NULL;
264
265 if (!strcasecmp(name, "http.chunked_decode")) {
266 http_filter_buffer *b = NULL;
267
268 /* FIXXME: allow usage with persistent streams */
269 if (p) {
270 return NULL;
271 }
272
273 if (b = pecalloc(1, sizeof(http_filter_buffer), p)) {
274 phpstr_init(PHPSTR(b));
275 if (!(f = php_stream_filter_alloc(&http_filter_ops_chunked_decode, b, p))) {
276 pefree(b, p);
277 }
278 }
279 } else
280
281 if (!strcasecmp(name, "http.chunked_encode")) {
282 f = php_stream_filter_alloc(&http_filter_ops_chunked_encode, NULL, p);
283 }
284
285 return f;
286 }
287
288 php_stream_filter_factory http_filter_factory = {
289 http_filter_create
290 };
291
292 PHP_MINIT_FUNCTION(http_filter)
293 {
294 php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC);
295 return SUCCESS;
296 }
297
298 /*
299 * Local variables:
300 * tab-width: 4
301 * c-basic-offset: 4
302 * End:
303 * vim600: noet sw=4 ts=4 fdm=marker
304 * vim<600: noet sw=4 ts=4
305 */
306