Changeset View
Changeset View
Standalone View
Standalone View
src/future/aws/PhutilAWSFuture.php
<?php | <?php | ||||
abstract class PhutilAWSFuture extends FutureProxy { | abstract class PhutilAWSFuture extends FutureProxy { | ||||
private $future; | private $future; | ||||
private $awsAccessKey; | private $accessKey; | ||||
private $awsPrivateKey; | private $secretKey; | ||||
private $awsRegion; | private $region; | ||||
private $builtRequest; | private $httpMethod = 'GET'; | ||||
private $params; | private $path = '/'; | ||||
private $params = array(); | |||||
private $endpoint; | |||||
abstract public function getServiceName(); | abstract public function getServiceName(); | ||||
public function __construct() { | public function __construct() { | ||||
parent::__construct(null); | parent::__construct(null); | ||||
} | } | ||||
public function setAWSKeys($access, $private) { | public function setAccessKey($access_key) { | ||||
$this->awsAccessKey = $access; | $this->accessKey = $access_key; | ||||
$this->awsPrivateKey = $private; | |||||
return $this; | return $this; | ||||
} | } | ||||
public function getAWSAccessKey() { | public function getAccessKey() { | ||||
return $this->awsAccessKey; | return $this->accessKey; | ||||
} | } | ||||
public function getAWSPrivateKey() { | public function setSecretKey(PhutilOpaqueEnvelope $secret_key) { | ||||
return $this->awsPrivateKey; | $this->secretKey = $secret_key; | ||||
return $this; | |||||
} | |||||
public function getSecretKey() { | |||||
return $this->secretKey; | |||||
} | } | ||||
public function getAWSRegion() { | public function getRegion() { | ||||
return $this->awsRegion; | return $this->region; | ||||
} | } | ||||
public function setAWSRegion($region) { | public function setRegion($region) { | ||||
$this->awsRegion = $region; | $this->region = $region; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getHost() { | public function setEndpoint($endpoint) { | ||||
$host = $this->getServiceName().'.'.$this->awsRegion.'.amazonaws.com'; | $this->endpoint = $endpoint; | ||||
return $host; | return $this; | ||||
} | } | ||||
public function setRawAWSQuery($action, array $params = array()) { | public function getEndpoint() { | ||||
$this->params = $params; | return $this->endpoint; | ||||
$this->params['Action'] = $action; | } | ||||
public function setHTTPMethod($method) { | |||||
$this->httpMethod = $method; | |||||
return $this; | return $this; | ||||
} | } | ||||
protected function getProxiedFuture() { | public function getHTTPMethod() { | ||||
if (!$this->future) { | return $this->httpMethod; | ||||
$params = $this->params; | } | ||||
if (!$this->params) { | public function setPath($path) { | ||||
throw new Exception( | $this->path = $path; | ||||
pht( | return $this; | ||||
'You must %s!', | |||||
'setRawAWSQuery()')); | |||||
} | } | ||||
if (!$this->getAWSAccessKey()) { | public function getPath() { | ||||
throw new Exception( | return $this->path; | ||||
pht( | |||||
'You must %s!', | |||||
'setAWSKeys()')); | |||||
} | } | ||||
$params['AWSAccessKeyId'] = $this->getAWSAccessKey(); | protected function getParameters() { | ||||
$params['Version'] = '2013-10-15'; | $params = $this->params; | ||||
$params['Timestamp'] = date('c'); | return $params; | ||||
} | |||||
protected function getProxiedFuture() { | |||||
if (!$this->future) { | |||||
$params = $this->getParameters(); | |||||
$method = $this->getHTTPMethod(); | |||||
$host = $this->getEndpoint(); | |||||
$path = $this->getPath(); | |||||
$uri = id(new PhutilURI("https://{$host}/")) | |||||
->setPath($path) | |||||
->setQueryParams($params); | |||||
$params = $this->sign($params); | $future = id(new HTTPSFuture($uri)) | ||||
->setMethod($method); | |||||
$uri = new PhutilURI('http://'.$this->getHost().'/'); | $this->signRequest($future); | ||||
$uri->setQueryParams($params); | |||||
$this->future = new HTTPFuture($uri); | $this->future = $future; | ||||
} | } | ||||
return $this->future; | return $this->future; | ||||
} | } | ||||
protected function signRequest(HTTPSFuture $future) { | |||||
$access_key = $this->getAccessKey(); | |||||
$secret_key = $this->getSecretKey(); | |||||
$region = $this->getRegion(); | |||||
id(new PhutilAWSv4Signature()) | |||||
->setRegion($region) | |||||
->setService($this->getServiceName()) | |||||
->setAccessKey($access_key) | |||||
->setSecretKey($secret_key) | |||||
->signRequest($future); | |||||
} | |||||
protected function didReceiveResult($result) { | protected function didReceiveResult($result) { | ||||
list($status, $body, $headers) = $result; | list($status, $body, $headers) = $result; | ||||
try { | try { | ||||
$xml = @(new SimpleXMLElement($body)); | $xml = @(new SimpleXMLElement($body)); | ||||
} catch (Exception $ex) { | } catch (Exception $ex) { | ||||
$xml = null; | $xml = null; | ||||
} | } | ||||
if ($status->isError() || !$xml) { | if ($status->isError() || !$xml) { | ||||
if (!($status instanceof HTTPFutureHTTPResponseStatus)) { | if (!($status instanceof HTTPFutureHTTPResponseStatus)) { | ||||
throw $status; | throw $status; | ||||
} | } | ||||
$params = array( | $params = array( | ||||
'body' => $body, | 'body' => $body, | ||||
); | ); | ||||
if ($xml) { | if ($xml) { | ||||
$params['RequestID'] = $xml->RequestID[0]; | $params['RequestID'] = $xml->RequestID[0]; | ||||
foreach ($xml->Errors[0] as $error) { | $errors = array($xml->Error); | ||||
foreach ($errors as $error) { | |||||
$params['Errors'][] = array($error->Code, $error->Message); | $params['Errors'][] = array($error->Code, $error->Message); | ||||
} | } | ||||
} | } | ||||
throw new PhutilAWSException($status->getStatusCode(), $params); | throw new PhutilAWSException($status->getStatusCode(), $params); | ||||
} | } | ||||
return $xml; | return $xml; | ||||
} | } | ||||
/** | |||||
* http://bit.ly/wU0JFh | |||||
*/ | |||||
private function sign(array $params) { | |||||
$params['SignatureMethod'] = 'HmacSHA256'; | |||||
$params['SignatureVersion'] = '2'; | |||||
ksort($params); | |||||
$pstr = array(); | |||||
foreach ($params as $key => $value) { | |||||
$pstr[] = rawurlencode($key).'='.rawurlencode($value); | |||||
} | |||||
$pstr = implode('&', $pstr); | |||||
$sign = "GET"."\n". | |||||
strtolower($this->getHost())."\n". | |||||
"/"."\n". | |||||
$pstr; | |||||
$hash = hash_hmac( | |||||
'sha256', | |||||
$sign, | |||||
$this->getAWSPrivateKey(), | |||||
$raw_ouput = true); | |||||
$params['Signature'] = base64_encode($hash); | |||||
return $params; | |||||
} | |||||
} | } |