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 {
|
class CURLTransport implements Transporter {
|
||||||
|
|
||||||
|
|
||||||
|
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 = '') {
|
||||||
|
|
||||||
$in = NULL;
|
$in = NULL;
|
||||||
@@ -58,9 +61,12 @@ class CURLTransport implements Transporter {
|
|||||||
*/
|
*/
|
||||||
protected function handleDoRequest($uri, $method, $headers, $in = NULL) {
|
protected function handleDoRequest($uri, $method, $headers, $in = NULL) {
|
||||||
|
|
||||||
|
//$urlParts = parse_url($uri);
|
||||||
|
|
||||||
|
|
||||||
// Write to in-mem handle backed by a temp file.
|
// 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);
|
$curl = curl_init($uri);
|
||||||
|
|
||||||
@@ -73,20 +79,102 @@ class CURLTransport implements Transporter {
|
|||||||
// Set the upload
|
// Set the upload
|
||||||
if (!empty($in)) {
|
if (!empty($in)) {
|
||||||
curl_setopt($curl, CURLOPT_INFILE, $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.
|
// Get the output.
|
||||||
curl_setopt($curl, CURLOPT_FILE, $out);
|
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.
|
// Now we need to build a response.
|
||||||
// Option 1: Subclass response.
|
$resp = new Response($out, $info, $responseHeaders);
|
||||||
// Option 2: Build an adapter.
|
|
||||||
|
|
||||||
curl_close($curl);
|
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);
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,27 +148,7 @@ class PHPStreamTransport implements Transporter {
|
|||||||
throw new \HPCloud\Exception($err);
|
throw new \HPCloud\Exception($err);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($matches[1]) {
|
Response::failure($matches[1], $matches[0], $uri, $method);
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -36,16 +36,65 @@ class Response {
|
|||||||
protected $metadata;
|
protected $metadata;
|
||||||
protected $headers;
|
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.
|
* Construct a new Response.
|
||||||
*
|
*
|
||||||
* The Transporter implementations use this to
|
* The Transporter implementations use this to
|
||||||
* construct a response.
|
* construct a response.
|
||||||
*/
|
*/
|
||||||
public function __construct($handle, $metadata) {
|
public function __construct($handle, $metadata, $headers = NULL) {
|
||||||
$this->handle = $handle;
|
$this->handle = $handle;
|
||||||
$this->metadata = $metadata;
|
$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