Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

HTML5, SSE, flush output '$| = 1' not working as expected

by ehaase (Initiate)
on Sep 04, 2015 at 07:29 UTC ( [id://1140968]=perlquestion: print w/replies, xml ) Need Help??

ehaase has asked for the wisdom of the Perl Monks concerning the following question:

Serverside script
#!/usr/bin/perl -w use strict; use CGI; my @tickets = ( [ "GOOG", 533.37 ], [ "MSFT", 47.59 ], [ "IBM", + 162.99 ], [ "AAPL", 114.12 ], [ "MSFT", 47.29 ], [ "GOOG" +, 533.95 ], [ "IBM", 163.78 ], [ "GOOG", 533.55 ], [ "AAPL" +, 113.67 ] ); my $ticketsLength = scalar ( @tickets ); my $lastId = 0; my $q = new CGI ; local $| = 1; print "Content-Type: text/event-stream\n"; print "Cache-Control: no-cache\n"; print "Connection: keep-alive\n\n"; while (1) { sendMessage($lastId, $tickets[$lastId][0], $tickets[$lastId][1]); $lastId++; die() if ($lastId >= $ticketsLength); # Check that lastId is not l +arger than the size of array - if it is larger close connection. sleep(1); } # Function to send data in format "ticket:price". sub sendMessage { my $id = shift(); my $ticket = shift(); my $price = shift(); print "id: $id\n"; print "data: $ticket:$price\n\n"; }
Browser request
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" +/> <title>Server Sent Events Perl Example - Stock Tickets</title> <script type="text/javascript"> window.onload = function setDataSource() { if (!!window.EventSource) { var source = new EventSource("stocks.htm"); source.addEventListener("message", function(e) { updatePrice(e.data); logMessage(e); }, false); source.addEventListener("open", function(e) { logMessage("OPENED"); }, false); source.addEventListener("error", function(e) { logMessage("ERROR"); if (e.readyState == EventSource.CLOSED) { logMessage("CLOSED"); } }, false); } else { document.getElementById("notSupported").style.display = "b +lock"; } } function updatePrice(data) { var ar = data.split(":"); var ticket = ar[0]; var price = ar[1]; var el = document.getElementById("t_" + ticket); var oldPrice = el.innerHTML; el.innerHTML = price; if (parseFloat(oldPrice) < parseFloat(price)) { el.style.backgroundColor = "lightgreen"; } else { el.style.backgroundColor = "tomato"; } window.setTimeout(function clearBackground() { el.style.backgroundColor = "white"; }, 500); } function logMessage(obj) { var el = document.getElementById("log"); if (typeof obj === "string") { el.innerHTML += obj + "<br>"; } else { el.innerHTML += obj.lastEventId + " - " + obj.data + "<br> +"; } el.scrollTop += 20; } </script> </head> <body> <h1>Server Sent Events Perl Example</h1> <p class="hint"> This is simple Server Sent Events (SSE) example that updates stock + prices when market moves. Data source is predefined array with prices and an update every se +cond. This script is adapted from http://demo.howopensource.com/sse/ </p> <h2>Tickets</h2> <div id="tickets"> <div class="ticket"><div class="name">IBM</div><div class="price" +id="t_IBM">161.57</div></div> <div class="ticket"><div class="name">AAPL</div><div class="price" + id="t_AAPL">114.45</div></div> <div class="ticket"><div class="name">GOOG</div><div class="price" + id="t_GOOG">532.94</div></div> <div class="ticket"><div class="name">MSFT</div><div class="price" + id="t_MSFT">47.12</div></div> </div> <h2>Simple Log Console</h2> <p class="hint"> This is simple log console. It is useful for testing purposes and +to understand better how SSE works. Event id and data are logged for each event. </p> <div id="log"> </div> </body> </html>

It should update a ticker price every second but all events are updated at the end of the script (it works in general, but flush does not work this way). This is an adaption of a working php-version at http://demo.howopensource.com/sse/

Environment is a Linux V-Server 2.6.32-042stab092.3 with Apache2.

The php-Version does work as expected on the this environment.

Replies are listed 'Best First'.
Re: HTML5, SSE, flush output '$| = 1' not working as expected
by Corion (Patriarch) on Sep 04, 2015 at 15:08 UTC

    I guess that Apache will try to parse your headers and thus waits until your script exits.

    I would investigate on whether you need to output a HTTP status line to make Apache ignore your output or if you need to configure Apache differently ("non-parsed headers", NPH).

    In general, Server-Sent Events work, as I've written a Dancer/Plack application HTTP::ServerEvents. If you're not tied to Apache, maybe you can run this or Plack::App::EventSource by vti.

      Thanks Corion, you brougth me on the way of success. Turning off mod_deflate in the Apache2 configuration was the key.

      a2dismod deflate apache2 restart

      The module mod_deflate does the buffering. Turning that module of will provide updates with the Perl-Environment every second as expected.

      I assume that the problem is may not the Apache Server. The given sample in PHP5 works with the same server and the flush() is initiated by PHP as i think it is implemented in my sample code using Perl.

        I think the problem is the web server, on my Win8.1 desktop with IIS6.2 it updates every 10 seconds as multiples but with Apache2 it works correctly every second. If I set $| = 0; on Apache it updates every 10 seconds the same as IIS.

        What value is this line in your php.ini

        output_buffering=??
        poj
Re: HTML5, SSE, flush output '$| = 1' not working as expected
by u65 (Chaplain) on Sep 04, 2015 at 10:59 UTC

    Can you elaborate a bit on "it works in general..." What do you expect and what is actually happening?

      'In general' means, that the tickers are sent to the client and the client (Browser) receives the data. But all single updates are shown together at the end of the scripts instead of flushing each single ticker. The expected result is an update every second of one of the ticker symbols.

        Try printing headers using CGI, for example

        use CGI qw//; my $q = CGI->new(); print $q->header( -nph => 0, 'Content-Type' => ' text/event-stream', 'Cache-Control' => 'no-cache', 'Connection' => ' keep-alive', ); print $q->header( -nph => 1, 'Content-Type' => ' text/event-stream', 'Cache-Control' => 'no-cache', 'Connection' => ' keep-alive', ); __END__ Cache-control: no-cache Connection: keep-alive Content-Type: text/event-stream; charset=ISO-8859-1 HTTP/1.0 200 OK Server: cmdline Date: Fri, 04 Sep 2015 23:33:29 GMT Cache-control: no-cache Connection: keep-alive Content-Type: text/event-stream; charset=ISO-8859-1

        Do you see the difference? Without nph off, apache is going to fill in the missing headers

        With nph on, apache is not touching the headers .... and probably not buffering

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1140968]
Approved by planetscape
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (4)
As of 2024-04-24 19:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found