From 8ada66b0e15f8618d16562108427713f4e9a0f72 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Thu, 11 Jan 2024 17:06:05 +0100 Subject: [PATCH 1/7] fix: propagate status code from platform api when routing wiki requests --- dist-persist/wbstack/src/Info/GlobalSet.php | 32 ++++++++++++--------- dist-persist/wbstack/src/Shim/Cli.php | 6 ++-- dist-persist/wbstack/src/Shim/Web.php | 10 ++++--- dist/wbstack/src/Info/GlobalSet.php | 32 ++++++++++++--------- dist/wbstack/src/Shim/Cli.php | 6 ++-- dist/wbstack/src/Shim/Web.php | 10 ++++--- 6 files changed, 56 insertions(+), 40 deletions(-) diff --git a/dist-persist/wbstack/src/Info/GlobalSet.php b/dist-persist/wbstack/src/Info/GlobalSet.php index c7a615aff..491b4cf52 100644 --- a/dist-persist/wbstack/src/Info/GlobalSet.php +++ b/dist-persist/wbstack/src/Info/GlobalSet.php @@ -10,26 +10,26 @@ class GlobalSet { /** * @param string $requestDomain A request domain, or 'maint' Example: 'addshore-alpha.wiki.opencura.com' - * @return bool was the global successfully set? + * @return void */ - public static function forDomain( $requestDomain ) { + public static function forDomain($requestDomain) { // Normalize the domain by setting to lowercase $requestDomain = strtolower($requestDomain); // If the domain is 'maint' then set the maint settings - if($requestDomain === 'maint') { + if ($requestDomain === 'maint') { self::forMaint(); - return true; + return; } - $info = self::getCachedOrFreshInfo( $requestDomain ); + $info = self::getCachedOrFreshInfo($requestDomain); if($info === null) { - return false; + $notFound = new \Exception("No wiki was found for domain $requestDomain", 404); + throw $notFound; } - self::setGlobal( $info ); - return true; + self::setGlobal($info); } /** @@ -120,16 +120,20 @@ private static function getInfoFromApi( $requestDomain ) { curl_setopt($client, CURLOPT_HTTPHEADER, $headers); curl_setopt($client, CURLOPT_RETURNTRANSFER, true); curl_setopt( $client, CURLOPT_USERAGENT, "WBStack - MediaWiki - WBStackInfo::getInfoFromApi" ); - + $response = curl_exec($client); + $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); - // TODO detect non 200 response here, and pass that out to the user as an error - - $info = WBStackInfo::newFromJsonString($response, $requestDomain); - if (!$info) { + if ($responseCode === 404) { return null; } + + if ($responseCode > 299) { + throw new \Exception("Unexpected status code $responseCode from Platform API for domain $requestDomain.", $responseCode); + } + + $info = WBStackInfo::newFromJsonString($response, $requestDomain); return $info; } -} \ No newline at end of file +} diff --git a/dist-persist/wbstack/src/Shim/Cli.php b/dist-persist/wbstack/src/Shim/Cli.php index 7e4301676..707476816 100644 --- a/dist-persist/wbstack/src/Shim/Cli.php +++ b/dist-persist/wbstack/src/Shim/Cli.php @@ -15,7 +15,9 @@ } // Try and get the wiki info (from env var) or fail with a message -if(!\WBStack\Info\GlobalSet::forDomain( getenv( 'WBS_DOMAIN' ) )) { +try { + \WBStack\Info\GlobalSet::forDomain( getenv( 'WBS_DOMAIN' ) ); +} catch (Exception $ex) { echo 'Failed to work for WBS_DOMAIN: ' . getenv( 'WBS_DOMAIN' ); die(1); -} \ No newline at end of file +} diff --git a/dist-persist/wbstack/src/Shim/Web.php b/dist-persist/wbstack/src/Shim/Web.php index 2a9df13b4..65000f177 100644 --- a/dist-persist/wbstack/src/Shim/Web.php +++ b/dist-persist/wbstack/src/Shim/Web.php @@ -2,11 +2,13 @@ require_once __DIR__ . '/../loadShim.php'; -// Try and get the wiki info, for fail with a 404 web page -if(!\WBStack\Info\GlobalSet::forDomain( $_SERVER['SERVER_NAME'] ) ) { - http_response_code(404); +// Try and get the wiki info or fail +try { + \WBStack\Info\GlobalSet::forDomain($_SERVER['SERVER_NAME']); +} catch (Exception $ex) { + http_response_code($ex->getCode()); echo "You have requested the domain: " . $_SERVER['SERVER_NAME'] . ". But that wiki can not currently be loaded.\n"; - echo "It may never have existed, or it might now be deleted.\n"; + echo "It may never have existed, it might now be deleted, or there was a server error.\n"; die(1); } diff --git a/dist/wbstack/src/Info/GlobalSet.php b/dist/wbstack/src/Info/GlobalSet.php index c7a615aff..491b4cf52 100644 --- a/dist/wbstack/src/Info/GlobalSet.php +++ b/dist/wbstack/src/Info/GlobalSet.php @@ -10,26 +10,26 @@ class GlobalSet { /** * @param string $requestDomain A request domain, or 'maint' Example: 'addshore-alpha.wiki.opencura.com' - * @return bool was the global successfully set? + * @return void */ - public static function forDomain( $requestDomain ) { + public static function forDomain($requestDomain) { // Normalize the domain by setting to lowercase $requestDomain = strtolower($requestDomain); // If the domain is 'maint' then set the maint settings - if($requestDomain === 'maint') { + if ($requestDomain === 'maint') { self::forMaint(); - return true; + return; } - $info = self::getCachedOrFreshInfo( $requestDomain ); + $info = self::getCachedOrFreshInfo($requestDomain); if($info === null) { - return false; + $notFound = new \Exception("No wiki was found for domain $requestDomain", 404); + throw $notFound; } - self::setGlobal( $info ); - return true; + self::setGlobal($info); } /** @@ -120,16 +120,20 @@ private static function getInfoFromApi( $requestDomain ) { curl_setopt($client, CURLOPT_HTTPHEADER, $headers); curl_setopt($client, CURLOPT_RETURNTRANSFER, true); curl_setopt( $client, CURLOPT_USERAGENT, "WBStack - MediaWiki - WBStackInfo::getInfoFromApi" ); - + $response = curl_exec($client); + $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); - // TODO detect non 200 response here, and pass that out to the user as an error - - $info = WBStackInfo::newFromJsonString($response, $requestDomain); - if (!$info) { + if ($responseCode === 404) { return null; } + + if ($responseCode > 299) { + throw new \Exception("Unexpected status code $responseCode from Platform API for domain $requestDomain.", $responseCode); + } + + $info = WBStackInfo::newFromJsonString($response, $requestDomain); return $info; } -} \ No newline at end of file +} diff --git a/dist/wbstack/src/Shim/Cli.php b/dist/wbstack/src/Shim/Cli.php index 7e4301676..707476816 100644 --- a/dist/wbstack/src/Shim/Cli.php +++ b/dist/wbstack/src/Shim/Cli.php @@ -15,7 +15,9 @@ } // Try and get the wiki info (from env var) or fail with a message -if(!\WBStack\Info\GlobalSet::forDomain( getenv( 'WBS_DOMAIN' ) )) { +try { + \WBStack\Info\GlobalSet::forDomain( getenv( 'WBS_DOMAIN' ) ); +} catch (Exception $ex) { echo 'Failed to work for WBS_DOMAIN: ' . getenv( 'WBS_DOMAIN' ); die(1); -} \ No newline at end of file +} diff --git a/dist/wbstack/src/Shim/Web.php b/dist/wbstack/src/Shim/Web.php index 2a9df13b4..65000f177 100644 --- a/dist/wbstack/src/Shim/Web.php +++ b/dist/wbstack/src/Shim/Web.php @@ -2,11 +2,13 @@ require_once __DIR__ . '/../loadShim.php'; -// Try and get the wiki info, for fail with a 404 web page -if(!\WBStack\Info\GlobalSet::forDomain( $_SERVER['SERVER_NAME'] ) ) { - http_response_code(404); +// Try and get the wiki info or fail +try { + \WBStack\Info\GlobalSet::forDomain($_SERVER['SERVER_NAME']); +} catch (Exception $ex) { + http_response_code($ex->getCode()); echo "You have requested the domain: " . $_SERVER['SERVER_NAME'] . ". But that wiki can not currently be loaded.\n"; - echo "It may never have existed, or it might now be deleted.\n"; + echo "It may never have existed, it might now be deleted, or there was a server error.\n"; die(1); } From b7666ab6ae816c8236236eb0291684b2ea2579bf Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Mon, 15 Jan 2024 09:19:48 +0100 Subject: [PATCH 2/7] refactor: use specific exception --- dist-persist/wbstack/src/Info/GlobalSet.php | 28 ++++++++++++++------- dist-persist/wbstack/src/Shim/Web.php | 11 +++++--- dist/wbstack/src/Info/GlobalSet.php | 28 ++++++++++++++------- dist/wbstack/src/Shim/Web.php | 11 +++++--- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/dist-persist/wbstack/src/Info/GlobalSet.php b/dist-persist/wbstack/src/Info/GlobalSet.php index 491b4cf52..cd749d580 100644 --- a/dist-persist/wbstack/src/Info/GlobalSet.php +++ b/dist-persist/wbstack/src/Info/GlobalSet.php @@ -2,10 +2,13 @@ namespace WBStack\Info; +class GlobalSetException extends \Exception {} + /** * A class for setting a global holding a WBStackInfo object. * This includes lookup of the data for the global from cache or API. */ + class GlobalSet { /** @@ -24,9 +27,10 @@ public static function forDomain($requestDomain) { $info = self::getCachedOrFreshInfo($requestDomain); - if($info === null) { - $notFound = new \Exception("No wiki was found for domain $requestDomain", 404); - throw $notFound; + if ($info === null) { + throw new GlobalSetException( + "No wiki was found for domain $requestDomain", 404, + ); } self::setGlobal($info); @@ -71,7 +75,14 @@ private static function getCachedOrFreshInfo( $requestDomain ) { // TODO create an APC lock saying this proc is going to get fresh data? // TODO in reality all of this needs to change... - $info = self::getInfoFromApi( $requestDomain ); + try { + $info = self::getInfoFromApi( $requestDomain ); + } catch (GlobalSetException $ex) { + if ($ex->getCode() !== 404) { + throw $ex; + } + $info = null; + } // Cache positive results for 10 seconds, negative for 2 $ttl = $info ? 10 : 2; @@ -124,12 +135,11 @@ private static function getInfoFromApi( $requestDomain ) { $response = curl_exec($client); $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); - if ($responseCode === 404) { - return null; - } - if ($responseCode > 299) { - throw new \Exception("Unexpected status code $responseCode from Platform API for domain $requestDomain.", $responseCode); + throw new GlobalSetException( + "Unexpected status code $responseCode from Platform API for domain $requestDomain.", + $responseCode, + ); } $info = WBStackInfo::newFromJsonString($response, $requestDomain); diff --git a/dist-persist/wbstack/src/Shim/Web.php b/dist-persist/wbstack/src/Shim/Web.php index 65000f177..f11c557d4 100644 --- a/dist-persist/wbstack/src/Shim/Web.php +++ b/dist-persist/wbstack/src/Shim/Web.php @@ -5,10 +5,15 @@ // Try and get the wiki info or fail try { \WBStack\Info\GlobalSet::forDomain($_SERVER['SERVER_NAME']); -} catch (Exception $ex) { +} catch (\WBStack\Info\GlobalSetException $ex) { http_response_code($ex->getCode()); - echo "You have requested the domain: " . $_SERVER['SERVER_NAME'] . ". But that wiki can not currently be loaded.\n"; - echo "It may never have existed, it might now be deleted, or there was a server error.\n"; + echo "You have requested the domain: " . $_SERVER['SERVER_NAME'] . ". But that wiki can not currently be loaded.".PHP_EOL; + if ($ex->getCode() === 404) { + echo "It may never have existed or it might now be deleted.".PHP_EOL; + } else { + echo "There was a server error in the platform API.".PHP_EOL; + } + echo $ex->getMessage(); die(1); } diff --git a/dist/wbstack/src/Info/GlobalSet.php b/dist/wbstack/src/Info/GlobalSet.php index 491b4cf52..cd749d580 100644 --- a/dist/wbstack/src/Info/GlobalSet.php +++ b/dist/wbstack/src/Info/GlobalSet.php @@ -2,10 +2,13 @@ namespace WBStack\Info; +class GlobalSetException extends \Exception {} + /** * A class for setting a global holding a WBStackInfo object. * This includes lookup of the data for the global from cache or API. */ + class GlobalSet { /** @@ -24,9 +27,10 @@ public static function forDomain($requestDomain) { $info = self::getCachedOrFreshInfo($requestDomain); - if($info === null) { - $notFound = new \Exception("No wiki was found for domain $requestDomain", 404); - throw $notFound; + if ($info === null) { + throw new GlobalSetException( + "No wiki was found for domain $requestDomain", 404, + ); } self::setGlobal($info); @@ -71,7 +75,14 @@ private static function getCachedOrFreshInfo( $requestDomain ) { // TODO create an APC lock saying this proc is going to get fresh data? // TODO in reality all of this needs to change... - $info = self::getInfoFromApi( $requestDomain ); + try { + $info = self::getInfoFromApi( $requestDomain ); + } catch (GlobalSetException $ex) { + if ($ex->getCode() !== 404) { + throw $ex; + } + $info = null; + } // Cache positive results for 10 seconds, negative for 2 $ttl = $info ? 10 : 2; @@ -124,12 +135,11 @@ private static function getInfoFromApi( $requestDomain ) { $response = curl_exec($client); $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); - if ($responseCode === 404) { - return null; - } - if ($responseCode > 299) { - throw new \Exception("Unexpected status code $responseCode from Platform API for domain $requestDomain.", $responseCode); + throw new GlobalSetException( + "Unexpected status code $responseCode from Platform API for domain $requestDomain.", + $responseCode, + ); } $info = WBStackInfo::newFromJsonString($response, $requestDomain); diff --git a/dist/wbstack/src/Shim/Web.php b/dist/wbstack/src/Shim/Web.php index 65000f177..f11c557d4 100644 --- a/dist/wbstack/src/Shim/Web.php +++ b/dist/wbstack/src/Shim/Web.php @@ -5,10 +5,15 @@ // Try and get the wiki info or fail try { \WBStack\Info\GlobalSet::forDomain($_SERVER['SERVER_NAME']); -} catch (Exception $ex) { +} catch (\WBStack\Info\GlobalSetException $ex) { http_response_code($ex->getCode()); - echo "You have requested the domain: " . $_SERVER['SERVER_NAME'] . ". But that wiki can not currently be loaded.\n"; - echo "It may never have existed, it might now be deleted, or there was a server error.\n"; + echo "You have requested the domain: " . $_SERVER['SERVER_NAME'] . ". But that wiki can not currently be loaded.".PHP_EOL; + if ($ex->getCode() === 404) { + echo "It may never have existed or it might now be deleted.".PHP_EOL; + } else { + echo "There was a server error in the platform API.".PHP_EOL; + } + echo $ex->getMessage(); die(1); } From 0b9240f0c5fe01fbd7deae724a0a356de837f3ba Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Mon, 15 Jan 2024 12:15:29 +0100 Subject: [PATCH 3/7] fix: handle curl errors when fetching wiki info --- dist-persist/wbstack/src/Info/GlobalSet.php | 6 ++++++ dist/wbstack/src/Info/GlobalSet.php | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/dist-persist/wbstack/src/Info/GlobalSet.php b/dist-persist/wbstack/src/Info/GlobalSet.php index cd749d580..2cf20ff92 100644 --- a/dist-persist/wbstack/src/Info/GlobalSet.php +++ b/dist-persist/wbstack/src/Info/GlobalSet.php @@ -133,6 +133,12 @@ private static function getInfoFromApi( $requestDomain ) { curl_setopt( $client, CURLOPT_USERAGENT, "WBStack - MediaWiki - WBStackInfo::getInfoFromApi" ); $response = curl_exec($client); + if ($response === false) { + throw new GlobalSetException( + "Unexpected error getting wiki info from api: ".curl_error($client), + 502, + ); + } $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); if ($responseCode > 299) { diff --git a/dist/wbstack/src/Info/GlobalSet.php b/dist/wbstack/src/Info/GlobalSet.php index cd749d580..2cf20ff92 100644 --- a/dist/wbstack/src/Info/GlobalSet.php +++ b/dist/wbstack/src/Info/GlobalSet.php @@ -133,6 +133,12 @@ private static function getInfoFromApi( $requestDomain ) { curl_setopt( $client, CURLOPT_USERAGENT, "WBStack - MediaWiki - WBStackInfo::getInfoFromApi" ); $response = curl_exec($client); + if ($response === false) { + throw new GlobalSetException( + "Unexpected error getting wiki info from api: ".curl_error($client), + 502, + ); + } $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); if ($responseCode > 299) { From 9e1437371dbb03af3df02c1dbea85afb67064bf0 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Mon, 15 Jan 2024 15:17:47 +0100 Subject: [PATCH 4/7] refactor: model failed responses in result class --- dist-persist/wbstack/src/Info/GlobalSet.php | 45 +++++++------------ dist-persist/wbstack/src/Info/WBStackInfo.php | 9 ++++ dist/wbstack/src/Info/GlobalSet.php | 45 +++++++------------ dist/wbstack/src/Info/WBStackInfo.php | 9 ++++ 4 files changed, 48 insertions(+), 60 deletions(-) diff --git a/dist-persist/wbstack/src/Info/GlobalSet.php b/dist-persist/wbstack/src/Info/GlobalSet.php index 2cf20ff92..1960519e5 100644 --- a/dist-persist/wbstack/src/Info/GlobalSet.php +++ b/dist-persist/wbstack/src/Info/GlobalSet.php @@ -26,10 +26,10 @@ public static function forDomain($requestDomain) { } $info = self::getCachedOrFreshInfo($requestDomain); - - if ($info === null) { + if ($info instanceof WBStackLookupFailure) { throw new GlobalSetException( - "No wiki was found for domain $requestDomain", 404, + "Failure looking up wiki $requestDomain.", + $info->statusCode, ); } @@ -64,7 +64,7 @@ private static function setGlobal( $info ) { /** * @param string $requestDomain - * @return WBStackInfo|null + * @return WBStackInfo|WBStackLookupFailure */ private static function getCachedOrFreshInfo( $requestDomain ) { $info = self::getInfoFromApcCache( $requestDomain ); @@ -74,18 +74,10 @@ private static function getCachedOrFreshInfo( $requestDomain ) { // TODO create an APC lock saying this proc is going to get fresh data? // TODO in reality all of this needs to change... + $info = self::getInfoFromApi( $requestDomain ); - try { - $info = self::getInfoFromApi( $requestDomain ); - } catch (GlobalSetException $ex) { - if ($ex->getCode() !== 404) { - throw $ex; - } - $info = null; - } - - // Cache positive results for 10 seconds, negative for 2 - $ttl = $info ? 10 : 2; + // Cache positive results for 10 seconds, failures for 2 + $ttl = $info instanceof WBStackInfo ? 10 : 2; self::writeInfoToApcCache( $requestDomain, $info, $ttl ); return $info; @@ -93,7 +85,7 @@ private static function getCachedOrFreshInfo( $requestDomain ) { /** * @param string $requestDomain - * @return WBStackInfo|null|bool false if no info is cached (should check), null for cached empty data (should not check) + * @return WBStackInfo|WBStackLookupFailure|false false if no info is cached (should check) */ private static function getInfoFromApcCache( $requestDomain ) { return apcu_fetch( self::cacheKey($requestDomain) ); @@ -101,10 +93,10 @@ private static function getInfoFromApcCache( $requestDomain ) { /** * @param string $requestDomain - * @param WBStackInfo|null $info + * @param WBStackInfo|WBStackLookupFailure $info * @param int $ttl */ - private static function writeInfoToApcCache( $requestDomain, ?WBStackInfo $info, $ttl ) { + private static function writeInfoToApcCache( $requestDomain, $info, $ttl ) { $result = apcu_store( self::cacheKey($requestDomain), $info, $ttl ); if(!$result) { // TODO log failed stores?! @@ -117,7 +109,7 @@ private static function cacheKey( $requestDomain ) { /** * @param string $requestDomain - * @return WBStackInfo|null + * @return WBStackInfo|WBStackLookupFailure */ private static function getInfoFromApi( $requestDomain ) { // START generic getting of wiki info from domain @@ -134,22 +126,15 @@ private static function getInfoFromApi( $requestDomain ) { $response = curl_exec($client); if ($response === false) { - throw new GlobalSetException( - "Unexpected error getting wiki info from api: ".curl_error($client), - 502, - ); + return new WBStackLookupFailure(502); } $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); - if ($responseCode > 299) { - throw new GlobalSetException( - "Unexpected status code $responseCode from Platform API for domain $requestDomain.", - $responseCode, - ); + if ($responseCode > 399) { + return new WBStackLookupFailure($responseCode); } - $info = WBStackInfo::newFromJsonString($response, $requestDomain); - return $info; + return WBStackInfo::newFromJsonString($response, $requestDomain); } } diff --git a/dist-persist/wbstack/src/Info/WBStackInfo.php b/dist-persist/wbstack/src/Info/WBStackInfo.php index 3ad6df1c7..c38316bc8 100644 --- a/dist-persist/wbstack/src/Info/WBStackInfo.php +++ b/dist-persist/wbstack/src/Info/WBStackInfo.php @@ -75,3 +75,12 @@ public function __get($name) } } + +class WBStackLookupFailure +{ + public $statusCode; + + public function __construct($statusCode) { + $this->statusCode = $statusCode; + } +} diff --git a/dist/wbstack/src/Info/GlobalSet.php b/dist/wbstack/src/Info/GlobalSet.php index 2cf20ff92..1960519e5 100644 --- a/dist/wbstack/src/Info/GlobalSet.php +++ b/dist/wbstack/src/Info/GlobalSet.php @@ -26,10 +26,10 @@ public static function forDomain($requestDomain) { } $info = self::getCachedOrFreshInfo($requestDomain); - - if ($info === null) { + if ($info instanceof WBStackLookupFailure) { throw new GlobalSetException( - "No wiki was found for domain $requestDomain", 404, + "Failure looking up wiki $requestDomain.", + $info->statusCode, ); } @@ -64,7 +64,7 @@ private static function setGlobal( $info ) { /** * @param string $requestDomain - * @return WBStackInfo|null + * @return WBStackInfo|WBStackLookupFailure */ private static function getCachedOrFreshInfo( $requestDomain ) { $info = self::getInfoFromApcCache( $requestDomain ); @@ -74,18 +74,10 @@ private static function getCachedOrFreshInfo( $requestDomain ) { // TODO create an APC lock saying this proc is going to get fresh data? // TODO in reality all of this needs to change... + $info = self::getInfoFromApi( $requestDomain ); - try { - $info = self::getInfoFromApi( $requestDomain ); - } catch (GlobalSetException $ex) { - if ($ex->getCode() !== 404) { - throw $ex; - } - $info = null; - } - - // Cache positive results for 10 seconds, negative for 2 - $ttl = $info ? 10 : 2; + // Cache positive results for 10 seconds, failures for 2 + $ttl = $info instanceof WBStackInfo ? 10 : 2; self::writeInfoToApcCache( $requestDomain, $info, $ttl ); return $info; @@ -93,7 +85,7 @@ private static function getCachedOrFreshInfo( $requestDomain ) { /** * @param string $requestDomain - * @return WBStackInfo|null|bool false if no info is cached (should check), null for cached empty data (should not check) + * @return WBStackInfo|WBStackLookupFailure|false false if no info is cached (should check) */ private static function getInfoFromApcCache( $requestDomain ) { return apcu_fetch( self::cacheKey($requestDomain) ); @@ -101,10 +93,10 @@ private static function getInfoFromApcCache( $requestDomain ) { /** * @param string $requestDomain - * @param WBStackInfo|null $info + * @param WBStackInfo|WBStackLookupFailure $info * @param int $ttl */ - private static function writeInfoToApcCache( $requestDomain, ?WBStackInfo $info, $ttl ) { + private static function writeInfoToApcCache( $requestDomain, $info, $ttl ) { $result = apcu_store( self::cacheKey($requestDomain), $info, $ttl ); if(!$result) { // TODO log failed stores?! @@ -117,7 +109,7 @@ private static function cacheKey( $requestDomain ) { /** * @param string $requestDomain - * @return WBStackInfo|null + * @return WBStackInfo|WBStackLookupFailure */ private static function getInfoFromApi( $requestDomain ) { // START generic getting of wiki info from domain @@ -134,22 +126,15 @@ private static function getInfoFromApi( $requestDomain ) { $response = curl_exec($client); if ($response === false) { - throw new GlobalSetException( - "Unexpected error getting wiki info from api: ".curl_error($client), - 502, - ); + return new WBStackLookupFailure(502); } $responseCode = intval(curl_getinfo($client, CURLINFO_RESPONSE_CODE)); - if ($responseCode > 299) { - throw new GlobalSetException( - "Unexpected status code $responseCode from Platform API for domain $requestDomain.", - $responseCode, - ); + if ($responseCode > 399) { + return new WBStackLookupFailure($responseCode); } - $info = WBStackInfo::newFromJsonString($response, $requestDomain); - return $info; + return WBStackInfo::newFromJsonString($response, $requestDomain); } } diff --git a/dist/wbstack/src/Info/WBStackInfo.php b/dist/wbstack/src/Info/WBStackInfo.php index 3ad6df1c7..c38316bc8 100644 --- a/dist/wbstack/src/Info/WBStackInfo.php +++ b/dist/wbstack/src/Info/WBStackInfo.php @@ -75,3 +75,12 @@ public function __get($name) } } + +class WBStackLookupFailure +{ + public $statusCode; + + public function __construct($statusCode) { + $this->statusCode = $statusCode; + } +} From 0116f7532959f15a2934b0f62bd3c6d86217102e Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Mon, 15 Jan 2024 15:23:19 +0100 Subject: [PATCH 5/7] test: cover newly introduced behavior in verification tests --- .github/workflows/mediawiki.verify.yml | 4 ++++ docker-compose/fake-api/index.php | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/mediawiki.verify.yml b/.github/workflows/mediawiki.verify.yml index ddae8d9ad..422c56e31 100644 --- a/.github/workflows/mediawiki.verify.yml +++ b/.github/workflows/mediawiki.verify.yml @@ -32,3 +32,7 @@ jobs: - run: curl -L -s -N "http://site2.localhost:8001/w/api.php" | grep -q "Main module" - run: curl -L -s -N "http://site2.localhost:8001/w/load.php" | grep -q "no modules were requested" - run: curl -L -s -N "http://site2.localhost:8001/w/rest.php" | grep -q "did not match any known handler" + + - run: curl -L -s -N "http://notfound.localhost:8001/wiki/Main_Page" | grep -q "It may never have existed" + + - run: curl -L -s -N "http://failwith500.localhost:8001/wiki/Main_Page" | grep -q "server error in the platform API" diff --git a/docker-compose/fake-api/index.php b/docker-compose/fake-api/index.php index 418d227a9..054ddfd48 100644 --- a/docker-compose/fake-api/index.php +++ b/docker-compose/fake-api/index.php @@ -16,8 +16,15 @@ // subdomain is 1 element $subdomain = $matches[1]; +if ( $subdomain === 'failwith500' ) { + http_response_code(500); + echo "Internal server error"; + die(1); +} + $file = __DIR__ . '/WikiInfo-'.$subdomain.'.json'; if ( !file_exists($file) ) { + http_response_code(404); echo 'Requested subdomain does not exist in test data'; die(1); } From 75c7bd9505826fb41bd8abc6083af195a7fc156f Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Tue, 16 Jan 2024 12:20:40 +0100 Subject: [PATCH 6/7] fix: handle malformed payloads from api --- .github/workflows/mediawiki.verify.yml | 2 ++ composer.json | 2 +- dist-persist/wbstack/src/Info/GlobalSet.php | 6 +++++- dist-persist/wbstack/src/Info/WBStackInfo.php | 4 ++-- dist/wbstack/src/Info/GlobalSet.php | 6 +++++- dist/wbstack/src/Info/WBStackInfo.php | 4 ++-- docker-compose/fake-api/WikiInfo-broken.json | 1 + 7 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 docker-compose/fake-api/WikiInfo-broken.json diff --git a/.github/workflows/mediawiki.verify.yml b/.github/workflows/mediawiki.verify.yml index 422c56e31..e62931944 100644 --- a/.github/workflows/mediawiki.verify.yml +++ b/.github/workflows/mediawiki.verify.yml @@ -36,3 +36,5 @@ jobs: - run: curl -L -s -N "http://notfound.localhost:8001/wiki/Main_Page" | grep -q "It may never have existed" - run: curl -L -s -N "http://failwith500.localhost:8001/wiki/Main_Page" | grep -q "server error in the platform API" + + - run: curl -L -s -N "http://broken.localhost:8001/wiki/Main_Page" | grep -q "server error in the platform API" diff --git a/composer.json b/composer.json index f27b48c84..60156b4cb 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "scripts": { "lint": [ "parallel-lint --exclude ./dist --exclude ./sync/.tmp .", - "rc=0 && for x in `find . -not -path './sync/.tmp/*' -not -path './dist/*' -name \"*.json\" -type f`; do vendor/bin/jsonlint \"$x\" || rc=$?; done && exit $rc" + "rc=0 && for x in `find . -not -path './sync/.tmp/*' -not -path './dist/*' -name \"*.json\" ! -name \"WikiInfo-broken.json\" -type f`; do vendor/bin/jsonlint \"$x\" || rc=$?; done && exit $rc" ] } } diff --git a/dist-persist/wbstack/src/Info/GlobalSet.php b/dist-persist/wbstack/src/Info/GlobalSet.php index 1960519e5..f69a470d8 100644 --- a/dist-persist/wbstack/src/Info/GlobalSet.php +++ b/dist-persist/wbstack/src/Info/GlobalSet.php @@ -134,7 +134,11 @@ private static function getInfoFromApi( $requestDomain ) { return new WBStackLookupFailure($responseCode); } - return WBStackInfo::newFromJsonString($response, $requestDomain); + try { + return WBStackInfo::newFromJsonString($response, $requestDomain); + } catch (\Exception $ex) { + return new WBStackLookupFailure(502); + } } } diff --git a/dist-persist/wbstack/src/Info/WBStackInfo.php b/dist-persist/wbstack/src/Info/WBStackInfo.php index c38316bc8..7ce403c57 100644 --- a/dist-persist/wbstack/src/Info/WBStackInfo.php +++ b/dist-persist/wbstack/src/Info/WBStackInfo.php @@ -41,7 +41,7 @@ public static function newFromJsonString( $infoJsonString, $requestDomain ) { // Check if the string we were given was invalid and thus not parsed! if ( $data === null ) { - return null; + throw new \Exception('Unexpected malformed payload.'); } // Get the inner data from the response @@ -49,7 +49,7 @@ public static function newFromJsonString( $infoJsonString, $requestDomain ) { // Data from the api should always be an array with at least an id... if (!is_object($data) || !property_exists($data, 'id')) { - return null; + throw new \Exception('Unexpected payload shape.'); } $info = new self($data); diff --git a/dist/wbstack/src/Info/GlobalSet.php b/dist/wbstack/src/Info/GlobalSet.php index 1960519e5..f69a470d8 100644 --- a/dist/wbstack/src/Info/GlobalSet.php +++ b/dist/wbstack/src/Info/GlobalSet.php @@ -134,7 +134,11 @@ private static function getInfoFromApi( $requestDomain ) { return new WBStackLookupFailure($responseCode); } - return WBStackInfo::newFromJsonString($response, $requestDomain); + try { + return WBStackInfo::newFromJsonString($response, $requestDomain); + } catch (\Exception $ex) { + return new WBStackLookupFailure(502); + } } } diff --git a/dist/wbstack/src/Info/WBStackInfo.php b/dist/wbstack/src/Info/WBStackInfo.php index c38316bc8..7ce403c57 100644 --- a/dist/wbstack/src/Info/WBStackInfo.php +++ b/dist/wbstack/src/Info/WBStackInfo.php @@ -41,7 +41,7 @@ public static function newFromJsonString( $infoJsonString, $requestDomain ) { // Check if the string we were given was invalid and thus not parsed! if ( $data === null ) { - return null; + throw new \Exception('Unexpected malformed payload.'); } // Get the inner data from the response @@ -49,7 +49,7 @@ public static function newFromJsonString( $infoJsonString, $requestDomain ) { // Data from the api should always be an array with at least an id... if (!is_object($data) || !property_exists($data, 'id')) { - return null; + throw new \Exception('Unexpected payload shape.'); } $info = new self($data); diff --git a/docker-compose/fake-api/WikiInfo-broken.json b/docker-compose/fake-api/WikiInfo-broken.json new file mode 100644 index 000000000..79a498548 --- /dev/null +++ b/docker-compose/fake-api/WikiInfo-broken.json @@ -0,0 +1 @@ +{"success":tr From 41e6c571924974977b48d419ca4f6f8445f92406 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Tue, 16 Jan 2024 17:09:26 +0100 Subject: [PATCH 7/7] docs: add CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3aae8a64..1abdaa6ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ Tags have the format: `---` +## 1.39-7.4-20240116-0 +- On failure, propagate status code from Platform API to clients (T343744) + ## 1.39-7.4-20240103-0 - Add QuestyCaptcha config option