From 19cad54672b6babfac2b7c9c64c415c9a24b888a Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Sat, 23 Jul 2005 20:32:32 +0000 Subject: [PATCH 1/1] - fix write access of HttpMessage headers array in inherited context - fix float/double of http message's http version - fix request pool handles destruction - HttpRequestPool now implements Iterator - nuke unused variables --- http_message_object.c | 35 +++++++--- http_request_pool_api.c | 48 ++++++++++---- http_requestpool_object.c | 114 ++++++++++++++++++++++++++++++--- http_response_object.c | 6 +- php_http_requestpool_object.h | 8 +++ php_http_std_defs.h | 4 ++ tests/HttpRequestPool_001.phpt | 40 +++--------- tests/HttpRequest_002.phpt | 3 +- tests/HttpRequest_003.phpt | 3 +- tests/HttpResponse_002.phpt | 4 +- 10 files changed, 195 insertions(+), 70 deletions(-) diff --git a/http_message_object.c b/http_message_object.c index 08f37f4..8cf6969 100644 --- a/http_message_object.c +++ b/http_message_object.c @@ -136,6 +136,7 @@ void _http_message_object_init(INIT_FUNC_ARGS) http_message_object_handlers.read_property = http_message_object_read_prop; http_message_object_handlers.write_property = http_message_object_write_prop; http_message_object_handlers.get_properties = http_message_object_get_props; + http_message_object_handlers.get_property_ptr_ptr = NULL; } zend_object_value _http_message_object_new(zend_class_entry *ce TSRMLS_DC) @@ -216,10 +217,13 @@ static zval *_http_message_object_read_prop(zval *object, zval *member, int type http_message *msg = obj->message; zval *return_value; - /* tmp var */ - ALLOC_ZVAL(return_value); + return_value = &EG(uninitialized_zval); return_value->refcount = 0; + return_value->is_ref = 0; +#if 0 + fprintf(stderr, "Read HttpMessage::$%s\n", Z_STRVAL_P(member)); +#endif if (!EG(scope) || !instanceof_function(EG(scope), obj->zo.ce TSRMLS_CC)) { zend_error(E_WARNING, "Cannot access protected property %s::$%s", obj->zo.ce->name, Z_STRVAL_P(member)); return EG(uninitialized_zval_ptr); @@ -255,8 +259,18 @@ static zval *_http_message_object_read_prop(zval *object, zval *member, int type break; case HTTP_MSG_PROPHASH_HEADERS: - array_init(return_value); - zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + /* This is needed for situations like + * $this->headers['foo'] = 'bar'; + */ + if (type == BP_VAR_W) { + return_value->refcount = 1; + return_value->is_ref = 1; + Z_TYPE_P(return_value) = IS_ARRAY; + Z_ARRVAL_P(return_value) = &msg->hdrs; + } else { + array_init(return_value); + zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + } break; case HTTP_MSG_PROPHASH_PARENT_MESSAGE: @@ -307,6 +321,9 @@ static void _http_message_object_write_prop(zval *object, zval *member, zval *va getObjectEx(http_message_object, obj, object); http_message *msg = obj->message; +#if 0 + fprintf(stderr, "Write HttpMessage::$%s\n", Z_STRVAL_P(member)); +#endif if (!EG(scope) || !instanceof_function(EG(scope), obj->zo.ce TSRMLS_CC)) { zend_error(E_WARNING, "Cannot access protected property %s::$%s", obj->zo.ce->name, Z_STRVAL_P(member)); } @@ -784,7 +801,7 @@ PHP_METHOD(HttpMessage, getHttpVersion) IF_RETVAL_USED { char ver[4] = {0}; - float version; + double version; getObject(http_message_object, obj); switch (obj->message->type) @@ -801,7 +818,7 @@ PHP_METHOD(HttpMessage, getHttpVersion) default: RETURN_NULL(); } - sprintf(ver, "%1.1f", version); + sprintf(ver, "%1.1lf", version); RETURN_STRINGL(ver, 3, 1); } } @@ -828,16 +845,16 @@ PHP_METHOD(HttpMessage, setHttpVersion) } convert_to_double(zv); - sprintf(v, "%1.1f", Z_DVAL_P(zv)); + sprintf(v, "%1.1lf", Z_DVAL_P(zv)); if (strcmp(v, "1.0") && strcmp(v, "1.1")) { http_error_ex(E_WARNING, HTTP_E_PARAM, "Invalid HTTP protocol version (1.0 or 1.1): %s", v); RETURN_FALSE; } if (HTTP_MSG_TYPE(RESPONSE, obj->message)) { - obj->message->info.response.http_version = (float) Z_DVAL_P(zv); + obj->message->info.response.http_version = Z_DVAL_P(zv); } else { - obj->message->info.request.http_version = (float) Z_DVAL_P(zv); + obj->message->info.request.http_version = Z_DVAL_P(zv); } RETURN_TRUE; } diff --git a/http_request_pool_api.c b/http_request_pool_api.c index 7dc809b..b62eeea 100644 --- a/http_request_pool_api.c +++ b/http_request_pool_api.c @@ -42,7 +42,7 @@ PHP_HTTP_API http_request_pool *_http_request_pool_init(http_request_pool *pool { zend_bool free_pool; #if HTTP_DEBUG_REQPOOLS - fprintf(stderr, "Initializing request pool\n"); + fprintf(stderr, "Initializing request pool %p\n", pool); #endif if ((free_pool = (!pool))) { pool = emalloc(sizeof(http_request_pool)); @@ -74,26 +74,33 @@ PHP_HTTP_API STATUS _http_request_pool_attach(http_request_pool *pool, zval *req { getObjectEx(http_request_object, req, request); #if HTTP_DEBUG_REQPOOLS - fprintf(stderr, "Attaching request %p to pool %p\n", req, pool); + fprintf(stderr, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request), req, pool); #endif if (req->pool) { - http_error(E_WARNING, HTTP_E_CURL, "HttpRequest object is already member of an HttpRequestPool"); + http_error_ex(E_WARNING, HTTP_E_CURL, "HttpRequest object(#%d) is already member of %s HttpRequestPool", Z_OBJ_HANDLE_P(request), req->pool == pool ? "this" : "another"); } else { - CURLMcode code; http_request_body *body = http_request_body_new(); zval *info = GET_PROP_EX(req, request, responseInfo); if (SUCCESS != http_request_object_requesthandler(req, request, body)) { http_error_ex(E_WARNING, HTTP_E_CURL, "Could not initialize HttpRequest object for attaching to the HttpRequestPool"); } else { - code = curl_multi_add_handle(pool->ch, req->ch); + CURLMcode code = curl_multi_add_handle(pool->ch, req->ch); + if ((CURLM_OK != code) && (CURLM_CALL_MULTI_PERFORM != code)) { http_error_ex(E_WARNING, HTTP_E_CURL, "Could not attach HttpRequest object to the HttpRequestPool: %s", curl_multi_strerror(code)); } else { req->pool = pool; + zend_llist_add_element(&pool->handles, &request); zend_llist_add_element(&pool->bodies, &body); + zval_add_ref(&request); + zend_objects_store_add_ref(request TSRMLS_CC); + +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool->handles), pool); +#endif return SUCCESS; } } @@ -108,18 +115,26 @@ PHP_HTTP_API STATUS _http_request_pool_detach(http_request_pool *pool, zval *req { getObjectEx(http_request_object, req, request); #if HTTP_DEBUG_REQPOOLS - fprintf(stderr, "Detaching request %p from pool %p\n", req, pool); + fprintf(stderr, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request), req, pool); #endif - if (req->pool != pool) { - http_error(E_WARNING, HTTP_E_CURL, "HttpRequest object is not attached to this HttpRequestPool"); + if (!req->pool) { + /* not attached to any pool */ +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "HttpRequest object(#%d) %p is not attached to any HttpRequestPool\n", Z_OBJ_HANDLE_P(request), req); +#endif + } else if (req->pool != pool) { + http_error_ex(E_WARNING, HTTP_E_CURL, "HttpRequest object(#%d) is not attached to this HttpRequestPool", Z_OBJ_HANDLE_P(request)); } else { CURLMcode code; + req->pool = NULL; + zend_llist_del_element(&pool->handles, request, http_request_pool_compare_handles); +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "> %d HttpRequests remaining in pool %p\n", zend_llist_count(&pool->handles), pool); +#endif if (CURLM_OK != (code = curl_multi_remove_handle(pool->ch, req->ch))) { http_error_ex(E_WARNING, HTTP_E_CURL, "Could not detach HttpRequest object from the HttpRequestPool: %s", curl_multi_strerror(code)); } else { - req->pool = NULL; - zend_llist_del_element(&pool->handles, request, http_request_pool_compare_handles); return SUCCESS; } } @@ -163,13 +178,16 @@ PHP_HTTP_API STATUS _http_request_pool_send(http_request_pool *pool TSRMLS_DC) #endif while (http_request_pool_perform(pool)) { #if HTTP_DEBUG_REQPOOLS - fprintf(stderr, "%d unfinished requests of pool %p remaining\n", pool->unfinished, pool); + fprintf(stderr, "> %d unfinished requests of pool %p remaining\n", pool->unfinished, pool); #endif if (SUCCESS != http_request_pool_select(pool)) { http_error(E_WARNING, HTTP_E_CURL, "Socket error"); return FAILURE; } } +#if HTTP_DEBUG_REQPOOLS + fprintf(stderr, "Finished sending %d HttpRequests of pool %p (still unfinished: %d)\n", zend_llist_count(&pool->handles), pool, pool->unfinished); +#endif zend_llist_apply(&pool->handles, (llist_apply_func_t) http_request_pool_responsehandler TSRMLS_CC); return SUCCESS; } @@ -217,7 +235,7 @@ void _http_request_pool_responsehandler(zval **req TSRMLS_DC) { getObjectEx(http_request_object, obj, *req); #if HTTP_DEBUG_REQPOOLS - fprintf(stderr, "Fetching data from request %p of pool %p\n", obj, obj->pool); + fprintf(stderr, "Fetching data from HttpRequest(#%d) %p of pool %p\n", Z_OBJ_HANDLE_PP(req), obj, obj->pool); #endif http_request_object_responsehandler(obj, *req); } @@ -236,7 +254,11 @@ static void http_request_pool_freebody(http_request_body **body) /* {{{ static int http_request_pool_compare_handles(void *, void *) */ static int http_request_pool_compare_handles(void *h1, void *h2) { - return ((*((zval **) h1)) == ((zval *) h2)); + int match = (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2)); +#if HTTP_DEBUG_REQPOOLS + /* if(match) fprintf(stderr, "OK\n"); */ +#endif + return match; } /* }}} */ diff --git a/http_requestpool_object.c b/http_requestpool_object.c index 28aa366..5323342 100644 --- a/http_requestpool_object.c +++ b/http_requestpool_object.c @@ -29,6 +29,8 @@ #include "php_http_request_object.h" #include "php_http_exception_object.h" +#include "zend_interfaces.h" + #ifdef PHP_WIN32 # include #endif @@ -60,6 +62,11 @@ HTTP_EMPTY_ARGS(socketSend, 0); HTTP_EMPTY_ARGS(socketSelect, 0); HTTP_EMPTY_ARGS(socketRead, 0); +HTTP_EMPTY_ARGS(valid, 0); +HTTP_EMPTY_ARGS(current, 1); +HTTP_EMPTY_ARGS(key, 0); +HTTP_EMPTY_ARGS(next, 0); +HTTP_EMPTY_ARGS(rewind, 0); #define http_requestpool_object_declare_default_properties() _http_requestpool_object_declare_default_properties(TSRMLS_C) static inline void _http_requestpool_object_declare_default_properties(TSRMLS_D); @@ -77,6 +84,13 @@ zend_function_entry http_requestpool_object_fe[] = { HTTP_REQPOOL_ME(socketSelect, ZEND_ACC_PROTECTED) HTTP_REQPOOL_ME(socketRead, ZEND_ACC_PROTECTED) + /* implements Interator */ + HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC) + HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} }; static zend_object_handlers http_requestpool_object_handlers; @@ -84,6 +98,7 @@ static zend_object_handlers http_requestpool_object_handlers; void _http_requestpool_object_init(INIT_FUNC_ARGS) { HTTP_REGISTER_CLASS_EX(HttpRequestPool, http_requestpool_object, NULL, 0); + zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 1, zend_ce_iterator); } zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC) @@ -95,6 +110,7 @@ zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC) o->zo.ce = ce; http_request_pool_init(&o->pool); + o->iterator.pos = 0; ALLOC_HASHTABLE(OBJ_PROP(o)); zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0); @@ -135,19 +151,22 @@ void _http_requestpool_object_free(zend_object *object TSRMLS_DC) * Example: *
  * attach($req[$url]);
- *     }
+ * try {
+ *     $pool = new HttpRequestPool(
+ *         new HttpRequest('http://www.google.com/', HTTP_HEAD),
+ *         new HttpRequest('http://www.php.net/', HTTP_HEAD)
+ *     );
  *     $pool->send();
- *     foreach ($urls as $url) {
- *         printf("%s (%s) is %s\n",
- *             $url, $req[$url]->getResponseInfo('effective_url'),
- *             $r->getResponseCode() == 200 ? 'alive' : 'not alive'
+ *     foreach($pool as $request) {
+ *         printf("%s is %s (%d)\n",
+ *             $request->getResponseInfo('effective_url'),
+ *             $request->getResponseCode() ? 'alive' : 'not alive',
+ *             $request->getResponseCode()
  *         );
  *     }
+ * } catch (HttpException $e) {
+ *     echo $e;
+ * }
  * ?>
  * 
*/ @@ -308,6 +327,81 @@ PHP_METHOD(HttpRequestPool, socketRead) } /* }}} */ +/* implements Iterator */ + +/* {{{ proto bool HttpRequestPool::valid() + */ +PHP_METHOD(HttpRequestPool, valid) +{ + NO_ARGS; + + IF_RETVAL_USED { + getObject(http_requestpool_object, obj); + RETURN_BOOL(obj->iterator.pos < zend_llist_count(&obj->pool.handles)); + } +} +/* }}} */ + +/* {{{ proto HttpRequest HttpRequestPool::current() + */ +PHP_METHOD(HttpRequestPool, current) +{ + NO_ARGS; + + IF_RETVAL_USED { + long pos = 0; + zval **current = NULL; + zend_llist_position lpos; + getObject(http_requestpool_object, obj); + + if (obj->iterator.pos < zend_llist_count(&obj->pool.handles)) { + for ( current = zend_llist_get_first_ex(&obj->pool.handles, &lpos); + current && obj->iterator.pos != pos++; + current = zend_llist_get_next_ex(&obj->pool.handles, &lpos)); + if (current) { + RETURN_OBJECT(*current); + } + } + RETURN_NULL(); + } +} +/* }}} */ + +/* {{{ proto long HttpRequestPool::key() + */ +PHP_METHOD(HttpRequestPool, key) +{ + NO_ARGS; + + IF_RETVAL_USED { + getObject(http_requestpool_object, obj); + RETURN_LONG(obj->iterator.pos); + } +} +/* }}} */ + +/* {{{ proto void HttpRequestPool::next() + */ +PHP_METHOD(HttpRequestPool, next) +{ + NO_ARGS { + getObject(http_requestpool_object, obj); + ++(obj->iterator.pos); + } +} +/* }}} */ + +/* {{{ proto void HttpRequestPool::rewind() + */ +PHP_METHOD(HttpRequestPool, rewind) +{ + NO_ARGS { + getObject(http_requestpool_object, obj); + obj->iterator.pos = 0; + } +} +/* }}} */ + #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */ /* diff --git a/http_response_object.c b/http_response_object.c index 5303d2d..f7cb216 100644 --- a/http_response_object.c +++ b/http_response_object.c @@ -469,7 +469,7 @@ PHP_METHOD(HttpResponse, getBufferSize) */ PHP_METHOD(HttpResponse, setData) { - zval *the_data, **data; + zval *the_data; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) { RETURN_FALSE; @@ -601,7 +601,7 @@ PHP_METHOD(HttpResponse, getFile) */ PHP_METHOD(HttpResponse, send) { - zval *do_cache, *do_gzip, *sent; + zval *sent; zend_bool clean_ob = 1; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean_ob)) { @@ -655,8 +655,6 @@ PHP_METHOD(HttpResponse, send) /* caching */ if (Z_LVAL_P(GET_STATIC_PROP(cache))) { - char *cc_hdr; - int cc_len; zval *cctl, *etag, *lmod; etag = GET_STATIC_PROP(eTag); diff --git a/php_http_requestpool_object.h b/php_http_requestpool_object.h index 336fed2..d3954f8 100644 --- a/php_http_requestpool_object.h +++ b/php_http_requestpool_object.h @@ -31,6 +31,9 @@ typedef struct { zend_object zo; http_request_pool pool; + struct { + long pos; + } iterator; } http_requestpool_object; extern zend_class_entry *http_requestpool_object_ce; @@ -52,6 +55,11 @@ PHP_METHOD(HttpRequestPool, reset); PHP_METHOD(HttpRequestPool, socketSend); PHP_METHOD(HttpRequestPool, socketSelect); PHP_METHOD(HttpRequestPool, socketRead); +PHP_METHOD(HttpRequestPool, valid); +PHP_METHOD(HttpRequestPool, current); +PHP_METHOD(HttpRequestPool, key); +PHP_METHOD(HttpRequestPool, next); +PHP_METHOD(HttpRequestPool, rewind); #endif #endif diff --git a/php_http_std_defs.h b/php_http_std_defs.h index 6e1c7ab..65c72c1 100644 --- a/php_http_std_defs.h +++ b/php_http_std_defs.h @@ -198,7 +198,11 @@ typedef int STATUS; # define DCL_STATIC_PROP_Z(a, n, v) zend_declare_property(ce, (#n), sizeof(#n), (v), (ZEND_ACC_ ##a | ZEND_ACC_STATIC) TSRMLS_CC) # define DCL_STATIC_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n), (ZEND_ACC_ ##a | ZEND_ACC_STATIC) TSRMLS_CC) # define GET_STATIC_PROP_EX(ce, n) zend_std_get_static_property(ce, (#n), sizeof(#n), 0 TSRMLS_CC) +#ifdef zend_update_class_constants # define USE_STATIC_PROP_EX(ce) zend_update_class_constants(ce TSRMLS_CC) +#else +# define USE_STATIC_PROP_EX(ce) +#endif # define SET_STATIC_PROP_EX(ce, n, v) \ { \ int refcount; \ diff --git a/tests/HttpRequestPool_001.phpt b/tests/HttpRequestPool_001.phpt index 3eecd11..0b276b8 100644 --- a/tests/HttpRequestPool_001.phpt +++ b/tests/HttpRequestPool_001.phpt @@ -4,47 +4,27 @@ HttpRequestPool --FILE-- attach($reqs[] = new HttpRequest($url, HTTP_HEAD)); -} $pool->send(); -foreach ($reqs as $req) { - echo $req->getResponseInfo('effective_url'), '=', +foreach ($pool as $req) { + echo $req->getUrl(), '=', $req->getResponseCode(), ':', $req->getResponseMessage()->getResponseCode(), "\n"; - $pool->detach($req); - $pool->attach($req); - $pool->detach($req); - $pool->attach($req); - $pool->detach($req); - $pool->attach($req); - $pool->detach($req); - $pool->attach($req); - $req->getResponseMessage()->getResponseCode(); - $req->getResponseMessage()->getResponseCode(); - $req->getResponseMessage()->getResponseCode(); - $req->getResponseMessage()->getResponseCode(); } -$pool->send(); -$pool->reset(); -$pool->attach($req); echo "Done\n"; ?> --EXPECTF-- -Content-type: text/html -X-Powered-By: PHP/%s - -http://www.php.net/=200:200 +%shttp://www.php.net/=200:200 http://pear.php.net/=200:200 http://pecl.php.net/=200:200 Done diff --git a/tests/HttpRequest_002.phpt b/tests/HttpRequest_002.phpt index e2e9672..3e95b45 100644 --- a/tests/HttpRequest_002.phpt +++ b/tests/HttpRequest_002.phpt @@ -4,7 +4,7 @@ HttpRequest GET/POST --FILE-- --FILE--