Re: transparent output compression patch
| From: | Jade Nicoletti | Date: | Mon, 13 Nov 2000 15:12:08 +0000 |
| Subject: | Re: transparent output compression patch | ||
| References: | 1 2 | Groups: | php.dev |
| Request: | Send a blank email to php-dev+get-37929@lists.php.net to get a copy of this message | ||
Here is an updated patch. Just in case... :)
The deflate coding and the autodetection works now. I've definied some constants
and I've put in some warning messages.
-Jade.
On Mon, Nov 13, 2000 at 12:32:57PM +0200, Zeev Suraski wrote:
> I'm working on converting your work to a output handler in zlib.c - I'll
> send you the diff when I'm done.
>
> Zeev
--
===============================================================================
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 Mon Nov 13 15:53:34 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) @@ -713,6 +715,10 @@ PHP_MINIT(syslog)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(array)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(assert)(INIT_FUNC_ARGS_PASSTHRU); + +#if HAVE_ZLIB + PHP_MINIT(output)(INIT_FUNC_ARGS_PASSTHRU); +#endif return SUCCESS; } --- php_output.h~ Sat Sep 2 20:03:58 2000 +++ php_output.h Mon Nov 13 15:47:57 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,8 +45,13 @@ 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); +PHP_MINIT_FUNCTION(output); typedef struct _php_ob_buffer { char *buffer; @@ -63,6 +71,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~ Tue Sep 5 00:21:10 2000 +++ output.c Mon Nov 13 15:49:53 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,17 @@ #include "basic_functions.h" #include "SAPI.h" +#if HAVE_ZLIB +#include "zlib.h" +#include "ext/standard/php_string.h" +#define OS_CODE 0x03 /* FIXME */ +#define CODING_GZIP 1 +#define CODING_DEFLATE 2 +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); @@ -65,6 +77,14 @@ return SUCCESS; } +PHP_MINIT_FUNCTION(output) +{ + REGISTER_LONG_CONSTANT("FORCE_GZIP", CODING_GZIP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("FORCE_DEFLATE", CODING_DEFLATE, CONST_CS | CONST_PERSISTENT); + + return SUCCESS; +} + /* Start output layer */ PHPAPI void php_output_startup() { @@ -74,6 +94,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 +359,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 +535,186 @@ } } /* }}} */ + +#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, **data, **a_encoding; + int coding, result; + Bytef buffer[10]; /* FIXME: this is not thread safe */ + OLS_FETCH(); + ELS_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) { + if (zend_hash_find(&EG(symbol_table), "HTTP_SERVER_VARS", + sizeof("HTTP_SERVER_VARS"), (void **) &data) == + SUCCESS && Z_TYPE_PP(data) == IS_ARRAY && + zend_hash_find(Z_ARRVAL_PP(data), + "HTTP_ACCEPT_ENCODING", + sizeof("HTTP_ACCEPT_ENCODING"), + (void **) &a_encoding) == SUCCESS) { + convert_to_string_ex(a_encoding); + if (php_memnstr(Z_STRVAL_PP(a_encoding), "gzip", 4, + Z_STRVAL_PP(a_encoding) + + Z_STRLEN_PP(a_encoding))) { + coding = CODING_GZIP; + } else if(php_memnstr(Z_STRVAL_PP(a_encoding), + "deflate", 7, Z_STRVAL_PP(a_encoding) + + Z_STRLEN_PP(a_encoding))) { + coding = CODING_DEFLATE; + } else { + RETURN_FALSE; + } + } else { + php_error(E_NOTICE, "No HTTP_ACCEPT_ENCODING found."); + RETURN_FALSE; + } + } + + OG(compression_coding) = coding; + + OG(stream).zalloc = Z_NULL; + OG(stream).zfree = Z_NULL; + OG(stream).opaque = Z_NULL; + + + /* 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) { + php_error(E_WARNING, "zlib initialisation failed"); + RETURN_FALSE; + } + + if (coding == CODING_GZIP) { + 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 == CODING_DEFLATE) { + 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)) { + /* php_error(E_NOTICE, "output compression isn't active"); */ + RETURN_FALSE; + } + + if (php_deflate(512, &buffer, &buf_used, Z_FINISH) != Z_STREAM_END) { + php_error(E_WARNING, "zlib error"); + RETURN_FALSE; + } + result = OG(php_header_write)(buffer, buf_used); + + if (OG(compression_coding) == CODING_GZIP) { + /* 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) == CODING_GZIP) { + 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()
--- basic_functions.c~ Wed Oct 11 13:25:13 2000 +++ basic_functions.c Mon Nov 13 15:53:34 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) @@ -713,6 +715,10 @@ PHP_MINIT(syslog)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(array)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(assert)(INIT_FUNC_ARGS_PASSTHRU); + +#if HAVE_ZLIB + PHP_MINIT(output)(INIT_FUNC_ARGS_PASSTHRU); +#endif return SUCCESS; } --- php_output.h~ Sat Sep 2 20:03:58 2000 +++ php_output.h Mon Nov 13 15:47:57 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,8 +45,13 @@ 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); +PHP_MINIT_FUNCTION(output); typedef struct _php_ob_buffer { char *buffer; @@ -63,6 +71,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~ Tue Sep 5 00:21:10 2000 +++ output.c Mon Nov 13 15:49:53 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,17 @@ #include "basic_functions.h" #include "SAPI.h" +#if HAVE_ZLIB +#include "zlib.h" +#include "ext/standard/php_string.h" +#define OS_CODE 0x03 /* FIXME */ +#define CODING_GZIP 1 +#define CODING_DEFLATE 2 +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); @@ -65,6 +77,14 @@ return SUCCESS; } +PHP_MINIT_FUNCTION(output) +{ + REGISTER_LONG_CONSTANT("FORCE_GZIP", CODING_GZIP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("FORCE_DEFLATE", CODING_DEFLATE, CONST_CS | CONST_PERSISTENT); + + return SUCCESS; +} + /* Start output layer */ PHPAPI void php_output_startup() { @@ -74,6 +94,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 +359,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 +535,186 @@ } } /* }}} */ + +#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, **data, **a_encoding; + int coding, result; + Bytef buffer[10]; /* FIXME: this is not thread safe */ + OLS_FETCH(); + ELS_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) { + if (zend_hash_find(&EG(symbol_table), "HTTP_SERVER_VARS", + sizeof("HTTP_SERVER_VARS"), (void **) &data) == + SUCCESS && Z_TYPE_PP(data) == IS_ARRAY && + zend_hash_find(Z_ARRVAL_PP(data), + "HTTP_ACCEPT_ENCODING", + sizeof("HTTP_ACCEPT_ENCODING"), + (void **) &a_encoding) == SUCCESS) { + convert_to_string_ex(a_encoding); + if (php_memnstr(Z_STRVAL_PP(a_encoding), "gzip", 4, + Z_STRVAL_PP(a_encoding) + + Z_STRLEN_PP(a_encoding))) { + coding = CODING_GZIP; + } else if(php_memnstr(Z_STRVAL_PP(a_encoding), + "deflate", 7, Z_STRVAL_PP(a_encoding) + + Z_STRLEN_PP(a_encoding))) { + coding = CODING_DEFLATE; + } else { + RETURN_FALSE; + } + } else { + php_error(E_NOTICE, "No HTTP_ACCEPT_ENCODING found."); + RETURN_FALSE; + } + } + + OG(compression_coding) = coding; + + OG(stream).zalloc = Z_NULL; + OG(stream).zfree = Z_NULL; + OG(stream).opaque = Z_NULL; + + + /* 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) { + php_error(E_WARNING, "zlib initialisation failed"); + RETURN_FALSE; + } + + if (coding == CODING_GZIP) { + 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 == CODING_DEFLATE) { + 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)) { + /* php_error(E_NOTICE, "output compression isn't active"); */ + RETURN_FALSE; + } + + if (php_deflate(512, &buffer, &buf_used, Z_FINISH) != Z_STREAM_END) { + php_error(E_WARNING, "zlib error"); + RETURN_FALSE; + } + result = OG(php_header_write)(buffer, buf_used); + + if (OG(compression_coding) == CODING_GZIP) { + /* 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) == CODING_GZIP) { + 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()