2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
15 #define HTTP_WANT_ZLIB
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"
25 PHP_MINIT_FUNCTION(http_encoding
)
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
);
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
);
46 PHP_RINIT_FUNCTION(http_encoding
)
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
);
52 php_ob_set_internal_handler(_http_ob_inflatehandler
, HTTP_INFLATE_BUFFER_SIZE
, "http inflate", 0 TSRMLS_CC
);
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
);
59 php_ob_set_internal_handler(_http_ob_deflatehandler
, HTTP_DEFLATE_BUFFER_SIZE
, "http deflate", 0 TSRMLS_CC
);
65 PHP_RSHUTDOWN_FUNCTION(http_encoding
)
67 if (HTTP_G
->send
.deflate
.stream
) {
68 http_encoding_deflate_stream_free((http_encoding_stream
**) &HTTP_G
->send
.deflate
.stream
);
70 if (HTTP_G
->send
.inflate
.stream
) {
71 http_encoding_inflate_stream_free((http_encoding_stream
**) &HTTP_G
->send
.inflate
.stream
);
78 /* {{{ eol_match(char **, int *) */
79 static inline int eol_match(char **line
, int *eol_len
)
83 while (' ' == *ptr
) ++ptr
;
85 if (ptr
== http_locate_eol(*line
, eol_len
)) {
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
)
99 const char *e_ptr
= encoded
;
102 *decoded
= ecalloc(1, encoded_len
);
104 while ((encoded
+ encoded_len
- e_ptr
) > 0) {
105 ulong chunk_len
= 0, rest
;
107 chunk_len
= strtoul(e_ptr
, &n_ptr
, 16);
109 /* we could not read in chunk size */
110 if (n_ptr
== e_ptr
) {
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
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
;
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
);
128 /* reached the end */
130 /* move over '0' chunked encoding terminator */
131 while (*e_ptr
== '0') ++e_ptr
;
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
)) {
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));
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
);
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
);
152 memcpy(*decoded
+ *decoded_len
, n_ptr
, chunk_len
);
153 *decoded_len
+= chunk_len
;
155 if (chunk_len
== rest
) {
156 e_ptr
= n_ptr
+ chunk_len
;
159 /* advance to next chunk */
160 e_ptr
= n_ptr
+ chunk_len
+ eol_len
;
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
)
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
);
175 int ohandler
= php_ob_handler_used("ob_gzhandler" TSRMLS_CC
) || php_ob_handler_used("zlib output compression" TSRMLS_CC
);
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
);
182 ohandler
= php_ob_handler_used("ob_deflatehandler" TSRMLS_CC
) || php_ob_handler_used("http deflate" TSRMLS_CC
);
186 if (response
&& !ohandler
) {
187 #ifdef HTTP_HAVE_ZLIB
191 HTTP_G
->send
.deflate
.encoding
= 0;
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);
199 if ((selected
= http_negotiate_encoding(&zsupported
))) {
201 char *encoding
= NULL
;
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
;
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
;
215 http_send_header_string("Vary: Accept-Encoding");
219 zend_hash_destroy(selected
);
220 FREE_HASHTABLE(selected
);
223 zval_dtor(&zsupported
);
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 */
232 phpstr_init(&header
);
233 phpstr_appendf(&header
, "Content-Length: %zu", content_length
);
235 http_send_header_string_ex(PHPSTR_VAL(&header
), PHPSTR_LEN(&header
), 1);
236 phpstr_dtor(&header
);
238 HTTP_G
->send
.deflate
.encoding
= 0;
241 return HTTP_G
->send
.deflate
.encoding
;
245 #ifdef HTTP_HAVE_ZLIB
247 /* {{{ inline int http_inflate_rounds */
248 static inline int http_inflate_rounds(z_stream
*Z
, int flush
, char **buf
, size_t *len
)
250 int status
= 0, round
= 0;
256 phpstr_init_ex(&buffer
, Z
->avail_in
, PHPSTR_INIT_PREALLOC
);
259 if (PHPSTR_NOMEM
== phpstr_resize_ex(&buffer
, buffer
.size
, 0, 1)) {
260 status
= Z_MEM_ERROR
;
262 Z
->avail_out
= buffer
.free
;
263 Z
->next_out
= (Bytef
*) buffer
.data
+ buffer
.used
;
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
);
267 status
= inflate(Z
, flush
);
269 buffer
.used
+= buffer
.free
- Z
->avail_out
;
270 buffer
.free
= Z
->avail_out
;
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
);
274 HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer
.size
);
276 } while ((Z_BUF_ERROR
== status
|| (Z_OK
== status
&& Z
->avail_in
)) && ++round
< HTTP_INFLATE_ROUNDS
);
278 if (status
== Z_OK
|| status
== Z_STREAM_END
) {
279 phpstr_shrink(&buffer
);
284 phpstr_dtor(&buffer
);
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
)
294 int status
, level
, wbits
, strategy
;
297 HTTP_DEFLATE_LEVEL_SET(flags
, level
);
298 HTTP_DEFLATE_WBITS_SET(flags
, wbits
);
299 HTTP_DEFLATE_STRATEGY_SET(flags
, strategy
);
301 memset(&Z
, 0, sizeof(z_stream
));
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
);
310 Z
.next_in
= (Bytef
*) data
;
311 Z
.next_out
= (Bytef
*) *encoded
;
312 Z
.avail_in
= data_len
;
313 Z
.avail_out
= *encoded_len
;
315 status
= deflate(&Z
, Z_FINISH
);
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';
324 STR_SET(*encoded
, NULL
);
329 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not deflate data: %s", zError(status
));
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
)
338 int status
, wbits
= HTTP_WINDOW_BITS_ANY
;
340 memset(&Z
, 0, sizeof(z_stream
));
343 status
= inflateInit2(&Z
, wbits
);
344 if (Z_OK
== status
) {
345 Z
.next_in
= (Bytef
*) data
;
346 Z
.avail_in
= data_len
;
348 switch (status
= http_inflate_rounds(&Z
, Z_NO_FLUSH
, decoded
, decoded_len
)) {
354 status
= Z_DATA_ERROR
;
358 /* raw deflated data? */
359 if (HTTP_WINDOW_BITS_ANY
== wbits
) {
361 wbits
= HTTP_WINDOW_BITS_RAW
;
362 goto retry_raw_inflate
;
368 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not inflate data: %s", zError(status
));
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
)
376 int status
, level
, wbits
, strategy
, free_stream
;
378 if ((free_stream
= !s
)) {
379 s
= pemalloc_rel(sizeof(http_encoding_stream
), (flags
& HTTP_ENCODING_STREAM_PERSISTENT
));
381 memset(s
, 0, sizeof(http_encoding_stream
));
384 HTTP_DEFLATE_LEVEL_SET(flags
, level
);
385 HTTP_DEFLATE_WBITS_SET(flags
, wbits
);
386 HTTP_DEFLATE_STRATEGY_SET(flags
, strategy
);
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;
391 if ((s
->stream
.opaque
= phpstr_init_ex(NULL
, HTTP_DEFLATE_BUFFER_SIZE
, p
))) {
394 deflateEnd(&s
->stream
);
395 status
= Z_MEM_ERROR
;
398 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to initialize deflate encoding stream: %s", zError(status
));
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
)
409 int status
, wbits
, free_stream
;
411 if ((free_stream
= !s
)) {
412 s
= pemalloc_rel(sizeof(http_encoding_stream
), (flags
& HTTP_ENCODING_STREAM_PERSISTENT
));
414 memset(s
, 0, sizeof(http_encoding_stream
));
417 HTTP_INFLATE_WBITS_SET(flags
, wbits
);
419 if (Z_OK
== (status
= inflateInit2(&s
->stream
, wbits
))) {
420 int p
= (flags
& HTTP_ENCODING_STREAM_PERSISTENT
) ? PHPSTR_INIT_PERSISTENT
:0;
422 if ((s
->stream
.opaque
= phpstr_init_ex(NULL
, HTTP_DEFLATE_BUFFER_SIZE
, p
))) {
425 inflateEnd(&s
->stream
);
426 status
= Z_MEM_ERROR
;
429 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to initialize inflate stream: %s", zError(status
));
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
)
442 /* append input to our buffer */
443 phpstr_append(PHPSTR(s
->stream
.opaque
), data
, data_len
);
445 s
->stream
.next_in
= (Bytef
*) PHPSTR_VAL(s
->stream
.opaque
);
446 s
->stream
.avail_in
= PHPSTR_LEN(s
->stream
.opaque
);
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
;
454 switch (status
= deflate(&s
->stream
, HTTP_ENCODING_STREAM_FLUSH_FLAG(s
->flags
))) {
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
);
461 phpstr_reset(PHPSTR(s
->stream
.opaque
));
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';
471 STR_SET(*encoded
, NULL
);
473 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to update deflate stream: %s", zError(status
));
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
)
483 /* append input to buffer */
484 phpstr_append(PHPSTR(s
->stream
.opaque
), data
, data_len
);
487 s
->stream
.next_in
= (Bytef
*) PHPSTR_VAL(s
->stream
.opaque
);
488 s
->stream
.avail_in
= PHPSTR_LEN(s
->stream
.opaque
);
490 switch (status
= http_inflate_rounds(&s
->stream
, HTTP_ENCODING_STREAM_FLUSH_FLAG(s
->flags
), decoded
, decoded_len
)) {
494 if (s
->stream
.avail_in
) {
495 phpstr_cut(PHPSTR(s
->stream
.opaque
), 0, PHPSTR_LEN(s
->stream
.opaque
) - s
->stream
.avail_in
);
497 phpstr_reset(PHPSTR(s
->stream
.opaque
));
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
;
511 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to update inflate stream: %s", zError(status
));
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
)
521 *encoded_len
= HTTP_DEFLATE_BUFFER_SIZE
;
522 *encoded
= emalloc_rel(*encoded_len
);
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
;
529 switch (status
= deflate(&s
->stream
, Z_FULL_FLUSH
)) {
532 *encoded_len
= HTTP_DEFLATE_BUFFER_SIZE
- s
->stream
.avail_out
;
533 *encoded
= erealloc_rel(*encoded
, *encoded_len
+ 1);
534 (*encoded
)[*encoded_len
] = '\0';
538 STR_SET(*encoded
, NULL
);
540 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to flush deflate stream: %s", zError(status
));
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
)
549 *decoded
= estrndup("", *decoded_len
= 0);
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
)
559 *encoded_len
= HTTP_DEFLATE_BUFFER_SIZE
;
560 *encoded
= emalloc_rel(*encoded_len
);
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
);
566 s
->stream
.avail_out
= *encoded_len
;
567 s
->stream
.next_out
= (Bytef
*) *encoded
;
570 status
= deflate(&s
->stream
, Z_FINISH
);
571 } while (Z_OK
== status
);
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
);
578 *encoded_len
-= s
->stream
.avail_out
;
579 *encoded
= erealloc_rel(*encoded
, *encoded_len
+ 1);
580 (*encoded
)[*encoded_len
] = '\0';
584 STR_SET(*encoded
, NULL
);
586 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to finish deflate stream: %s", zError(status
));
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
)
596 if (!PHPSTR_LEN(s
->stream
.opaque
)) {
602 *decoded_len
= (PHPSTR_LEN(s
->stream
.opaque
) + 1) * HTTP_INFLATE_ROUNDS
;
603 *decoded
= emalloc_rel(*decoded_len
);
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
);
609 s
->stream
.avail_out
= *decoded_len
;
610 s
->stream
.next_out
= (Bytef
*) *decoded
;
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
);
617 *decoded_len
-= s
->stream
.avail_out
;
618 *decoded
= erealloc_rel(*decoded
, *decoded_len
+ 1);
619 (*decoded
)[*decoded_len
] = '\0';
623 STR_SET(*decoded
, NULL
);
625 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to finish inflate stream: %s", zError(status
));
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
)
634 if (s
->stream
.opaque
) {
635 phpstr_free((phpstr
**) &s
->stream
.opaque
);
637 deflateEnd(&s
->stream
);
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
)
646 if (s
->stream
.opaque
) {
647 phpstr_free((phpstr
**) &s
->stream
.opaque
);
649 inflateEnd(&s
->stream
);
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
)
658 http_encoding_deflate_stream_dtor(*s
);
660 pefree(*s
, (*s
)->flags
& HTTP_ENCODING_STREAM_PERSISTENT
);
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
)
671 http_encoding_inflate_stream_dtor(*s
);
673 pefree(*s
, (*s
)->flags
& HTTP_ENCODING_STREAM_PERSISTENT
);
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
)
685 *handled_output
= NULL
;
686 *handled_output_len
= 0;
688 if (mode
& PHP_OUTPUT_HANDLER_START
) {
691 if (HTTP_G
->send
.deflate
.stream
) {
692 zend_error(E_ERROR
, "ob_deflatehandler() can only be used once");
696 HTTP_G
->send
.deflate
.response
= 1;
697 encoding
= http_encoding_response_start(0, 1);
698 HTTP_G
->send
.deflate
.response
= 0;
701 case HTTP_ENCODING_GZIP
:
702 flags
= HTTP_DEFLATE_TYPE_GZIP
;
705 case HTTP_ENCODING_DEFLATE
:
706 flags
= HTTP_DEFLATE_TYPE_ZLIB
;
710 goto deflate_passthru_plain
;
713 flags
|= (HTTP_G
->send
.deflate
.start_flags
&~ 0xf0);
714 HTTP_G
->send
.deflate
.stream
= http_encoding_deflate_stream_init(NULL
, flags
);
717 if (HTTP_G
->send
.deflate
.stream
) {
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
;
725 if (mode
& PHP_OUTPUT_HANDLER_END
) {
726 char *remaining
= NULL
;
727 size_t remaining_len
= 0;
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
);
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';
739 deflate_passthru_plain
:
740 *handled_output
= estrndup(output
, *handled_output_len
= output_len
);
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
)
748 *handled_output
= NULL
;
749 *handled_output_len
= 0;
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");
756 HTTP_G
->send
.inflate
.stream
= http_encoding_inflate_stream_init(NULL
, (HTTP_G
->send
.inflate
.start_flags
&~ 0xf0));
759 if (HTTP_G
->send
.inflate
.stream
) {
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
;
767 if (mode
& PHP_OUTPUT_HANDLER_END
) {
768 char *remaining
= NULL
;
769 size_t remaining_len
= 0;
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
);
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';
781 *handled_output
= estrndup(output
, *handled_output_len
= output_len
);
786 #endif /* HTTP_HAVE_ZLIB */
793 * vim600: noet sw=4 ts=4 fdm=marker
794 * vim<600: noet sw=4 ts=4