public $created;
public $modified;
- protected static function hashkey($topic, $callback)
+ static function hashkey($topic, $callback)
{
return sha1($topic . '|' . $callback);
}
$qm->enqueue($data, 'hubconf');
}
+ public function getTopic()
+ {
+ return $this->topic;
+ }
+
/**
* Send a verification ping to subscriber, and if confirmed apply the changes.
* This may create, update, or delete the database record.
$challenge = common_random_hexstr(32);
$params = array('hub.mode' => $mode,
- 'hub.topic' => $this->topic,
+ 'hub.topic' => $this->getTopic(),
'hub.challenge' => $challenge);
if ($mode == 'subscribe') {
$params['hub.lease_seconds'] = $this->lease;
$status = $response->getStatus();
if ($status >= 200 && $status < 300) {
- common_log(LOG_INFO, "Verified {$mode} of {$this->callback}:{$this->topic}");
+ common_log(LOG_INFO, "Verified {$mode} of {$this->callback}:{$this->getTopic()}");
} else {
// TRANS: Client exception. %s is a HTTP status code.
throw new ClientException(sprintf(_m('Hub subscriber verification returned HTTP %s.'),$status));
}
- $old = HubSub::getByHashkey($this->topic, $this->callback);
+ $old = HubSub::getByHashkey($this->getTopic(), $this->callback);
if ($mode == 'subscribe') {
if ($old instanceof HubSub) {
$this->update($old);
*/
function insert()
{
- $this->hashkey = self::hashkey($this->topic, $this->callback);
+ $this->hashkey = self::hashkey($this->getTopic(), $this->callback);
$this->created = common_sql_now();
$this->modified = common_sql_now();
return parent::insert();
// destroy the result data for the parent query.
// @fixme use clone() again when it's safe to copy an
// individual item from a multi-item query again.
- $sub = HubSub::getByHashkey($this->topic, $this->callback);
+ $sub = HubSub::getByHashkey($this->getTopic(), $this->callback);
$data = array('sub' => $sub,
'atom' => $atom,
'retries' => $retries);
- common_log(LOG_INFO, "Queuing PuSH: $this->topic to $this->callback");
+ common_log(LOG_INFO, "Queuing PuSH: {$this->getTopic()} to {$this->callback}");
$qm = QueueManager::get();
$qm->enqueue($data, 'hubout');
}
function bulkDistribute($atom, $pushCallbacks)
{
$data = array('atom' => $atom,
- 'topic' => $this->topic,
+ 'topic' => $this->getTopic(),
'pushCallbacks' => $pushCallbacks);
- common_log(LOG_INFO, "Queuing PuSH batch: $this->topic to " .
- count($pushCallbacks) . " sites");
+ common_log(LOG_INFO, "Queuing PuSH batch: {$this->getTopic()} to ".count($pushCallbacks)." sites");
$qm = QueueManager::get();
$qm->enqueue($data, 'hubprep');
}
} else {
$hmac = '(none)';
}
- common_log(LOG_INFO, "About to push feed to $this->callback for $this->topic, HMAC $hmac");
+ common_log(LOG_INFO, "About to push feed to $this->callback for {$this->getTopic()}, HMAC $hmac");
$request = new HTTPClient();
$request->setBody($atom);
- $response = $request->post($this->callback, $headers);
+ try {
+ $response = $request->post($this->callback, $headers);
- if ($response->isOk()) {
- return true;
- } else {
- // TRANS: Exception. %1$s is a response status code, %2$s is the body of the response.
- throw new Exception(sprintf(_m('Callback returned status: %1$s. Body: %2$s'),
- $response->getStatus(),trim($response->getBody())));
+ if ($response->isOk()) {
+ return true;
+ }
+ } catch (Exception $e) {
+ $response = null;
+
+ common_debug('PuSH callback to '._ve($this->callback).' for '._ve($this->getTopic()).' failed with exception: '._ve($e->getMessage()));
}
+
+ // XXX: DO NOT trust a Location header here, _especially_ from 'http' protocols,
+ // but not 'https' either at least if we don't do proper CA verification. Trust that
+ // the most common change here is simply switching 'http' to 'https' and we will
+ // solve 99% of all of these issues for now. There should be a proper mechanism
+ // if we want to change the callback URLs, preferrably just manual resubscriptions
+ // from the remote side, combined with implemented PuSH subscription timeouts.
+
+ // We failed the PuSH, but it might be that the remote site has changed their configuration to HTTPS
+ if ('http' === parse_url($this->callback, PHP_URL_SCHEME)) {
+ // Test if the feed callback for this node has migrated to HTTPS
+ $httpscallback = preg_replace('/^http/', 'https', $this->callback, 1);
+ if ($httpscallback === $this->callback) {
+ throw new ServerException('Trying to preg_replace http to https on '._ve($this->callback).' failed and resulted in an identical string: '._ve($httpscallback).'.');
+ }
+ common_debug('PuSH callback to '._ve($this->callback).' for '._ve($this->getTopic()).' testing with HTTPS callback: '._ve($httpscallback));
+ $response = $request->post($httpscallback, $headers);
+ if ($response->isOk()) {
+ $orig = clone($this);
+ $this->callback = $httpscallback;
+ $this->hashkey = self::hashkey($this->getTopic(), $this->callback);
+ common_debug('HubSub DEBUG, from '._ve($orig).' to '._ve($this));
+ $this->updateWithKeys($orig, 'hashkey');
+ return true;
+ }
+ }
+
+ // FIXME: Add 'failed' incremental count for this callback.
+
+ if (is_null($response)) {
+ // This means we got a lower-than-HTTP level error, like domain not found or maybe connection refused
+ // This should be using a more distinguishable exception class, but for now this will do.
+ throw new Exception(sprintf(_m('HTTP request failed without response to URL: %s'), var_export($target, true)));
+ }
+
+ // TRANS: Exception. %1$s is a response status code, %2$s is the body of the response.
+ throw new Exception(sprintf(_m('Callback returned status: %1$s. Body: %2$s'),
+ $response->getStatus(),trim($response->getBody())));
}
}