fix problem when finishing a chunked encoding stream
[m6w6/ext-http] / php_http_buffer.c
1
2 /* $Id: php_http_buffer.c 211942 2006-04-24 17:17:09Z mike $ */
3
4 #include "php.h"
5 #include "php_http_buffer.h"
6
7 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_init_ex(php_http_buffer *buf, size_t chunk_size, int flags)
8 {
9 if (!buf) {
10 buf = pemalloc(sizeof(php_http_buffer), flags & PHP_HTTP_BUFFER_INIT_PERSISTENT);
11 }
12
13 if (buf) {
14 buf->size = (chunk_size) ? chunk_size : PHP_HTTP_BUFFER_DEFAULT_SIZE;
15 buf->pmem = (flags & PHP_HTTP_BUFFER_INIT_PERSISTENT) ? 1 : 0;
16 buf->data = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? pemalloc(buf->size, buf->pmem) : NULL;
17 buf->free = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? buf->size : 0;
18 buf->used = 0;
19 }
20
21 return buf;
22 }
23
24 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_from_string_ex(php_http_buffer *buf, const char *string, size_t length)
25 {
26 if ((buf = php_http_buffer_init(buf))) {
27 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, string, length)) {
28 pefree(buf, buf->pmem);
29 buf = NULL;
30 }
31 }
32 return buf;
33 }
34
35 PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer *buf, size_t len, size_t override_size, int allow_error)
36 {
37 char *ptr = NULL;
38 #if 0
39 fprintf(stderr, "RESIZE: size=%lu, used=%lu, free=%lu\n", buf->size, buf->used, buf->free);
40 #endif
41 if (buf->free < len) {
42 size_t size = override_size ? override_size : buf->size;
43
44 while ((size + buf->free) < len) {
45 size <<= 1;
46 }
47
48 if (allow_error) {
49 ptr = perealloc_recoverable(buf->data, buf->used + buf->free + size, buf->pmem);
50 } else {
51 ptr = perealloc(buf->data, buf->used + buf->free + size, buf->pmem);
52 }
53
54 if (ptr) {
55 buf->data = ptr;
56 } else {
57 return PHP_HTTP_BUFFER_NOMEM;
58 }
59
60 buf->free += size;
61 return size;
62 }
63 return 0;
64 }
65
66 PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer *buf)
67 {
68 /* avoid another realloc on fixation */
69 if (buf->free > 1) {
70 char *ptr = perealloc(buf->data, buf->used + 1, buf->pmem);
71
72 if (ptr) {
73 buf->data = ptr;
74 } else {
75 return PHP_HTTP_BUFFER_NOMEM;
76 }
77 buf->free = 1;
78 }
79 return buf->used;
80 }
81
82 PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer *buf, const char *append, size_t append_len)
83 {
84 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, append_len)) {
85 return PHP_HTTP_BUFFER_NOMEM;
86 }
87 memcpy(buf->data + buf->used, append, append_len);
88 buf->used += append_len;
89 buf->free -= append_len;
90 return append_len;
91 }
92
93 PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer *buf, const char *format, ...)
94 {
95 va_list argv;
96 char *append;
97 size_t append_len, alloc;
98
99 va_start(argv, format);
100 append_len = vspprintf(&append, 0, format, argv);
101 va_end(argv);
102
103 alloc = php_http_buffer_append(buf, append, append_len);
104 efree(append);
105
106 if (PHP_HTTP_BUFFER_NOMEM == alloc) {
107 return PHP_HTTP_BUFFER_NOMEM;
108 }
109 return append_len;
110 }
111
112 PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer *buf, const char *insert, size_t insert_len, size_t offset)
113 {
114 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, insert_len)) {
115 return PHP_HTTP_BUFFER_NOMEM;
116 }
117 memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
118 memcpy(buf->data + offset, insert, insert_len);
119 buf->used += insert_len;
120 buf->free -= insert_len;
121 return insert_len;
122 }
123
124 PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer *buf, size_t offset, const char *format, ...)
125 {
126 va_list argv;
127 char *insert;
128 size_t insert_len, alloc;
129
130 va_start(argv, format);
131 insert_len = vspprintf(&insert, 0, format, argv);
132 va_end(argv);
133
134 alloc = php_http_buffer_insert(buf, insert, insert_len, offset);
135 efree(insert);
136
137 if (PHP_HTTP_BUFFER_NOMEM == alloc) {
138 return PHP_HTTP_BUFFER_NOMEM;
139 }
140 return insert_len;
141 }
142
143 PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer *buf, const char *prepend, size_t prepend_len)
144 {
145 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, prepend_len)) {
146 return PHP_HTTP_BUFFER_NOMEM;
147 }
148 memmove(buf->data + prepend_len, buf->data, buf->used);
149 memcpy(buf->data, prepend, prepend_len);
150 buf->used += prepend_len;
151 buf->free -= prepend_len;
152 return prepend_len;
153 }
154
155 PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer *buf, const char *format, ...)
156 {
157 va_list argv;
158 char *prepend;
159 size_t prepend_len, alloc;
160
161 va_start(argv, format);
162 prepend_len = vspprintf(&prepend, 0, format, argv);
163 va_end(argv);
164
165 alloc = php_http_buffer_prepend(buf, prepend, prepend_len);
166 efree(prepend);
167
168 if (PHP_HTTP_BUFFER_NOMEM == alloc) {
169 return PHP_HTTP_BUFFER_NOMEM;
170 }
171 return prepend_len;
172 }
173
174 PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer *buf, char **into, size_t *len)
175 {
176 char *copy = ecalloc(1, buf->used + 1);
177 memcpy(copy, buf->data, buf->used);
178 if (into) {
179 *into = copy;
180 }
181 if (len) {
182 *len = buf->used;
183 }
184 return copy;
185 }
186
187 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_dup(const php_http_buffer *buf)
188 {
189 php_http_buffer *dup = php_http_buffer_clone(buf);
190 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(dup, buf->data, buf->used)) {
191 php_http_buffer_free(&dup);
192 }
193 return dup;
194 }
195
196 PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer *buf, size_t offset, size_t length)
197 {
198 if (offset > buf->used) {
199 return 0;
200 }
201 if (offset + length > buf->used) {
202 length = buf->used - offset;
203 }
204 memmove(buf->data + offset, buf->data + offset + length, buf->used - length - offset);
205 buf->used -= length;
206 buf->free += length;
207 return length;
208 }
209
210 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_sub(const php_http_buffer *buf, size_t offset, size_t length)
211 {
212 if (offset >= buf->used) {
213 return NULL;
214 } else {
215 size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset));
216 php_http_buffer *sub = php_http_buffer_init_ex(NULL, need, PHP_HTTP_BUFFER_INIT_PREALLOC | (buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0));
217 if (sub) {
218 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(sub, buf->data + offset, need)) {
219 php_http_buffer_free(&sub);
220 } else {
221 sub->size = buf->size;
222 }
223 }
224 return sub;
225 }
226 }
227
228 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_right(const php_http_buffer *buf, size_t length)
229 {
230 if (length < buf->used) {
231 return php_http_buffer_sub(buf, buf->used - length, length);
232 } else {
233 return php_http_buffer_sub(buf, 0, buf->used);
234 }
235 }
236
237
238 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_merge_va(php_http_buffer *buf, unsigned argc, va_list argv)
239 {
240 unsigned i = 0;
241 buf = php_http_buffer_init(buf);
242
243 if (buf) {
244 while (argc > i++) {
245 php_http_buffer_free_t f = va_arg(argv, php_http_buffer_free_t);
246 php_http_buffer *current = va_arg(argv, php_http_buffer *);
247 php_http_buffer_append(buf, current->data, current->used);
248 FREE_PHP_HTTP_BUFFER(f, current);
249 }
250 }
251
252 return buf;
253 }
254
255 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_merge_ex(php_http_buffer *buf, unsigned argc, ...)
256 {
257 va_list argv;
258 php_http_buffer *ret;
259
260 va_start(argv, argc);
261 ret = php_http_buffer_merge_va(buf, argc, argv);
262 va_end(argv);
263 return ret;
264 }
265
266 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_merge(unsigned argc, ...)
267 {
268 va_list argv;
269 php_http_buffer *ret;
270
271 va_start(argv, argc);
272 ret = php_http_buffer_merge_va(NULL, argc, argv);
273 va_end(argv);
274 return ret;
275 }
276
277 PHP_HTTP_BUFFER_API php_http_buffer *php_http_buffer_fix(php_http_buffer *buf)
278 {
279 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(buf, 1, 1, 0)) {
280 return NULL;
281 }
282 buf->data[buf->used] = '\0';
283 return buf;
284 }
285
286 PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer *left, php_http_buffer *right)
287 {
288 if (left->used > right->used) {
289 return -1;
290 } else if (right->used > left->used) {
291 return 1;
292 } else {
293 return memcmp(left->data, right->data, left->used);
294 }
295 }
296
297 PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer *buf)
298 {
299 buf->free += buf->used;
300 buf->used = 0;
301 }
302
303 PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer *buf)
304 {
305 if (buf->data) {
306 pefree(buf->data, buf->pmem);
307 buf->data = NULL;
308 }
309 buf->used = 0;
310 buf->free = 0;
311 }
312
313 PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer **buf)
314 {
315 if (*buf) {
316 php_http_buffer_dtor(*buf);
317 pefree(*buf, (*buf)->pmem);
318 *buf = NULL;
319 }
320 }
321
322 PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer **s, const char *data, size_t data_len, char **chunk, size_t chunk_size)
323 {
324 php_http_buffer *storage;
325
326 *chunk = NULL;
327
328 if (!*s) {
329 *s = php_http_buffer_init_ex(NULL, chunk_size << 1, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
330 }
331 storage = *s;
332
333 if (data_len) {
334 php_http_buffer_append(storage, data, data_len);
335 }
336
337 if (!chunk_size) {
338 php_http_buffer_data(storage, chunk, &chunk_size);
339 php_http_buffer_free(s);
340 return chunk_size;
341 }
342
343 if (storage->used >= (chunk_size = storage->size >> 1)) {
344 *chunk = estrndup(storage->data, chunk_size);
345 php_http_buffer_cut(storage, 0, chunk_size);
346 return chunk_size;
347 }
348
349 return 0;
350 }
351
352 PHP_HTTP_BUFFER_API void php_http_buffer_chunked_output(php_http_buffer **s, const char *data, size_t data_len, size_t chunk_len, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC)
353 {
354 char *chunk = NULL;
355 size_t got = 0;
356
357 while ((got = php_http_buffer_chunk_buffer(s, data, data_len, &chunk, chunk_len))) {
358 passout(opaque, chunk, got TSRMLS_CC);
359 if (!chunk_len) {
360 /* we already got the last chunk,
361 and freed all resources */
362 break;
363 }
364 data = NULL;
365 data_len = 0;
366 STR_SET(chunk, NULL);
367 }
368 STR_FREE(chunk);
369 }
370
371 PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer *s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC)
372 {
373 size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(&s, chunk_size, passin, passin_arg TSRMLS_CC);
374
375 if (passed_in == PHP_HTTP_BUFFER_PASS0) {
376 return passed_in;
377 }
378 if (passed_in) {
379 passed_on = passon(passon_arg, s->data, passed_in TSRMLS_CC);
380
381 if (passed_on == PHP_HTTP_BUFFER_PASS0) {
382 return passed_on;
383 }
384
385 if (passed_on) {
386 php_http_buffer_cut(s, 0, passed_on);
387 }
388 }
389
390 return passed_on - passed_in;
391 }
392
393 PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC)
394 {
395 php_http_buffer *str;
396 size_t passed;
397
398 if (!*s) {
399 *s = php_http_buffer_init_ex(NULL, chunk_size, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0);
400 }
401 str = *s;
402
403 php_http_buffer_resize(str, chunk_size);
404 passed = passin(opaque, str->data + str->used, chunk_size TSRMLS_CC);
405
406 if (passed != PHP_HTTP_BUFFER_PASS0) {
407 str->used += passed;
408 str->free -= passed;
409 }
410
411 php_http_buffer_fix(str);
412
413 return passed;
414 }
415
416 /*
417 * Local variables:
418 * tab-width: 4
419 * c-basic-offset: 4
420 * End:
421 * vim600: sw=4 ts=4 fdm=marker
422 * vim<600: sw=4 ts=4
423 */
424