X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=http_request_object.c;h=2ab84c0c3fe0300efbe271254a58be09e62d6b3f;hb=96c0ae82ca8cda98d70b87597d765a9d050129de;hp=bb3ebf46527e02a498cba94567e907bdb9209e24;hpb=1a0dbb96fbe46a2edec1c813798cef7b0898eebe;p=m6w6%2Fext-http diff --git a/http_request_object.c b/http_request_object.c index bb3ebf4..2ab84c0 100644 --- a/http_request_object.c +++ b/http_request_object.c @@ -20,6 +20,7 @@ #include "zend_interfaces.h" #include "php_http_api.h" +#include "php_http_cookie_api.h" #include "php_http_exception_object.h" #include "php_http_message_api.h" #include "php_http_message_object.h" @@ -134,8 +135,9 @@ HTTP_BEGIN_ARGS(getResponseHeader, 0) HTTP_ARG_VAL(name, 0) HTTP_END_ARGS; -HTTP_BEGIN_ARGS(getResponseCookie, 0) - HTTP_ARG_VAL(name, 0) +HTTP_BEGIN_ARGS(getResponseCookies, 0) + HTTP_ARG_VAL(flags, 0) + HTTP_ARG_VAL(allowed_extras, 0) HTTP_END_ARGS; HTTP_EMPTY_ARGS(getResponseBody); @@ -263,7 +265,7 @@ zend_function_entry http_request_object_fe[] = { HTTP_REQUEST_ME(getResponseData, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseHeader, ZEND_ACC_PUBLIC) - HTTP_REQUEST_ME(getResponseCookie, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseCookies, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseCode, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseStatus, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseBody, ZEND_ACC_PUBLIC) @@ -311,7 +313,6 @@ zend_object_value _http_request_object_new_ex(zend_class_entry *ce, CURL *ch, ht o = ecalloc(1, sizeof(http_request_object)); o->zo.ce = ce; o->request = http_request_init_ex(NULL, ch, 0, NULL); - phpstr_init(&o->history); if (ptr) { *ptr = o; @@ -341,7 +342,6 @@ zend_object_value _http_request_object_clone_obj(zval *this_ptr TSRMLS_DC) } zend_objects_clone_members(&new_obj->zo, new_ov, old_zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); - phpstr_append(&new_obj->history, old_obj->history.data, old_obj->history.used); phpstr_append(&new_obj->request->conv.request, old_obj->request->conv.request.data, old_obj->request->conv.request.used); phpstr_append(&new_obj->request->conv.response, old_obj->request->conv.response.data, old_obj->request->conv.response.used); @@ -366,7 +366,7 @@ static inline void _http_request_object_declare_default_properties(TSRMLS_D) DCL_PROP(PRIVATE, string, rawPostData, ""); DCL_PROP(PRIVATE, string, queryData, ""); DCL_PROP(PRIVATE, string, putFile, ""); - + DCL_PROP_N(PRIVATE, history); DCL_PROP(PUBLIC, bool, recordHistory, 0); #ifndef WONKY @@ -429,7 +429,6 @@ void _http_request_object_free(zend_object *object TSRMLS_DC) FREE_HASHTABLE(OBJ_PROP(o)); } http_request_free(&o->request); - phpstr_dtor(&o->history); efree(o); } @@ -552,44 +551,82 @@ STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ STATUS _http_request_object_responsehandler(http_request_object *obj, zval *this_ptr TSRMLS_DC) { + STATUS ret; + zval *info; http_message *msg; + /* always fetch info */ + MAKE_STD_ZVAL(info); + array_init(info); + http_request_info(obj->request, Z_ARRVAL_P(info)); + SET_PROP(responseInfo, info); + zval_ptr_dtor(&info); + + /* parse response message */ phpstr_fix(&obj->request->conv.request); phpstr_fix(&obj->request->conv.response); - msg = http_message_parse(PHPSTR_VAL(&obj->request->conv.response), PHPSTR_LEN(&obj->request->conv.response)); - - if (!msg) { - return FAILURE; - } else { + if ((msg = http_message_parse(PHPSTR_VAL(&obj->request->conv.response), PHPSTR_LEN(&obj->request->conv.response)))) { char *body; size_t body_len; - zval *headers, *message, *resp, *info; + zval *headers, *message, *resp; if (zval_is_true(GET_PROP(recordHistory))) { /* we need to act like a zipper, as we'll receive * the requests and the responses in separate chains * for redirects */ - http_message *response = msg, *request = http_message_parse(PHPSTR_VAL(&obj->request->conv.request), PHPSTR_LEN(&obj->request->conv.request)); - http_message *free_msg = request; - - do { - char *message; - size_t msglen; - - http_message_tostring(response, &message, &msglen); - phpstr_append(&obj->history, message, msglen); - efree(message); - - http_message_tostring(request, &message, &msglen); - phpstr_append(&obj->history, message, msglen); - efree(message); - - } while ((response = response->parent) && (request = request->parent)); - - http_message_free(&free_msg); - phpstr_fix(&obj->history); + http_message *response = http_message_dup(msg); + http_message *request = http_message_parse(PHPSTR_VAL(&obj->request->conv.request), PHPSTR_LEN(&obj->request->conv.request)); + + if (request && response) { + int num_req, num_resp; + + http_message_count(num_req, request); + http_message_count(num_resp, response); + + /* + stuck request messages in between response messages + + response request + v v + response request + v v + response request + ================== + response > request + ,---' + response > request + ,---' + response > request + */ + if (num_req == num_resp) { + int i; + zval *hist, *history = GET_PROP(history); + http_message *res_tmp = response, *req_tmp = request, *req_par, *res_par; + + for (i = 0; i < num_req; ++i) { + res_par = res_tmp->parent; + req_par = req_tmp->parent; + res_tmp->parent = req_tmp; + req_tmp->parent = res_par; + res_tmp = res_par; + req_tmp = req_par; + } + + MAKE_STD_ZVAL(hist); + ZVAL_OBJVAL(hist, http_message_object_new_ex(http_message_object_ce, response, NULL), 0); + if (Z_TYPE_P(history) == IS_OBJECT) { + http_message_object_prepend(hist, history); + } + SET_PROP(history, hist); + zval_ptr_dtor(&hist); + } + /* TODO: error? */ + } else { + http_message_free(&response); + http_message_free(&request); + } } UPD_PROP(long, responseCode, msg->http.info.response.code); @@ -611,18 +648,53 @@ STATUS _http_request_object_responsehandler(http_request_object *obj, zval *this SET_PROP(responseMessage, message); zval_ptr_dtor(&message); - MAKE_STD_ZVAL(info); - array_init(info); - http_request_info(obj->request, Z_ARRVAL_P(info)); - SET_PROP(responseInfo, info); - zval_ptr_dtor(&info); + ret = SUCCESS; + } else { + /* update properties with empty values*/ + zval *resp = GET_PROP(responseData), *znull; - if (zend_hash_exists(&Z_OBJCE_P(getThis())->function_table, "onfinish", sizeof("onfinish"))) { - zend_call_method_with_0_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "onfinish", NULL); + MAKE_STD_ZVAL(znull); + ZVAL_NULL(znull); + SET_PROP(responseMessage, znull); + zval_ptr_dtor(&znull); + + if (Z_TYPE_P(resp) == IS_ARRAY) { + zend_hash_clean(Z_ARRVAL_P(resp)); + } + + UPD_PROP(long, responseCode, 0); + UPD_PROP(string, responseStatus, ""); + + /* append request message to history */ + if (zval_is_true(GET_PROP(recordHistory))) { + http_message *request; + + if ((request = http_message_parse(PHPSTR_VAL(&obj->request->conv.request), PHPSTR_LEN(&obj->request->conv.request)))) { + zval *hist, *history = GET_PROP(history); + + MAKE_STD_ZVAL(hist); + ZVAL_OBJVAL(hist, http_message_object_new_ex(http_message_object_ce, request, NULL), 0); + if (Z_TYPE_P(history) == IS_OBJECT) { + http_message_object_prepend(hist, history); + } + SET_PROP(history, hist); + zval_ptr_dtor(&hist); + } } - return SUCCESS; + ret = FAILURE; } + + if (zend_hash_exists(&Z_OBJCE_P(getThis())->function_table, "onfinish", sizeof("onfinish"))) { + zval *param; + + MAKE_STD_ZVAL(param); + ZVAL_BOOL(param, ret == SUCCESS); + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "onfinish", NULL, param); + zval_ptr_dtor(¶m); + } + + return ret; } #define http_request_object_set_options_subr(key, ow) \ @@ -1493,107 +1565,95 @@ PHP_METHOD(HttpRequest, getResponseHeader) } /* }}} */ -/* {{{ proto array HttpRequest::getResponseCookie([string name]) +/* {{{ proto array HttpRequest::getResponseCookies([int flags[, array allowed_extras]]) * * Get response cookie(s) after the request has been sent. * - * Accepts a string as optional parameter specifying the name of the cookie to read. - * If the parameter is empty or omitted, an associative array with all received - * cookies will be returned. - * - * Returns either an associative array with the cookie's name, value and any - * additional params of the cookie matching name if requested, FALSE on failure, - * or an array containing all received cookies as arrays. + * Returns an array of stdClass objects like http_parse_cookie would return. * * If redirects were allowed and several responses were received, the data * references the last received response. */ -PHP_METHOD(HttpRequest, getResponseCookie) +PHP_METHOD(HttpRequest, getResponseCookies) { IF_RETVAL_USED { - zval *data, **headers; - char *cookie_name = NULL; - int cookie_len = 0; + long flags = 0; + zval *allowed_extras_array = NULL, *data, **headers; - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cookie_name, &cookie_len)) { + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la", &flags, &allowed_extras_array)) { RETURN_FALSE; } - array_init(return_value); - data = GET_PROP(responseData); if ( (Z_TYPE_P(data) == IS_ARRAY) && (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) && (Z_TYPE_PP(headers) == IS_ARRAY)) { + int i = 0; ulong idx = 0; - char *key = NULL; - zval **header = NULL; - HashPosition pos1; - - convert_to_array(*headers); + char *key = NULL, **allowed_extras = NULL; + zval **header = NULL, **entry = NULL; + HashPosition pos, pos1, pos2; + + array_init(return_value); + + if (allowed_extras_array) { + allowed_extras = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(allowed_extras_array)) + 1, sizeof(char *)); + FOREACH_VAL(pos, allowed_extras_array, entry) { + ZVAL_ADDREF(*entry); + convert_to_string_ex(entry); + allowed_extras[i++] = estrndup(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry)); + zval_ptr_dtor(entry); + } + } + FOREACH_HASH_KEYVAL(pos1, Z_ARRVAL_PP(headers), key, idx, header) { if (key && !strcasecmp(key, "Set-Cookie")) { - /* several cookies? */ + http_cookie_list list; + if (Z_TYPE_PP(header) == IS_ARRAY) { - zval **cookie; - HashPosition pos2; - - FOREACH_HASH_VAL(pos2, Z_ARRVAL_PP(header), cookie) { - zval *cookie_hash; - MAKE_STD_ZVAL(cookie_hash); - array_init(cookie_hash); - - if (SUCCESS == http_parse_cookie(Z_STRVAL_PP(cookie), Z_ARRVAL_P(cookie_hash))) { - if (!cookie_len) { - add_next_index_zval(return_value, cookie_hash); - } else { - zval **name; - - if ( (SUCCESS == zend_hash_find(Z_ARRVAL_P(cookie_hash), "name", sizeof("name"), (void **) &name)) && - (!strcmp(Z_STRVAL_PP(name), cookie_name))) { - add_next_index_zval(return_value, cookie_hash); - return; /* <<< FOUND >>> */ - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); - } - } - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); + zval **single_header; + + FOREACH_VAL(pos2, *header, single_header) { + ZVAL_ADDREF(*single_header); + convert_to_string_ex(single_header); + if (http_parse_cookie_ex(&list, Z_STRVAL_PP(single_header), flags, allowed_extras)) { + zval *cookie; + + MAKE_STD_ZVAL(cookie); + object_init(cookie); + http_cookie_list_tostruct(&list, cookie); + add_next_index_zval(return_value, cookie); + http_cookie_list_dtor(&list); } + zval_ptr_dtor(single_header); } } else { - zval *cookie_hash; - - MAKE_STD_ZVAL(cookie_hash); - array_init(cookie_hash); + ZVAL_ADDREF(*header); convert_to_string_ex(header); - - if (SUCCESS == http_parse_cookie(Z_STRVAL_PP(header), Z_ARRVAL_P(cookie_hash))) { - if (!cookie_len) { - add_next_index_zval(return_value, cookie_hash); - } else { - zval **name; - - if ( (SUCCESS == zend_hash_find(Z_ARRVAL_P(cookie_hash), "name", sizeof("name"), (void **) &name)) && - (!strcmp(Z_STRVAL_PP(name), cookie_name))) { - add_next_index_zval(return_value, cookie_hash); - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); - } - } - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); + if (http_parse_cookie_ex(&list, Z_STRVAL_PP(header), flags, allowed_extras)) { + zval *cookie; + + MAKE_STD_ZVAL(cookie); + object_init(cookie); + http_cookie_list_tostruct(&list, cookie); + add_next_index_zval(return_value, cookie); + http_cookie_list_dtor(&list); } + zval_ptr_dtor(header); } - break; } /* reset key */ key = NULL; } + + if (allowed_extras) { + for (i = 0; allowed_extras[i]; ++i) { + efree(allowed_extras[i]); + } + efree(allowed_extras); + } + } else { + RETURN_FALSE; } } } @@ -1817,8 +1877,8 @@ PHP_METHOD(HttpRequest, getRawResponseMessage) * * Get all sent requests and received responses as an HttpMessage object. * - * If you don't want to record history at all, set the instance variable - * HttpRequest::$recordHistory to FALSE. + * If you want to record history, set the instance variable + * HttpRequest::$recordHistory to TRUE. * * Returns an HttpMessage object representing the complete request/response * history. @@ -1826,23 +1886,21 @@ PHP_METHOD(HttpRequest, getRawResponseMessage) * The object references the last received response, use HttpMessage::getParentMessage() * to access the data of previously sent requests and received responses. * - * Note that the internal history is immutable, that means that any changes - * you make the the message list won't affect a history message list newly - * created by another call to HttpRequest::getHistory(). - * - * Throws HttpMalformedHeaderException, HttpEncodingException. + * Throws HttpRuntimeException. */ PHP_METHOD(HttpRequest, getHistory) { NO_ARGS; IF_RETVAL_USED { - http_message *msg; - getObject(http_request_object, obj); - + zval *hist; + SET_EH_THROW_HTTP(); - if ((msg = http_message_parse(PHPSTR_VAL(&obj->history), PHPSTR_LEN(&obj->history)))) { - RETVAL_OBJVAL(http_message_object_new_ex(http_message_object_ce, msg, NULL), 0); + hist = GET_PROP(history); + if (Z_TYPE_P(hist) == IS_OBJECT) { + RETVAL_OBJECT(hist, 1); + } else { + http_error(HE_WARNING, HTTP_E_RUNTIME, "The history is empty"); } SET_EH_NORMAL(); } @@ -1856,8 +1914,12 @@ PHP_METHOD(HttpRequest, getHistory) PHP_METHOD(HttpRequest, clearHistory) { NO_ARGS { - getObject(http_request_object, obj); - phpstr_dtor(&obj->history); + zval *hist; + + MAKE_STD_ZVAL(hist); + ZVAL_NULL(hist); + SET_PROP(history, hist); + zval_ptr_dtor(&hist); } } /* }}} */