Fixed a bug in POST handling.

POST data was not being sent appropriately. Also made it possible
to send configuration params into CURL from the config.
This commit is contained in:
Matt Butcher
2012-01-23 18:38:28 -06:00
parent cf8e6146e3
commit c048042e79
4 changed files with 78 additions and 26 deletions

View File

@@ -87,6 +87,18 @@ class Bootstrap {
* All of the HPCloud classes share the same configuration. This * All of the HPCloud classes share the same configuration. This
* ensures that a stable runtime environment is maintained. * ensures that a stable runtime environment is maintained.
* *
* Common configuration directives:
*
* - 'transport': The namespaced classname for the transport that
* should be used. Example: `\HPCloud\Transport\CURLTransport`
* - 'transport.debug': The integer 1 for enabling debug, 0 for
* disabling. Enabling will turn on verbose debugging output
* for any transport that supports it.
* - 'transport.timeout': An integer value indicating how long
* the transport layer should wait for an HTTP request. A
* transport MAY ignore this parameter, but the ones included
* with the library honor it.
*
* @param array $array * @param array $array
* An associative array of configuration directives. * An associative array of configuration directives.
*/ */
@@ -167,4 +179,8 @@ class Bootstrap {
// Otherwise, just return the default value. // Otherwise, just return the default value.
return $default; return $default;
} }
public static function hasConfig($name) {
return isset(self::$config[$name]);
}
} }

View File

@@ -6,6 +6,8 @@
namespace HPCloud\Transport; namespace HPCloud\Transport;
use \HPCloud\Bootstrap;
/** /**
* Provide HTTP transport with CURL. * Provide HTTP transport with CURL.
* *
@@ -33,16 +35,25 @@ class CURLTransport implements Transporter {
const HTTP_USER_AGENT_SUFFIX = ' (c93c0a) CURL/1.0'; const HTTP_USER_AGENT_SUFFIX = ' (c93c0a) CURL/1.0';
public function doRequest($uri, $method = 'GET', $headers = array(), $body = '') { public function doRequest($uri, $method = 'GET', $headers = array(), $body = NULL) {
$in = NULL; $in = NULL;
if (!empty($body)) { if (!empty($body)) {
// First we turn our body into a temp-backed buffer. // For whatever reason, CURL seems to want POST request data to be
$in = fopen('php://temp', 'wr', FALSE); // a string, not a file handle. So we adjust. PUT, on the other hand,
fwrite($in, $body, strlen($body)); // needs to be in a file handle.
rewind($in); if ($method == 'POST') {
$in = $body;
}
else {
// First we turn our body into a temp-backed buffer.
$in = fopen('php://temp', 'wr', FALSE);
fwrite($in, $body, strlen($body));
rewind($in);
}
} }
return $this->handleDoRequest($uri, $method, $headers, $in); return $this->handleDoRequest($uri, $method, $headers, $in);
//return $this->handleDoRequest($uri, $method, $headers, $body);
} }
@@ -51,9 +62,17 @@ class CURLTransport implements Transporter {
$in = open($resource, 'rb', FALSE); $in = open($resource, 'rb', FALSE);
} }
else { else {
$in = $resource; // FIXME: Is there a better way?
// There is a bug(?) in CURL which prevents it
// from writing the same stream twice. But we
// need to be able to flush a file multiple times.
// So we have to create a new temp buffer for each
// write operation.
$in = fopen('php://temp', 'rb+'); //tmpfile();
stream_copy_to_stream($resource, $in);
rewind($in);
} }
return $this->handleDoRequest($uri, $method, $headers, $resource); return $this->handleDoRequest($uri, $method, $headers, $in);
} }
/** /**
@@ -77,18 +96,19 @@ class CURLTransport implements Transporter {
// Set the upload // Set the upload
$copy = NULL; $copy = NULL;
if (!empty($in)) {
// FIXME: Is there a better way? // If we get a string, we send the string
// There is a bug(?) in CURL which prevents it // data.
// from writing the same stream twice. But we if (is_string($in)) {
// need to be able to flush a file multiple times. curl_setopt($curl, CURLOPT_POSTFIELDS, $in);
// So we have to create a new temp buffer for each if (!isset($headers['Content-Length'])) {
// write operation. $headers['Content-Length'] = strlen($in);
$copy = fopen('php://temp', 'rb+'); //tmpfile(); }
stream_copy_to_stream($in, $copy); }
rewind($copy); // If we get a resource, we treat it like a stream
curl_setopt($curl, CURLOPT_INFILE, $copy); // and pass it into CURL as a file.
elseif (is_resource($in)) {
curl_setopt($curl, CURLOPT_INFILE, $in);
// Tell CURL about the content length if we know it. // Tell CURL about the content length if we know it.
if (!empty($headers['Content-Length'])) { if (!empty($headers['Content-Length'])) {
@@ -97,7 +117,7 @@ class CURLTransport implements Transporter {
} }
} }
// Set headers // Set headers.
$this->setHeaders($curl, $headers); $this->setHeaders($curl, $headers);
// Get the output. // Get the output.
@@ -114,9 +134,6 @@ class CURLTransport implements Transporter {
// Timeout if the remote has not connected in 30 sec. // Timeout if the remote has not connected in 30 sec.
//CURLOPT_CONNECTTIMEOUT => 30, //CURLOPT_CONNECTTIMEOUT => 30,
// Max time to allow CURL to do the transaction.
// CURLOPT_TIMEOUT => 120,
// If this is set, CURL will auto-deflate any encoding it can. // If this is set, CURL will auto-deflate any encoding it can.
// CURLOPT_ENCODING => '', // CURLOPT_ENCODING => '',
@@ -127,11 +144,24 @@ class CURLTransport implements Transporter {
// Limit curl to only these protos. // Limit curl to only these protos.
// CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, // CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
// If you are desparate and need to debug, uncomment this.
//CURLOPT_VERBOSE => 1,
); );
curl_setopt_array($curl, $opts); curl_setopt_array($curl, $opts);
if (Bootstrap::hasConfig('transport.debug')) {
$debug = Bootstrap::config('transport.debug', NULL);
curl_setopt($curl, CURLOPT_VERBOSE, (int) $debug);
}
if (Bootstrap::hasConfig('transport.timeout')) {
curl_setopt($curl, CURLOPT_TIMEOUT, (int) Bootstrap::config('transport.timeout'));
}
if (Bootstrap::hasConfig('transport.ssl.verify')) {
$validate = (boolean) Bootstrap::config('transport.ssl.verify', TRUE);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $validate);
}
$ret = curl_exec($curl); $ret = curl_exec($curl);
$info = curl_getinfo($curl); $info = curl_getinfo($curl);
$status = $info['http_code']; $status = $info['http_code'];

View File

@@ -150,7 +150,8 @@ class RemoteObjectTest extends \HPCloud\Tests\TestCase {
// This will be HTTP if we are using the PHP stream // This will be HTTP if we are using the PHP stream
// wrapper, but for CURL this will be PHP. // wrapper, but for CURL this will be PHP.
if (self::$settings['transport'] == '\HPCloud\Transport\PHPStreamTransport') { $klass = \HPCloud\Bootstrap::config('transport', NULL);
if ($klass == '\HPCloud\Transport\PHPStreamTransport') {
$expect = 'http'; $expect = 'http';
} }
else { else {
@@ -224,7 +225,8 @@ class RemoteObjectTest extends \HPCloud\Tests\TestCase {
// The CURL, though, backs its up with a temp file wrapped in a PHP // The CURL, though, backs its up with a temp file wrapped in a PHP
// stream. Other backends are likely to do the same. So this test // stream. Other backends are likely to do the same. So this test
// is weakened for CURL backends. // is weakened for CURL backends.
if (self::$settings['transport'] == '\HPCloud\Transport\PHPStreamTransport') { $transport = \HPCloud\Bootstrap::config('transport');
if ($transport == '\HPCloud\Transport\PHPStreamTransport') {
$expect = 'http'; $expect = 'http';
} }
else { else {

View File

@@ -45,6 +45,10 @@ class StreamWrapperTest extends \HPCloud\Tests\TestCase {
$scheme = StreamWrapper::DEFAULT_SCHEME; $scheme = StreamWrapper::DEFAULT_SCHEME;
} }
if (empty(self::$ostore)) {
throw new \Exception('OStore is gone.');
}
$params = $add + array( $params = $add + array(
'token' => self::$ostore->token(), 'token' => self::$ostore->token(),
'swift_endpoint' => self::$ostore->url(), 'swift_endpoint' => self::$ostore->url(),