transparent output compression patch

From: Date: Sun, 12 Nov 2000 21:54:56 +0000
Subject: transparent output compression patch
Groups: php.dev 
Request: Send a blank email to php-dev+get-37849@lists.php.net to get a copy of this message
Hi Here's a patch that adds transparent output compression in the output layer. The patch is just a start. I've mainly posted it to get some feedback... So far, it isn't thread safe and only the 'gzip' coding really works. Some questions that I have: - Is output.c the right place to put this stuff in? - There shouln't be a ob_compress_finish(). The gzip/deflate trailer should be sent automatically. Where should I place the call to that function? - The patch won't work if you dynamically load the zlib extension. How can I fix that? HAVE_ZLIB isn't set... should I remove the '#if's and check at runtime if zlib is available? I think this won't work if the compression is done in output.c... Usage: ------ example.php <? ob_compress(1); echo 'this is just test'; ob_compress_finish(); ------ Bugs/TODOs: 1. Grep for them (TODO|FIXME). 2. The deflate coding dosn't work. I don't see why, right now... hint? -Jade. PS: Thank you for your feedback. -- =============================================================================== Jade Nicoletti Nicoletti Net Services Tel. 01 240 4774 Geschäftsleitung Postfach 2519 Fax 01 240 4775 System-Administration 8021 Zürich ============================================[ Weitere Infos: http://nns.ch/ ]==

--- basic_functions.c~ Wed Oct 11 13:25:13 2000 +++ basic_functions.c Sun Nov 12 22:09:27 2000 @@ -499,6 +499,8 @@ PHP_FE(ob_get_length, NULL) PHP_FE(ob_get_contents, NULL) PHP_FE(ob_implicit_flush, NULL) + PHP_FE(ob_compress, NULL) + PHP_FE(ob_compress_finish, NULL) /* functions from array.c */ PHP_FE(ksort, first_arg_force_ref) --- php_output.h~ Sun Nov 12 22:13:09 2000 +++ php_output.h Sun Nov 12 15:56:34 2000 @@ -22,6 +22,9 @@ #define PHP_OUTPUT_H #include "php.h" +#if HAVE_ZLIB +#include "zlib.h" +#endif PHPAPI void php_output_startup(void); PHPAPI int php_body_write(const char *str, uint str_length); @@ -42,6 +45,10 @@ PHP_FUNCTION(ob_get_contents); PHP_FUNCTION(ob_get_length); PHP_FUNCTION(ob_implicit_flush); +#if HAVE_ZLIB +PHP_FUNCTION(ob_compress); +PHP_FUNCTION(ob_compress_finish); +#endif PHP_GINIT_FUNCTION(output); @@ -63,6 +70,11 @@ zend_stack ob_buffers; int nesting_level; zend_bool lock; +#if HAVE_ZLIB + int compression_coding; + z_stream stream; + uLong crc; +#endif } php_output_globals; --- output.c~ Sun Nov 12 22:12:56 2000 +++ output.c Sun Nov 12 21:56:56 2000 @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ | Authors: Zeev Suraski <zeev@zend.com> | | Thies C. Arntzen <thies@digicol.de> | + | Jade Nicoletti <nicoletti@nns.ch> | +----------------------------------------------------------------------+ */ @@ -25,6 +26,14 @@ #include "basic_functions.h" #include "SAPI.h" +#if HAVE_ZLIB +#include "zlib.h" +#define OS_CODE 0x03 /* FIXME */ +static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ +static void php_compress_output(const char *, uint, char **, uint *); +static int php_deflate(uint str_length, Bytef **p_buffer, uint *p_buf_used, int flush); +#endif + /* output functions */ static int php_ub_body_write(const char *str, uint str_length); static int php_ub_body_write_no_header(const char *str, uint str_length); @@ -74,6 +83,7 @@ OG(php_header_write) = sapi_module.ub_write; OG(nesting_level) = 0; OG(lock) = 0; + OG(compression_coding) = 0; } PHPAPI int php_body_write(const char *str, uint str_length) @@ -338,12 +348,30 @@ if (BG(use_trans_sid)) { session_adapt_uris(str, str_length, &newstr, &new_length); } - + if (newstr) { str = newstr; str_length = new_length; } +#if HAVE_ZLIB + if (OG(compression_coding)) { + if (newstr) { + free(newstr); + } + + php_compress_output(str, str_length, &newstr, &new_length); + + if (!new_length) { + free(newstr); + return; + } + + str = newstr; + str_length = new_length; + } +#endif + result = OG(php_header_write)(str, str_length); if (newstr) { @@ -496,6 +524,166 @@ } } /* }}} */ + +#if HAVE_ZLIB +/* {{{ proto void ob_compress([int coding]) + Activate the compression of output */ +/* coding: 0=autodetect; 1=force gzip; 2=force deflate */ +PHP_FUNCTION(ob_compress) +{ + zval **zv_coding; + int coding, result; + Bytef buffer[10]; /* FIXME: this is not thread safe */ + OLS_FETCH(); + + if (SG(headers_sent)) { + RETURN_FALSE; + } + + switch(ZEND_NUM_ARGS()) { + case 0: + coding = 0; + break; + case 1: + if (zend_get_parameters_ex(1, &zv_coding)==FAILURE) { + RETURN_FALSE; + } + convert_to_long_ex(zv_coding); + coding = (*zv_coding)->value.lval; + break; + default: + WRONG_PARAM_COUNT; + break; + } + + if (!coding) { + /* TODO: check HTTP_ACCEPT_ENCODING */ + RETURN_FALSE; + } + + OG(compression_coding) = coding; + + OG(stream).zalloc = Z_NULL; + OG(stream).zfree = Z_NULL; + OG(stream).opaque = Z_NULL; + + + if (coding == 1) { + /* windowBits is passed < 0 to suppress zlib header & trailer */ + if (deflateInit2(&OG(stream), Z_DEFAULT_COMPRESSION, Z_DEFLATED, + -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) + != Z_OK) { + /* TODO: print out error */ + RETURN_FALSE; + } + + sapi_add_header("Content-Encoding: gzip", + sizeof("Content-Encoding: gzip") - 1, 1); + sapi_send_headers(); + + /* Write a very simple .gz header: */ + sprintf(buffer, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], + gz_magic[1], Z_DEFLATED, 0 /*flags*/, + 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + result = OG(php_header_write)(buffer, 10); + OG(crc) = crc32(0L, Z_NULL, 0); + } else if (coding == 2) { + if (deflateInit(&OG(stream), Z_DEFAULT_COMPRESSION) != Z_OK) { + /* TODO: print out error */ + RETURN_FALSE; + } + + sapi_add_header("Content-Encoding: deflate", + sizeof("Content-Encoding: deflate") - 1, 1); + } +} +/* }}} */ + +/* {{{ proto void ob_compress_finish(void) + Finish the compression of output */ +PHP_FUNCTION(ob_compress_finish) +{ + Bytef *buffer; + Bytef trailer[8]; /* FIXME: is not thread safe */ + uInt buf_used; + uLong crc, total; + int n, result; + + if (!OG(compression_coding)) { + RETURN_FALSE; /* TODO: print error */ + } + + if (php_deflate(512, &buffer, &buf_used, Z_FINISH) != Z_STREAM_END) { + RETURN_FALSE; /* TODO: print error */ + } + result = OG(php_header_write)(buffer, buf_used); + + if (OG(compression_coding) == 1) { + /* write crc & stream.total_in in LSB order */ + crc = OG(crc); + total = OG(stream).total_in; + sprintf(trailer, "%c%c%c%c%c%c%c%c", crc & 0xFF, + (crc >> 8) & 0xFF, (crc >> 16) & 0xFF, + (crc >> 24) & 0xFF, total & 0xFF, (total >> 8) & 0xFF, + (total >> 16) & 0xFF, (total >> 24) & 0xFF); + + result = OG(php_header_write)(trailer, 8); + } + + free(buffer); + sapi_flush(); +} +/* }}} */ + +static void php_compress_output(const char *str, uint str_length, char **newstr, uint *new_length) +{ + Bytef *buffer; + uInt buf_used; + int err; + + OG(stream).next_in = (Bytef*) str; + OG(stream).avail_in = (uInt) str_length; + + if (OG(compression_coding) == 1) { + OG(crc) = crc32(OG(crc), (const Bytef *)str, str_length); + } + + err = php_deflate(str_length, &buffer, &buf_used, Z_NO_FLUSH); + /* TODO: error handling (err may be Z_STREAM_ERROR, Z_BUF_ERROR, ?) */ + + *new_length = buf_used; + *newstr = buffer; +} + +static int php_deflate(uint str_length, Bytef **p_buffer, uint *p_buf_used, int flush) +{ + Bytef *buffer; + uInt prev_outlen, outlen; + int err; + + outlen = sizeof(char) * (str_length * 1.001 + 12); + buffer = (Bytef *) malloc(outlen); + + OG(stream).next_out = buffer; + OG(stream).avail_out = outlen; + + err = deflate(&OG(stream), flush); + while (err == Z_OK && !OG(stream).avail_out) { + prev_outlen = outlen; + outlen *= 3; + buffer = realloc(buffer, outlen); + + OG(stream).next_out = buffer + prev_outlen; + OG(stream).avail_out = prev_outlen * 2; + + err = deflate(&OG(stream), flush); + } + + *p_buffer = buffer; + *p_buf_used = outlen - OG(stream).avail_out; + return err; +} +#endif PHPAPI char *php_get_output_start_filename()
« previous php.dev (#37849) next »