diff --git a/actions/DisplayAction.php b/actions/DisplayAction.php index 5db92b386a7..ad874b6ea3d 100644 --- a/actions/DisplayAction.php +++ b/actions/DisplayAction.php @@ -224,7 +224,11 @@ public function execute() { $items[] = $item; } elseif(Configuration::getConfig('error', 'output') === 'http') { - header('Content-Type: text/html', true, $this->get_return_code($e)); + $code = $this->get_return_code($e); + if ($code == 503) { + header('Retry-After: 600', true); + } + header('Content-Type: text/html', true, $code); die(buildTransformException($e, $bridge)); } } diff --git a/bridges/TwitterBridge.php b/bridges/TwitterBridge.php index a14e5062221..3fa00735fcf 100644 --- a/bridges/TwitterBridge.php +++ b/bridges/TwitterBridge.php @@ -5,6 +5,7 @@ class TwitterBridge extends BridgeAbstract { const API_URI = 'https://api.twitter.com'; const GUEST_TOKEN_USES = 100; const GUEST_TOKEN_EXPIRY = 10800; // 3hrs + const API_RETRIES = 3; const CACHE_TIMEOUT = 300; // 5min const DESCRIPTION = 'returns tweets'; const MAINTAINER = 'pmaziere'; @@ -481,7 +482,7 @@ private static function compareTweetId($tweet1, $tweet2) { //The aim of this function is to get an API key and a guest token //This function takes 2 requests, and therefore is cached - private function getApiKey() { + private function getApiKey($dumpToken) { $cacheFac = new CacheFactory(); $cacheFac->setWorkingDir(PATH_LIB_CACHES); @@ -506,7 +507,7 @@ private function getApiKey() { $data = $cache->loadData(); $apiKey = null; - if($data === null || (time() - $refresh) > self::GUEST_TOKEN_EXPIRY) { + if($dumpToken || $data === null || (time() - $refresh) > self::GUEST_TOKEN_EXPIRY) { $twitterPage = getContents('https://twitter.com'); $jsLink = false; @@ -543,9 +544,9 @@ private function getApiKey() { $guestTokenUses = $gt_cache->loadData(); $guestToken = null; - if($guestTokenUses === null || !is_array($guestTokenUses) || count($guestTokenUses) != 2 + if($dumpToken || $guestTokenUses === null || !is_array($guestTokenUses) || count($guestTokenUses) != 2 || $guestTokenUses[0] <= 0 || (time() - $refresh) > self::GUEST_TOKEN_EXPIRY) { - $guestToken = $this->getGuestToken(); + $guestToken = $this->getGuestToken($apiKey); if ($guestToken === null) { if($guestTokenUses === null) { returnServerError('Could not parse guest token'); @@ -568,24 +569,55 @@ private function getApiKey() { // Get a guest token. This is different to an API key, // and it seems to change more regularly than the API key. - private function getGuestToken() { - $pageContent = getContents('https://twitter.com', array(), array(), true); - - $guestTokenRegex = '/gt=([0-9]*)/m'; - preg_match_all($guestTokenRegex, $pageContent['header'], $guestTokenMatches, PREG_SET_ORDER, 0); - if (!$guestTokenMatches) - preg_match_all($guestTokenRegex, $pageContent['content'], $guestTokenMatches, PREG_SET_ORDER, 0); - if (!$guestTokenMatches) return null; - $guestToken = $guestTokenMatches[0][1]; + private function getGuestToken($apiKey) { + $headers = array( + 'authorization: Bearer ' . $apiKey, + ); + $opts = array( + CURLOPT_POST => 1, + ); + + try { + $pageContent = getContents('https://api.twitter.com/1.1/guest/activate.json', $headers, $opts, true); + $guestToken = json_decode($pageContent['content'])->guest_token; + } catch (Exception $e) { + $guestToken = null; + } return $guestToken; } private function getApiContents($uri) { - $apiKeys = $this->getApiKey(); - $headers = array('authorization: Bearer ' . $apiKeys[0], - 'x-guest-token: ' . $apiKeys[1], - ); - return getContents($uri, $headers); + $dump = false; + $lastCode = 500; + for ($i = 1; $i <= self::API_RETRIES; $i++) { + $apiKeys = $this->getApiKey($dump); + $headers = array('authorization: Bearer ' . $apiKeys[0], + 'x-guest-token: ' . $apiKeys[1], + ); + try { + $answer = getContents($uri, $headers); + return $answer; + } catch (Exception $e) { + // Sometimes Twitter invalidates our cached guest token, + // and returns 403. When this happens, dump the cached token, + // and retry. + $lastCode = $e->getCode(); + if ($lastCode == 403) { + $dump = true; + } else { + throw $e; + } + } + } + // If we reach here, either we never got a guestToken (500), or the token was invalid (403), + // so return a meaningful error. + if ($lastCode == 500) { + returnError('Could not obtain a guest token', 503); + } else if ($lastCode = 403) { + returnError('Our guest token is not valid', 503); + } else { + returnServerError('Unable to obtain contents'); + } } private function getRestId($username) {