atk4 / ui

Robust and easy to use PHP Framework for Web Apps
https://atk4-ui.readthedocs.io
MIT License
439 stars 104 forks source link

JsSse in PHP-FPM setup requires special configuration #2178

Closed mkrecek234 closed 3 months ago

mkrecek234 commented 4 months ago

Situation:

Solution:

    <FilesMatch ".+\.ph(?:ar|p|tml)$">
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>

    <Proxy "fcgi://localhost">
        ProxySet flushpackets=on
        ProxySet flushwait=20
        ProxySet max=10
        ProxySet timeout=100
    </Proxy>

Suggestions to limit flushing packets only on selected scripts or directories are welcome. This way you can keep output buffering for scripts that are non-Sse.

Also see: https://github.com/php/php-src/pull/12785#discussion_r1412054712

mvorisek commented 4 months ago

This seems to be not-output buffer related. Can anything from atk4/ui be done?

I tried to find some solutions and it seems sending at leasst 4096 bytes per each JSSE output can help, can you evaluate the possibilities?

https://gist.github.com/hranicka/368dfbf3df971f021fa70047eb894058

https://syntaxfix.com/question/45690/how-to-flush-output-after-each-echo-call

mkrecek234 commented 4 months ago

The PR in #2175 generally works. Only issue is that in FPM setup by default flush is not working (mod_fcgi) unless you activate it. Also you cannot reconfigure output buffer programmatically so you are stuck with an output buffer of 4096.

If you reconfigure PHP-FPM to allow flush as in above configuration, then Atk4 Ui works out of the box

mvorisek commented 4 months ago

then Atk4 Ui works out of the box

great

you are stuck with an output buffer of 4096.

we can account for that in SSE and always append 4kiB dummy string


working FMP/FCGI repro:

SetHandler "proxy:fcgi://127.0.0.1:9001/"
ProxyFCGISetEnvIf "true" SCRIPT_FILENAME "C:/.../atk4/ui/demos/interactive/sse.php"

<Proxy fcgi://127.0.0.1:9001/>
  ProxySet flushpackets=on
</Proxy>

and

php-cgi.exe -b 127.0.0.1:9001 -c "C:\...\Apache24\php\php.ini" -d display_errors=1
    -d display_startup_errors=1 -d error_reporting=E_ALL -d fastcgi.daemonize=0
mvorisek commented 3 months ago

I conducted a testing with various webservers:

Apache /w PHP FCGI is problematic as the response is bufferent by Apache and flush() called in php is completely ignored.

Based on https://stackoverflow.com/questions/30707792/how-to-disable-buffering-with-apache2-and-mod-proxy-fcgi#36298336 and testing, I found out that JsSse->output('x-flush: ' . str_repeat('x', 4_096) . "\n"); does reliably work as a flush() replacement.

However, when Apache is configured to compress the output (using SetOutputFilter DEFLATE) for any content-type, ie. also text/event-stream responses, then 4096 bytes are not enough to enforce the output to be flushed. Based on testing, something between bin2hex(random_bytes(32 * 1024)) and bin2hex(random_bytes(64 * 1024)) was needed, ie. about 50 KB of incompressible overhead.

The question is, is it worth adding 4KiB of overheader to cover Apache /w PHP FCGI (and even only when configured to not compress all files)?

Apache issue: https://bz.apache.org/bugzilla/show_bug.cgi?id=68827

mkrecek234 commented 3 months ago

@mvorisek Can confirm that with fix #2187 we no longer have to adjust flushing in PHP-FPM to make JsSse work.