ccec761cb6593611cf97191207c5d5c29c1ddc7c
[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 #ifndef HTTP_DEBUG_FILTERS
32 # define HTTP_DEBUG_FILTERS 0
33 #endif
34
35 /*
36 * TODO: phpstr is not persistent aware
37 */
38
39 typedef enum {
40 HFS_HEX = 0,
41 HFS_DATA,
42 } http_filter_status;
43
44 typedef struct {
45 phpstr buffer;
46 size_t wanted;
47 int eollen;
48 int passon;
49 http_filter_status status;
50 } http_filter_buffer;
51
52 #define PHP_STREAM_FILTER_OP_FILTER_PARAMS \
53 php_stream *stream, \
54 php_stream_filter *this, \
55 php_stream_bucket_brigade *buckets_in, \
56 php_stream_bucket_brigade *buckets_out, \
57 size_t *bytes_consumed, int flags \
58 TSRMLS_DC
59 #define PHP_STREAM_FILTER_OP_FILTER(function) \
60 static php_stream_filter_status_t function(PHP_STREAM_FILTER_OP_FILTER_PARAMS)
61
62 #define NEW_BUCKET(data, length) \
63 php_stream_bucket_append(buckets_out, php_stream_bucket_new(stream, pestrndup(data, length, this->is_persistent), (length), 1, this->is_persistent TSRMLS_CC) TSRMLS_CC);
64
65 inline void *pestrndup(const char *s, size_t l, int p)
66 {
67 void *d = pemalloc(l + 1, p);
68 if (d) {
69 memcpy(d, s, l);
70 ((char *) d)[l] = 0;
71 }
72 return d;
73 }
74
75 PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_decode)
76 {
77 php_stream_bucket *ptr, *nxt;
78 http_filter_buffer *buffer = (http_filter_buffer *) (this->abstract);
79
80 if (bytes_consumed) {
81 *bytes_consumed = 0;
82 }
83
84 if (!buckets_in->head) {
85 return PSFS_FEED_ME;
86 }
87
88 #if HTTP_DEBUG_FILTERS
89 fprintf(stderr, "Reading in bucket buffers ");
90 #endif
91
92 /* fetch available bucket data */
93 for (ptr = buckets_in->head; ptr; ptr = nxt) {
94 nxt = ptr->next;
95 phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen);
96 php_stream_bucket_unlink(ptr TSRMLS_CC);
97 php_stream_bucket_delref(ptr TSRMLS_CC);
98
99 #if HTTP_DEBUG_FILTERS
100 fprintf(stderr, ".");
101 #endif
102 }
103 if (bytes_consumed) {
104 *bytes_consumed = PHPSTR_LEN(buffer);
105 }
106 phpstr_fix(PHPSTR(buffer));
107
108 #if HTTP_DEBUG_FILTERS
109 fprintf(stderr, " done\nCurrent buffer length: %lu bytes\n", PHPSTR_LEN(buffer));
110 #endif
111
112 buffer->passon = 0;
113 while (1) {
114 if (buffer->status == HFS_HEX) {
115 const char *eol;
116 char *stop;
117 ulong clen;
118
119 #if HTTP_DEBUG_FILTERS
120 fprintf(stderr, "Status HFS_HEX: ");
121 #endif
122
123 if (!(eol = http_locate_eol(PHPSTR_VAL(buffer), &buffer->eollen))) {
124 #if HTTP_DEBUG_FILTERS
125 fprintf(stderr, "return PFSF_FEED_ME (no eol)\n");
126 #endif
127 return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME;
128 }
129 if (!(clen = strtoul(PHPSTR_VAL(buffer), &stop, 16))) {
130 #if HTTP_DEBUG_FILTERS
131 fprintf(stderr, "return PFSF_FEED_ME (no len)\n");
132 #endif
133 phpstr_dtor(PHPSTR(buffer));
134 return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME;
135 }
136
137 buffer->status = HFS_DATA;
138 buffer->wanted = clen;
139 phpstr_cut(PHPSTR(buffer), 0, eol + buffer->eollen - PHPSTR_VAL(buffer));
140
141 #if HTTP_DEBUG_FILTERS
142 fprintf(stderr, "read %lu bytes chunk size\n", buffer->wanted);
143 #endif
144 }
145
146 #if HTTP_DEBUG_FILTERS
147 fprintf(stderr, "Current status: %s\n", buffer->status == HFS_DATA?"HFS_DATA":"HFS_HEX");
148 fprintf(stderr, "Current buffer length: %lu bytes\n", PHPSTR_LEN(buffer));
149 #endif
150
151 if (buffer->status == HFS_DATA && buffer->wanted > 0 && buffer->wanted <= PHPSTR_LEN(buffer)) {
152
153 #if HTTP_DEBUG_FILTERS
154 fprintf(stderr, "Passing on %lu(%lu) bytes\n", buffer->wanted, PHPSTR_LEN(buffer));
155 #endif
156
157 NEW_BUCKET(PHPSTR_VAL(buffer), buffer->wanted);
158 phpstr_cut(PHPSTR(buffer), 0, buffer->wanted + buffer->eollen);
159 buffer->wanted = 0;
160 buffer->eollen = 0;
161 buffer->passon = 1;
162 buffer->status = HFS_HEX;
163 continue;
164 }
165 return buffer->passon ? PSFS_PASS_ON : PSFS_FEED_ME;
166 }
167
168 return PSFS_FEED_ME;
169 }
170
171 static void http_filter_chunked_decode_dtor(php_stream_filter *this TSRMLS_DC)
172 {
173 http_filter_buffer *b = (http_filter_buffer *) (this->abstract);
174
175 phpstr_dtor(PHPSTR(b));
176 pefree(b, this->is_persistent);
177 }
178
179 PHP_STREAM_FILTER_OP_FILTER(http_filter_chunked_encode)
180 {
181 phpstr buf;
182 php_stream_bucket *ptr, *nxt;
183
184 if (bytes_consumed) {
185 *bytes_consumed = 0;
186 }
187
188 if (!buckets_in->head) {
189 return PSFS_FEED_ME;
190 }
191
192 phpstr_init(&buf);
193 for (ptr = buckets_in->head; ptr; ptr = nxt) {
194 if (bytes_consumed) {
195 *bytes_consumed += ptr->buflen;
196 }
197 nxt = ptr->next;
198
199 phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen);
200 phpstr_append(&buf, ptr->buf, ptr->buflen);
201 phpstr_appends(&buf, HTTP_CRLF);
202 NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf));
203 PHPSTR_LEN(&buf) = 0;
204
205 php_stream_bucket_unlink(ptr TSRMLS_CC);
206 php_stream_bucket_delref(ptr TSRMLS_CC);
207 }
208 phpstr_dtor(&buf);
209
210 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
211 NEW_BUCKET("0"HTTP_CRLF, lenof("0"HTTP_CRLF));
212 }
213
214 return PSFS_PASS_ON;
215 }
216
217 static php_stream_filter_ops http_filter_ops_chunked_decode = {
218 http_filter_chunked_decode,
219 http_filter_chunked_decode_dtor,
220 "http.chunked_decode"
221 };
222
223 static php_stream_filter_ops http_filter_ops_chunked_encode = {
224 http_filter_chunked_encode,
225 NULL,
226 "http.chunked_encode"
227 };
228
229 static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
230 {
231 php_stream_filter *f = NULL;
232 void *b = NULL;
233
234 if (!strcasecmp(name, "http.chunked_decode")) {
235 if (b = pecalloc(1, sizeof(http_filter_buffer), p)) {
236 phpstr_init(PHPSTR(b));
237 if (!(f = php_stream_filter_alloc(&http_filter_ops_chunked_decode, b, p))) {
238 pefree(b, p);
239 }
240 }
241 } else
242 if (!strcasecmp(name, "http.chunked_encode")) {
243 f = php_stream_filter_alloc(&http_filter_ops_chunked_encode, NULL, p);
244 }
245
246 return f;
247 }
248
249 php_stream_filter_factory http_filter_factory = {
250 http_filter_create
251 };
252
253 PHP_MINIT_FUNCTION(http_filter)
254 {
255 php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC);
256 return SUCCESS;
257 }
258
259 /*
260 * Local variables:
261 * tab-width: 4
262 * c-basic-offset: 4
263 * End:
264 * vim600: noet sw=4 ts=4 fdm=marker
265 * vim<600: noet sw=4 ts=4
266 */
267