*/
use Civi\Cxn\Rpc\Constants;
+use Civi\Cxn\Rpc\DefaultCertificateValidator;
/**
*
$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;
}
$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()
+ );
}
}
+
}
--- /dev/null
+<?php
+
+/**
+ * Class CRM_Cxn_CiviCxnHttp
+ *
+ * This extends the PhpHttp client used by CiviConnect and adds:
+ * - Force-cache support for GET requests
+ * - Compliance with SSL policy
+ */
+class CRM_Cxn_CiviCxnHttp extends \Civi\Cxn\Rpc\Http\PhpHttp {
+
+ protected static $singleton = NULL;
+
+ /**
+ * @var CRM_Utils_Cache_Interface|null
+ */
+ protected $cache;
+
+ /**
+ * @param bool $fresh
+ * @return CRM_Cxn_CiviCxnHttp
+ */
+ public static function singleton($fresh = FALSE) {
+ if (self::$singleton === NULL || $fresh) {
+ $config = CRM_Core_Config::singleton();
+
+ if ($config->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;
+ }
+
+}
--- /dev/null
+<?php
+
+class CRM_Utils_Http {
+
+ /**
+ * Parse the expiration time from a series of HTTP headers.
+ *
+ * @param array $headers
+ * @return int|NULL
+ * Expiration tme as seconds since epoch, or NULL if not cacheable.
+ */
+ public static function parseExpiration($headers) {
+ $headers = CRM_Utils_Array::rekey($headers, function ($k, $v) {
+ return strtolower($k);
+ });
+
+ if (!empty($headers['cache-control'])) {
+ $cc = self::parseCacheControl($headers['cache-control']);
+ if ($cc['max-age'] && is_numeric($cc['max-age'])) {
+ return CRM_Utils_Time::getTimeRaw() + $cc['max-age'];
+ }
+ }
+
+ return NULL;
+ }
+
+ /**
+ * @param string $value
+ * Ex: "max-age=86400, public".
+ * @return array
+ * Ex: Array("max-age"=>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;
+ }
+
+}
* @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.");
}