From b562e34cf4f8c8fae7b8fe773e0eed71592b09c2 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 12 Sep 2005 11:55:48 +0000 Subject: [PATCH] - add missing support for raw post data (HttpRequest) - use Z_STRLEN in update property - add a tiny tutorial with examples --- KnownIssues.txt | 3 +- docs/examples/Bandwidth_Throttling.php | 9 + docs/examples/Cached_Responses.php | 8 + docs/examples/GET_Queries.php | 23 ++ docs/examples/KISS_XMLRPC_Client.php | 43 +++ docs/examples/Multipart_Posts.php | 26 ++ docs/examples/Parallel_Requests.php | 22 ++ docs/examples/Parallel_Requests_.php | 32 +++ docs/examples/Simple_Feed_Aggregator.php | 115 ++++++++ docs/examples/extract.php | 9 + docs/examples/tutorial.txt | 342 +++++++++++++++++++++++ http_request_object.c | 142 +++++++++- http_response_object.c | 16 +- php_http.h | 2 +- php_http_request_object.h | 3 + php_http_std_defs.h | 2 + 16 files changed, 778 insertions(+), 19 deletions(-) create mode 100644 docs/examples/Bandwidth_Throttling.php create mode 100644 docs/examples/Cached_Responses.php create mode 100644 docs/examples/GET_Queries.php create mode 100644 docs/examples/KISS_XMLRPC_Client.php create mode 100644 docs/examples/Multipart_Posts.php create mode 100644 docs/examples/Parallel_Requests.php create mode 100644 docs/examples/Parallel_Requests_.php create mode 100644 docs/examples/Simple_Feed_Aggregator.php create mode 100644 docs/examples/extract.php create mode 100644 docs/examples/tutorial.txt diff --git a/KnownIssues.txt b/KnownIssues.txt index c0cb89f..ed19198 100644 --- a/KnownIssues.txt +++ b/KnownIssues.txt @@ -10,4 +10,5 @@ Not all places where files are handled check for open_basedir and/or safe_mode. Internals: - the request bodies created in http_request_pool_attach() are not destroyed in http_request_pool_detach(); may be a memory problem - in long running scripts \ No newline at end of file + in long running scripts + - protected class members are not type safe diff --git a/docs/examples/Bandwidth_Throttling.php b/docs/examples/Bandwidth_Throttling.php new file mode 100644 index 0000000..981272c --- /dev/null +++ b/docs/examples/Bandwidth_Throttling.php @@ -0,0 +1,9 @@ + diff --git a/docs/examples/Cached_Responses.php b/docs/examples/Cached_Responses.php new file mode 100644 index 0000000..8ac2bcf --- /dev/null +++ b/docs/examples/Cached_Responses.php @@ -0,0 +1,8 @@ + diff --git a/docs/examples/GET_Queries.php b/docs/examples/GET_Queries.php new file mode 100644 index 0000000..cb21cb6 --- /dev/null +++ b/docs/examples/GET_Queries.php @@ -0,0 +1,23 @@ +setOptions( + array( 'cookiestore' => '../cookies/google.txt', + ) +); + +$r->setQueryData( + array( 'q' => '+"pecl_http" -msg -cvs -list', + 'hl' => 'de' + ) +); + +// HttpRequest::send() returns an HttpMessage object +// of type HttpMessage::RESPONSE or throws an exception +try { + print $r->send()->getBody(); +} catch (HttpException $e) { + print $e; +} +?> diff --git a/docs/examples/KISS_XMLRPC_Client.php b/docs/examples/KISS_XMLRPC_Client.php new file mode 100644 index 0000000..35380db --- /dev/null +++ b/docs/examples/KISS_XMLRPC_Client.php @@ -0,0 +1,43 @@ +namespace = $namespace; + $this->request = new HttpRequest($url, HTTP_POST); + $this->request->setContentType('text/xml'); + } + + public function setOptions($options = array()) + { + return $this->request->setOptions($options); + } + + public function addOptions($options) + { + return $this->request->addOptions($options); + } + + public function __call($method, $params) + { + if ($this->namespace) { + $method = $this->namespace .'.'. $method; + } + $this->request->setPostData(xmlrpc_encode_request($method, $params)); + $response = $this->request->send(); + if ($response->getResponseCode() != 200) { + throw new Exception($response->getBody(), $response->getResponseCode()); + } + return xmlrpc_decode($response->getBody(), 'utf-8'); + } + + public function getHistory() + { + return $this->request->getHistory(); + } +} + +?> diff --git a/docs/examples/Multipart_Posts.php b/docs/examples/Multipart_Posts.php new file mode 100644 index 0000000..eb9b171 --- /dev/null +++ b/docs/examples/Multipart_Posts.php @@ -0,0 +1,26 @@ +setOptions( + array( 'cookies' => array('MyCookie' => 'has a value'), + 'redirect' => true, + ) +); + +// common form data +$r->setPostFields( + array( 'name' => 'Mike', + 'mail' => 'mike@php.net', + ) +); +// add the file to post (form name, file name, file type) +$r->addPostFile('image', 'profile.jpg', 'image/jpeg'); + +try { + print $r->send()->getBody(); +} catch (HttpException $e) { + print $e; +} +?> diff --git a/docs/examples/Parallel_Requests.php b/docs/examples/Parallel_Requests.php new file mode 100644 index 0000000..962ea05 --- /dev/null +++ b/docs/examples/Parallel_Requests.php @@ -0,0 +1,22 @@ +attach(new HttpRequest('http://pear.php.net', HTTP_HEAD)); + $p->attach(new HttpRequest('http://pecl.php.net', HTTP_HEAD)); +} catch (HttpException $e) { + print $e; + exit; +} + +try { + $p->send(); + // HttpRequestPool implements an iterator over attached HttpRequest objects + foreach ($p as $r) { + print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n"; + } +} catch (HttpException $e) { + print $e; +} +?> diff --git a/docs/examples/Parallel_Requests_.php b/docs/examples/Parallel_Requests_.php new file mode 100644 index 0000000..e1eb88f --- /dev/null +++ b/docs/examples/Parallel_Requests_.php @@ -0,0 +1,32 @@ +socketPerform(); $i++) { + $i % 3 or print "."; + if (!$this->socketSelect()) { + throw new HttpException("Socket error!"); + } + } + print "\nDone!\n"; + } +} + +try { + foreach (new Pool as $r) { + print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n"; + } +} catch (HttpException $ex) { + print $e; +} +?> diff --git a/docs/examples/Simple_Feed_Aggregator.php b/docs/examples/Simple_Feed_Aggregator.php new file mode 100644 index 0000000..f82e316 --- /dev/null +++ b/docs/examples/Simple_Feed_Aggregator.php @@ -0,0 +1,115 @@ +setDirectory($directory); + } + + public function setDirectory($directory) + { + $this->directory = $directory; + foreach (glob($this->directory .'/*.xml') as $feed) { + $this->feeds[basename($feed, '.xml')] = filemtime($feed); + } + } + + public function url2name($url) + { + return preg_replace('/[^\w\.-]+/', '_', $url); + } + + public function hasFeed($url) + { + return isset($this->feeds[$this->url2name($url)]); + } + + public function addFeed($url) + { + $r = $this->setupRequest($url); + $r->send(); + $this->handleResponse($r); + } + + public function addFeeds($urls) + { + $pool = new HttpRequestPool; + foreach ($urls as $url) { + $pool->attach($this->setupRequest($url)); + } + $pool->send(); + + foreach ($pool as $request) { + $this->handleResponse($request); + } + } + + public function getFeed($url) + { + $this->addFeed($url); + return $this->loadFeed($this->url2name($url)); + } + + public function getFeeds($urls) + { + $feeds = array(); + $this->addFeeds($urls); + foreach ($urls as $url) { + $feeds[] = $this->loadFeed($this->url2name($url)); + } + return $feeds; + } + + protected function saveFeed($file, $contents) + { + if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) { + $this->feeds[$file] = time(); + } else { + throw new Exception("Could not save feed contents to $file.xml"); + } + } + + protected function loadFeed($file) + { + if (isset($this->feeds[$file]) { + if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) { + return $data; + } else { + throw new Exception("Could not load feed contents from $file.xml"); + } + } else { + throw new Exception("Unknown feed/file $file.xml"); + } + } + + protected function setupRequest($url) + { + $r = new HttpRequest($url); + $r->setOptions(array('redirect' => true)); + + $file = $this->url2name($url); + + if (isset($this->feeds[$file])) { + $r->addOptions(array('lastmodified' => $this->feeds[$file])); + } + + return $r; + } + + protected function handleResponse(HttpRequest $r) + { + if ($r->getResponseCode() != 304) { + if ($r->getResponseCode() != 200) { + throw new Exception("Unexpected response code ". $r->getResponseCode()); + } + if (!strlen($body = $r->getResponseBody())) { + throw new Exception("Received empty feed from ". $r->getUrl()); + } + $this->saveFeed($file, $body); + } + } +} +?> diff --git a/docs/examples/extract.php b/docs/examples/extract.php new file mode 100644 index 0000000..32aa173 --- /dev/null +++ b/docs/examples/extract.php @@ -0,0 +1,9 @@ +)/s', file_get_contents($_SERVER['argv'][1]), $matches)) { + for ($i = 0; $i < count($matches[0]); $i++) { + file_put_contents(preg_replace('/\W/', '_', $matches[1][$i]).".php", $matches[2][$i]."\n"); + } +} + +?> diff --git a/docs/examples/tutorial.txt b/docs/examples/tutorial.txt new file mode 100644 index 0000000..6dcae5a --- /dev/null +++ b/docs/examples/tutorial.txt @@ -0,0 +1,342 @@ + +A Beginners Tutorial +-------------------- +$Revision$ + + +- GET Queries + + The HttpRequest class can be used to execute any HTTP request method. + The following example shows a simple GET request where a few query + parameters are supplied. Additionally potential cookies will be + read from and written to a file. + +setOptions( + array( 'cookiestore' => '../cookies/google.txt', + ) +); + +$r->setQueryData( + array( 'q' => '+"pecl_http" -msg -cvs -list', + 'hl' => 'de' + ) +); + +// HttpRequest::send() returns an HttpMessage object +// of type HttpMessage::RESPONSE or throws an exception +try { + print $r->send()->getBody(); +} catch (HttpException $e) { + print $e; +} +?> + +- Multipart Posts + + The following example shows an multipart POST request, with two form + fields and an image that's supposed to be uploaded to the server. + It's a bad habit as well as common practice to issue a redirect after + an received POST request, so we'll allow a redirect by enabling the + redirect option. + +setOptions( + array( 'cookies' => array('MyCookie' => 'has a value'), + 'redirect' => true, + ) +); + +// common form data +$r->setPostFields( + array( 'name' => 'Mike', + 'mail' => 'mike@php.net', + ) +); +// add the file to post (form name, file name, file type) +$r->addPostFile('image', 'profile.jpg', 'image/jpeg'); + +try { + print $r->send()->getBody(); +} catch (HttpException $e) { + print $e; +} +?> + +- Parallel Requests + + It's possible to execute several HttpRequests in parallel with the + HttpRequestPool class. HttpRequests to send, do not need to perform + the same request method, but can only be attached to one HttpRequestPool + at the same time. + +attach(new HttpRequest('http://pear.php.net', HTTP_HEAD)); + $p->attach(new HttpRequest('http://pecl.php.net', HTTP_HEAD)); +} catch (HttpException $e) { + print $e; + exit; +} + +try { + $p->send(); + // HttpRequestPool implements an iterator over attached HttpRequest objects + foreach ($p as $r) { + print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n"; + } +} catch (HttpException $e) { + print $e; +} +?> + +- Parallel Requests? + + You can use a more advanced approach by using the protected interface of + the HttpRequestPool class. This allows you to perform some other tasks + while the requests are executed. + +socketPerform(); $i++) { + $i % 3 or print "."; + if (!$this->socketSelect()) { + throw new HttpException("Socket error!"); + } + } + print "\nDone!\n"; + } +} + +try { + foreach (new Pool as $r) { + print "Checking ", $r->getUrl(), " reported ", $r->getResponseCode(), "\n"; + } +} catch (HttpException $ex) { + print $e; +} +?> + +- Cached Responses + + One of the main key features of HttpResponse is HTTP caching. HttpResponse + will calculate an ETag based on the http.etag_mode INI setting as well as + it will determine the last modification time of the sent entity. It uses + those two indicators to decide if the cache entry on the client side is + still valid and will emit an "304 Not Modified" response if applicable. + + + +- Bandwidth Throttling + + HttpResponse supports a basic throttling mechanism, which is enabled by + setting a throttle delay and a buffer size. PHP will sleep the specified + amount of seconds after each sent chunk of specified bytes. + + + +Exemplar Use Cases +------------------ + +- KISS XMLRPC Client + +namespace = $namespace; + $this->request = new HttpRequest($url, HTTP_POST); + $this->request->setContentType('text/xml'); + } + + public function setOptions($options = array()) + { + return $this->request->setOptions($options); + } + + public function addOptions($options) + { + return $this->request->addOptions($options); + } + + public function __call($method, $params) + { + if ($this->namespace) { + $method = $this->namespace .'.'. $method; + } + $this->request->setPostData(xmlrpc_encode_request($method, $params)); + $response = $this->request->send(); + if ($response->getResponseCode() != 200) { + throw new Exception($response->getBody(), $response->getResponseCode()); + } + return xmlrpc_decode($response->getBody(), 'utf-8'); + } + + public function getHistory() + { + return $this->request->getHistory(); + } +} + +?> + +- Simple Feed Aggregator + +setDirectory($directory); + } + + public function setDirectory($directory) + { + $this->directory = $directory; + foreach (glob($this->directory .'/*.xml') as $feed) { + $this->feeds[basename($feed, '.xml')] = filemtime($feed); + } + } + + public function url2name($url) + { + return preg_replace('/[^\w\.-]+/', '_', $url); + } + + public function hasFeed($url) + { + return isset($this->feeds[$this->url2name($url)]); + } + + public function addFeed($url) + { + $r = $this->setupRequest($url); + $r->send(); + $this->handleResponse($r); + } + + public function addFeeds($urls) + { + $pool = new HttpRequestPool; + foreach ($urls as $url) { + $pool->attach($this->setupRequest($url)); + } + $pool->send(); + + foreach ($pool as $request) { + $this->handleResponse($request); + } + } + + public function getFeed($url) + { + $this->addFeed($url); + return $this->loadFeed($this->url2name($url)); + } + + public function getFeeds($urls) + { + $feeds = array(); + $this->addFeeds($urls); + foreach ($urls as $url) { + $feeds[] = $this->loadFeed($this->url2name($url)); + } + return $feeds; + } + + protected function saveFeed($file, $contents) + { + if (file_put_contents($this->directory .'/'. $file .'.xml', $contents)) { + $this->feeds[$file] = time(); + } else { + throw new Exception("Could not save feed contents to $file.xml"); + } + } + + protected function loadFeed($file) + { + if (isset($this->feeds[$file]) { + if ($data = file_get_contents($this->directory .'/'. $file .'.xml')) { + return $data; + } else { + throw new Exception("Could not load feed contents from $file.xml"); + } + } else { + throw new Exception("Unknown feed/file $file.xml"); + } + } + + protected function setupRequest($url) + { + $r = new HttpRequest($url); + $r->setOptions(array('redirect' => true)); + + $file = $this->url2name($url); + + if (isset($this->feeds[$file])) { + $r->addOptions(array('lastmodified' => $this->feeds[$file])); + } + + return $r; + } + + protected function handleResponse(HttpRequest $r) + { + if ($r->getResponseCode() != 304) { + if ($r->getResponseCode() != 200) { + throw new Exception("Unexpected response code ". $r->getResponseCode()); + } + if (!strlen($body = $r->getResponseBody())) { + throw new Exception("Received empty feed from ". $r->getUrl()); + } + $this->saveFeed($file, $body); + } + } +} +?> + + diff --git a/http_request_object.c b/http_request_object.c index 1ed0e2c..6527cce 100644 --- a/http_request_object.c +++ b/http_request_object.c @@ -126,6 +126,15 @@ HTTP_BEGIN_ARGS(addPostFile, 0, 2) HTTP_ARG_VAL(content_type, 0) HTTP_END_ARGS; +HTTP_EMPTY_ARGS(getRawPostData, 0); +HTTP_BEGIN_ARGS(setRawPostData, 0, 0) + HTTP_ARG_VAL(raw_post_data, 0) +HTTP_END_ARGS; + +HTTP_BEGIN_ARGS(addRawPostData, 0, 1) + HTTP_ARG_VAL(raw_post_data, 0) +HTTP_END_ARGS; + HTTP_EMPTY_ARGS(getPutFile, 0); HTTP_BEGIN_ARGS(setPutFile, 0, 0) HTTP_ARG_VAL(filename, 0) @@ -244,6 +253,10 @@ zend_function_entry http_request_object_fe[] = { HTTP_REQUEST_ME(setPostFields, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getPostFields, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(addPostFields, ZEND_ACC_PUBLIC) + + HTTP_REQUEST_ME(setRawPostData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getRawPostData, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(addRawPostData, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(setPostFiles, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(addPostFile, ZEND_ACC_PUBLIC) @@ -364,6 +377,7 @@ static inline void _http_request_object_declare_default_properties(TSRMLS_D) DCL_PROP(PROTECTED, string, url, ""); DCL_PROP(PROTECTED, string, contentType, ""); + DCL_PROP(PROTECTED, string, rawPostData, ""); DCL_PROP(PROTECTED, string, queryData, ""); DCL_PROP(PROTECTED, string, putFile, ""); @@ -438,7 +452,40 @@ STATUS _http_request_object_requesthandler(http_request_object *obj, zval *this_ case HTTP_POST: default: - status = http_request_body_fill(body, Z_ARRVAL_P(GET_PROP(obj, postFields)), Z_ARRVAL_P(GET_PROP(obj, postFiles))); + { + /* check for raw post data */ + zval *raw_data = GET_PROP(obj, rawPostData); + + if (Z_STRLEN_P(raw_data)) { + zval *ctype = GET_PROP(obj, contentType); + + if (Z_STRLEN_P(ctype)) { + zval **headers, *opts = GET_PROP(obj, options); + + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(opts), "headers", sizeof("headers"), (void **) &headers)) { + 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 { + zval *headers; + + MAKE_STD_ZVAL(headers); + array_init(headers); + add_assoc_stringl(headers, "Content-Type", Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); + add_assoc_zval(opts, "headers", headers); + } + } + + body->type = HTTP_REQUEST_BODY_CSTRING; + body->data = estrndup(Z_STRVAL_P(raw_data), Z_STRLEN_P(raw_data)); + body->size = Z_STRLEN_P(raw_data); + } else { + status = http_request_body_fill(body, Z_ARRVAL_P(GET_PROP(obj, postFields)), Z_ARRVAL_P(GET_PROP(obj, postFiles))); + } + } break; } @@ -614,7 +661,7 @@ PHP_METHOD(HttpRequest, __construct) INIT_PARR(obj, postFiles); if (URL) { - UPD_PROP(obj, string, url, URL); + UPD_STRL(obj, url, URL, URL_len); } if (meth > -1) { UPD_PROP(obj, long, method, meth); @@ -689,7 +736,7 @@ PHP_METHOD(HttpRequest, setOptions) if (Z_TYPE_PP(opt) != IS_STRING) { convert_to_string_ex(opt); } - UPD_PROP(obj, string, url, Z_STRVAL_PP(opt)); + UPD_STRL(obj, url, Z_STRVAL_PP(opt), Z_STRLEN_PP(opt)); continue; } else if (!strcmp(key, "method")) { if (Z_TYPE_PP(opt) != IS_LONG) { @@ -833,7 +880,7 @@ PHP_METHOD(HttpRequest, setUrl) RETURN_FALSE; } - UPD_PROP(obj, string, url, URL); + UPD_STRL(obj, url, URL, URL_len); RETURN_TRUE; } /* }}} */ @@ -913,7 +960,7 @@ PHP_METHOD(HttpRequest, setContentType) RETURN_FALSE; } - UPD_PROP(obj, string, contentType, ctype); + UPD_STRL(obj, contentType, ctype, ct_len); RETURN_TRUE; } /* }}} */ @@ -952,7 +999,7 @@ PHP_METHOD(HttpRequest, setQueryData) } if ((!qdata) || Z_TYPE_P(qdata) == IS_NULL) { - UPD_PROP(obj, string, queryData, ""); + UPD_STRL(obj, queryData, "", 0); } else if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) { char *query_data = NULL; @@ -964,7 +1011,7 @@ PHP_METHOD(HttpRequest, setQueryData) efree(query_data); } else { convert_to_string(qdata); - UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata)); + UPD_STRL(obj, queryData, Z_STRVAL_P(qdata), Z_STRLEN_P(qdata)); } RETURN_TRUE; } @@ -997,6 +1044,7 @@ PHP_METHOD(HttpRequest, addQueryData) { zval *qdata, *old_qdata; char *query_data = NULL; + size_t query_data_len = 0; getObject(http_request_object, obj); if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) { @@ -1005,11 +1053,11 @@ PHP_METHOD(HttpRequest, addQueryData) old_qdata = GET_PROP(obj, queryData); - if (SUCCESS != http_urlencode_hash_ex(HASH_OF(qdata), 1, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata), &query_data, NULL)) { + if (SUCCESS != http_urlencode_hash_ex(HASH_OF(qdata), 1, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata), &query_data, &query_data_len)) { RETURN_FALSE; } - UPD_PROP(obj, string, queryData, query_data); + UPD_STRL(obj, queryData, query_data, query_data_len); efree(query_data); RETURN_TRUE; @@ -1082,6 +1130,80 @@ PHP_METHOD(HttpRequest, getPostFields) } /* }}} */ +/* {{{ proto bool HttpRequest::setRawPostData([string raw_post_data]) + * + * Set raw post data to send. Don't forget to specify a content type. + * Affects only POST requests. + */ +PHP_METHOD(HttpRequest, setRawPostData) +{ + char *raw_data = NULL; + int data_len = 0; + getObject(http_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &raw_data, &data_len)) { + RETURN_FALSE; + } + + if (!raw_data) { + raw_data = ""; + } + + UPD_STRL(obj, rawPostData, raw_data, data_len); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HttpRequest::addRawPostData(string raw_post_data) + * + * Add raw post data. + * Affects only POST requests. + */ +PHP_METHOD(HttpRequest, addRawPostData) +{ + char *raw_data, *new_data; + int data_len; + getObject(http_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &raw_data, &data_len)) { + RETURN_FALSE; + } + + if (data_len) { + zval *zdata = GET_PROP(obj, rawPostData); + + new_data = emalloc(Z_STRLEN_P(zdata) + data_len + 1); + new_data[Z_STRLEN_P(zdata) + data_len] = '\0'; + + if (Z_STRLEN_P(zdata)) { + memcpy(new_data, Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)); + } + + memcpy(new_data + Z_STRLEN_P(zdata), raw_data, data_len); + UPD_STRL(obj, rawPostData, new_data, Z_STRLEN_P(zdata) + data_len); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HttpRequest::getRawPostData() + * + * Get previously set raw post data. + */ +PHP_METHOD(HttpRequest, getRawPostData) +{ + NO_ARGS; + + IF_RETVAL_USED { + getObject(http_request_object, obj); + zval *raw_data = GET_PROP(obj, rawPostData); + + RETURN_STRINGL(Z_STRVAL_P(raw_data), Z_STRLEN_P(raw_data), 1); + } +} +/* }}} */ + /* {{{ proto bool HttpRequest::addPostFile(string name, string file[, string content_type = "application/x-octetstream"]) * * Add a file to the POST request. @@ -1183,7 +1305,7 @@ PHP_METHOD(HttpRequest, setPutFile) RETURN_FALSE; } - UPD_PROP(obj, string, putFile, file); + UPD_STRL(obj, putFile, file, file_len); RETURN_TRUE; } /* }}} */ diff --git a/http_response_object.c b/http_response_object.c index 84e979c..68de2a4 100644 --- a/http_response_object.c +++ b/http_response_object.c @@ -42,6 +42,7 @@ ZEND_EXTERN_MODULE_GLOBALS(http); #define GET_STATIC_PROP(n) *GET_STATIC_PROP_EX(http_response_object_ce, n) #define UPD_STATIC_PROP(t, n, v) UPD_STATIC_PROP_EX(http_response_object_ce, t, n, v) #define SET_STATIC_PROP(n, v) SET_STATIC_PROP_EX(http_response_object_ce, n, v) +#define UPD_STATIC_STRL(n, v, l) UPD_STATIC_STRL_EX(http_response_object_ce, n, v, l) #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpResponse, method, 0, req_args) #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpResponse, method, ret_ref) @@ -388,8 +389,8 @@ PHP_METHOD(HttpResponse, setCacheControl) http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol); RETURN_FALSE; } else { - spprintf(&cctl, 0, "%s, must-revalidate, max_age=%ld", ccontrol, max_age); - RETVAL_SUCCESS(UPD_STATIC_PROP(string, cacheControl, cctl)); + size_t cctl_len = spprintf(&cctl, 0, "%s, must-revalidate, max_age=%ld", ccontrol, max_age); + RETVAL_SUCCESS(UPD_STATIC_STRL(cacheControl, cctl, cctl_len)); efree(cctl); } } @@ -428,7 +429,7 @@ PHP_METHOD(HttpResponse, setContentType) RETURN_FALSE; } - RETURN_SUCCESS(UPD_STATIC_PROP(string, contentType, ctype)); + RETURN_SUCCESS(UPD_STATIC_STRL(contentType, ctype, ctype_len)); } /* }}} */ @@ -503,14 +504,15 @@ PHP_METHOD(HttpResponse, setContentDisposition) { char *file, *cd; int file_len; + size_t cd_len; zend_bool send_inline = 0; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) { RETURN_FALSE; } - spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file); - RETVAL_SUCCESS(UPD_STATIC_PROP(string, contentDisposition, cd)); + cd_len = spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file); + RETVAL_SUCCESS(UPD_STATIC_STRL(contentDisposition, cd, cd_len)); efree(cd); } /* }}} */ @@ -543,7 +545,7 @@ PHP_METHOD(HttpResponse, setETag) RETURN_FALSE; } - RETURN_SUCCESS(UPD_STATIC_PROP(string, eTag, etag)); + RETURN_SUCCESS(UPD_STATIC_STRL(eTag, etag, etag_len)); } /* }}} */ @@ -764,7 +766,7 @@ PHP_METHOD(HttpResponse, setFile) RETURN_FALSE; } - if ( (SUCCESS != UPD_STATIC_PROP(string, file, the_file)) || + if ( (SUCCESS != UPD_STATIC_STRL(file, the_file, file_len)) || (SUCCESS != UPD_STATIC_PROP(long, mode, -1))) { RETURN_FALSE; } diff --git a/php_http.h b/php_http.h index 7dd931f..45e3b24 100644 --- a/php_http.h +++ b/php_http.h @@ -18,7 +18,7 @@ #ifndef PHP_EXT_HTTP_H #define PHP_EXT_HTTP_H -#define HTTP_PEXT_VERSION "0.13.0" +#define HTTP_PEXT_VERSION "0.14.0dev" /* make compile on Win32 */ #ifdef HTTP_HAVE_CURL diff --git a/php_http_request_object.h b/php_http_request_object.h index 11f78c5..6be6718 100644 --- a/php_http_request_object.h +++ b/php_http_request_object.h @@ -78,6 +78,9 @@ PHP_METHOD(HttpRequest, addQueryData); PHP_METHOD(HttpRequest, setPostFields); PHP_METHOD(HttpRequest, getPostFields); PHP_METHOD(HttpRequest, addPostFields); +PHP_METHOD(HttpRequest, getRawPostData); +PHP_METHOD(HttpRequest, setRawPostData); +PHP_METHOD(HttpRequest, addRawPostData); PHP_METHOD(HttpRequest, addPostFile); PHP_METHOD(HttpRequest, setPostFiles); PHP_METHOD(HttpRequest, getPostFiles); diff --git a/php_http_std_defs.h b/php_http_std_defs.h index f166fe5..74db7d4 100644 --- a/php_http_std_defs.h +++ b/php_http_std_defs.h @@ -223,6 +223,7 @@ typedef int STATUS; # define DCL_STATIC_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n)-1, (ZEND_ACC_ ##a | ZEND_ACC_STATIC) TSRMLS_CC) # define GET_STATIC_PROP_EX(ce, n) zend_std_get_static_property(ce, (#n), sizeof(#n)-1, 0 TSRMLS_CC) # define UPD_STATIC_PROP_EX(ce, t, n, v) zend_update_static_property_ ##t(ce, #n, sizeof(#n)-1, (v) TSRMLS_CC) +# define UPD_STATIC_STRL_EX(ce, n, v, l) zend_update_static_property_stringl(ce, #n, sizeof(#n)-1, (v), (l) TSRMLS_CC) # define SET_STATIC_PROP_EX(ce, n, v) zend_update_static_property(ce, #n, sizeof(#n)-1, v TSRMLS_CC) # define DCL_PROP(a, t, n, v) zend_declare_property_ ##t(ce, (#n), sizeof(#n)-1, (v), (ZEND_ACC_ ##a) TSRMLS_CC) @@ -230,6 +231,7 @@ typedef int STATUS; # define DCL_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n)-1, (ZEND_ACC_ ##a) TSRMLS_CC) # define UPD_PROP(o, t, n, v) UPD_PROP_EX(o, getThis(), t, n, v) # define UPD_PROP_EX(o, this, t, n, v) zend_update_property_ ##t(o->zo.ce, this, (#n), sizeof(#n)-1, (v) TSRMLS_CC) +# define UPD_STRL(o, n, v, l) zend_update_property_stringl(o->zo.ce, getThis(), (#n), sizeof(#n)-1, (v), (l) TSRMLS_CC) # define SET_PROP(o, n, z) SET_PROP_EX(o, getThis(), n, z) # define SET_PROP_EX(o, this, n, z) zend_update_property(o->zo.ce, this, (#n), sizeof(#n)-1, (z) TSRMLS_CC) # define GET_PROP(o, n) GET_PROP_EX(o, getThis(), n) -- 2.30.2