From 061fbbe598d6e28b35c408b35beec1a6508abac9 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Thu, 25 Aug 2005 11:51:39 +0000 Subject: [PATCH] - add etag generation through mhash - fix several memleaks --- KnownIssues.txt | 3 +- config.m4 | 20 +++++++ config.w32 | 11 ++++ http.c | 6 ++ http_cache_api.c | 54 ++++++++--------- http_response_object.c | 20 +++---- missing.c | 5 +- php_http.h | 4 +- php_http_cache_api.h | 132 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 212 insertions(+), 43 deletions(-) diff --git a/KnownIssues.txt b/KnownIssues.txt index dfe8c82..04e55a6 100644 --- a/KnownIssues.txt +++ b/KnownIssues.txt @@ -2,8 +2,7 @@ Known Issues ============ $Id$ -HttpResponse class is infunctional with current ZendEngine2, +HttpResponse class may be infunctional with current ZendEngine2, a fix is pending. -http_chunked_decode() still depends on CRLF. diff --git a/config.m4 b/config.m4 index de29b53..25570f4 100644 --- a/config.m4 +++ b/config.m4 @@ -5,6 +5,8 @@ PHP_ARG_ENABLE([http], [whether to enable extended HTTP support], [ --enable-http Enable extended HTTP support]) PHP_ARG_WITH([curl], [for CURL support], [ --with-curl[=DIR] Include CURL support]) +PHP_ARG_WITH([mhash], [for mhash support], +[ --with-mhash[=DIR] Include mhash support]) if test "$PHP_HTTP" != "no"; then @@ -68,6 +70,24 @@ dnl ---- fi +dnl ---- +dnl MHASH +dnl ---- + + if test "$PHP_MHASH" != "no"; then + for i in $PHP_MHASH /usr/local /usr /opt/mhash; do + test -f $i/include/mhash.h && MHASH_DIR=$i && break + done + + if test -z "$MHASH_DIR"; then + AC_MSG_ERROR(Please reinstall libmhash - cannot find mhash.h) + fi + + PHP_ADD_INCLUDE($MHASH_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(mhash, $MHASH_DIR/lib, MHASH_SHARED_LIBADD) + AC_DEFINE(HAVE_LIBMHASH,1,[HAve mhash support]) + fi + dnl ---- dnl DONE dnl ---- diff --git a/config.w32 b/config.w32 index 941ca4d..39a6427 100644 --- a/config.w32 +++ b/config.w32 @@ -2,8 +2,19 @@ // $Id$ ARG_ENABLE("http", "whether to enable extended HTTP support", "no"); +ARG_WITH("mhash", "mhash support", "no"); if (PHP_HTTP != "no") { + + if (PHP_MHASH != "no") { + if (CHECK_HEADER_ADD_INCLUDE('mhash.h', 'CFLAGS_HTTP') && + CHECK_LIB('libmhash.lib', 'mhash')) { + AC_DEFINE('HAVE_LIBMHASH', 1 , "Have mhash library"); + } else { + WARNING("mhash not enabled; libraries and headers not found"); + } + } + EXTENSION("http", "missing.c http.c http_functions.c http_exception_object.c "+ "http_util_object.c http_message_object.c http_requestpool_object.c "+ diff --git a/http.c b/http.c index 61ee3e7..1c901d1 100644 --- a/http.c +++ b/http.c @@ -29,6 +29,7 @@ #include "php_http_std_defs.h" #include "php_http_api.h" #include "php_http_send_api.h" +#include "php_http_cache_api.h" #ifdef HTTP_HAVE_CURL # include "php_http_request_api.h" #endif @@ -190,6 +191,7 @@ PHP_INI_BEGIN() #ifdef ZEND_ENGINE_2 HTTP_PHP_INI_ENTRY("http.only_exceptions", "0", PHP_INI_ALL, OnUpdateBool, only_exceptions) #endif + HTTP_PHP_INI_ENTRY("http.etag_mode", "-2", PHP_INI_ALL, OnUpdateLong, etag.mode) PHP_INI_END() /* }}} */ @@ -204,6 +206,10 @@ PHP_MINIT_FUNCTION(http) #endif REGISTER_INI_ENTRIES(); + + HTTP_LONG_CONSTANT("HTTP_ETAG_MD5", HTTP_ETAG_MD5); + HTTP_LONG_CONSTANT("HTTP_ETAG_SHA1", HTTP_ETAG_SHA1); + HTTP_LONG_CONSTANT("HTTP_ETAG_MHASH", HTTP_ETAG_MHASH); #ifdef HTTP_HAVE_CURL if (CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)) { diff --git a/http_cache_api.c b/http_cache_api.c index 52d76ea..0c96f4a 100644 --- a/http_cache_api.c +++ b/http_cache_api.c @@ -24,6 +24,7 @@ #include "php_streams.h" #include "php_output.h" #include "ext/standard/md5.h" +#include "ext/standard/sha1.h" #include "php_http.h" #include "php_http_std_defs.h" @@ -39,47 +40,39 @@ PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_m { php_stream_statbuf ssb; char ssb_buf[128] = {0}; - unsigned char digest[16]; - PHP_MD5_CTX ctx; - char *new_etag = ecalloc(1, 33); - - PHP_MD5Init(&ctx); - + size_t ssb_len; + void *ctx = http_etag_init(); + switch (data_mode) { case SEND_DATA: - PHP_MD5Update(&ctx, data_ptr, data_len); + http_etag_update(ctx, data_ptr, data_len); break; case SEND_RSRC: { if (php_stream_stat((php_stream *) data_ptr, &ssb)) { - efree(new_etag); + efree(ctx); return NULL; } - - snprintf(ssb_buf, 127, "%ld=%ld=%ld", ssb.sb.st_mtime, ssb.sb.st_ino, ssb.sb.st_size); - PHP_MD5Update(&ctx, ssb_buf, strlen(ssb_buf)); + ssb_len = snprintf(ssb_buf, 127, "%ld=%ld=%ld", ssb.sb.st_mtime, ssb.sb.st_ino, ssb.sb.st_size); + http_etag_update(ctx, ssb_buf, ssb_len); } break; default: { if (php_stream_stat_path((char *) data_ptr, &ssb)) { - efree(new_etag); + efree(ctx); return NULL; } - - snprintf(ssb_buf, 127, "%ld=%ld=%ld", ssb.sb.st_mtime, ssb.sb.st_ino, ssb.sb.st_size); - PHP_MD5Update(&ctx, ssb_buf, strlen(ssb_buf)); + ssb_len = snprintf(ssb_buf, 127, "%ld=%ld=%ld", ssb.sb.st_mtime, ssb.sb.st_ino, ssb.sb.st_size); + http_etag_update(ctx, ssb_buf, ssb_len); } break; } - PHP_MD5Final(digest, &ctx); - make_digest(new_etag, digest); - - return new_etag; + return http_etag_finish(ctx); } /* }}} */ @@ -205,33 +198,38 @@ PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len, PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC) { - char etag[33] = { 0 }; - unsigned char digest[16]; + char etag[41] = { 0 }; + unsigned char digest[20]; if (mode & PHP_OUTPUT_HANDLER_START) { + if (HTTP_G(etag).started) { + http_error(HE_WARNING, HTTP_E_RUNTIME, "ob_etaghandler can only be used once"); + return; + } HTTP_G(etag).started = 1; - PHP_MD5Init(&HTTP_G(etag).md5ctx); + HTTP_G(etag).ctx = http_etag_init(); } - PHP_MD5Update(&HTTP_G(etag).md5ctx, output, output_len); + http_etag_update(HTTP_G(etag).ctx, output, output_len); if (mode & PHP_OUTPUT_HANDLER_END) { - PHP_MD5Final(digest, &HTTP_G(etag).md5ctx); - + char *etag = http_etag_finish(HTTP_G(etag).ctx); + /* just do that if desired */ if (HTTP_G(etag).started) { char *sent_header = NULL; - make_digest(etag, digest); http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL)); - http_send_etag_ex(etag, 32, &sent_header); - + http_send_etag_ex(etag, strlen(etag), &sent_header); + if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) { + efree(etag); http_exit_ex(304, sent_header, NULL, 0); } else { STR_FREE(sent_header); } } + efree(etag); } *handled_output_len = output_len; diff --git a/http_response_object.c b/http_response_object.c index 0fb1248..5e588ea 100644 --- a/http_response_object.c +++ b/http_response_object.c @@ -773,19 +773,19 @@ PHP_METHOD(HttpResponse, send) /* capture mode */ if (Z_BVAL_P(GET_STATIC_PROP(catch))) { - zval *the_data; + zval the_data; - MAKE_STD_ZVAL(the_data); - php_ob_get_buffer(the_data TSRMLS_CC); - - SET_STATIC_PROP(data, the_data); + INIT_PZVAL(&the_data); + php_ob_get_buffer(&the_data TSRMLS_CC); + SET_STATIC_PROP(data, &the_data); ZVAL_LONG(GET_STATIC_PROP(mode), SEND_DATA); if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) { - char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA); + char *etag = http_etag(Z_STRVAL(the_data), Z_STRLEN(the_data), SEND_DATA); UPD_STATIC_PROP(string, eTag, etag); efree(etag); } + zval_dtor(&the_data); clean_ob = 1; } @@ -919,14 +919,14 @@ PHP_METHOD(HttpResponse, send) */ PHP_METHOD(HttpResponse, capture) { - zval *do_catch; + zval do_catch; NO_ARGS; - MAKE_STD_ZVAL(do_catch); - ZVAL_LONG(do_catch, 1); + INIT_PZVAL(&do_catch); + ZVAL_LONG(&do_catch, 1); - SET_STATIC_PROP(catch, do_catch); + SET_STATIC_PROP(catch, &do_catch); php_end_ob_buffers(0 TSRMLS_CC); php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC); diff --git a/missing.c b/missing.c index dcf4709..fea611c 100644 --- a/missing.c +++ b/missing.c @@ -38,6 +38,7 @@ static inline zval *tmp_zval(void) static void dup_zval(zval **z) { + zval *o = *z; zval_add_ref(z); SEPARATE_ZVAL(z); } @@ -145,7 +146,7 @@ int zend_update_static_property(zend_class_entry *scope, char *name, size_t name } retval = SUCCESS; } - + zval_ptr_dtor(&value); EG(scope) = old_scope; return retval; @@ -190,7 +191,7 @@ void zend_fix_static_properties(zend_class_entry *ce, HashTable *static_members { zend_hash_copy(static_members, ce->static_members, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); zend_hash_destroy(ce->static_members); - zend_hash_init_ex(ce->static_members, 0, NULL, ZVAL_PTR_DTOR, 1, 0); + zend_hash_init_ex(ce->static_members, static_members->nNumOfElements, NULL, ZVAL_PTR_DTOR, 1, 0); } void zend_init_static_properties(zend_class_entry *ce, HashTable *static_members TSRMLS_DC) diff --git a/php_http.h b/php_http.h index 4dffa67..9137b81 100644 --- a/php_http.h +++ b/php_http.h @@ -28,6 +28,7 @@ # include #endif #include "ext/standard/md5.h" +#include "ext/standard/sha1.h" #include "phpstr/phpstr.h" extern zend_module_entry http_module_entry; @@ -41,8 +42,9 @@ ZEND_BEGIN_MODULE_GLOBALS(http) zend_bool only_exceptions; #endif struct _http_globals_etag { + long mode; + void *ctx; zend_bool started; - PHP_MD5_CTX md5ctx; } etag; struct _http_globals_log { diff --git a/php_http_cache_api.h b/php_http_cache_api.h index e420963..8a9e55d 100644 --- a/php_http_cache_api.h +++ b/php_http_cache_api.h @@ -19,9 +19,141 @@ #define PHP_HTTP_CACHE_API_H #include "php_http_std_defs.h" +#include "php_http.h" #include "php_http_api.h" #include "php_http_send_api.h" +#include "zend_ini.h" + +#ifdef HAVE_LIBMHASH +# include +#endif + +ZEND_EXTERN_MODULE_GLOBALS(http); + +typedef enum { + HTTP_ETAG_MD5 = -2, + HTTP_ETAG_SHA1 = -1, + HTTP_ETAG_MHASH = 0, +} http_etag_mode; + +#ifdef HAVE_LIBMHASH +static void *http_etag_alloc_mhash_digest(size_t size) +{ + return emalloc(size); +} +#endif + +#define http_etag_digest(d, l) _http_etag_digest((d), (l) TSRMLS_CC) +static inline char *_http_etag_digest(const unsigned char *digest, int len TSRMLS_DC) +{ + int i; + char *hex = emalloc(len * 2 + 1); + char *ptr = hex; + + /* optimize this -- + look at apache's make_etag */ + for (i = 0; i < len; ++i) { + sprintf(ptr, "%02x", digest[i]); + ptr += 2; + } + *ptr = '\0'; + + return hex; +} + +#define http_etag_init() _http_etag_init(TSRMLS_C) +static inline void *_http_etag_init(TSRMLS_D) +{ + void *ctx; + long mode = INI_INT("http.etag_mode"); + + switch (mode) + { + case HTTP_ETAG_SHA1: + PHP_SHA1Init(ctx = emalloc(sizeof(PHP_SHA1_CTX))); + break; + + case HTTP_ETAG_MD5: +invalid_flag: + PHP_MD5Init(ctx = emalloc(sizeof(PHP_MD5_CTX))); + break; + + default: + { +#ifdef HAVE_LIBMHASH + if ((mode >= 0) && (mode <= mhash_count())) { + ctx = mhash_init(mode); + } + if ((!ctx) || (ctx == MHASH_FAILED)) +#endif + { + HTTP_G(etag).mode = HTTP_ETAG_MD5; + goto invalid_flag; + } + } + break; + } + + return ctx; +} + +#define http_etag_finish(c) _http_etag_finish((c) TSRMLS_CC) +static inline char *_http_etag_finish(void *ctx TSRMLS_DC) +{ + char *etag = NULL; + unsigned char digest[20]; + long mode = INI_INT("http.etag_mode"); + + switch (mode) + { + case HTTP_ETAG_SHA1: + PHP_SHA1Final(digest, ctx); + etag = http_etag_digest(digest, 20); + efree(ctx); + break; + + case HTTP_ETAG_MD5: + PHP_MD5Final(digest, ctx); + etag = http_etag_digest(digest, 16); + efree(ctx); + break; + + default: + { +#ifdef HAVE_LIBMHASH + unsigned char *mhash_digest = mhash_end_m(ctx, http_etag_alloc_mhash_digest); + etag = http_etag_digest(mhash_digest, mhash_get_block_size(mode)); + efree(mhash_digest); +#endif + } + break; + } + + return etag; +} + +#define http_etag_update(c, d, l) _http_etag_update((c), (d), (l) TSRMLS_CC) +static inline void _http_etag_update(void *ctx, const char *data_ptr, size_t data_len TSRMLS_DC) +{ + switch (INI_INT("http.etag_mode")) + { + case HTTP_ETAG_SHA1: + PHP_SHA1Update(ctx, data_ptr, data_len); + break; + + case HTTP_ETAG_MD5: + PHP_MD5Update(ctx, data_ptr, data_len); + break; + + default: +#ifdef HAVE_LIBMHASH + mhash(ctx, data_ptr, data_len); +#endif + break; + } +} + #define http_etag(p, l, m) _http_etag((p), (l), (m) TSRMLS_CC) PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC); -- 2.30.2