From 5063e355518ff18e7e675e7b6739b18b09a9fa07 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 3 Jul 2015 18:17:03 -0700 Subject: [PATCH] CRM-16757 - Cache CiviConnect apps and CRLs. --- CRM/Cxn/BAO/Cxn.php | 20 ++++++-- CRM/Cxn/CiviCxnHttp.php | 104 ++++++++++++++++++++++++++++++++++++++++ CRM/Utils/Http.php | 50 +++++++++++++++++++ api/v3/CxnApp.php | 6 +-- 4 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 CRM/Cxn/CiviCxnHttp.php create mode 100644 CRM/Utils/Http.php diff --git a/CRM/Cxn/BAO/Cxn.php b/CRM/Cxn/BAO/Cxn.php index 83267fbe1f..d34271ecf8 100644 --- a/CRM/Cxn/BAO/Cxn.php +++ b/CRM/Cxn/BAO/Cxn.php @@ -27,6 +27,7 @@ */ use Civi\Cxn\Rpc\Constants; +use Civi\Cxn\Rpc\DefaultCertificateValidator; /** * @@ -145,6 +146,7 @@ class CRM_Cxn_BAO_Cxn extends CRM_Cxn_DAO_Cxn { $client = new \Civi\Cxn\Rpc\RegistrationClient($cxnStore, \CRM_Cxn_BAO_Cxn::getSiteCallbackUrl()); $client->setLog(new \CRM_Utils_SystemLogger()); $client->setCertValidator(self::createCertificateValidator()); + $client->setHttp(CRM_Cxn_CiviCxnHttp::singleton()); return $client; } @@ -158,21 +160,33 @@ class CRM_Cxn_BAO_Cxn extends CRM_Cxn_DAO_Cxn { $apiServer = new \Civi\Cxn\Rpc\ApiServer($cxnStore); $apiServer->setLog(new CRM_Utils_SystemLogger()); $apiServer->setCertValidator(self::createCertificateValidator()); + $apiServer->setHttp(CRM_Cxn_CiviCxnHttp::singleton()); $apiServer->setRouter(array('CRM_Cxn_ApiRouter', 'route')); return $apiServer; } /** - * @return \Civi\Cxn\Rpc\CertificateValidatorInterface + * @return DefaultCertificateValidator * @throws CRM_Core_Exception */ public static function createCertificateValidator() { $caCert = self::getCACert(); if ($caCert === NULL) { - return new \Civi\Cxn\Rpc\DefaultCertificateValidator(NULL, NULL, NULL); + return new DefaultCertificateValidator( + NULL, + NULL, + NULL, + NULL + ); } else { - return new \Civi\Cxn\Rpc\DefaultCertificateValidator($caCert); + return new DefaultCertificateValidator( + $caCert, + DefaultCertificateValidator::AUTOLOAD, + DefaultCertificateValidator::AUTOLOAD, + CRM_Cxn_CiviCxnHttp::singleton() + ); } } + } diff --git a/CRM/Cxn/CiviCxnHttp.php b/CRM/Cxn/CiviCxnHttp.php new file mode 100644 index 0000000000..249575847b --- /dev/null +++ b/CRM/Cxn/CiviCxnHttp.php @@ -0,0 +1,104 @@ +debug) { + $cache = new CRM_Utils_Cache_Arraycache(array()); + } + else { + $cache = new CRM_Utils_Cache_SqlGroup(array( + 'group' => 'CiviCxnHttp', + 'prefetch' => FALSE, + )); + } + + self::$singleton = new CRM_Cxn_CiviCxnHttp($cache); + } + return self::$singleton; + } + + /** + * @param CRM_Utils_Cache_Interface|NULL $cache + * The cache data store. + */ + public function __construct($cache) { + $this->cache = $cache; + } + + /** + * @param string $verb + * @param string $url + * @param string $blob + * @param array $headers + * Array of headers (e.g. "Content-type" => "text/plain"). + * @return array + * array($headers, $blob, $code) + */ + public function send($verb, $url, $blob, $headers = array()) { + $lowVerb = strtolower($verb); + + if ($lowVerb === 'get' && $this->cache) { + $cachePath = 'get/' . md5($url); + $cacheLine = $this->cache->get($cachePath); + if ($cacheLine && $cacheLine['expires'] > CRM_Utils_Time::getTimeRaw()) { + return $cacheLine['data']; + } + } + + $result = parent::send($verb, $url, $blob, $headers); + + if ($lowVerb === 'get' && $this->cache) { + $expires = CRM_Utils_Http::parseExpiration($result[0]); + if ($expires !== NULL) { + $cachePath = 'get/' . md5($url); + $cacheLine = array( + 'url' => $url, + 'expires' => $expires, + 'data' => $result, + ); + $this->cache->set($cachePath, $cacheLine); + } + } + + return $result; + } + + protected function createStreamOpts($verb, $url, $blob, $headers) { + $result = parent::createStreamOpts($verb, $url, $blob, $headers); + + $caConfig = CA_Config_Stream::probe(array( + 'verify_peer' => (bool) CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL', NULL, TRUE), + )); + if ($caConfig->isEnableSSL()) { + $result['ssl'] = $caConfig->toStreamOptions(); + } + if (!$caConfig->isEnableSSL() && preg_match('/^https:/', $url)) { + CRM_Core_Error::fatal('Cannot fetch document - system does not support SSL'); + } + + return $result; + } + +} diff --git a/CRM/Utils/Http.php b/CRM/Utils/Http.php new file mode 100644 index 0000000000..688ef8c175 --- /dev/null +++ b/CRM/Utils/Http.php @@ -0,0 +1,50 @@ +86400, "public"=>1). + */ + public static function parseCacheControl($value) { + $result = array(); + + $parts = preg_split('/, */', $value); + foreach ($parts as $part) { + if (strpos($part, '=') !== FALSE) { + list ($key, $value) = explode('=', $part, 2); + $result[$key] = $value; + } + else { + $result[$part] = TRUE; + } + } + + return $result; + } + +} diff --git a/api/v3/CxnApp.php b/api/v3/CxnApp.php index 5f7afd6a41..2b811e408c 100644 --- a/api/v3/CxnApp.php +++ b/api/v3/CxnApp.php @@ -94,13 +94,11 @@ function _civicrm_api3_cxn_app_get_spec(&$spec) { * @throws \Civi\Cxn\Rpc\Exception\InvalidMessageException */ function civicrm_api3_cxn_app_get($params) { - // FIXME: We should cache, but CRM_Utils_Cache and CRM_Core_BAO_Cache don't seem to support TTL... - // You should not change CIVICRM_CXN_APPS_URL in production; this is for local development. $url = defined('CIVICRM_CXN_APPS_URL') ? CIVICRM_CXN_APPS_URL : \Civi\Cxn\Rpc\Constants::OFFICIAL_APPMETAS_URL; - list ($status, $blob) = CRM_Utils_HttpClient::singleton()->get($url); - if (CRM_Utils_HttpClient::STATUS_OK != $status) { + list ($headers, $blob, $code) = CRM_Cxn_CiviCxnHttp::singleton()->send('GET', $url, ''); + if ($code != 200) { throw new API_Exception("Failed to download application list."); } -- 2.25.1