PHP Server Sent Events Connection won't close?
Asked Answered
F

1

6

I have implemented a server sent events with eventsource on my web application. Basically in javascript my code look like :

    var myEventSource;
    if (typeof(EventSource) !== "undefined" && !myJsIssetFunction(viridem.serverSideEvent.config.reindexProcessingEvent)) {
        myEventSource = new EventSource('/my/url/path.php?event=myevent');
        EventSource.onmessage = function(e) {
          [...] //Dealing with e.data that i received ...
        }
    }

on the PHP side I have something Like this :

<?php
  header('Content-Type: text/event-stream');
  header('Cache-Control: no-cache');
  header("Access-Control-Allow-Origin: *");

  //this or set_the_limit don't work but whatever I can deal without it
  ini_set('max_execution_time', 300);
  //ignore_user_abort(true); tried with true and false

  bool $mustQuit = false;

  while (!$mustQuit && connection_status() == CONNECTION_NORMAL) {
     if(connection_aborted()){
      exit();
     }
     [...] //doing some checkup

    if ($hasChange) {
      //Output stuffs
      echo 'data:';
      echo json_encode($result);
      echo "\n\n";
      ob_flush();
      flush();
      sleep(5);
    }

  }

from the answer found at : PHP Event Source keeps executing , the "text/event-stream" headers should make the connection close automatically but it doesn't in my case ..

I did add an eventsource.close in the window.onbeforeunload event but it didn't close the event.

window.onbeforeunload =  function() {
    myEventSource.close();
    myEventSource = null;
};

If I look the network section of my browser I can see the Headers are (after adding maximum loop of 30) : Content-Type: text/event-stream;charset=UTF-8

Response Headers:

Access-Control-Allow-Origin: *

Cache-Control: no-cache

Connection: Keep-Alive

Content-Type: text/event-stream;charset=UTF-8

Server: Apache/2.4.18 (Ubuntu)

Date: Thu, 26 Apr 2018 20:29:46 GMT

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Request Headers:

Connection: keep-alive

Accept: text/event-stream

Cache-Control: no-cache

Note : I confirm that the script is still running with logs and by checking apache2 process with bash (ps -ax | grep -c apache2) that are always incrementing.

Freudian answered 26/4, 2018 at 20:50 Comment(8)
Just do while (true) { if (connection_aborted()) { exit(); I've used this for a while and kills process fine. For example: https://mcmap.net/q/1471532/-how-do-i-put-this-on-real-time-i-already-put-async-true-but-it-doesnt-workRuhl
connection_aborted() is always returning int(0) :/Freudian
Not sure how your testing that but you also need ignore_user_abort(true); which you have it off.Ruhl
And you shouldnt be using max_execution_timeRuhl
I ended using a maximum loop number instead of max execution time and set_time_limit because this didn't stop the script to continuously run.. I guess I will set them at infinite execution time or remove them.Freudian
The idea is that it does run continuously, and only exits once a client disconnects.. you could put inside the loop a check which safeguards from overunning like github.com/lcherone/sse-chat-example/blob/master/sse.php#L89 but it wouldn't be needed in most cases.. as connection_aborted works fine.Ruhl
You were right about connection_aborted but the missing part is that I needed to output data for this function to work. I'm guessing otherwise the server don't poke the client and then won't know if the connection is still active.Freudian
Does this answer your question? PHP Event Source keeps executingProduction
F
6

Thanks to @LawrenceCherone help, I did find out that you need to "output data" for connection_aborted to work...

In my case I was outputing data only when I needed to ...

by adding

   if ($hasChange) {
      //Output stuffs
      echo 'data:';
      echo json_encode($result);
      echo "\n\n";
      ob_flush();
      flush();
      sleep(5);

    } else {
       echo 'data:';
       echo "\n\n";
       ob_flush();
       flush();
       if(connection_aborted()){
         exit();
       }
       sleep(5);
    }

connection_aborted started working.

Freudian answered 26/4, 2018 at 22:0 Comment(5)
You simply need to make sure that you are also sending a keep alive message on a regular basis. Typically no longer than every 40 seconds. That will ensure that the connection_abordted() will be delayed by at most 40 seconds.Larkin
Instead of sending empty data it's better to send a comment which is any string that starts with semicolon, like echo ": ping\n\n";Production
Jordan you saved my day, thank youSophiesophism
This would constantly send data to the client. And server-sent events are made for reducing payload.Hertz
@Hertz connection_aborted and connection_status only return what the server know. At that time eventsource.close() or closing the browser wasn't notifying apache that the connection was aborted. There is also the possibility that someone lose his internet connection and therefore can't notify the server so you would still need to check the connection and therefore send data to the client. There should be a lower HTTP overhead from what I understand (https://mcmap.net/q/241541/-server-sent-events-vs-polling) In my case we now use a websocket for versatility and to avoid concurrents connections.Freudian

© 2022 - 2024 — McMap. All rights reserved.