Skip to content
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 49 additions & 7 deletions resources/lib/UnityHTTPD.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace UnityWebPortal\lib;

use stdClass;
use UnityWebPortal\lib\exceptions\NoDieException;
use UnityWebPortal\lib\exceptions\ArrayKeyException;
use RuntimeException;
Expand Down Expand Up @@ -43,6 +44,31 @@ public static function redirect(?string $dest = null): never
self::die();
}

// assumes that a session is started and has a good unique session_id
public static function errorID(?\Throwable $e = null): string
{
$e ??= new stdClass();
return md5(strval(session_id()) . spl_object_id($e));
}

public static function logThrowableAndMessageUser(
\Throwable $error,
string $log_title,
string $log_message,
string $user_message_title,
string $user_message_body,
) {
$errorid = self::errorID($error);
$data = [];
self::errorLog($log_title, $log_message, error: $error, errorid: $errorid, data: $data);
if (strlen($user_message_body) == 0) {
$user_message_body = "error ID: $errorid";
} else {
$user_message_body .= " error ID: $errorid";
}
self::messageError($user_message_title, $user_message_body);
}

// $data must be JSON serializable
public static function errorLog(
string $title,
Expand Down Expand Up @@ -72,8 +98,9 @@ public static function errorLog(
}
$output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null;
$output["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? null;
if (!is_null($errorid)) {
$output["errorid"] = $errorid;
$output["_REQUEST"] = $_REQUEST;
if (!is_null($errorid) || !is_null($error)) {
$output["errorid"] = self::errorID($error);
}
error_log("$title: " . \jsonEncode($output));
}
Expand Down Expand Up @@ -119,7 +146,7 @@ public static function badRequest(
?\Throwable $error = null,
?array $data = null,
): never {
$errorid = uniqid();
$errorid = self::errorID($error);
self::errorToUser("Invalid requested action or submitted data.", 400, $errorid);
self::errorLog("bad request", $message, $errorid, $error, $data);
self::die($message);
Expand All @@ -130,7 +157,7 @@ public static function forbidden(
?\Throwable $error = null,
?array $data = null,
): never {
$errorid = uniqid();
$errorid = self::errorID($error);
self::errorToUser("Permission denied.", 403, $errorid);
self::errorLog("forbidden", $message, $errorid, $error, $data);
self::die($message);
Expand All @@ -141,7 +168,7 @@ public static function internalServerError(
?\Throwable $error = null,
?array $data = null,
): never {
$errorid = uniqid();
$errorid = self::errorID($error);
self::errorToUser("An internal server error has occurred.", 500, $errorid);
self::errorLog("internal server error", $message, $errorid, $error, $data);
if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) {
Expand All @@ -155,8 +182,23 @@ public static function internalServerError(
// https://www.php.net/manual/en/function.set-exception-handler.php
public static function exceptionHandler(\Throwable $e): void
{
ini_set("log_errors", true); // in case something goes wrong and error is not logged
self::internalServerError("An internal server error has occurred.", error: $e);
// we disable log_errors before we enable this exception handler to avoid duplicate logging
// if this exception handler itself fails, information will be lost unless we re-enable it
ini_set("log_errors", true);
// if the user was doing HTTP POST, then make a pretty error and redirect
// else, a redirect may cause an infinite loop, so fall back on the old ugly error
if ($_SERVER["REQUEST_METHOD"] == "POST") {
self::logThrowableAndMessageUser(
$e,
"uncaught exception",
strval($e),
"An internal server error has occurred.",
"",
);
self::redirect();
} else {
self::internalServerError("", error: $e);
}
}

public static function errorHandler(int $severity, string $message, string $file, int $line)
Expand Down