mirror of
https://github.com/Qbix/webserver.git
synced 2026-07-22 07:57:23 +02:00
Added support for legacy scripts via CGI mode instead of CLI SAPI
This commit is contained in:
+49
-24
@@ -873,7 +873,40 @@ class Q_WebServer
|
||||
return self::handlePhp($client, $parsed, $indexPhp);
|
||||
}
|
||||
|
||||
// 8. Not found
|
||||
// 8. Configurable fallback (SPA routing, custom 404 page, etc.)
|
||||
// Q.webserver.fallback can be:
|
||||
// - string: path to a static file relative to web/ (e.g. "index.html")
|
||||
// - object with "handler": event name to dispatch via Q::event()
|
||||
// - object with "file": static file + auto-detect Content-Type
|
||||
$fallback = Q_Config::get('Q', 'webserver', 'fallback', null);
|
||||
if ($fallback !== null) {
|
||||
if (is_string($fallback)) {
|
||||
// Static file (SPA catch-all: serve index.html for all routes)
|
||||
$fbPath = self::$rootDir . str_replace('/', DS, $fallback);
|
||||
if (is_file($fbPath)) {
|
||||
return self::serveFile($client, $parsed, $fbPath);
|
||||
}
|
||||
} elseif (is_array($fallback)) {
|
||||
if (!empty($fallback['handler'])) {
|
||||
// Route to a handler — build a synthetic Q_Uri
|
||||
$uri = Q_Uri::from(array(
|
||||
'module' => dirname($fallback['handler']),
|
||||
'action' => basename($fallback['handler']),
|
||||
'_originalPath' => $path,
|
||||
));
|
||||
if ($uri) {
|
||||
return self::handleRoute($client, $parsed, $uri);
|
||||
}
|
||||
} elseif (!empty($fallback['file'])) {
|
||||
$fbPath = self::$rootDir . str_replace('/', DS, $fallback['file']);
|
||||
if (is_file($fbPath)) {
|
||||
return self::serveFile($client, $parsed, $fbPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Not found
|
||||
self::sendResponse($client, 404, self::render404($path), 'text/html; charset=utf-8');
|
||||
return false;
|
||||
}
|
||||
@@ -959,14 +992,10 @@ class Q_WebServer
|
||||
// 3. Response
|
||||
Q::event("$module/$action/response", $routed, false, true);
|
||||
|
||||
foreach (headers_list() as $h) {
|
||||
if (strpos($h, ':') !== false) {
|
||||
list($k, $v) = explode(':', $h, 2);
|
||||
$headers[trim($k)] = trim($v);
|
||||
}
|
||||
}
|
||||
$headers = Q::getResponseHeaders();
|
||||
$code = http_response_code();
|
||||
if ($code) $status = $code;
|
||||
if ($code && $code !== 200) $status = $code;
|
||||
if (Q::$_responseCode !== 200) $status = Q::$_responseCode;
|
||||
} catch (\Throwable $e) {
|
||||
$status = 500;
|
||||
ob_clean();
|
||||
@@ -1011,14 +1040,10 @@ class Q_WebServer
|
||||
echo 'Method Not Allowed';
|
||||
}
|
||||
Q::event("$module/$action/response", $routed, false, true);
|
||||
foreach (headers_list() as $h) {
|
||||
if (strpos($h, ':') !== false) {
|
||||
list($k, $v) = explode(':', $h, 2);
|
||||
$headers[trim($k)] = trim($v);
|
||||
}
|
||||
}
|
||||
$headers = Q::getResponseHeaders();
|
||||
$code = http_response_code();
|
||||
if ($code) $status = $code;
|
||||
if ($code && $code !== 200) $status = $code;
|
||||
if (Q::$_responseCode !== 200) $status = Q::$_responseCode;
|
||||
} catch (\Throwable $e) {
|
||||
$status = 500;
|
||||
ob_clean();
|
||||
@@ -1161,8 +1186,8 @@ if (class_exists('Q_Request',false)) Q_Request::$input = $raw;
|
||||
ob_start(); $status = 200; $headers = [];
|
||||
try {
|
||||
if (is_file($req['scriptPath'])) include $req['scriptPath']; else { $status = 404; echo 'Not Found'; }
|
||||
foreach (headers_list() as $h) { if (strpos($h,':')!==false) { [$k,$v] = explode(':',$h,2); $headers[trim($k)] = trim($v); } }
|
||||
$code = http_response_code(); if ($code) $status = $code;
|
||||
$headers = Q::getResponseHeaders();
|
||||
$code = http_response_code(); if (Q::$_responseCode !== 200) $status = Q::$_responseCode; if ($code) $status = $code;
|
||||
} catch (Throwable $e) { $status = 500; ob_clean(); echo $e->getMessage(); $headers['Content-Type']='text/plain'; }
|
||||
$body = ob_get_clean();
|
||||
echo json_encode(compact('status','body','headers'), JSON_UNESCAPED_SLASHES);
|
||||
@@ -1729,6 +1754,8 @@ HTML
|
||||
while (ob_get_level()) ob_end_clean();
|
||||
header_remove();
|
||||
http_response_code(200);
|
||||
Q::clearResponseHeaders();
|
||||
if (class_exists('Q_Response', false)) Q_Response::clear();
|
||||
ob_start();
|
||||
$status = 200;
|
||||
$headers = array();
|
||||
@@ -1746,14 +1773,10 @@ HTML
|
||||
echo 'Not Found';
|
||||
}
|
||||
}
|
||||
foreach (headers_list() as $h) {
|
||||
if (strpos($h, ':') !== false) {
|
||||
list($k, $v) = explode(':', $h, 2);
|
||||
$headers[trim($k)] = trim($v);
|
||||
}
|
||||
}
|
||||
$headers = Q::getResponseHeaders();
|
||||
$code = http_response_code();
|
||||
if ($code) $status = $code;
|
||||
if ($code && $code !== 200) $status = $code;
|
||||
if (Q::$_responseCode !== 200) $status = Q::$_responseCode;
|
||||
} catch (\Throwable $e) {
|
||||
$status = 500;
|
||||
ob_clean();
|
||||
@@ -2076,6 +2099,8 @@ HTML
|
||||
private static function resolveStatic($urlPath)
|
||||
{
|
||||
$rel = str_replace('/', DS, ltrim($urlPath, '/'));
|
||||
// Block null bytes (directory traversal via null byte injection)
|
||||
if (strpos($rel, "\0") !== false) return null;
|
||||
$fsPath = realpath(self::$rootDir . $rel);
|
||||
if (!$fsPath) return null;
|
||||
$fsPath = str_replace(array('/','\\'), DS, $fsPath);
|
||||
|
||||
@@ -118,6 +118,14 @@ class Q_WebServer_Headers
|
||||
$headers['Content-Length'] = strlen($body);
|
||||
$headers['Connection'] = 'close';
|
||||
|
||||
// Merge Q_Response cookies into Set-Cookie headers
|
||||
if (class_exists('Q_Response', false)) {
|
||||
$cookieHeaders = Q_Response::cookieHeaders();
|
||||
foreach ($cookieHeaders as $ch) {
|
||||
$headers['Set-Cookie'] = $ch; // last one wins for single-value
|
||||
}
|
||||
}
|
||||
|
||||
static $reasons = array(
|
||||
200=>'OK', 201=>'Created', 204=>'No Content',
|
||||
301=>'Moved Permanently', 302=>'Found', 304=>'Not Modified',
|
||||
@@ -132,6 +140,17 @@ class Q_WebServer_Headers
|
||||
foreach ($headers as $k => $v) {
|
||||
$out .= "$k: $v\r\n";
|
||||
}
|
||||
// Multiple Set-Cookie headers (can't use the associative array for dupes)
|
||||
if (class_exists('Q_Response', false)) {
|
||||
$cookieHeaders = Q_Response::cookieHeaders();
|
||||
if (count($cookieHeaders) > 1) {
|
||||
// Remove the single Set-Cookie we added above
|
||||
$out = preg_replace("/Set-Cookie:.*\r\n/", "", $out);
|
||||
foreach ($cookieHeaders as $ch) {
|
||||
$out .= "Set-Cookie: $ch\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@fwrite($client, $out . "\r\n" . $body);
|
||||
return true;
|
||||
}
|
||||
@@ -337,7 +356,7 @@ class Q_WebServer_Headers
|
||||
*/
|
||||
static function resolveAccelPath($accelPath)
|
||||
{
|
||||
// Check configured mappings first
|
||||
// 1. Check configured mappings first (absolute path overrides)
|
||||
$mappings = Q_Config::get('Q', 'webserver', 'accel', 'mappings', array());
|
||||
foreach ($mappings as $prefix => $diskPath) {
|
||||
if (strpos($accelPath, $prefix) === 0) {
|
||||
@@ -345,7 +364,6 @@ class Q_WebServer_Headers
|
||||
$fsPath = rtrim($diskPath, DS) . DS
|
||||
. ltrim(str_replace('/', DS, $relative), DS);
|
||||
$real = realpath($fsPath);
|
||||
// Ensure we don't escape the mapped directory
|
||||
if ($real && strpos($real, realpath($diskPath)) === 0) {
|
||||
return $real;
|
||||
}
|
||||
@@ -353,12 +371,21 @@ class Q_WebServer_Headers
|
||||
}
|
||||
}
|
||||
|
||||
// Default: resolve relative to APP_DIR (not web root —
|
||||
// the point is to serve files OUTSIDE the web root)
|
||||
// 2. Default: resolve relative to APP_DIR (project root).
|
||||
// By convention, private files live in files/ (sibling of web/).
|
||||
// X-Accel-Redirect: /files/private/doc.pdf
|
||||
// → APP_DIR/files/private/doc.pdf
|
||||
if (defined('APP_DIR')) {
|
||||
$fsPath = APP_DIR . DS . ltrim(str_replace('/', DS, $accelPath), DS);
|
||||
$real = realpath($fsPath);
|
||||
if ($real && strpos($real, realpath(APP_DIR)) === 0) {
|
||||
// Ensure we're NOT serving from web/ — that defeats the purpose
|
||||
if (isset(Q_WebServer::$rootDir)) {
|
||||
$webRoot = realpath(rtrim(Q_WebServer::$rootDir, DS));
|
||||
if ($webRoot && strpos($real, $webRoot) === 0) {
|
||||
return null; // don't serve public files via accel
|
||||
}
|
||||
}
|
||||
return $real;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user