From 22b3af61b568732861be17e9759be68715a709fb Mon Sep 17 00:00:00 2001
From: Kevin Morris <kevr@0cost.org>
Date: Wed, 13 Oct 2021 17:10:16 -0700
Subject: [PATCH] fix(PHP): sanitize and produce metrics at shutdown

This change now requires that PHP routes do not return HTTP 404
to be considered for the /metrics population. Additionally,
we make a small sanitization here to avoid trailing '/'
characters, unless we're on the homepage route.

Signed-off-by: Kevin Morris <kevr@0cost.org>
---
 web/html/index.php          | 24 ++---------------------
 web/lib/metricfuncs.inc.php | 38 +++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 22 deletions(-)

diff --git a/web/html/index.php b/web/html/index.php
index 82a44c55b..990469303 100644
--- a/web/html/index.php
+++ b/web/html/index.php
@@ -13,28 +13,8 @@ $query_string = $_SERVER['QUERY_STRING'];
 
 // If no options.cache is configured, we no-op metric storage operations.
 $is_cached = defined('EXTENSION_LOADED_APC') || defined('EXTENSION_LOADED_MEMCACHE');
-if ($is_cached) {
-	$method = $_SERVER['REQUEST_METHOD'];
-	// We'll always add +1 to our total request count to this $path,
-	// unless this path == /metrics.
-	if ($path !== "/metrics")
-		add_metric("http_requests_count", $method, $path);
-
-	// Extract $type out of $query_string, if we can.
-	$type = null;
-	$query = array();
-	if ($query_string)
-		parse_str($query_string, $query);
-	$type = $query['type'];
-
-	// Only store RPC metrics for valid types.
-	$good_types = [
-		"info", "multiinfo", "search", "msearch",
-		"suggest", "suggest-pkgbase", "get-comment-form"
-	];
-	if ($path === "/rpc" && in_array($type, $good_types))
-		add_metric("api_requests_count", $method, $path, $type);
-}
+if ($is_cached)
+	register_shutdown_function('update_metrics');
 
 if (config_get_bool('options', 'enable-maintenance') && (empty($tokens[1]) || ($tokens[1] != "css" && $tokens[1] != "images"))) {
 	if (!in_array($_SERVER['REMOTE_ADDR'], explode(" ", config_get('options', 'maintenance-exceptions')))) {
diff --git a/web/lib/metricfuncs.inc.php b/web/lib/metricfuncs.inc.php
index acfc30d7d..7ebb59be8 100644
--- a/web/lib/metricfuncs.inc.php
+++ b/web/lib/metricfuncs.inc.php
@@ -13,6 +13,44 @@ use \Prometheus\RenderTextFormat;
 // and will start again at 0 if it's restarted.
 $registry = new CollectorRegistry(new InMemory());
 
+function update_metrics() {
+	// With no code given to http_response_code, it gets the current
+	// response code set (via http_response_code or header).
+	if(http_response_code() == 404)
+		return;
+
+	$path = $_SERVER['PATH_INFO'];
+	$method = $_SERVER['REQUEST_METHOD'];
+	$query_string = $_SERVER['QUERY_STRING'];
+
+	// If $path is at least 1 character, strip / off the end.
+	// This turns $paths like '/packages/' into '/packages'.
+	if (strlen($path) > 1)
+		$path = rtrim($path, "/");
+
+	// We'll always add +1 to our total request count to this $path,
+	// unless this path == /metrics.
+	if ($path !== "/metrics")
+		add_metric("http_requests_count", $method, $path);
+
+	// Extract $type out of $query_string, if we can.
+	$type = null;
+	$query = array();
+	if ($query_string)
+		parse_str($query_string, $query);
+
+	if (array_key_exists("type", $query))
+		$type = $query["type"];
+
+	// Only store RPC metrics for valid types.
+	$good_types = [
+		"info", "multiinfo", "search", "msearch",
+		"suggest", "suggest-pkgbase", "get-comment-form"
+	];
+	if ($path === "/rpc" && in_array($type, $good_types))
+		add_metric("api_requests_count", $method, $path, $type);
+}
+
 function add_metric($anchor, $method, $path, $type = null) {
 
 	global $registry;
-- 
GitLab