I am using the CodeIgniter 3 PHP framework on an nginx/1.14.1 server with PHP/7.4.19.
I am trying to set up event streams. The controller method looks as so:
public function foo()
{
// Try everything I can to disable output buffering
ini_set('output_buffering', 'Off');
ini_set('implicit_flush', 'On');
ini_set('zlib.output_compression', 'Off');
header('X-Accel-Buffering: no');
header("X-Robots-Tag: noindex");
header('Cache-Control: no-store, no-cache');
header('Content-Type: text/event-stream');
header('Content-Encoding: none');
$i = 0;
// Loop 10 times
while ($i < 10)
{
// Codeigniter built-in function to generate a random alphanumeric string of n characters
$random_string = random_string('alnum', 10);
$array = array(
'foo' => $random_string
);
// Put together an event
$output = "event: pingn";
$output .= "id: $random_stringn";
$output .= "data: ";
$output .= json_encode($array);
$output .= "nn";
echo $output;
$i++;
ob_end_flush();
flush();
// Break the loop if the client closed the page
if (connection_aborted())
{
break;
}
sleep(1);
}
The expected behavior is for each event to come in one at a time, one per second.
However, it seems I am unable to get output buffering to be disabled. The full 10s passes before all 10 events load in full.
If I generate a large random string such as random_string('alnum', 500);
then the page loads in ~4K character chunks. In other words, the page will flush the buffer approximately every ~4K characters, and it will take about 3 of these chunks (with a 2-3s wait between each one, to accomodate 2-3 iterations of the while loop) before the page finishes loading.
I am using Cloudflare but I have a page rule in place to bypass caching on the page in question. It is the first page rule.
Response buffering is disabled in Cloudflare. By default, Cloudflare sends packets to the client as they receive them. Enterprise accounts (which we are not) can optionally enable response buffering.
Cloudflare Brotli compression is enabled. Temporarily disabling it did not fix the issue.
Obviously, it works as expected when the php script is run from the command line.
2
Answers
I am able to achieve the desired results in Chrome by adding the line
$output .= str_pad('', 4096) . "nn";
just before echo'ing the output.However, although this solution/hack "works", it does not seem to disable buffering, which is what my question was.
Update: I discovered this hack does not work in Safari.
I’m wondering if this is an Nginx issue? I had a similar problem when running a Server-Sent Event behind Nginx. The fix was to add a ‘X-Accel-Buffering’ header with value ‘no’ in the response.