]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/blame_incremental - api/common_api.php
Fix some virtual webserver problems with regards to flushing by @Madriix
[irc/unrealircd/unrealircd-webpanel.git] / api / common_api.php
... / ...
CommitLineData
1<?php
2include "../inc/common.php";
3
4if(session_status() !== PHP_SESSION_ACTIVE) session_start();
5
6if (!isset($_SESSION['id']))
7 die("Access denied");
8
9// Close the session now, otherwise other pages block
10session_write_close();
11
12// Apache w/FPM is shit because it doesn't have flushpackets=on
13// or not by default anyway, so we will fill up 4k buffers.
14// Yeah, really silly... I know.
15$fpm_workaround_needed = false;
16if (str_contains($_SERVER['SERVER_SOFTWARE'], 'Apache') &&
17 function_exists('fpm_get_status') &&
18 is_array(fpm_get_status()))
19{
20 $fpm_workaround_needed = true;
21}
22
23// Only now make the connection (this can take a short while)
24include "../inc/connection.php";
25
26header("Content-type: application/json; charset=utf-8");
27// Server Side Events
28if (!defined('NO_EVENT_STREAM_HEADER'))
29 header('Content-Type: text/event-stream');
30
31// Explicitly disable caching so Varnish and other upstreams won't cache.
32header("Cache-Control: no-cache, must-revalidate");
33
34// Setting this header instructs Nginx to disable fastcgi_buffering and disable
35// gzip for this request.
36header('X-Accel-Buffering: no');
37
38// No time limit
39set_time_limit(0);
40
41// Send content immediately
42ob_implicit_flush(1);
43
44// Flush and stop output buffering (eg fastcgi w/NGINX)
45function flush_completely()
46{
47 while (1)
48 {
49 try {
50 $ret = @ob_end_flush();
51 if ($ret === false)
52 break;
53 } catch(Exception $e)
54 {
55 break;
56 }
57 }
58}
59
60if (!defined('NO_EVENT_STREAM_HEADER'))
61 flush_completely();
62
63/* Send server-sent events (SSE) message */
64function send_sse($json)
65{
66 GLOBAL $fpm_workaround_needed;
67 $str = "data: ".json_encode($json)."\n\n";
68 if ($fpm_workaround_needed)
69 $str .= str_repeat(" ", 4096 - ((strlen($str)+1) % 4096))."\n";
70 echo $str;
71}
72
73function api_log_loop($sources)
74{
75 GLOBAL $rpc;
76 GLOBAL $fpm_workaround_needed;
77
78 $rpc->log()->subscribe($sources);
79 if ($rpc->error)
80 {
81 echo $rpc->error;
82 die;
83 }
84
85 for(;;)
86 {
87 $res = $rpc->eventloop();
88 if (!$res)
89 {
90 /* Output at least something every timeout (10) seconds,
91 * otherwise PHP may not
92 * notice when the webclient is gone.
93 */
94 if ($fpm_workaround_needed)
95 echo str_repeat(" ", 4095)."\n";
96 else
97 echo "\n";
98 continue;
99 }
100 send_sse($res);
101 }
102}
103
104function api_timer_loop(int $every_msec, string $method, array|null $params = null)
105{
106 GLOBAL $rpc;
107
108 /* First, execute it immediately */
109 $res = $rpc->query($method, $params);
110 if (!$res)
111 die;
112 send_sse($res);
113 $rpc->rpc()->add_timer("timer", $every_msec, $method, $params);
114 if ($rpc->error)
115 {
116 /* Have to resort to old style: client-side timer */
117 while(1)
118 {
119 $res = $rpc->query($method, $params);
120 if (!$res)
121 die;
122 send_sse($res);
123 usleep($every_msec * 1000);
124 }
125 }
126
127 /* New style: use server-side timers */
128 /* - First, execute it immediately */
129 $res = $rpc->query($method, $params);
130 if (!$res)
131 die;
132 send_sse($res);
133 /* - Then add the timer */
134 for(;;)
135 {
136 $res = $rpc->eventloop();
137 if (!$res)
138 {
139 /* Output at least something every timeout (10) seconds,
140 * otherwise PHP may not
141 * notice when the webclient is gone.
142 */
143 if ($fpm_workaround_needed)
144 echo str_repeat(" ", 4095)."\n";
145 else
146 echo "\n";
147 continue;
148 }
149 send_sse($res);
150 }
151}