- add missing support for raw post data (HttpRequest)
authorMichael Wallner <mike@php.net>
Mon, 12 Sep 2005 11:55:48 +0000 (11:55 +0000)
committerMichael Wallner <mike@php.net>
Mon, 12 Sep 2005 11:55:48 +0000 (11:55 +0000)
- use Z_STRLEN in update property
- add a tiny tutorial with examples

16 files changed:
KnownIssues.txt
docs/examples/Bandwidth_Throttling.php [new file with mode: 0644]
docs/examples/Cached_Responses.php [new file with mode: 0644]
docs/examples/GET_Queries.php [new file with mode: 0644]
docs/examples/KISS_XMLRPC_Client.php [new file with mode: 0644]
docs/examples/Multipart_Posts.php [new file with mode: 0644]
docs/examples/Parallel_Requests.php [new file with mode: 0644]
docs/examples/Parallel_Requests_.php [new file with mode: 0644]
docs/examples/Simple_Feed_Aggregator.php [new file with mode: 0644]
docs/examples/extract.php [new file with mode: 0644]
docs/examples/tutorial.txt [new file with mode: 0644]
http_request_object.c
http_response_object.c
php_http.h
php_http_request_object.h
php_http_std_defs.h

index c0cb89f86fdc9062e43f303ef9238f6b6f13d5ff..ed19198e29b887ba34d61dfdee8e74b872d33692 100644 (file)
@@ -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 (file)
index 0000000..981272c
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+// send 5000 bytes every 0.2 seconds, i.e. max ~25kByte/s
+HttpResponse::setThrottleDelay(0.2);
+HttpResponse::setBufferSize(5000);
+HttpResponse::setCache(true);
+HttpResponse::setContentType('application/x-zip');
+HttpResponse::setFile('../archive.zip');
+HttpResponse::send();
+?>
diff --git a/docs/examples/Cached_Responses.php b/docs/examples/Cached_Responses.php
new file mode 100644 (file)
index 0000000..8ac2bcf
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+HttpResponse::setCacheControl('public');
+HttpResponse::setCache(true);
+HttpResponse::capture();
+
+print "This will be cached until content changes!\n";
+print "Note that this approach will only save the clients download time.\n";
+?>
diff --git a/docs/examples/GET_Queries.php b/docs/examples/GET_Queries.php
new file mode 100644 (file)
index 0000000..cb21cb6
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+$r = new HttpRequest('http://www.google.com');
+
+// store Googles cookies in a dedicated file
+$r->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 (file)
index 0000000..35380db
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+class XmlRpcClient
+{
+       public $namespace;
+       protected $request;
+
+       public function __construct($url, $namespace = '')
+       {
+               $this->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 (file)
index 0000000..eb9b171
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+$r = new HttpRequest('http://dev.iworks.at/.print_request.php', HTTP_POST);
+
+// if redirects is set to true, a single redirect is allowed;
+// one can set any reasonable count of allowed redirects
+$r->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 (file)
index 0000000..962ea05
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+try {
+       $p = new HttpRequestPool;
+       // if you want to set _any_ options of the HttpRequest object,
+       // you need to do so *prior attaching* to the request pool!
+       $p->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 (file)
index 0000000..e1eb88f
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+class Pool extends HttpRequestPool
+{
+       public function __construct()
+       {
+               parent::__construct(
+                       new HttpRequest('http://pear.php.net', HTTP_HEAD),
+                       new HttpRequest('http://pecl.php.net', HTTP_HEAD)
+               );
+
+               // HttpRequestPool methods socketPerform() and socketSelect() are
+               // protected;  one could use this approach to do something else
+               // while the requests are being executed
+               print "Executing requests";
+               for ($i = 0; $this->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 (file)
index 0000000..f82e316
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+class FeedAggregator
+{
+       public $directory;
+       protected $feeds = array();
+
+       public function __construct($directory = 'feeds')
+       {
+               $this->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 (file)
index 0000000..32aa173
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+if (preg_match_all('/\n- ([^\n]+).*?(\<\?.+?\?\>)/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 (file)
index 0000000..6dcae5a
--- /dev/null
@@ -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.
+
+<?php
+$r = new HttpRequest('http://www.google.com');
+
+// store Googles cookies in a dedicated file
+$r->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.
+
+<?php
+$r = new HttpRequest('http://dev.iworks.at/.print_request.php', HTTP_POST);
+
+// if redirects is set to true, a single redirect is allowed;
+// one can set any reasonable count of allowed redirects
+$r->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.
+
+<?php
+try {
+       $p = new HttpRequestPool;
+       // if you want to set _any_ options of the HttpRequest object,
+       // you need to do so *prior attaching* to the request pool!
+       $p->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.
+
+<?php
+class Pool extends HttpRequestPool
+{
+       public function __construct()
+       {
+               parent::__construct(
+                       new HttpRequest('http://pear.php.net', HTTP_HEAD),
+                       new HttpRequest('http://pecl.php.net', HTTP_HEAD)
+               );
+
+               // HttpRequestPool methods socketPerform() and socketSelect() are
+               // protected;  one could use this approach to do something else
+               // while the requests are being executed
+               print "Executing requests";
+               for ($i = 0; $this->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.
+
+<?php
+HttpResponse::setCacheControl('public');
+HttpResponse::setCache(true);
+HttpResponse::capture();
+
+print "This will be cached until content changes!\n";
+print "Note that this approach will only save the clients download time.\n";
+?>
+
+- 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.
+
+<?php
+// send 5000 bytes every 0.2 seconds, i.e. max ~25kByte/s
+HttpResponse::setThrottleDelay(0.2);
+HttpResponse::setBufferSize(5000);
+HttpResponse::setCache(true);
+HttpResponse::setContentType('application/x-zip');
+HttpResponse::setFile('../archive.zip');
+HttpResponse::send();
+?>
+
+Exemplar Use Cases
+------------------
+
+- KISS XMLRPC Client
+
+<?php
+class XmlRpcClient
+{
+       public $namespace;
+       protected $request;
+
+       public function __construct($url, $namespace = '')
+       {
+               $this->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
+
+<?php
+class FeedAggregator
+{
+       public $directory;
+       protected $feeds = array();
+
+       public function __construct($directory = 'feeds')
+       {
+               $this->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);
+        }
+       }
+}
+?>
+
+
index 1ed0e2c154d71d12c14dc69bd47a825f01238927..6527cce5cefaefac8ba6293bf3ebb80d378862a1 100644 (file)
@@ -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;
 }
 /* }}} */
index 84e979c7610992381283cf730836765a6d2dace6..68de2a43fde6a82cc9800fe3804e013a23761e25 100644 (file)
@@ -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;
        }
index 7dd931fb3ee647baf2b6d8eb73a0656bd5977b63..45e3b2474e71b89bf4382e2b40a5d0a65fc6fba4 100644 (file)
@@ -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
index 11f78c56482d7ec84f8f9d925ce2c6714c4a812b..6be6718eb2dafda93176e3255412a5cb3bd78d25 100644 (file)
@@ -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);
index f166fe5ade8d77c77c7b89649b09fec91f14a27a..74db7d43070acab2bb822e69117519ffebee3651 100644 (file)
@@ -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)