<?php
-include "../common.php";
+include "../inc/common.php";
+
+if(session_status() !== PHP_SESSION_ACTIVE) session_start();
-session_start();
if (!isset($_SESSION['id']))
die("Access denied");
// Close the session now, otherwise other pages block
session_write_close();
+// Apache w/FPM is shit because it doesn't have flushpackets=on
+// or not by default anyway, so we will fill up 4k buffers.
+// Yeah, really silly... I know.
+$fpm_workaround_needed = false;
+if (str_contains($_SERVER['SERVER_SOFTWARE'], 'Apache') &&
+ function_exists('fpm_get_status') &&
+ is_array(fpm_get_status()))
+{
+ $fpm_workaround_needed = true;
+}
+
// Only now make the connection (this can take a short while)
-include "../connection.php";
+include "../inc/connection.php";
+header("Content-type: application/json; charset=utf-8");
// Server Side Events
-header('Content-Type: text/event-stream');
+if (!defined('NO_EVENT_STREAM_HEADER'))
+ header('Content-Type: text/event-stream');
// Explicitly disable caching so Varnish and other upstreams won't cache.
header("Cache-Control: no-cache, must-revalidate");
// Send content immediately
ob_implicit_flush(1);
-// Eh.. yeah...
-ob_end_flush();
+// Flush and stop output buffering (eg fastcgi w/NGINX)
+function flush_completely()
+{
+ while (1)
+ {
+ try {
+ $ret = @ob_end_flush();
+ if ($ret === false)
+ break;
+ } catch(Exception $e)
+ {
+ break;
+ }
+ }
+}
-// If we use fastcgi, then finish the request now (UNTESTED)
-if (function_exists('fastcgi_finish_request'))
- fastcgi_finish_request();
+if (!defined('NO_EVENT_STREAM_HEADER'))
+ flush_completely();
/* Send server-sent events (SSE) message */
function send_sse($json)
{
- echo "data: ".json_encode($json)."\n\n";
+ GLOBAL $fpm_workaround_needed;
+ $str = "data: ".json_encode($json)."\n\n";
+ if ($fpm_workaround_needed)
+ $str .= str_repeat(" ", 4096 - ((strlen($str)+1) % 4096))."\n";
+ echo $str;
}
function api_log_loop($sources)
{
GLOBAL $rpc;
+ GLOBAL $fpm_workaround_needed;
$rpc->log()->subscribe($sources);
if ($rpc->error)
{
$res = $rpc->eventloop();
if (!$res)
+ {
+ /* Output at least something every timeout (10) seconds,
+ * otherwise PHP may not
+ * notice when the webclient is gone.
+ */
+ if ($fpm_workaround_needed)
+ echo str_repeat(" ", 4095)."\n";
+ else
+ echo "\n";
continue;
+ }
send_sse($res);
}
}
{
GLOBAL $rpc;
+ /* First, execute it immediately */
+ $res = $rpc->query($method, $params);
+ if (!$res)
+ die;
+ send_sse($res);
$rpc->rpc()->add_timer("timer", $every_msec, $method, $params);
if ($rpc->error)
{
if (!$res)
die;
send_sse($res);
+ usleep($every_msec * 1000);
}
}
/* New style: use server-side timers */
+ /* - First, execute it immediately */
+ $res = $rpc->query($method, $params);
+ if (!$res)
+ die;
+ send_sse($res);
+ /* - Then add the timer */
for(;;)
{
$res = $rpc->eventloop();
if (!$res)
+ {
+ /* Output at least something every timeout (10) seconds,
+ * otherwise PHP may not
+ * notice when the webclient is gone.
+ */
+ if ($fpm_workaround_needed)
+ echo str_repeat(" ", 4095)."\n";
+ else
+ echo "\n";
continue;
+ }
send_sse($res);
}
}