/* {{{ php_zlib_output_handler() */ static int php_zlib_output_handler(void **handler_context, php_output_context *output_context) { php_zlib_context *ctx = *(php_zlib_context **) handler_context; PHP_OUTPUT_TSRMLS(output_context); if (!php_zlib_output_encoding(TSRMLS_C)) { /* "Vary: Accept-Encoding" header sent along uncompressed content breaks caching in MSIE, so let's just send it with successfully compressed content or unless the complete buffer gets discarded, see http://bugs.php.net/40325; Test as follows: +Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";' +Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";' -Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();' -Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();' */ if ((output_context->op & PHP_OUTPUT_HANDLER_START) && (output_context->op != (PHP_OUTPUT_HANDLER_START|PHP_OUTPUT_HANDLER_CLEAN|PHP_OUTPUT_HANDLER_FINAL)) ) { sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC); } return FAILURE; } if (SUCCESS != php_zlib_output_handler_ex(ctx, output_context)) { return FAILURE; } if (!(output_context->op & PHP_OUTPUT_HANDLER_CLEAN)) { int flags; if (SUCCESS == php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS, &flags TSRMLS_CC)) { /* only run this once */ if (!(flags & PHP_OUTPUT_HANDLER_STARTED)) { if (SG(headers_sent) || !ZLIBG(output_compression)) { deflateEnd(&ctx->Z); return FAILURE; } switch (ZLIBG(compression_coding)) { case PHP_ZLIB_ENCODING_GZIP: sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1 TSRMLS_CC); break; case PHP_ZLIB_ENCODING_DEFLATE: sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1 TSRMLS_CC); break; default: deflateEnd(&ctx->Z); return FAILURE; } sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC); php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL TSRMLS_CC); } } } return SUCCESS; }
/* {{{ php_zlib_output_handler() */ static int php_zlib_output_handler(void **handler_context, php_output_context *output_context) { php_zlib_context *ctx = *(php_zlib_context **) handler_context; int flags = Z_SYNC_FLUSH; PHP_OUTPUT_TSRMLS(output_context); if (!php_zlib_output_encoding(TSRMLS_C)) { /* "Vary: Accept-Encoding" header sent along uncompressed content breaks caching in MSIE, so let's just send it with successfully compressed content or unless the complete buffer gets discarded, see http://bugs.php.net/40325; Test as follows: +Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";' +Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";' -Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();' -Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();' */ if (output_context->op != (PHP_OUTPUT_HANDLER_START|PHP_OUTPUT_HANDLER_CLEAN|PHP_OUTPUT_HANDLER_FINAL)) { sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 1 TSRMLS_CC); } return FAILURE; } if (output_context->op & PHP_OUTPUT_HANDLER_START) { /* start up */ if (Z_OK != deflateInit2(&ctx->Z, ZLIBG(output_compression_level), Z_DEFLATED, ZLIBG(compression_coding), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) { return FAILURE; } } if (output_context->op & PHP_OUTPUT_HANDLER_CLEAN) { /* free buffers */ deflateEnd(&ctx->Z); if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) { /* discard */ return SUCCESS; } else { /* restart */ if (Z_OK != deflateInit2(&ctx->Z, ZLIBG(output_compression_level), Z_DEFLATED, ZLIBG(compression_coding), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) { return FAILURE; } ctx->buffer.used = 0; } } else { if (output_context->in.used) { /* append input */ if (ctx->buffer.free < output_context->in.used) { if (!(ctx->buffer.aptr = erealloc_recoverable(ctx->buffer.data, ctx->buffer.used + ctx->buffer.free + output_context->in.used))) { deflateEnd(&ctx->Z); return FAILURE; } ctx->buffer.data = ctx->buffer.aptr; ctx->buffer.free += output_context->in.used; } memcpy(ctx->buffer.data + ctx->buffer.used, output_context->in.data, output_context->in.used); ctx->buffer.free -= output_context->in.used; ctx->buffer.used += output_context->in.used; } output_context->out.size = PHP_ZLIB_BUFFER_SIZE_GUESS(output_context->in.used); output_context->out.data = emalloc(output_context->out.size); output_context->out.free = 1; output_context->out.used = 0; ctx->Z.avail_in = ctx->buffer.used; ctx->Z.next_in = (Bytef *) ctx->buffer.data; ctx->Z.avail_out = output_context->out.size; ctx->Z.next_out = (Bytef *) output_context->out.data; if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) { flags = Z_FINISH; } else if (output_context->op & PHP_OUTPUT_HANDLER_FLUSH) { flags = Z_FULL_FLUSH; } switch (deflate(&ctx->Z, flags)) { case Z_OK: if (flags == Z_FINISH) { deflateEnd(&ctx->Z); return FAILURE; } case Z_STREAM_END: if (ctx->Z.avail_in) { memmove(ctx->buffer.data, ctx->buffer.data + ctx->buffer.used - ctx->Z.avail_in, ctx->Z.avail_in); } ctx->buffer.free += ctx->buffer.used - ctx->Z.avail_in; ctx->buffer.used = ctx->Z.avail_in; output_context->out.used = output_context->out.size - ctx->Z.avail_out; break; default: deflateEnd(&ctx->Z); return FAILURE; } php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS, &flags TSRMLS_CC); if (!(flags & PHP_OUTPUT_HANDLER_STARTED)) { if (SG(headers_sent) || !ZLIBG(output_compression)) { deflateEnd(&ctx->Z); return FAILURE; } switch (ZLIBG(compression_coding)) { case PHP_ZLIB_ENCODING_GZIP: sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1 TSRMLS_CC); break; case PHP_ZLIB_ENCODING_DEFLATE: sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1 TSRMLS_CC); break; default: deflateEnd(&ctx->Z); return FAILURE; } sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 1 TSRMLS_CC); php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL TSRMLS_CC); } if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) { deflateEnd(&ctx->Z); } } return SUCCESS; }