]> mop.ddnsfree.com - git repositories - brisk.git/commitdiff
the displaced stream was closed without a word
authorMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 10:39:10 +0000 (12:39 +0200)
committerMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 10:39:10 +0000 (12:39 +0200)
When a new read stream arrives for a session that already has one, the daemon
replaces the old with the new: that is the intended behaviour, it is what
lets you carry on from where you were after a browser restart.

The displaced client, though, was told nothing: its socket was simply closed.
It reopened it, displacing the new one in turn, which reopened in turn: two
windows open on the same session bounced each other forever, each with a red
indicator and no explanation.

Now the replaced stream is dismissed: it stops the streaming and shows a
notice. It is not sent back to the login, because when the two windows share
the same session the entry page would let it straight back in, starting the
bouncing again.

The message travels over the transport of the OLD connection, which may
differ from the one of the new, and is followed by an orderly close
(stream_bye in user.phh). In the normal case - the same client reopening
after having given the connection up for lost - it ends up on a socket nobody
reads any more and does no harm.

Checked with two streams on the same session, in both combinations: the first
receives xstm.stop() and the notice, as text over xhr and framed (0x81) over
websocket, and is not sent back to the login. A complete game confirms the
normal path is intact.

NOTE: the case of a login by a user already present was handled before, by
the "ghost" mechanism of add_user(): the new access inherits the old user,
seat at the table included, and the displaced session is recorded in
ghost_sess with reason ANOT, which index.php turns into "La tua sessione e'
stata assegnata ad un altro browser". What was missing is precisely the piece
added here: if the displaced client was reading a stream, it never reached
index.php and never saw that message.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014M1jiEq9cHdE5SE5j6vFuE

web/Obj/brisk.phh
web/Obj/user.phh
web/briskin5/Obj/briskin5.phh

index b556210f376fe4043aa6f5af35231c3b6343825d..71ed6156b1739b7f8023f774b3594e29ee404fde 100644 (file)
@@ -119,6 +119,8 @@ $mlang_brisk = array( 'btn_backstand'=> array( 'it' => 'torna in piedi',
                                            'en' => '<br>You are being idle for ' ),
                       'tabtout_b'=> array( 'it' => ' minuti. <br><br>Quindi ritorni tra i <b>Giocatori in piedi</b>.',
                                            'en' => ' minutes. <br><br>Then you return with the <b>standing players</b>.'),
+                      'sessmoved' => array( 'it' => '<br>Questa sessione &egrave; stata ripresa da un&#39;altra finestra o da un altro dispositivo.<br>Questa pagina non &egrave; pi&ugrave; collegata.<br><br>',
+                                            'en' => '<br>This session has been resumed from another window or device.<br>This page is no longer connected.<br><br>'),
                       'tickmust' => array( 'it' => '<br>Per attivare il messaggio di segnalazione del tavolo occorre essere seduti.<br><br>',
                                            'en' => '<br>To activate the signalling message of the table it\'s necessary to be sitting<br><br>'),
                       'tickjust' => array( 'it' => '<br>Il messaggio di segnalazione del tavolo &egrave; gi&agrave; attivato.<br><br> ',
@@ -2745,6 +2747,7 @@ class Brisk
   function request_mgr(&$s_a_p, $header, &$header_out, &$new_socket, $path, $addr, $get, $post, $cookie)
   {
       GLOBAL $G_ban_list, $G_black_list, $G_cloud_smasher;
+      GLOBAL $mlang_brisk, $G_lang;
 
       // printf("NEW_SOCKET (root): %d PATH [%s]\n", intval($new_socket), $path);
 
@@ -2825,8 +2828,24 @@ class Brisk
               $this->sess_cur_set($user->sess);
               // close a previous opened index_read_ifra socket, if exists
               if (($prev = $user->rd_socket_get()) != NULL) {
-                  $s_a_p->socks_unset($user->rd_socket_get());
-                  fclose($user->rd_socket_get());
+                  /* The previous stream is being replaced by this one. Up to
+                     now its socket was closed without a word, and the client on
+                     the other side reopened it, displacing the new one in turn:
+                     two windows open on the same session bounced each other
+                     forever, with a red indicator and no explanation. Now it is
+                     dismissed: it stops the stream and shows a notice. It is
+                     not sent back to the login, because the session is valid
+                     and it would come back in, starting the bouncing again.
+                     In the normal case - the same client reopening after having
+                     given the connection up for lost - the message ends up on a
+                     socket nobody reads any more, and does no harm. */
+                  $bye = $user->stream_bye('xstm.stop(); '
+                      .show_notify($mlang_brisk['sessmoved'][$G_lang], 0, "chiudi", 400, 120));
+                  if ($bye != "") {
+                      @fwrite($prev, $bye, mb_strlen($bye, "ASCII"));
+                  }
+                  $s_a_p->socks_unset($prev);
+                  fclose($prev);
                   // printf("CLOSE AND OPEN AGAIN ON IFRA2\n");
                   $user->rd_socket_set(NULL);
               }
index 4c13325f9a1379b8e5f8711b7bdcb01df4256731..71f682b5849a0f4d1adec8f16a7c57f7fcb5ddbe 100644 (file)
@@ -894,6 +894,18 @@ function stream_keepalive($with_ping)
     return ($this->rd_transp->chunk( $this->rd_scristp++, ($with_ping ? "act_ping();" : NULL)));
 }
 
+/* Farewell for the stream about to be replaced by another one: the message
+   is framed according to the transport of the OLD connection, which may differ
+   from the one of the new, and is followed by an orderly close. If the old
+   connection is already dead the write fails with no consequence. */
+function stream_bye($js)
+{
+    if ($this->rd_transp == NULL) {
+        return ("");
+    }
+    return ($this->chunked_content($this->rd_transp->chunk(0, $js)).$this->stream_close());
+}
+
 function stream_close()
 {
     $clo = $this->rd_transp->close();
index 86bd77dfd024f0afee9a2003ffc7d8c9589d5960..190c88dea0d5617b64cccc2bbf2b5b9574d8b2cb 100644 (file)
@@ -1463,6 +1463,7 @@ class Bin5 {
     static function request_mgr(&$s_a_p, $header, &$header_out, &$new_socket, $path, $addr, $get, $post, $cookie)
     {
         GLOBAL $G_ban_list, $G_black_list;
+        GLOBAL $mlang_brisk, $G_lang;
 
         // printf("NEW_SOCKET (root): %d\n", intval($new_socket));
 
@@ -1548,8 +1549,16 @@ class Bin5 {
 
                 // close a previous opened index_read_ifra socket, if exists
                 if (($prev = $user->rd_socket_get()) != NULL) {
-                    $s_a_p->socks_unset($user->rd_socket_get());
-                    fclose($user->rd_socket_get());
+                    /* as in the room: the stream being replaced is told what
+                       happened to it, otherwise it reopens and displaces the
+                       new one in turn, forever. */
+                    $bye = $user->stream_bye('xstm.stop(); '
+                        .show_notify($mlang_brisk['sessmoved'][$G_lang], 0, "chiudi", 400, 120));
+                    if ($bye != "") {
+                        @fwrite($prev, $bye, mb_strlen($bye, "ASCII"));
+                    }
+                    $s_a_p->socks_unset($prev);
+                    fclose($prev);
                     // printf("CLOSE AND OPEN AGAIN ON IFRA2\n");
                     $user->rd_socket_set(NULL);
                 }