X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=http_request_object.c;h=c5fd2936b76688d74f0e1ed7e3e3bd2b969b8e88;hb=64616c6675baf9dd168a7e2763e3124db0219921;hp=40b9dd3451bb6cb6366674cbc300c0e7f7670ab6;hpb=74b9f91575137bb94dfccb54bd5fd6a17d077c43;p=m6w6%2Fext-http diff --git a/http_request_object.c b/http_request_object.c index 40b9dd3..c5fd293 100644 --- a/http_request_object.c +++ b/http_request_object.c @@ -130,6 +130,15 @@ HTTP_BEGIN_ARGS(setPutFile, 0) HTTP_ARG_VAL(filename, 0) HTTP_END_ARGS; +HTTP_EMPTY_ARGS(getPutData); +HTTP_BEGIN_ARGS(setPutData, 0) + HTTP_ARG_VAL(put_data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addPutData, 1) + HTTP_ARG_VAL(put_data, 0) +HTTP_END_ARGS; + HTTP_EMPTY_ARGS(getResponseData); HTTP_BEGIN_ARGS(getResponseHeader, 0) HTTP_ARG_VAL(name, 0) @@ -261,6 +270,10 @@ zend_function_entry http_request_object_fe[] = { HTTP_REQUEST_ME(setPutFile, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getPutFile, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(setPutData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getPutData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addPutData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(send, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseData, ZEND_ACC_PUBLIC) @@ -313,7 +326,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; @@ -343,7 +355,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); @@ -368,7 +379,8 @@ 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(PRIVATE, string, putData, ""); + DCL_PROP_N(PRIVATE, history); DCL_PROP(PUBLIC, bool, recordHistory, 0); #ifndef WONKY @@ -431,10 +443,55 @@ 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); } +#define http_request_object_check_request_content_type(t) _http_request_object_check_request_content_type((t) TSRMLS_CC) +static inline void _http_request_object_check_request_content_type(zval *this_ptr TSRMLS_DC) +{ + zval *ctype = GET_PROP(contentType); + + if (Z_STRLEN_P(ctype)) { + zval **headers, *opts = GET_PROP(options); + + if ( (Z_TYPE_P(opts) == IS_ARRAY) && + (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), "headers", sizeof("headers"), (void **) &headers)) && + (Z_TYPE_PP(headers) == IS_ARRAY)) { + zval **ct_header; + + /* only override if not already set */ + if ((SUCCESS != zend_hash_find(Z_ARRVAL_PP(headers), "Content-Type", sizeof("Content-Type"), (void **) &ct_header))) { + add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + } else + /* or not a string, zero length string or a string of spaces */ + if ((Z_TYPE_PP(ct_header) != IS_STRING) || !Z_STRLEN_PP(ct_header)) { + add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + } else { + int i, only_space = 1; + + /* check for spaces only */ + for (i = 0; i < Z_STRLEN_PP(ct_header); ++i) { + if (!isspace(Z_STRVAL_PP(ct_header)[i])) { + only_space = 0; + break; + } + } + if (only_space) { + add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + } + } + } else { + zval *headers; + + MAKE_STD_ZVAL(headers); + array_init(headers); + add_assoc_stringl(headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addheaders", NULL, headers); + zval_ptr_dtor(&headers); + } + } +} + STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ptr TSRMLS_DC) { STATUS status = SUCCESS; @@ -452,13 +509,21 @@ STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ case HTTP_PUT: { - php_stream_statbuf ssb; - php_stream *stream = php_stream_open_wrapper(Z_STRVAL_P(GET_PROP(putFile)), "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); + zval *put_data = GET_PROP(putData); - if (stream && !php_stream_stat(stream, &ssb)) { - obj->request->body = http_request_body_init_ex(obj->request->body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 1); + http_request_object_check_request_content_type(getThis()); + if (Z_STRLEN_P(put_data)) { + obj->request->body = http_request_body_init_ex(obj->request->body, HTTP_REQUEST_BODY_CSTRING, + estrndup(Z_STRVAL_P(put_data), Z_STRLEN_P(put_data)), Z_STRLEN_P(put_data), 1); } else { - status = FAILURE; + php_stream_statbuf ssb; + php_stream *stream = php_stream_open_wrapper_ex(Z_STRVAL_P(GET_PROP(putFile)), "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL, HTTP_DEFAULT_STREAM_CONTEXT); + + if (stream && !php_stream_stat(stream, &ssb)) { + obj->request->body = http_request_body_init_ex(obj->request->body, HTTP_REQUEST_BODY_UPLOADFILE, stream, ssb.sb.st_size, 1); + } else { + status = FAILURE; + } } } break; @@ -470,34 +535,9 @@ STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ zval *raw_data = GET_PROP(rawPostData); if (Z_STRLEN_P(raw_data)) { - zval *ctype = GET_PROP(contentType); - - if (Z_STRLEN_P(ctype)) { - zval **headers, *opts = GET_PROP(options); - - if ( (Z_TYPE_P(opts) == IS_ARRAY) && - (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), "headers", sizeof("headers"), (void **) &headers)) && - (Z_TYPE_PP(headers) == IS_ARRAY)) { - zval **ct_header; - - /* only override if not already set */ - if ((SUCCESS != zend_hash_find(Z_ARRVAL_PP(headers), "Content-Type", sizeof("Content-Type"), (void **) &ct_header)) && (Z_TYPE_PP(ct_header) == IS_STRING)) { - add_assoc_stringl(*headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); - } - } else { - zval *headers; - - MAKE_STD_ZVAL(headers); - array_init(headers); - add_assoc_stringl(headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); - zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "addheaders", NULL, headers); - zval_ptr_dtor(&headers); - } - } - + http_request_object_check_request_content_type(getThis()); obj->request->body = http_request_body_init_ex(obj->request->body, HTTP_REQUEST_BODY_CSTRING, estrndup(Z_STRVAL_P(raw_data), Z_STRLEN_P(raw_data)), Z_STRLEN_P(raw_data), 1); - } else { zval *zfields = GET_PROP(postFields), *zfiles = GET_PROP(postFiles); HashTable *fields; @@ -554,44 +594,38 @@ 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); + zval *hist, *history = GET_PROP(history); + http_message *response = http_message_parse(PHPSTR_VAL(&obj->request->conv.response), PHPSTR_LEN(&obj->request->conv.response)); + http_message *request = http_message_parse(PHPSTR_VAL(&obj->request->conv.request), PHPSTR_LEN(&obj->request->conv.request)); + + MAKE_STD_ZVAL(hist); + ZVAL_OBJVAL(hist, http_message_object_new_ex(http_message_object_ce, http_message_interconnect(response, request), NULL), 0); + if (Z_TYPE_P(history) == IS_OBJECT) { + http_message_object_prepend(hist, history); + } + SET_PROP(history, hist); + zval_ptr_dtor(&hist); } UPD_PROP(long, responseCode, msg->http.info.response.code); @@ -613,25 +647,68 @@ 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; + + MAKE_STD_ZVAL(znull); + ZVAL_NULL(znull); + SET_PROP(responseMessage, znull); + zval_ptr_dtor(&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); + if (Z_TYPE_P(resp) == IS_ARRAY) { + zend_hash_clean(Z_ARRVAL_P(resp)); } - return SUCCESS; + 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); + } + } + + 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; +} + +static int apply_pretty_key(void *pDest, int num_args, va_list args, zend_hash_key *hash_key) +{ + if (hash_key->nKeyLength > 1) { + hash_key->h = zend_get_hash_value(pretty_key(hash_key->arKey, hash_key->nKeyLength - 1, 1, 0), hash_key->nKeyLength); } + return ZEND_HASH_APPLY_KEEP; } -#define http_request_object_set_options_subr(key, ow) \ - _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, (key), sizeof(key), (ow)) -static inline void _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAMETERS, char *key, size_t len, int overwrite) +#define http_request_object_set_options_subr(key, ow, pk) \ + _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAM_PASSTHRU, (key), sizeof(key), (ow), (pk)) +static inline void _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAMETERS, char *key, size_t len, int overwrite, int prettify_keys) { - zval *old_opts, *new_opts, *opts = NULL, **entry; + zval *old_opts, *new_opts, *opts = NULL, **entry = NULL; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &opts)) { RETURN_FALSE; @@ -644,6 +721,9 @@ static inline void _http_request_object_set_options_subr(INTERNAL_FUNCTION_PARAM array_copy(old_opts, new_opts); } + if (prettify_keys && opts) { + zend_hash_apply_with_arguments(Z_ARRVAL_P(opts), apply_pretty_key, 0); + } if (SUCCESS == zend_hash_find(Z_ARRVAL_P(new_opts), key, len, (void **) &entry)) { if (overwrite) { zend_hash_clean(Z_ARRVAL_PP(entry)); @@ -816,7 +896,7 @@ PHP_METHOD(HttpRequest, getOptions) */ PHP_METHOD(HttpRequest, setSslOptions) { - http_request_object_set_options_subr("ssl", 1); + http_request_object_set_options_subr("ssl", 1, 0); } /* }}} */ @@ -830,7 +910,7 @@ PHP_METHOD(HttpRequest, setSslOptions) */ PHP_METHOD(HttpRequest, addSslOptions) { - http_request_object_set_options_subr("ssl", 0); + http_request_object_set_options_subr("ssl", 0, 0); } /* }}} */ @@ -857,7 +937,7 @@ PHP_METHOD(HttpRequest, getSslOptions) */ PHP_METHOD(HttpRequest, addHeaders) { - http_request_object_set_options_subr("headers", 0); + http_request_object_set_options_subr("headers", 0, 1); } /* {{{ proto bool HttpRequest::setHeaders([array headers]) @@ -871,7 +951,7 @@ PHP_METHOD(HttpRequest, addHeaders) */ PHP_METHOD(HttpRequest, setHeaders) { - http_request_object_set_options_subr("headers", 1); + http_request_object_set_options_subr("headers", 1, 1); } /* }}} */ @@ -898,7 +978,7 @@ PHP_METHOD(HttpRequest, getHeaders) */ PHP_METHOD(HttpRequest, setCookies) { - http_request_object_set_options_subr("cookies", 1); + http_request_object_set_options_subr("cookies", 1, 0); } /* }}} */ @@ -913,7 +993,7 @@ PHP_METHOD(HttpRequest, setCookies) */ PHP_METHOD(HttpRequest, addCookies) { - http_request_object_set_options_subr("cookies", 0); + http_request_object_set_options_subr("cookies", 0, 0); } /* }}} */ @@ -1024,7 +1104,9 @@ PHP_METHOD(HttpRequest, setContentType) RETURN_FALSE; } - HTTP_CHECK_CONTENT_TYPE(ctype, RETURN_FALSE); + if (ct_len) { + HTTP_CHECK_CONTENT_TYPE(ctype, RETURN_FALSE); + } UPD_STRL(contentType, ctype, ct_len); RETURN_TRUE; } @@ -1266,13 +1348,15 @@ PHP_METHOD(HttpRequest, addRawPostData) } if (data_len) { - zval *data = zval_copy(IS_STRING, GET_PROP(rawPostData)); + zval *data = GET_PROP(rawPostData); - Z_STRVAL_P(data) = erealloc(Z_STRVAL_P(data), (Z_STRLEN_P(data) += data_len) + 1); - Z_STRVAL_P(data)[Z_STRLEN_P(data)] = '\0'; - memcpy(Z_STRVAL_P(data) + Z_STRLEN_P(data) - data_len, raw_data, data_len); - SET_PROP(rawPostData, data); - zval_free(&data); + if (Z_STRLEN_P(data)) { + Z_STRVAL_P(data) = erealloc(Z_STRVAL_P(data), (Z_STRLEN_P(data) += data_len) + 1); + Z_STRVAL_P(data)[Z_STRLEN_P(data)] = '\0'; + memcpy(Z_STRVAL_P(data) + Z_STRLEN_P(data) - data_len, raw_data, data_len); + } else { + UPD_STRL(putData, raw_data, data_len); + } } RETURN_TRUE; @@ -1432,6 +1516,86 @@ PHP_METHOD(HttpRequest, getPutFile) } /* }}} */ +/* {{{ proto bool HttpRequest::setPutData([string put_data]) + * + * Set PUT data to send, overwriting previously set PUT data. + * Affects only PUT requests. + * Only either PUT data or PUT file can be used for each request. + * PUT data has higher precedence and will be used even if a PUT + * file is set. + * + * Accepts a string as parameter containing the data to upload. + * + * Returns TRUE on success, or FALSE on failure. + */ +PHP_METHOD(HttpRequest, setPutData) +{ + char *put_data = NULL; + int data_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &put_data, &data_len)) { + RETURN_FALSE; + } + + if (!put_data) { + put_data = ""; + } + + UPD_STRL(putData, put_data, data_len); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addPutData(string put_data) + * + * Add PUT data, leaving previously set PUT data unchanged. + * Affects only PUT requests. + * + * Expects a string as parameter containing the data to concatenate. + * + * Returns TRUE on success, or FALSE on failure. + */ +PHP_METHOD(HttpRequest, addPutData) +{ + char *put_data; + int data_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &put_data, &data_len)) { + RETURN_FALSE; + } + + if (data_len) { + zval *data = GET_PROP(putData); + + if (Z_STRLEN_P(data)) { + Z_STRVAL_P(data) = erealloc(Z_STRVAL_P(data), (Z_STRLEN_P(data) += data_len) + 1); + Z_STRVAL_P(data)[Z_STRLEN_P(data)] = '\0'; + memcpy(Z_STRVAL_P(data) + Z_STRLEN_P(data) - data_len, put_data, data_len); + } else { + UPD_STRL(putData, put_data, data_len); + } + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getPutData() + * + * Get previously set PUT data. + * + * Returns a string containing the currently set raw post data. + */ +PHP_METHOD(HttpRequest, getPutData) +{ + NO_ARGS; + + IF_RETVAL_USED { + RETURN_PROP(putData); + } +} +/* }}} */ + /* {{{ proto array HttpRequest::getResponseData() * * Get all response data after the request has been sent. @@ -1536,14 +1700,12 @@ PHP_METHOD(HttpRequest, getResponseCookies) } } - convert_to_array(*headers); FOREACH_HASH_KEYVAL(pos1, Z_ARRVAL_PP(headers), key, idx, header) { if (key && !strcasecmp(key, "Set-Cookie")) { http_cookie_list list; if (Z_TYPE_PP(header) == IS_ARRAY) { zval **single_header; - HashPosition pos; FOREACH_VAL(pos2, *header, single_header) { ZVAL_ADDREF(*single_header); @@ -1809,8 +1971,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. @@ -1818,23 +1980,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(); } @@ -1848,8 +2008,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); } } /* }}} */