]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/blobdiff - api/common_api.php
Move extra header where it should be
[irc/unrealircd/unrealircd-webpanel.git] / api / common_api.php
index 7da12c971219ab37dc9fb5549784b8d79fb65a07..b29e2826d010b024aa56eff96d8cd993ef6932b7 100644 (file)
@@ -1,5 +1,7 @@
 <?php
-include "../common.php";
+include "../inc/common.php";
+
+if(session_status() !== PHP_SESSION_ACTIVE) session_start();
 
 if (!isset($_SESSION['id']))
        die("Access denied");
@@ -7,11 +9,24 @@ if (!isset($_SESSION['id']))
 // 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");
@@ -26,22 +41,38 @@ set_time_limit(0);
 // 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();
+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)
@@ -54,7 +85,17 @@ function api_log_loop($sources)
        {
                $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);
        }
 }
@@ -63,18 +104,47 @@ function api_timer_loop(int $every_msec, string $method, array|null $params = nu
 {
        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)
        {
-               echo $rpc->error;
-               die;
+               /* Have to resort to old style: client-side timer */
+               while(1)
+               {
+                       $res = $rpc->query($method, $params);
+                       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);
        }
 }