fix tests
[m6w6/ext-http] / http_encoding_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-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_ZLIB
16 #include "php_http.h"
17
18 #include "php_http_api.h"
19 #include "php_http_encoding_api.h"
20 #include "php_http_send_api.h"
21 #include "php_http_headers_api.h"
22
23 /* {{{ */
24 #ifdef HTTP_HAVE_ZLIB
25 PHP_MINIT_FUNCTION(http_encoding)
26 {
27 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_DEF", HTTP_DEFLATE_LEVEL_DEF);
28 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MIN", HTTP_DEFLATE_LEVEL_MIN);
29 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MAX", HTTP_DEFLATE_LEVEL_MAX);
30 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_ZLIB", HTTP_DEFLATE_TYPE_ZLIB);
31 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_GZIP", HTTP_DEFLATE_TYPE_GZIP);
32 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_RAW", HTTP_DEFLATE_TYPE_RAW);
33 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_DEF", HTTP_DEFLATE_STRATEGY_DEF);
34 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FILT", HTTP_DEFLATE_STRATEGY_FILT);
35 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_HUFF", HTTP_DEFLATE_STRATEGY_HUFF);
36 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_RLE", HTTP_DEFLATE_STRATEGY_RLE);
37 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FIXED", HTTP_DEFLATE_STRATEGY_FIXED);
38
39 HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_NONE", HTTP_ENCODING_STREAM_FLUSH_NONE);
40 HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_SYNC", HTTP_ENCODING_STREAM_FLUSH_SYNC);
41 HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_FULL", HTTP_ENCODING_STREAM_FLUSH_FULL);
42
43 return SUCCESS;
44 }
45
46 PHP_RINIT_FUNCTION(http_encoding)
47 {
48 if (HTTP_G->send.inflate.start_auto) {
49 #ifdef PHP_OUTPUT_NEWAPI
50 php_output_start_internal(ZEND_STRL("http inflate"), _http_ob_inflatehandler, HTTP_INFLATE_BUFFER_SIZE, 0 TSRMLS_CC);
51 #else
52 php_ob_set_internal_handler(_http_ob_inflatehandler, HTTP_INFLATE_BUFFER_SIZE, "http inflate", 0 TSRMLS_CC);
53 #endif
54 }
55 if (HTTP_G->send.deflate.start_auto) {
56 #ifdef PHP_OUTPUT_NEWAPI
57 php_output_start_internal(ZEND_STRL("http deflate"), _http_ob_deflatehandler, HTTP_DEFLATE_BUFFER_SIZE, 0 TSRMLS_CC);
58 #else
59 php_ob_set_internal_handler(_http_ob_deflatehandler, HTTP_DEFLATE_BUFFER_SIZE, "http deflate", 0 TSRMLS_CC);
60 #endif
61 }
62 return SUCCESS;
63 }
64
65 PHP_RSHUTDOWN_FUNCTION(http_encoding)
66 {
67 if (HTTP_G->send.deflate.stream) {
68 http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream);
69 }
70 if (HTTP_G->send.inflate.stream) {
71 http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream);
72 }
73 return SUCCESS;
74 }
75 #endif
76 /* }}} */
77
78 /* {{{ eol_match(char **, int *) */
79 static inline int eol_match(char **line, int *eol_len)
80 {
81 char *ptr = *line;
82
83 while (' ' == *ptr) ++ptr;
84
85 if (ptr == http_locate_eol(*line, eol_len)) {
86 *line = ptr;
87 return 1;
88 } else {
89 return 0;
90 }
91 }
92 /* }}} */
93
94 /* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
95 PHP_HTTP_API const char *_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
96 {
97 int eol_len = 0;
98 char *n_ptr = NULL;
99 const char *e_ptr = encoded;
100
101 *decoded_len = 0;
102 *decoded = ecalloc(1, encoded_len);
103
104 while ((encoded + encoded_len - e_ptr) > 0) {
105 ulong chunk_len = 0, rest;
106
107 chunk_len = strtoul(e_ptr, &n_ptr, 16);
108
109 /* we could not read in chunk size */
110 if (n_ptr == e_ptr) {
111 /*
112 * if this is the first turn and there doesn't seem to be a chunk
113 * size at the begining of the body, do not fail on apparently
114 * not encoded data and return a copy
115 */
116 if (e_ptr == encoded) {
117 http_error(HE_NOTICE, HTTP_E_ENCODING, "Data does not seem to be chunked encoded");
118 memcpy(*decoded, encoded, encoded_len);
119 *decoded_len = encoded_len;
120 return encoded + encoded_len;
121 } else {
122 efree(*decoded);
123 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len);
124 return NULL;
125 }
126 }
127
128 /* reached the end */
129 if (!chunk_len) {
130 /* move over '0' chunked encoding terminator */
131 while (*e_ptr == '0') ++e_ptr;
132 break;
133 }
134
135 /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
136 if (*n_ptr && !eol_match(&n_ptr, &eol_len)) {
137 if (eol_len == 2) {
138 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1));
139 } else {
140 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr - encoded, encoded_len, *n_ptr);
141 }
142 }
143 n_ptr += eol_len;
144
145 /* chunk size pretends more data than we actually got, so it's probably a truncated message */
146 if (chunk_len > (rest = encoded + encoded_len - n_ptr)) {
147 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len, rest, n_ptr - encoded, encoded_len);
148 chunk_len = rest;
149 }
150
151 /* copy the chunk */
152 memcpy(*decoded + *decoded_len, n_ptr, chunk_len);
153 *decoded_len += chunk_len;
154
155 if (chunk_len == rest) {
156 e_ptr = n_ptr + chunk_len;
157 break;
158 } else {
159 /* advance to next chunk */
160 e_ptr = n_ptr + chunk_len + eol_len;
161 }
162 }
163
164 return e_ptr;
165 }
166 /* }}} */
167
168 /* {{{ int http_encoding_response_start(size_t) */
169 PHP_HTTP_API int _http_encoding_response_start(size_t content_length, zend_bool ignore_http_ohandler TSRMLS_DC)
170 {
171 int response = HTTP_G->send.deflate.response;
172 #ifdef PHP_OUTPUT_NEWAPI
173 int ohandler = php_output_handler_started(ZEND_STRL("ob_gzhandler") TSRMLS_CC) || php_output_handler_started(ZEND_STRL("zlib output compression") TSRMLS_CC);
174 #else
175 int ohandler = php_ob_handler_used("ob_gzhandler" TSRMLS_CC) || php_ob_handler_used("zlib output compression" TSRMLS_CC);
176 #endif
177
178 if (!ohandler && !ignore_http_ohandler) {
179 #ifdef PHP_OUTPUT_NEWAPI
180 ohandler = php_output_handler_started(ZEND_STRL("ob_defaltehandler") TSRMLS_CC) || php_output_handler_started(ZEND_STRL("http deflate") TSRMLS_CC);
181 #else
182 ohandler = php_ob_handler_used("ob_deflatehandler" TSRMLS_CC) || php_ob_handler_used("http deflate" TSRMLS_CC);
183 #endif
184 }
185
186 if (response && !ohandler) {
187 #ifdef HTTP_HAVE_ZLIB
188 HashTable *selected;
189 zval zsupported;
190
191 HTTP_G->send.deflate.encoding = 0;
192
193 INIT_PZVAL(&zsupported);
194 array_init(&zsupported);
195 add_next_index_stringl(&zsupported, "gzip", lenof("gzip"), 1);
196 add_next_index_stringl(&zsupported, "x-gzip", lenof("x-gzip"), 1);
197 add_next_index_stringl(&zsupported, "deflate", lenof("deflate"), 1);
198
199 if ((selected = http_negotiate_encoding(&zsupported))) {
200 STATUS hs = FAILURE;
201 char *encoding = NULL;
202 ulong idx;
203
204 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(selected, &encoding, &idx, 0) && encoding) {
205 if (!strcmp(encoding, "gzip") || !strcmp(encoding, "x-gzip")) {
206 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: gzip"))) {
207 HTTP_G->send.deflate.encoding = HTTP_ENCODING_GZIP;
208 }
209 } else if (!strcmp(encoding, "deflate")) {
210 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: deflate"))) {
211 HTTP_G->send.deflate.encoding = HTTP_ENCODING_DEFLATE;
212 }
213 }
214 if (SUCCESS == hs) {
215 http_send_header_string("Vary: Accept-Encoding");
216 }
217 }
218
219 zend_hash_destroy(selected);
220 FREE_HASHTABLE(selected);
221 }
222
223 zval_dtor(&zsupported);
224 #else
225 HTTP_G->send.deflate.encoding = 0;
226 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC);
227 #endif /* HTTP_HAVE_ZLIB */
228 } else if (content_length && !ohandler) {
229 /* emit a content-length header */
230 phpstr header;
231
232 phpstr_init(&header);
233 phpstr_appendf(&header, "Content-Length: %zu", content_length);
234 phpstr_fix(&header);
235 http_send_header_string_ex(PHPSTR_VAL(&header), PHPSTR_LEN(&header), 1);
236 phpstr_dtor(&header);
237 } else {
238 HTTP_G->send.deflate.encoding = 0;
239 }
240
241 return HTTP_G->send.deflate.encoding;
242 }
243 /* }}} */
244
245 #ifdef HTTP_HAVE_ZLIB
246
247 /* {{{ inline int http_inflate_rounds */
248 static inline int http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len)
249 {
250 int status = 0, round = 0;
251 phpstr buffer;
252
253 *buf = NULL;
254 *len = 0;
255
256 phpstr_init_ex(&buffer, Z->avail_in, PHPSTR_INIT_PREALLOC);
257
258 do {
259 if (PHPSTR_NOMEM == phpstr_resize_ex(&buffer, buffer.size, 0, 1)) {
260 status = Z_MEM_ERROR;
261 } else {
262 Z->avail_out = buffer.free;
263 Z->next_out = (Bytef *) buffer.data + buffer.used;
264 #if 0
265 fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
266 #endif
267 status = inflate(Z, flush);
268
269 buffer.used += buffer.free - Z->avail_out;
270 buffer.free = Z->avail_out;
271 #if 0
272 fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
273 #endif
274 HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size);
275 }
276 } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < HTTP_INFLATE_ROUNDS);
277
278 if (status == Z_OK || status == Z_STREAM_END) {
279 phpstr_shrink(&buffer);
280 phpstr_fix(&buffer);
281 *buf = buffer.data;
282 *len = buffer.used;
283 } else {
284 phpstr_dtor(&buffer);
285 }
286
287 return status;
288 }
289 /* }}} */
290
291 /* {{{ STATUS http_encoding_deflate(int, char *, size_t, char **, size_t *) */
292 PHP_HTTP_API STATUS _http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
293 {
294 int status, level, wbits, strategy;
295 z_stream Z;
296
297 HTTP_DEFLATE_LEVEL_SET(flags, level);
298 HTTP_DEFLATE_WBITS_SET(flags, wbits);
299 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
300
301 memset(&Z, 0, sizeof(z_stream));
302 *encoded = NULL;
303 *encoded_len = 0;
304
305 status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy);
306 if (Z_OK == status) {
307 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
308 *encoded = emalloc_rel(*encoded_len);
309
310 Z.next_in = (Bytef *) data;
311 Z.next_out = (Bytef *) *encoded;
312 Z.avail_in = data_len;
313 Z.avail_out = *encoded_len;
314
315 status = deflate(&Z, Z_FINISH);
316 deflateEnd(&Z);
317
318 if (Z_STREAM_END == status) {
319 /* size buffer down to actual length */
320 *encoded = erealloc_rel(*encoded, Z.total_out + 1);
321 (*encoded)[*encoded_len = Z.total_out] = '\0';
322 return SUCCESS;
323 } else {
324 STR_SET(*encoded, NULL);
325 *encoded_len = 0;
326 }
327 }
328
329 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not deflate data: %s", zError(status));
330 return FAILURE;
331 }
332 /* }}} */
333
334 /* {{{ STATUS http_encoding_inflate(char *, size_t, char **, size_t) */
335 PHP_HTTP_API STATUS _http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
336 {
337 z_stream Z;
338 int status, wbits = HTTP_WINDOW_BITS_ANY;
339
340 memset(&Z, 0, sizeof(z_stream));
341
342 retry_raw_inflate:
343 status = inflateInit2(&Z, wbits);
344 if (Z_OK == status) {
345 Z.next_in = (Bytef *) data;
346 Z.avail_in = data_len;
347
348 switch (status = http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) {
349 case Z_STREAM_END:
350 inflateEnd(&Z);
351 return SUCCESS;
352
353 case Z_OK:
354 status = Z_DATA_ERROR;
355 break;
356
357 case Z_DATA_ERROR:
358 /* raw deflated data? */
359 if (HTTP_WINDOW_BITS_ANY == wbits) {
360 inflateEnd(&Z);
361 wbits = HTTP_WINDOW_BITS_RAW;
362 goto retry_raw_inflate;
363 }
364 }
365 inflateEnd(&Z);
366 }
367
368 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not inflate data: %s", zError(status));
369 return FAILURE;
370 }
371 /* }}} */
372
373 /* {{{ http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *, int) */
374 PHP_HTTP_API http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
375 {
376 int status, level, wbits, strategy, free_stream;
377
378 if ((free_stream = !s)) {
379 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
380 }
381 memset(s, 0, sizeof(http_encoding_stream));
382 s->flags = flags;
383
384 HTTP_DEFLATE_LEVEL_SET(flags, level);
385 HTTP_DEFLATE_WBITS_SET(flags, wbits);
386 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
387
388 if (Z_OK == (status = deflateInit2(&s->stream, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) {
389 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
390
391 if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
392 return s;
393 }
394 deflateEnd(&s->stream);
395 status = Z_MEM_ERROR;
396 }
397
398 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize deflate encoding stream: %s", zError(status));
399 if (free_stream) {
400 efree(s);
401 }
402 return NULL;
403 }
404 /* }}} */
405
406 /* {{{ http_encoding_stream *http_encoding_inflate_stream_init(http_encoding_stream *, int) */
407 PHP_HTTP_API http_encoding_stream *_http_encoding_inflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
408 {
409 int status, wbits, free_stream;
410
411 if ((free_stream = !s)) {
412 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
413 }
414 memset(s, 0, sizeof(http_encoding_stream));
415 s->flags = flags;
416
417 HTTP_INFLATE_WBITS_SET(flags, wbits);
418
419 if (Z_OK == (status = inflateInit2(&s->stream, wbits))) {
420 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
421
422 if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
423 return s;
424 }
425 inflateEnd(&s->stream);
426 status = Z_MEM_ERROR;
427 }
428
429 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize inflate stream: %s", zError(status));
430 if (free_stream) {
431 efree(s);
432 }
433 return NULL;
434 }
435 /* }}} */
436
437 /* {{{ STATUS http_encoding_deflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */
438 PHP_HTTP_API STATUS _http_encoding_deflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
439 {
440 int status;
441
442 /* append input to our buffer */
443 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
444
445 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
446 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
447
448 /* deflate */
449 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
450 *encoded = emalloc_rel(*encoded_len);
451 s->stream.avail_out = *encoded_len;
452 s->stream.next_out = (Bytef *) *encoded;
453
454 switch (status = deflate(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags))) {
455 case Z_OK:
456 case Z_STREAM_END:
457 /* cut processed chunk off the buffer */
458 if (s->stream.avail_in) {
459 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
460 } else {
461 phpstr_reset(PHPSTR(s->stream.opaque));
462 }
463
464 /* size buffer down to actual size */
465 *encoded_len -= s->stream.avail_out;
466 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
467 (*encoded)[*encoded_len] = '\0';
468 return SUCCESS;
469 }
470
471 STR_SET(*encoded, NULL);
472 *encoded_len = 0;
473 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update deflate stream: %s", zError(status));
474 return FAILURE;
475 }
476 /* }}} */
477
478 /* {{{ STATUS http_encoding_inflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */
479 PHP_HTTP_API STATUS _http_encoding_inflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
480 {
481 int status;
482
483 /* append input to buffer */
484 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
485
486 retry_raw_inflate:
487 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
488 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
489
490 switch (status = http_inflate_rounds(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags), decoded, decoded_len)) {
491 case Z_OK:
492 case Z_STREAM_END:
493 /* cut off */
494 if (s->stream.avail_in) {
495 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
496 } else {
497 phpstr_reset(PHPSTR(s->stream.opaque));
498 }
499 return SUCCESS;
500
501 case Z_DATA_ERROR:
502 /* raw deflated data ? */
503 if (!(s->flags & HTTP_INFLATE_TYPE_RAW) && !s->stream.total_out) {
504 inflateEnd(&s->stream);
505 s->flags |= HTTP_INFLATE_TYPE_RAW;
506 inflateInit2(&s->stream, HTTP_WINDOW_BITS_RAW);
507 goto retry_raw_inflate;
508 }
509 }
510
511 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update inflate stream: %s", zError(status));
512 return FAILURE;
513 }
514 /* }}} */
515
516 /* {{{ STATUS http_encoding_deflate_stream_flush(http_encoding_stream *, char **, size_t *) */
517 PHP_HTTP_API STATUS _http_encoding_deflate_stream_flush(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
518 {
519 int status;
520
521 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
522 *encoded = emalloc_rel(*encoded_len);
523
524 s->stream.avail_in = 0;
525 s->stream.next_in = NULL;
526 s->stream.avail_out = *encoded_len;
527 s->stream.next_out = (Bytef *) *encoded;
528
529 switch (status = deflate(&s->stream, Z_FULL_FLUSH)) {
530 case Z_OK:
531 case Z_STREAM_END:
532 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE - s->stream.avail_out;
533 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
534 (*encoded)[*encoded_len] = '\0';
535 return SUCCESS;
536 }
537
538 STR_SET(*encoded, NULL);
539 *encoded_len = 0;
540 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to flush deflate stream: %s", zError(status));
541 return FAILURE;
542 }
543 /* }}} */
544
545 /* {{{ STATUS http_encoding_inflate_straem_flush(http_encoding_stream *, char **, size_t *) */
546 PHP_HTTP_API STATUS _http_encoding_inflate_stream_flush(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
547 {
548 /* noop */
549 *decoded = estrndup("", *decoded_len = 0);
550 return SUCCESS;
551 }
552 /* }}} */
553
554 /* {{{ STATUS http_encoding_deflate_stream_finish(http_encoding_stream *, char **, size_t *) */
555 PHP_HTTP_API STATUS _http_encoding_deflate_stream_finish(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
556 {
557 int status;
558
559 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
560 *encoded = emalloc_rel(*encoded_len);
561
562 /* deflate remaining input */
563 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
564 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
565
566 s->stream.avail_out = *encoded_len;
567 s->stream.next_out = (Bytef *) *encoded;
568
569 do {
570 status = deflate(&s->stream, Z_FINISH);
571 } while (Z_OK == status);
572
573 if (Z_STREAM_END == status) {
574 /* cut processed intp off */
575 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
576
577 /* size down */
578 *encoded_len -= s->stream.avail_out;
579 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
580 (*encoded)[*encoded_len] = '\0';
581 return SUCCESS;
582 }
583
584 STR_SET(*encoded, NULL);
585 *encoded_len = 0;
586 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish deflate stream: %s", zError(status));
587 return FAILURE;
588 }
589 /* }}} */
590
591 /* {{{ STATUS http_encoding_inflate_stream_finish(http_encoding_stream *, char **, size_t *) */
592 PHP_HTTP_API STATUS _http_encoding_inflate_stream_finish(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
593 {
594 int status;
595
596 if (!PHPSTR_LEN(s->stream.opaque)) {
597 *decoded = NULL;
598 *decoded_len = 0;
599 return SUCCESS;
600 }
601
602 *decoded_len = (PHPSTR_LEN(s->stream.opaque) + 1) * HTTP_INFLATE_ROUNDS;
603 *decoded = emalloc_rel(*decoded_len);
604
605 /* inflate remaining input */
606 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
607 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
608
609 s->stream.avail_out = *decoded_len;
610 s->stream.next_out = (Bytef *) *decoded;
611
612 if (Z_STREAM_END == (status = inflate(&s->stream, Z_FINISH))) {
613 /* cut processed input off */
614 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
615
616 /* size down */
617 *decoded_len -= s->stream.avail_out;
618 *decoded = erealloc_rel(*decoded, *decoded_len + 1);
619 (*decoded)[*decoded_len] = '\0';
620 return SUCCESS;
621 }
622
623 STR_SET(*decoded, NULL);
624 *decoded_len = 0;
625 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish inflate stream: %s", zError(status));
626 return FAILURE;
627 }
628 /* }}} */
629
630 /* {{{ void http_encoding_deflate_stream_dtor(http_encoding_stream *) */
631 PHP_HTTP_API void _http_encoding_deflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
632 {
633 if (s) {
634 if (s->stream.opaque) {
635 phpstr_free((phpstr **) &s->stream.opaque);
636 }
637 deflateEnd(&s->stream);
638 }
639 }
640 /* }}} */
641
642 /* {{{ void http_encoding_inflate_stream_dtor(http_encoding_stream *) */
643 PHP_HTTP_API void _http_encoding_inflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
644 {
645 if (s) {
646 if (s->stream.opaque) {
647 phpstr_free((phpstr **) &s->stream.opaque);
648 }
649 inflateEnd(&s->stream);
650 }
651 }
652 /* }}} */
653
654 /* {{{ void http_encoding_deflate_stream_free(http_encoding_stream **) */
655 PHP_HTTP_API void _http_encoding_deflate_stream_free(http_encoding_stream **s TSRMLS_DC)
656 {
657 if (s) {
658 http_encoding_deflate_stream_dtor(*s);
659 if (*s) {
660 pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
661 }
662 *s = NULL;
663 }
664 }
665 /* }}} */
666
667 /* {{{ void http_encoding_inflate_stream_free(http_encoding_stream **) */
668 PHP_HTTP_API void _http_encoding_inflate_stream_free(http_encoding_stream **s TSRMLS_DC)
669 {
670 if (s) {
671 http_encoding_inflate_stream_dtor(*s);
672 if (*s) {
673 pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
674 }
675 *s = NULL;
676 }
677 }
678 /* }}} */
679
680 /* {{{ void http_ob_deflatehandler(char *, uint, char **, uint *, int) */
681 void _http_ob_deflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
682 {
683 int encoding;
684
685 *handled_output = NULL;
686 *handled_output_len = 0;
687
688 if (mode & PHP_OUTPUT_HANDLER_START) {
689 int flags;
690
691 if (HTTP_G->send.deflate.stream) {
692 zend_error(E_ERROR, "ob_deflatehandler() can only be used once");
693 return;
694 }
695
696 HTTP_G->send.deflate.response = 1;
697 encoding = http_encoding_response_start(0, 1);
698 HTTP_G->send.deflate.response = 0;
699
700 switch (encoding) {
701 case HTTP_ENCODING_GZIP:
702 flags = HTTP_DEFLATE_TYPE_GZIP;
703 break;
704
705 case HTTP_ENCODING_DEFLATE:
706 flags = HTTP_DEFLATE_TYPE_ZLIB;
707 break;
708
709 default:
710 goto deflate_passthru_plain;
711 }
712
713 flags |= (HTTP_G->send.deflate.start_flags &~ 0xf0);
714 HTTP_G->send.deflate.stream = http_encoding_deflate_stream_init(NULL, flags);
715 }
716
717 if (HTTP_G->send.deflate.stream) {
718 if (output_len) {
719 size_t tmp_len;
720
721 http_encoding_deflate_stream_update((http_encoding_stream *) HTTP_G->send.deflate.stream, output, output_len, handled_output, &tmp_len);
722 *handled_output_len = tmp_len;
723 }
724
725 if (mode & PHP_OUTPUT_HANDLER_END) {
726 char *remaining = NULL;
727 size_t remaining_len = 0;
728
729 http_encoding_deflate_stream_finish((http_encoding_stream *) HTTP_G->send.deflate.stream, &remaining, &remaining_len);
730 http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream);
731 if (remaining) {
732 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
733 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
734 (*handled_output)[*handled_output_len += remaining_len] = '\0';
735 efree(remaining);
736 }
737 }
738 } else {
739 deflate_passthru_plain:
740 *handled_output = estrndup(output, *handled_output_len = output_len);
741 }
742 }
743 /* }}} */
744
745 /* {{{ void http_ob_inflatehandler(char *, uint, char **, uint *, int) */
746 void _http_ob_inflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
747 {
748 *handled_output = NULL;
749 *handled_output_len = 0;
750
751 if (mode & PHP_OUTPUT_HANDLER_START) {
752 if (HTTP_G->send.inflate.stream) {
753 zend_error(E_ERROR, "ob_inflatehandler() can only be used once");
754 return;
755 }
756 HTTP_G->send.inflate.stream = http_encoding_inflate_stream_init(NULL, (HTTP_G->send.inflate.start_flags &~ 0xf0));
757 }
758
759 if (HTTP_G->send.inflate.stream) {
760 if (output_len) {
761 size_t tmp_len;
762
763 http_encoding_inflate_stream_update((http_encoding_stream *) HTTP_G->send.inflate.stream, output, output_len, handled_output, &tmp_len);
764 *handled_output_len = tmp_len;
765 }
766
767 if (mode & PHP_OUTPUT_HANDLER_END) {
768 char *remaining = NULL;
769 size_t remaining_len = 0;
770
771 http_encoding_inflate_stream_finish((http_encoding_stream *) HTTP_G->send.inflate.stream, &remaining, &remaining_len);
772 http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream);
773 if (remaining) {
774 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
775 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
776 (*handled_output)[*handled_output_len += remaining_len] = '\0';
777 efree(remaining);
778 }
779 }
780 } else {
781 *handled_output = estrndup(output, *handled_output_len = output_len);
782 }
783 }
784 /* }}} */
785
786 #endif /* HTTP_HAVE_ZLIB */
787
788 /*
789 * Local variables:
790 * tab-width: 4
791 * c-basic-offset: 4
792 * End:
793 * vim600: noet sw=4 ts=4 fdm=marker
794 * vim<600: noet sw=4 ts=4
795 */
796