From 51b6f87a916ba80c063a2f5471034642c9e089b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Thu, 9 Jul 2026 13:45:27 +0200 Subject: [PATCH 1/2] Replace `Icinga\Web\Url\ with `GuzzleHttp\Psr7\Uri` The icinga `Url::fromPath` method builds a path for the use in a icingaweb, stripping scheme and host if it matches the current request. This is undesired behavior for backend use. Since the `GraphiteWebClient` already uses GuzzleHttp it is logical to use the more versatile `UriInterface` interface and `Uri` class. --- library/Graphite/Graphing/GraphingTrait.php | 5 +- .../Graphite/Graphing/GraphiteWebClient.php | 54 ++++++++++--------- library/Graphite/Graphing/MetricsQuery.php | 12 ++--- library/Graphite/Web/Widget/GraphImage.php | 13 ++--- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/library/Graphite/Graphing/GraphingTrait.php b/library/Graphite/Graphing/GraphingTrait.php index 84aa81c..8254232 100644 --- a/library/Graphite/Graphing/GraphingTrait.php +++ b/library/Graphite/Graphing/GraphingTrait.php @@ -5,12 +5,11 @@ namespace Icinga\Module\Graphite\Graphing; +use GuzzleHttp\Psr7\Uri; use Icinga\Application\Config; use Icinga\Application\Icinga; use Icinga\Data\ConfigObject; use Icinga\Exception\ConfigurationError; -use Icinga\Module\Graphite\Web\FakeSchemeRequest; -use Icinga\Web\Url; trait GraphingTrait { @@ -72,7 +71,7 @@ public static function getMetricsDataSource() } static::$metricsDataSource = new MetricsDataSource( - (new GraphiteWebClient(Url::fromPath($graphite->url, [], new FakeSchemeRequest()))) + (new GraphiteWebClient(new Uri($graphite->url))) ->setUser($graphite->user) ->setPassword($graphite->password) ->setInsecure((bool) $graphite->insecure) diff --git a/library/Graphite/Graphing/GraphiteWebClient.php b/library/Graphite/Graphing/GraphiteWebClient.php index 65c18dd..c858d96 100644 --- a/library/Graphite/Graphing/GraphiteWebClient.php +++ b/library/Graphite/Graphing/GraphiteWebClient.php @@ -8,7 +8,7 @@ use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; use GuzzleHttp\Psr7\Request; -use Icinga\Web\Url; +use Psr\Http\Message\UriInterface; /** * HTTP interface to Graphite Web @@ -18,9 +18,9 @@ class GraphiteWebClient /** * Base URL of every Graphite Web HTTP request * - * @var Url + * @var UriInterface */ - protected $baseUrl; + protected $baseUri; /** * HTTP basic auth user for every Graphite Web HTTP request @@ -60,26 +60,26 @@ class GraphiteWebClient /** * Constructor * - * @param Url $baseUrl Base URL of every Graphite Web HTTP request + * @param UriInterface $baseUrl Base URL of every Graphite Web HTTP request */ - public function __construct(Url $baseUrl) + public function __construct(UriInterface $baseUrl) { $this->httpClient = new Client(); - $this->setBaseUrl($baseUrl); + $this->setBaseUri($baseUrl); } /** * Send an HTTP request to the configured Graphite Web and return the response's body * - * @param Url $url - * @param string $method - * @param string[] $headers - * @param string $body + * @param UriInterface $uri + * @param string $method + * @param string[] $headers + * @param string $body * * @return string */ - public function request(Url $url, $method = 'GET', array $headers = [], $body = null) + public function request(UriInterface $uri, $method = 'GET', array $headers = [], $body = null) { $headers['User-Agent'] = 'icingaweb2-module-graphite'; if ($this->user !== null) { @@ -88,7 +88,7 @@ public function request(Url $url, $method = 'GET', array $headers = [], $body = // TODO(ak): keep connections alive (TCP handshakes are a bit expensive and TLS handshakes are very expensive) return (string) $this->httpClient->send( - new Request($method, $this->completeUrl($url)->getAbsoluteUrl(), $headers, $body), + new Request($method, $this->completeUri($uri), $headers, $body), [ 'curl' => [ CURLOPT_SSL_VERIFYPEER => ! $this->insecure @@ -101,38 +101,42 @@ public function request(Url $url, $method = 'GET', array $headers = [], $body = /** * Complete the given relative URL according to the base URL * - * @param Url $url + * @param UriInterface $uri * - * @return Url + * @return UriInterface */ - public function completeUrl(Url $url) + public function completeUri(UriInterface $uri) { - $completeUrl = clone $this->baseUrl; - return $completeUrl - ->setPath(ltrim(rtrim($completeUrl->getPath(), '/') . '/' . ltrim($url->getPath(), '/'), '/')) - ->setParams($url->getParams()); + parse_str($this->baseUri->getQuery(), $originalParams); + parse_str($uri->getQuery(), $newParams); + + $uri = $this->baseUri + ->withPath(ltrim(rtrim($this->baseUri->getPath(), '/') . '/' . ltrim($uri->getPath(), '/'), '/')) + ->withQuery(http_build_query(array_merge($originalParams, $newParams))); + + return $uri; } /** * Get the base URL of every Graphite Web HTTP request * - * @return Url + * @return UriInterface */ - public function getBaseUrl() + public function getBaseUri() { - return $this->baseUrl; + return $this->baseUri; } /** * Set the base URL of every Graphite Web HTTP request * - * @param Url $baseUrl + * @param UriInterface $baseUri * * @return $this */ - public function setBaseUrl(Url $baseUrl) + public function setBaseUri(UriInterface $baseUri) { - $this->baseUrl = $baseUrl; + $this->baseUri = $baseUri; return $this; } diff --git a/library/Graphite/Graphing/MetricsQuery.php b/library/Graphite/Graphing/MetricsQuery.php index 6a2839d..ea8dde3 100644 --- a/library/Graphite/Graphing/MetricsQuery.php +++ b/library/Graphite/Graphing/MetricsQuery.php @@ -6,6 +6,7 @@ namespace Icinga\Module\Graphite\Graphing; use GuzzleHttp\Exception\ServerException; +use GuzzleHttp\Psr7\Uri; use Icinga\Data\Fetchable; use Icinga\Data\Filter\Filter; use Icinga\Data\Filterable; @@ -20,7 +21,6 @@ use Icinga\Module\Monitoring\Object\Macro; use Icinga\Module\Monitoring\Object\MonitoredObject; use Icinga\Util\Json; -use Icinga\Web\Url; use InvalidArgumentException; use ipl\Orm\Model; use ipl\Stdlib\Filter as IplFilter; @@ -173,19 +173,17 @@ public function fetchColumn() } $client = $this->dataSource->getClient(); - $url = Url::fromPath('metrics/expand', [ - 'query' => $this->base->resolve($filter, '*') - ]); + $uri = new Uri('metrics/expand?' . http_build_query(['query' => $this->base->resolve($filter, '*')])); try { - $res = Json::decode($client->request($url)); + $res = Json::decode($client->request($uri)); } catch (ServerException $e) { - IPT::recordf('Fetched 0 metric(s) from %s: %s', (string) $client->completeUrl($url), $e->getMessage()); + IPT::recordf('Fetched 0 metric(s) from %s: %s', (string) $client->completeUri($uri), $e->getMessage()); return []; } natsort($res->results); - IPT::recordf('Fetched %s metric(s) from %s', count($res->results), (string) $client->completeUrl($url)); + IPT::recordf('Fetched %s metric(s) from %s', count($res->results), (string) $client->completeUri($uri)); return array_values($res->results); } diff --git a/library/Graphite/Web/Widget/GraphImage.php b/library/Graphite/Web/Widget/GraphImage.php index 735d577..96d2618 100644 --- a/library/Graphite/Web/Widget/GraphImage.php +++ b/library/Graphite/Web/Widget/GraphImage.php @@ -5,8 +5,8 @@ namespace Icinga\Module\Graphite\Web\Widget; +use GuzzleHttp\Psr7\Uri; use Icinga\Module\Graphite\Graphing\Chart; -use Icinga\Web\Url; use Icinga\Web\UrlParams; use Icinga\Web\Widget\AbstractWidget; use RuntimeException; @@ -109,7 +109,7 @@ public function render() $params->add('target', $template->getCurves()[$curveName][1]->resolve($allVars)); } - $url = Url::fromPath('/render')->setParams($params); + $uri = new Uri('/render?' . $params->toString()); $headers = [ 'Accept-language' => 'en', 'Content-type' => 'application/x-www-form-urlencoded' @@ -117,19 +117,20 @@ public function render() for (;;) { try { - $this->rendered = $graphiteWebClient->request($url, 'GET', $headers); + $this->rendered = $graphiteWebClient->request($uri, 'GET', $headers); } catch (RuntimeException $e) { if (preg_match('/\b500\b/', $e->getMessage())) { // A 500 Internal Server Error, probably because of // a division by zero because of a too low time range to render. - $until = (int) $url->getParam('until'); - $diff = $until - (int) $url->getParam('from'); + $until = (int) $params['until']; + $diff = $until - (int) $params['from']; // Try to render a higher time range, but give up // once our default (1h) has been reached (non successfully). if ($diff < 3600) { - $url->setParam('from', sprintf('%F', ($until - $diff * 2))); + $params['from'] = sprintf('%F', ($until - $diff * 2)); + $uri = $uri->withQuery(http_build_query($params)); continue; } } From c4ab1c288ef1bc06097a7124ed48bf409bdf1c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Thu, 9 Jul 2026 13:40:26 +0200 Subject: [PATCH 2/2] Remove `FakeSchemeRequest` This class was used to get the `Url::fromPath` method into never stripping the scheme and host parts, and forcing the URL to always be external, even if the configured URL matches the current request url. With the swtich to the `UriInterface` this workaround is no longer required. --- library/Graphite/Web/FakeSchemeRequest.php | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 library/Graphite/Web/FakeSchemeRequest.php diff --git a/library/Graphite/Web/FakeSchemeRequest.php b/library/Graphite/Web/FakeSchemeRequest.php deleted file mode 100644 index 66e2b8f..0000000 --- a/library/Graphite/Web/FakeSchemeRequest.php +++ /dev/null @@ -1,21 +0,0 @@ - -// SPDX-License-Identifier: GPL-3.0-or-later - -namespace Icinga\Module\Graphite\Web; - -use Icinga\Web\Request; - -/** - * Rationale: - * - * {@link Url::fromPath()} doesn't preserve URLs which seem to be internal as they are. - */ -class FakeSchemeRequest extends Request -{ - public function getScheme() - { - return 'a random url scheme which always differs from the current request\'s one'; - } -}