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-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
19 #define HTTP_WANT_ZLIB
22 #include "php_http_api.h"
23 #include "php_http_encoding_api.h"
24 #include "php_http_send_api.h"
25 #include "php_http_headers_api.h"
28 PHP_MINIT_FUNCTION(http_encoding
)
30 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_DEF", HTTP_DEFLATE_LEVEL_DEF
);
31 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MIN", HTTP_DEFLATE_LEVEL_MIN
);
32 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MAX", HTTP_DEFLATE_LEVEL_MAX
);
33 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_ZLIB", HTTP_DEFLATE_TYPE_ZLIB
);
34 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_GZIP", HTTP_DEFLATE_TYPE_GZIP
);
35 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_RAW", HTTP_DEFLATE_TYPE_RAW
);
36 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_DEF", HTTP_DEFLATE_STRATEGY_DEF
);
37 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FILT", HTTP_DEFLATE_STRATEGY_FILT
);
38 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_HUFF", HTTP_DEFLATE_STRATEGY_HUFF
);
39 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_RLE", HTTP_DEFLATE_STRATEGY_RLE
);
40 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FIXED", HTTP_DEFLATE_STRATEGY_FIXED
);
44 PHP_RINIT_FUNCTION(http_encoding
)
46 if (HTTP_G(send
).inflate
.start_auto
) {
47 php_ob_set_internal_handler(_http_ob_inflatehandler
, 0x1000, "http inflate", 0 TSRMLS_CC
);
49 if (HTTP_G(send
).deflate
.start_auto
) {
50 php_ob_set_internal_handler(_http_ob_deflatehandler
, 0x8000, "http deflate", 0 TSRMLS_CC
);
55 PHP_RSHUTDOWN_FUNCTION(http_encoding
)
57 if (G
->send
.deflate
.stream
) {
58 http_encoding_deflate_stream_free((http_encoding_stream
**) &G
->send
.deflate
.stream
);
60 if (G
->send
.inflate
.stream
) {
61 http_encoding_inflate_stream_free((http_encoding_stream
**) &G
->send
.inflate
.stream
);
67 static inline int eol_match(char **line
, int *eol_len
)
71 while (0x20 == *ptr
) ++ptr
;
73 if (ptr
== http_locate_eol(*line
, eol_len
)) {
81 /* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
82 PHP_HTTP_API
const char *_http_encoding_dechunk(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
86 const char *e_ptr
= encoded
;
89 *decoded
= ecalloc(1, encoded_len
);
91 while ((encoded
+ encoded_len
- e_ptr
) > 0) {
92 ulong chunk_len
= 0, rest
;
94 chunk_len
= strtoul(e_ptr
, &n_ptr
, 16);
96 /* we could not read in chunk size */
99 * if this is the first turn and there doesn't seem to be a chunk
100 * size at the begining of the body, do not fail on apparently
101 * not encoded data and return a copy
103 if (e_ptr
== encoded
) {
104 http_error(HE_NOTICE
, HTTP_E_ENCODING
, "Data does not seem to be chunked encoded");
105 memcpy(*decoded
, encoded
, encoded_len
);
106 *decoded_len
= encoded_len
;
107 return encoded
+ encoded_len
;
110 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Expected chunk size at pos %tu of %zu but got trash", n_ptr
- encoded
, encoded_len
);
115 /* reached the end */
117 /* move over '0' chunked encoding terminator */
118 while (*e_ptr
== '0') ++e_ptr
;
122 /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
123 if (*n_ptr
&& !eol_match(&n_ptr
, &eol_len
)) {
125 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));
127 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
);
132 /* chunk size pretends more data than we actually got, so it's probably a truncated message */
133 if (chunk_len
> (rest
= encoded
+ encoded_len
- n_ptr
)) {
134 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
);
139 memcpy(*decoded
+ *decoded_len
, n_ptr
, chunk_len
);
140 *decoded_len
+= chunk_len
;
142 if (chunk_len
== rest
) {
143 e_ptr
= n_ptr
+ chunk_len
;
146 /* advance to next chunk */
147 e_ptr
= n_ptr
+ chunk_len
+ eol_len
;
155 #ifdef HTTP_HAVE_ZLIB
157 #define HTTP_DEFLATE_LEVEL_SET(flags, level) \
158 switch (flags & 0xf) \
161 if ((flags & 0xf) < 10) { \
162 level = flags & 0xf; \
165 case HTTP_DEFLATE_LEVEL_DEF: \
166 level = Z_DEFAULT_COMPRESSION; \
170 #define HTTP_DEFLATE_WBITS_SET(flags, wbits) \
171 switch (flags & 0xf0) \
173 case HTTP_DEFLATE_TYPE_GZIP: \
174 wbits = HTTP_WINDOW_BITS_GZIP; \
176 case HTTP_DEFLATE_TYPE_RAW: \
177 wbits = HTTP_WINDOW_BITS_RAW; \
180 wbits = HTTP_WINDOW_BITS_ZLIB; \
184 #define HTTP_INFLATE_WBITS_SET(flags, wbits) \
185 if (flags & HTTP_INFLATE_TYPE_RAW) { \
186 wbits = HTTP_WINDOW_BITS_RAW; \
188 wbits = HTTP_WINDOW_BITS_ANY; \
191 #define HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \
192 switch (flags & 0xf00) \
194 case HTTP_DEFLATE_STRATEGY_FILT: \
195 strategy = Z_FILTERED; \
197 case HTTP_DEFLATE_STRATEGY_HUFF: \
198 strategy = Z_HUFFMAN_ONLY; \
200 case HTTP_DEFLATE_STRATEGY_RLE: \
203 case HTTP_DEFLATE_STRATEGY_FIXED: \
204 strategy = Z_FIXED; \
207 strategy = Z_DEFAULT_STRATEGY; \
211 #define HTTP_WINDOW_BITS_ZLIB 0x0000000f
212 #define HTTP_WINDOW_BITS_GZIP 0x0000001f
213 #define HTTP_WINDOW_BITS_ANY 0x0000002f
214 #define HTTP_WINDOW_BITS_RAW -0x000000f
216 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
)
218 int status
, level
, wbits
, strategy
;
221 HTTP_DEFLATE_LEVEL_SET(flags
, level
);
222 HTTP_DEFLATE_WBITS_SET(flags
, wbits
);
223 HTTP_DEFLATE_STRATEGY_SET(flags
, strategy
);
225 memset(&Z
, 0, sizeof(z_stream
));
229 status
= deflateInit2(&Z
, level
, Z_DEFLATED
, wbits
, MAX_MEM_LEVEL
, strategy
);
230 if (Z_OK
== status
) {
231 *encoded_len
= HTTP_ENCODING_BUFLEN(data_len
);
232 *encoded
= emalloc_rel(*encoded_len
);
234 Z
.next_in
= (Bytef
*) data
;
235 Z
.next_out
= (Bytef
*) *encoded
;
236 Z
.avail_in
= data_len
;
237 Z
.avail_out
= *encoded_len
;
239 status
= deflate(&Z
, Z_FINISH
);
242 if (Z_STREAM_END
== status
) {
243 /* size buffer down to actual length */
244 *encoded
= erealloc_rel(*encoded
, Z
.total_out
+ 1);
245 (*encoded
)[*encoded_len
= Z
.total_out
] = '\0';
248 STR_SET(*encoded
, NULL
);
253 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not deflate data: %s (%s)", zError(status
));
257 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
)
259 int status
, max
= 0, wbits
= HTTP_WINDOW_BITS_ANY
;
263 memset(&Z
, 0, sizeof(z_stream
));
267 phpstr_init_ex(&buffer
, data_len
<< 2, PHPSTR_INIT_PREALLOC
);
268 buffer
.size
= data_len
;
271 status
= inflateInit2(&Z
, wbits
);
272 if (Z_OK
== status
) {
273 Z
.next_in
= (Bytef
*) data
;
274 Z
.avail_in
= data_len
;
277 phpstr_resize(&buffer
, data_len
<< 2);
280 Z
.avail_out
= (buffer
.free
-= Z
.total_out
- buffer
.used
);
281 Z
.next_out
= (Bytef
*) buffer
.data
+ (buffer
.used
= Z
.total_out
);
282 status
= inflate(&Z
, Z_NO_FLUSH
);
283 } while (Z_OK
== status
);
284 } while (Z_BUF_ERROR
== status
&& ++max
< HTTP_ENCODING_MAXTRY
);
286 if (Z_DATA_ERROR
== status
&& HTTP_WINDOW_BITS_ANY
== wbits
) {
287 /* raw deflated data? */
289 wbits
= HTTP_WINDOW_BITS_RAW
;
295 if (Z_STREAM_END
== status
) {
296 *decoded_len
= Z
.total_out
;
297 *decoded
= erealloc_rel(buffer
.data
, *decoded_len
+ 1);
298 (*decoded
)[*decoded_len
] = '\0';
301 phpstr_dtor(&buffer
);
305 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not inflate data: %s", zError(status
));
310 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
)
312 int status
, level
, wbits
, strategy
, free_stream
;
314 if ((free_stream
= !s
)) {
315 s
= pemalloc_rel(sizeof(http_encoding_stream
), (flags
& HTTP_ENCODING_STREAM_PERSISTENT
));
317 memset(s
, 0, sizeof(http_encoding_stream
));
320 HTTP_DEFLATE_LEVEL_SET(flags
, level
);
321 HTTP_DEFLATE_WBITS_SET(flags
, wbits
);
322 HTTP_DEFLATE_STRATEGY_SET(flags
, strategy
);
324 if (Z_OK
== (status
= deflateInit2(&s
->stream
, level
, Z_DEFLATED
, wbits
, MAX_MEM_LEVEL
, strategy
))) {
325 int p
= (flags
& HTTP_ENCODING_STREAM_PERSISTENT
) ? PHPSTR_INIT_PERSISTENT
:0;
327 if ((s
->stream
.opaque
= phpstr_init_ex(NULL
, 0x8000, p
))) {
330 deflateEnd(&s
->stream
);
331 status
= Z_MEM_ERROR
;
334 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to initialize deflate encoding stream: %s", zError(status
));
341 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
)
343 int status
, wbits
, free_stream
;
345 if ((free_stream
= !s
)) {
346 s
= pemalloc_rel(sizeof(http_encoding_stream
), (flags
& HTTP_ENCODING_STREAM_PERSISTENT
));
348 memset(s
, 0, sizeof(http_encoding_stream
));
351 HTTP_INFLATE_WBITS_SET(flags
, wbits
);
353 if (Z_OK
== (status
= inflateInit2(&s
->stream
, wbits
))) {
354 int p
= (flags
& HTTP_ENCODING_STREAM_PERSISTENT
) ? PHPSTR_INIT_PERSISTENT
:0;
356 if ((s
->stream
.opaque
= phpstr_init_ex(NULL
, 0x8000, p
))) {
359 inflateEnd(&s
->stream
);
360 status
= Z_MEM_ERROR
;
363 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to initialize inflate stream: %s", zError(status
));
370 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
)
374 /* append input to our buffer */
375 phpstr_append(PHPSTR(s
->stream
.opaque
), data
, data_len
);
377 s
->stream
.next_in
= (Bytef
*) PHPSTR_VAL(s
->stream
.opaque
);
378 s
->stream
.avail_in
= PHPSTR_LEN(s
->stream
.opaque
);
381 *encoded_len
= HTTP_ENCODING_BUFLEN(data_len
);
382 *encoded
= emalloc_rel(*encoded_len
);
383 s
->stream
.avail_out
= *encoded_len
;
384 s
->stream
.next_out
= (Bytef
*) *encoded
;
386 switch (status
= deflate(&s
->stream
, Z_NO_FLUSH
))
390 /* cut processed chunk off the buffer */
391 phpstr_cut(PHPSTR(s
->stream
.opaque
), 0, data_len
- s
->stream
.avail_in
);
393 /* size buffer down to actual size */
394 *encoded_len
-= s
->stream
.avail_out
;
395 *encoded
= erealloc_rel(*encoded
, *encoded_len
+ 1);
396 (*encoded
)[*encoded_len
] = '\0';
401 STR_SET(*encoded
, NULL
);
403 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to update deflate stream: %s", zError(status
));
407 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
)
411 /* append input to buffer */
412 phpstr_append(PHPSTR(s
->stream
.opaque
), data
, data_len
);
416 *decoded_len
= data_len
<< 1;
421 *decoded
= erealloc_rel(*decoded
, *decoded_len
);
424 s
->stream
.next_in
= (Bytef
*) PHPSTR_VAL(s
->stream
.opaque
);
425 s
->stream
.avail_in
= PHPSTR_LEN(s
->stream
.opaque
);
427 s
->stream
.next_out
= (Bytef
*) *decoded
;
428 s
->stream
.avail_out
= *decoded_len
;
430 switch (status
= inflate(&s
->stream
, Z_NO_FLUSH
))
435 phpstr_cut(PHPSTR(s
->stream
.opaque
), 0, data_len
- s
->stream
.avail_in
);
438 *decoded_len
-= s
->stream
.avail_out
;
439 *decoded
= erealloc_rel(*decoded
, *decoded_len
+ 1);
440 (*decoded
)[*decoded_len
] = '\0';
445 /* raw deflated data ? */
446 if (!(s
->flags
& HTTP_INFLATE_TYPE_RAW
) && !s
->stream
.total_out
) {
447 inflateEnd(&s
->stream
);
448 s
->flags
|= HTTP_INFLATE_TYPE_RAW
;
449 inflateInit2(&s
->stream
, HTTP_WINDOW_BITS_RAW
);
450 goto retry_raw_inflate
;
454 } while (Z_BUF_ERROR
== status
&& ++max
< HTTP_ENCODING_MAXTRY
);
456 STR_SET(*decoded
, NULL
);
458 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to update inflate stream: %s", zError(status
));
462 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
)
466 *encoded_len
= 0x800;
467 *encoded
= emalloc_rel(*encoded_len
);
469 s
->stream
.avail_in
= 0;
470 s
->stream
.next_in
= NULL
;
471 s
->stream
.avail_out
= *encoded_len
;
472 s
->stream
.next_out
= (Bytef
*) *encoded
;
474 switch (status
= deflate(&s
->stream
, Z_SYNC_FLUSH
))
478 *encoded_len
= 0x800 - s
->stream
.avail_out
;
479 *encoded
= erealloc_rel(*encoded
, *encoded_len
+ 1);
480 (*encoded
)[*encoded_len
] = '\0';
485 STR_SET(*encoded
, NULL
);
487 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to flush deflate stream: %s", zError(status
));
491 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
)
495 *decoded_len
= 0x800;
496 *decoded
= emalloc_rel(*decoded_len
);
498 s
->stream
.avail_in
= 0;
499 s
->stream
.next_in
= NULL
;
500 s
->stream
.avail_out
= *decoded_len
;
501 s
->stream
.next_out
= (Bytef
*) *decoded
;
503 switch (status
= inflate(&s
->stream
, Z_SYNC_FLUSH
))
507 *decoded_len
= 0x800 - s
->stream
.avail_out
;
508 *decoded
= erealloc_rel(*decoded
, *decoded_len
+ 1);
509 (*decoded
)[*decoded_len
] = '\0';
514 STR_SET(*decoded
, NULL
);
516 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to flush inflate stream: %s", zError(status
));
520 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
)
524 *encoded_len
= 0x800;
525 *encoded
= emalloc_rel(*encoded_len
);
527 /* deflate remaining input */
528 s
->stream
.next_in
= (Bytef
*) PHPSTR_VAL(s
->stream
.opaque
);
529 s
->stream
.avail_in
= PHPSTR_LEN(s
->stream
.opaque
);
531 s
->stream
.avail_out
= *encoded_len
;
532 s
->stream
.next_out
= (Bytef
*) *encoded
;
535 status
= deflate(&s
->stream
, Z_FINISH
);
536 } while (Z_OK
== status
);
538 if (Z_STREAM_END
== status
) {
539 /* cut processed intp off */
540 phpstr_cut(PHPSTR(s
->stream
.opaque
), 0, PHPSTR_LEN(s
->stream
.opaque
) - s
->stream
.avail_in
);
543 *encoded_len
-= s
->stream
.avail_out
;
544 *encoded
= erealloc_rel(*encoded
, *encoded_len
+ 1);
545 (*encoded
)[*encoded_len
] = '\0';
549 STR_SET(*encoded
, NULL
);
551 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to finish deflate stream: %s", zError(status
));
555 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
)
559 *decoded_len
= s
->stream
.avail_in
<< 2;
560 *decoded
= emalloc_rel(*decoded_len
);
562 /* inflate 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
= *decoded_len
;
567 s
->stream
.next_out
= (Bytef
*) *decoded
;
569 if (Z_STREAM_END
== (status
= inflate(&s
->stream
, Z_FINISH
))) {
570 /* cut processed input off */
571 phpstr_cut(PHPSTR(s
->stream
.opaque
), 0, PHPSTR_LEN(s
->stream
.opaque
) - s
->stream
.avail_in
);
574 *decoded_len
-= s
->stream
.avail_out
;
575 *decoded
= erealloc_rel(*decoded
, *decoded_len
+ 1);
576 (*decoded
)[*decoded_len
] = '\0';
580 STR_SET(*decoded
, NULL
);
582 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Failed to finish inflate stream: %s", zError(status
));
586 PHP_HTTP_API
void _http_encoding_deflate_stream_dtor(http_encoding_stream
*s TSRMLS_DC
)
589 if (s
->stream
.opaque
) {
590 phpstr_free((phpstr
**) &s
->stream
.opaque
);
592 deflateEnd(&s
->stream
);
596 PHP_HTTP_API
void _http_encoding_inflate_stream_dtor(http_encoding_stream
*s TSRMLS_DC
)
599 if (s
->stream
.opaque
) {
600 phpstr_free((phpstr
**) &s
->stream
.opaque
);
602 inflateEnd(&s
->stream
);
606 PHP_HTTP_API
void _http_encoding_deflate_stream_free(http_encoding_stream
**s TSRMLS_DC
)
609 http_encoding_deflate_stream_dtor(*s
);
611 pefree(*s
, (*s
)->flags
& HTTP_ENCODING_STREAM_PERSISTENT
);
617 PHP_HTTP_API
void _http_encoding_inflate_stream_free(http_encoding_stream
**s TSRMLS_DC
)
620 http_encoding_inflate_stream_dtor(*s
);
622 pefree(*s
, (*s
)->flags
& HTTP_ENCODING_STREAM_PERSISTENT
);
628 void _http_ob_deflatehandler(char *output
, uint output_len
, char **handled_output
, uint
*handled_output_len
, int mode TSRMLS_DC
)
632 *handled_output
= NULL
;
633 *handled_output_len
= 0;
635 if (mode
& PHP_OUTPUT_HANDLER_START
) {
638 if (G
->send
.deflate
.stream
) {
639 zend_error(E_ERROR
, "ob_deflatehandler() can only be used once");
643 G
->send
.deflate
.encoding
= !0;
645 switch (http_encoding_response_start(0))
647 case HTTP_ENCODING_GZIP
:
648 flags
= HTTP_DEFLATE_TYPE_GZIP
;
651 case HTTP_ENCODING_DEFLATE
:
652 flags
= HTTP_DEFLATE_TYPE_ZLIB
;
656 goto deflate_passthru_plain
;
660 flags
|= (G
->send
.deflate
.start_flags
&~ 0xf);
661 G
->send
.deflate
.stream
= http_encoding_deflate_stream_init(NULL
, flags
);
664 if (G
->send
.deflate
.stream
) {
665 http_encoding_deflate_stream_update((http_encoding_stream
*) G
->send
.deflate
.stream
, output
, output_len
, handled_output
, handled_output_len
);
667 if (mode
& PHP_OUTPUT_HANDLER_END
) {
668 char *remaining
= NULL
;
669 size_t remaining_len
= 0;
671 http_encoding_deflate_stream_finish((http_encoding_stream
*) G
->send
.deflate
.stream
, &remaining
, &remaining_len
);
672 http_encoding_deflate_stream_free((http_encoding_stream
**) &G
->send
.deflate
.stream
);
674 *handled_output
= erealloc(*handled_output
, *handled_output_len
+ remaining_len
+ 1);
675 memcpy(*handled_output
+ *handled_output_len
, remaining
, remaining_len
);
676 (*handled_output
)[*handled_output_len
+= remaining_len
] = '\0';
681 deflate_passthru_plain
:
682 *handled_output
= estrndup(output
, *handled_output_len
= output_len
);
686 void _http_ob_inflatehandler(char *output
, uint output_len
, char **handled_output
, uint
*handled_output_len
, int mode TSRMLS_DC
)
690 *handled_output
= NULL
;
691 *handled_output_len
= 0;
693 if (mode
& PHP_OUTPUT_HANDLER_START
) {
694 if (G
->send
.inflate
.stream
) {
695 zend_error(E_ERROR
, "ob_inflatehandler() can only be used once");
698 G
->send
.inflate
.stream
= http_encoding_inflate_stream_init(NULL
, (HTTP_G(send
).inflate
.start_flags
&~ 0xf));
701 if (G
->send
.inflate
.stream
) {
702 http_encoding_inflate_stream_update((http_encoding_stream
*) G
->send
.inflate
.stream
, output
, output_len
, handled_output
, handled_output_len
);
704 if (mode
& PHP_OUTPUT_HANDLER_END
) {
705 char *remaining
= NULL
;
706 size_t remaining_len
= 0;
708 http_encoding_inflate_stream_finish((http_encoding_stream
*) G
->send
.inflate
.stream
, &remaining
, &remaining_len
);
709 http_encoding_inflate_stream_free((http_encoding_stream
**) &G
->send
.inflate
.stream
);
711 *handled_output
= erealloc(*handled_output
, *handled_output_len
+ remaining_len
+ 1);
712 memcpy(*handled_output
+ *handled_output_len
, remaining
, remaining_len
);
713 (*handled_output
)[*handled_output_len
+= remaining_len
] = '\0';
718 *handled_output
= estrndup(output
, *handled_output_len
= output_len
);
722 static const char http_encoding_gzip_header
[] = {
723 (const char) 0x1f, // fixed value
724 (const char) 0x8b, // fixed value
725 (const char) Z_DEFLATED
, // compression algorithm
726 (const char) 0, // none of the possible flags defined by the GZIP "RFC"
727 (const char) 0, // MTIME
728 (const char) 0, // =*=
729 (const char) 0, // =*=
730 (const char) 0, // =*=
731 (const char) 0, // two possible flag values for 9 compression levels? o_O
733 (const char) 0x0b // OS_CODE
735 (const char) 0x03 // OS_CODE
739 PHP_HTTP_API STATUS
_http_encoding_gzencode_verify(const char *data
, size_t data_len
, const char **encoded
, size_t *encoded_len
, int error_level TSRMLS_DC
)
741 size_t offset
= sizeof(http_encoding_gzip_header
);
743 if (data_len
< offset
) {
744 goto really_bad_gzip_header
;
747 if (data
[0] != (const char) 0x1F || data
[1] != (const char) 0x8B) {
748 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Unrecognized GZIP header start: 0x%02X 0x%02X", (int) data
[0], (int) (data
[1] & 0xFF));
752 if (data
[2] != (const char) Z_DEFLATED
) {
753 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Unrecognized compression format (%d)", (int) (data
[2] & 0xFF));
754 /* still try to decode */
756 if ((data
[3] & 0x4) == 0x4) {
757 if (data_len
< offset
+ 2) {
758 goto really_bad_gzip_header
;
760 /* there are extra fields, the length follows the common header as 2 bytes LSB */
761 offset
+= (unsigned) ((data
[offset
] & 0xFF));
763 offset
+= (unsigned) ((data
[offset
] & 0xFF) << 8);
766 if ((data
[3] & 0x8) == 0x8) {
767 if (data_len
<= offset
) {
768 goto really_bad_gzip_header
;
770 /* there's a file name */
771 offset
+= strlen(&data
[offset
]) + 1 /*NUL*/;
773 if ((data
[3] & 0x10) == 0x10) {
774 if (data_len
<= offset
) {
775 goto really_bad_gzip_header
;
777 /* there's a comment */
778 offset
+= strlen(&data
[offset
]) + 1 /* NUL */;
780 if ((data
[3] & 0x2) == 0x2) {
781 /* there's a CRC16 of the header */
783 if (data_len
<= offset
) {
784 goto really_bad_gzip_header
;
788 cmp
= (unsigned) ((data
[offset
-2] & 0xFF));
789 cmp
+= (unsigned) ((data
[offset
-1] & 0xFF) << 8);
791 crc
= crc32(0L, Z_NULL
, 0);
792 crc
= crc32(crc
, (const Bytef
*) data
, sizeof(http_encoding_gzip_header
));
794 if (cmp
!= (crc
& 0xFFFF)) {
795 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "GZIP headers CRC checksums so not match (%lu, %lu)", cmp
, crc
& 0xFFFF);
801 if (data_len
< offset
+ 8) {
802 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP footer");
807 *encoded
= data
+ offset
;
810 *encoded_len
= data_len
- offset
- 8 /* size of the assumed GZIP footer */;
815 really_bad_gzip_header
:
816 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP header");
820 PHP_HTTP_API STATUS
_http_encoding_gzdecode_verify(const char *data
, size_t data_len
, const char *decoded
, size_t decoded_len
, int error_level TSRMLS_DC
)
822 STATUS status
= SUCCESS
;
825 crc
= crc32(0L, Z_NULL
, 0);
826 crc
= crc32(crc
, (const Bytef
*) decoded
, decoded_len
);
828 cmp
= (unsigned) ((data
[data_len
-8] & 0xFF));
829 cmp
+= (unsigned) ((data
[data_len
-7] & 0xFF) << 8);
830 cmp
+= (unsigned) ((data
[data_len
-6] & 0xFF) << 16);
831 cmp
+= (unsigned) ((data
[data_len
-5] & 0xFF) << 24);
832 len
= (unsigned) ((data
[data_len
-4] & 0xFF));
833 len
+= (unsigned) ((data
[data_len
-3] & 0xFF) << 8);
834 len
+= (unsigned) ((data
[data_len
-2] & 0xFF) << 16);
835 len
+= (unsigned) ((data
[data_len
-1] & 0xFF) << 24);
838 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Could not verify data integrity: CRC checksums do not match (%lu, %lu)", cmp
, crc
);
841 if (len
!= decoded_len
) {
842 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Could not verify data integrity: data sizes do not match (%lu, %lu)", len
, decoded_len
);
848 #endif /* HTTP_HAVE_ZLIB */
850 PHP_HTTP_API
int _http_encoding_response_start(size_t content_length TSRMLS_DC
)
852 if ( php_ob_handler_used("ob_gzhandler" TSRMLS_CC
) ||
853 php_ob_handler_used("zlib output compression" TSRMLS_CC
)) {
854 HTTP_G(send
).deflate
.encoding
= 0;
856 if (!HTTP_G(send
).deflate
.encoding
) {
857 /* emit a content-length header */
858 if (content_length
) {
859 char cl_header_str
[128];
860 size_t cl_header_len
;
861 cl_header_len
= snprintf(cl_header_str
, lenof(cl_header_str
), "Content-Length: %zu", content_length
);
862 http_send_header_string_ex(cl_header_str
, cl_header_len
, 1);
865 #ifndef HTTP_HAVE_ZLIB
866 HTTP_G(send
).deflate
.encoding
= 0;
867 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC
);
872 INIT_PZVAL(&zsupported
);
873 array_init(&zsupported
);
874 add_next_index_stringl(&zsupported
, "gzip", lenof("gzip"), 1);
875 add_next_index_stringl(&zsupported
, "x-gzip", lenof("x-gzip"), 1);
876 add_next_index_stringl(&zsupported
, "deflate", lenof("deflate"), 1);
878 HTTP_G(send
).deflate
.encoding
= 0;
880 if ((selected
= http_negotiate_encoding(&zsupported
))) {
882 char *encoding
= NULL
;
885 if (HASH_KEY_IS_STRING
== zend_hash_get_current_key(selected
, &encoding
, &idx
, 0) && encoding
) {
886 if (!strcmp(encoding
, "gzip") || !strcmp(encoding
, "x-gzip")) {
887 if (SUCCESS
== (hs
= http_send_header_string("Content-Encoding: gzip"))) {
888 HTTP_G(send
).deflate
.encoding
= HTTP_ENCODING_GZIP
;
890 } else if (!strcmp(encoding
, "deflate")) {
891 if (SUCCESS
== (hs
= http_send_header_string("Content-Encoding: deflate"))) {
892 HTTP_G(send
).deflate
.encoding
= HTTP_ENCODING_DEFLATE
;
896 http_send_header_string("Vary: Accept-Encoding");
900 zend_hash_destroy(selected
);
901 FREE_HASHTABLE(selected
);
904 zval_dtor(&zsupported
);
905 return HTTP_G(send
).deflate
.encoding
;
917 * vim600: noet sw=4 ts=4 fdm=marker
918 * vim<600: noet sw=4 ts=4