Re-worked the Transport layer to support CURL and PHP Streams.
This commit is contained in:
@@ -30,6 +30,9 @@ namespace HPCloud\Transport;
|
||||
*/
|
||||
class CURLTransport implements Transporter {
|
||||
|
||||
|
||||
const HTTP_USER_AGENT_SUFFIX = ' (c93c0a) CURL/1.0';
|
||||
|
||||
public function doRequest($uri, $method = 'GET', $headers = array(), $body = '') {
|
||||
|
||||
$in = NULL;
|
||||
@@ -58,9 +61,12 @@ class CURLTransport implements Transporter {
|
||||
*/
|
||||
protected function handleDoRequest($uri, $method, $headers, $in = NULL) {
|
||||
|
||||
//$urlParts = parse_url($uri);
|
||||
|
||||
|
||||
// Write to in-mem handle backed by a temp file.
|
||||
$out = fopen('php://temp', 'w');
|
||||
$out = fopen('php://temp', 'wrb');
|
||||
$headerFile = fopen('php://temp', 'wr');
|
||||
|
||||
$curl = curl_init($uri);
|
||||
|
||||
@@ -73,20 +79,102 @@ class CURLTransport implements Transporter {
|
||||
// Set the upload
|
||||
if (!empty($in)) {
|
||||
curl_setopt($curl, CURLOPT_INFILE, $in);
|
||||
|
||||
// Tell CURL about the content length if we know it.
|
||||
if (!empty($headers['Content-Length'])) {
|
||||
curl_setopt($curl, CURLOPT_INFILESIZE, $headers['Content-Length']);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the output.
|
||||
curl_setopt($curl, CURLOPT_FILE, $out);
|
||||
|
||||
curl_exec($curl);
|
||||
// We need to capture the headers, too.
|
||||
curl_setopt($curl, CURLOPT_WRITEHEADER, $headerFile);
|
||||
|
||||
// Show me the money!
|
||||
// Results are now buffered into a tmpfile.
|
||||
//curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
|
||||
$opts = array(
|
||||
CURLOPT_USERAGENT => self::HTTP_USER_AGENT . self::HTTP_USER_AGENT_SUFFIX,
|
||||
// CURLOPT_RETURNTRANSFER => TRUE, // Make curl_exec return the results.
|
||||
// CURLOPT_BINARYTRANSFER => TRUE, // Raw output if RETURNTRANSFER is TRUE.
|
||||
|
||||
// Put the headers in the output.
|
||||
CURLOPT_HEADER => TRUE,
|
||||
|
||||
// Get the final header string sent to the remote.
|
||||
CURLINFO_HEADER_OUT => TRUE,
|
||||
|
||||
// Timeout if the remote has not connected in 30 sec.
|
||||
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.
|
||||
// CURLOPT_ENCODING => '',
|
||||
|
||||
// Later, we may want to do this to support range-based
|
||||
// fetching of large objects.
|
||||
// CURLOPT_RANGE => 'X-Y',
|
||||
|
||||
// Limit curl to only these protos.
|
||||
// CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
|
||||
|
||||
// I don't really need this, do I?
|
||||
// CURLOPT_HTTP200ALIASES => array(200, 201, 202, 203, 204),
|
||||
);
|
||||
|
||||
$ret = curl_exec($curl);
|
||||
$info = curl_getinfo($curl);
|
||||
$status = $info['http_code'];
|
||||
|
||||
rewind($headerFile);
|
||||
$responseHeaders = $this->fetchHeaders($headerFile);
|
||||
fclose($headerFile);
|
||||
|
||||
if (!$ret || $status < 200 || $status > 299) {
|
||||
$err = $responseHeaders[0];
|
||||
Response::failure($status, $err, $info['url'], $method);
|
||||
}
|
||||
|
||||
|
||||
rewind($out);
|
||||
// Now we need to build a response.
|
||||
// Option 1: Subclass response.
|
||||
// Option 2: Build an adapter.
|
||||
$resp = new Response($out, $info, $responseHeaders);
|
||||
|
||||
curl_close($curl);
|
||||
if (is_resource($in)) {
|
||||
fclose($in);
|
||||
}
|
||||
|
||||
fclose($in);
|
||||
//throw new \Exception(print_r($resp, TRUE));
|
||||
|
||||
return $resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function reads the header file into an array.
|
||||
*
|
||||
* This format mataches the format returned by the stream handlers, so
|
||||
* we can re-use the header parsing logic in Response.
|
||||
*
|
||||
* @param resource $file
|
||||
* A file pointer to the file that has the headers.
|
||||
* @return array
|
||||
* An array of headers, one header per line.
|
||||
*/
|
||||
protected function fetchHeaders($file) {
|
||||
$buffer = array();
|
||||
while ($header = fgets($file)) {
|
||||
$header = trim($header);
|
||||
if (!empty($header)) {
|
||||
$buffer[] = $header;
|
||||
}
|
||||
}
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,5 +228,4 @@ class CURLTransport implements Transporter {
|
||||
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -148,27 +148,7 @@ class PHPStreamTransport implements Transporter {
|
||||
throw new \HPCloud\Exception($err);
|
||||
}
|
||||
|
||||
switch ($matches[1]) {
|
||||
|
||||
case '403':
|
||||
case '401':
|
||||
throw new \HPCloud\Transport\AuthorizationException($matches[0]);
|
||||
case '404':
|
||||
throw new \HPCloud\Transport\FileNotFoundException($matches[0] . "($uri)");
|
||||
case '405':
|
||||
throw new \HPCloud\Transport\MethodNotAllowedException($matches[0] . " ($method $uri)");
|
||||
case '409':
|
||||
throw new \HPCloud\Transport\ConflictException($matches[0]);
|
||||
case '412':
|
||||
throw new \HPCloud\Transport\LengthRequiredException($matches[0]);
|
||||
case '422':
|
||||
throw new \HPCloud\Transport\UnprocessableEntityException($matches[0]);
|
||||
case '500':
|
||||
throw new \HPCloud\Transport\ServerException($matches[0]);
|
||||
default:
|
||||
throw new \HPCloud\Exception($matches[0]);
|
||||
|
||||
}
|
||||
Response::failure($matches[1], $matches[0], $uri, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,16 +36,65 @@ class Response {
|
||||
protected $metadata;
|
||||
protected $headers;
|
||||
|
||||
/**
|
||||
* Handle error response.
|
||||
*
|
||||
* When a response is a failure, it should pass through this function,
|
||||
* which generates the appropriate exception and then throws it.
|
||||
*
|
||||
* @param int $code
|
||||
* The HTTP status code, e.g. 404, 500.
|
||||
* @param string $err
|
||||
* The error string, as bubbled up.
|
||||
* @param string $uri
|
||||
* The URI.
|
||||
* @param string $method
|
||||
* The HTTP method, e.g. 'HEAD', 'GET', 'DELETE'.
|
||||
* @param string $extra
|
||||
* An extra string of debugging information. (NOT USED)
|
||||
* @throws \HPCloud\Exception
|
||||
* A wide variety of \HPCloud\Transport exceptions.
|
||||
*/
|
||||
public static function failure($code, $err = 'Unknown', $uri = '', $method = '', $extra = '') {
|
||||
switch ($code) {
|
||||
|
||||
case '403':
|
||||
case '401':
|
||||
throw new \HPCloud\Transport\AuthorizationException($err);
|
||||
case '404':
|
||||
throw new \HPCloud\Transport\FileNotFoundException($err . " ($uri)");
|
||||
case '405':
|
||||
throw new \HPCloud\Transport\MethodNotAllowedException($err . " ($method $uri)");
|
||||
case '409':
|
||||
throw new \HPCloud\Transport\ConflictException($err);
|
||||
case '412':
|
||||
throw new \HPCloud\Transport\LengthRequiredException($err);
|
||||
case '422':
|
||||
throw new \HPCloud\Transport\UnprocessableEntityException($err);
|
||||
case '500':
|
||||
throw new \HPCloud\Transport\ServerException($err);
|
||||
default:
|
||||
throw new \HPCloud\Exception($err);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new Response.
|
||||
*
|
||||
* The Transporter implementations use this to
|
||||
* construct a response.
|
||||
*/
|
||||
public function __construct($handle, $metadata) {
|
||||
public function __construct($handle, $metadata, $headers = NULL) {
|
||||
$this->handle = $handle;
|
||||
$this->metadata = $metadata;
|
||||
$this->headers = $this->parseHeaders($metadata['wrapper_data']);
|
||||
|
||||
if (!isset($headers) && isset($metadata['wrapper_data'])) {
|
||||
$headers = $metadata['wrapper_data'];
|
||||
}
|
||||
|
||||
$this->headers = $this->parseHeaders($headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user