]> mop.ddnsfree.com - git repositories - brisk.git/commitdiff
direct http mode becomes the default
authorMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 10:35:02 +0000 (12:35 +0200)
committerMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 10:35:33 +0000 (12:35 +0200)
The daemon no longer receives the client descriptor handed over by the
frontend: nginx opens an ordinary http connection to it on one of the unix
sockets of the pool. Neither the php ancillary extension, nor
mod_proxy_fdpass2 for apache, nor ngx_http_fdpass_module for nginx, nor kTLS
are needed any more: the debian nginx package is enough. In exchange nginx
stays in the middle for the whole life of the connection, which in a comet
application is long.

The historic mode is one INSTALL.sh -D FALSE away.

The sample configuration was added under system/nginx/, and WARNING.txt was
rewritten: it only documented the old apache setup.

Two log traces had to be fixed before the direct mode could be used, because
they fired once per request instead of occasionally:

 - "User associated with ID: N not found" was printed for every socket with
   no user attached, that is for the head of every request. The message now
   comes out only if the socket is in none of the known lists.

 - "PP_REM" traced the removal of a pending page, which now happens on every
   request: it moves under debug > 1.

Checked in the debian 13 container with the packaged nginx 1.26.3 (no added
module): static pages, the .htaccess protections rewritten in the
configuration, the login of five certified users, comet streaming, POST, and
three complete five player games over https recorded on postgresql. The
daemon log for a whole hand went from thousands of lines to about forty.

Behaviour under load and the reuse of connections between nginx and the
daemon remain to be checked.

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

INSTALL.sh
WARNING.txt
sql/sql.d/085-tourn-update.sql
system/nginx/brisk-daemon.inc [new file with mode: 0644]
system/nginx/brisk.conf [new file with mode: 0644]
web/Obj/sac-a-push.phh
web/spush/brisk-spush.phh

index 6d1478c48953b07b1836e0b5cc1c52be265fd329..fe9bbdfbff82f5b8e763bb25b8bde573091f2bcd 100755 (executable)
@@ -19,6 +19,7 @@ web_path="/home/nastasi/web/brisk"
 ftok_path="/home/nastasi/brisk-priv/ftok/brisk"
 proxy_path="/home/nastasi/brisk-priv/proxy/brisk"
 usock_path_pfx="/home/nastasi/brisk-priv/brisk"
+http_direct="TRUE"
 sys_user="www-data"
 legal_path="/home/nastasi/brisk-priv/brisk"
 prefix_path="/brisk/"
@@ -32,12 +33,12 @@ function usage () {
     echo "$1 -h"
     echo "$1 chk                          - run lintian on all ph* files."
     echo "$1 pkg                          - build brisk packages."
-    echo "$1 [-W] [-n 3|5] [-c 2|8] [-t <(n>=4)>] [-T <auth_tab>] [-r <appr_tab>] [-G <cert_tab>] [-A <apache-conf>] [-a <auth_file_name>] [-f <conffile>] [-p <outconf>] [-U <usock_path_pfx>] [-u <sys_user>] [-d <TRUE|FALSE>] [-w <web_dir>] [-k <ftok_dir>] [-l <legal_path>] [-y <proxy_path>] [-P <prefix_path>] [-x]"
+    echo "$1 [-W] [-n 3|5] [-c 2|8] [-t <(n>=4)>] [-T <auth_tab>] [-r <appr_tab>] [-G <cert_tab>] [-A <apache-conf>] [-a <auth_file_name>] [-f <conffile>] [-p <outconf>] [-U <usock_path_pfx>] [-D <TRUE|FALSE>] [-u <sys_user>] [-d <TRUE|FALSE>] [-w <web_dir>] [-k <ftok_dir>] [-l <legal_path>] [-y <proxy_path>] [-P <prefix_path>] [-x]"
     echo "  -h this help"
     echo "  -f use this config file"
     echo "  -p save preferences in the file"
     echo "  -W web files only"
-    echo "  -A server conf (per DocumentRoot) - def. $apache_conf"
+    echo "  -A server conf (for DocumentRoot) - def. $apache_conf"
     echo "  -R document_root                - def. ricavato da -w meno -P"
     echo "  -c number cards in hand         - def. $card_hand"
     echo "  -n number of players            - def. $players_n"
@@ -54,6 +55,8 @@ function usage () {
     echo "  -P prefix path                  - def. \"$prefix_path\""
     echo "  -C config filename              - def. \"$brisk_conf\""
     echo "  -U unix socket path prefix      - def. \"$usock_path_pfx\""
+    echo "  -D nginx speaks http with the daemon - def. \"$http_direct\""
+    echo "     (FALSE: the frontend hands over the descriptor, see WARNING.txt)"
     echo "  -u system user to run brisk dae - def. \"$sys_user\""
     echo "  -x copy tests as normal php     - def. \"$test_add\""
     echo
@@ -186,6 +189,7 @@ while [ $# -gt 0 ]; do
         -C*) brisk_conf="$(get_param "-C" "$1" "$2")"; sh=$?;;
         -l*) legal_path="$(get_param "-l" "$1" "$2")"; sh=$?;;
         -U*) usock_path_pfx="$(get_param "-U" "$1" "$2")"; sh=$?;;
+        -D*) http_direct="$(get_param "-D" "$1" "$2")"; sh=$?;;
         -u*) sys_user="$(get_param "-u" "$1" "$2")"; sh=$?;;
         system) action=system ; sh=1;;
         -W) web_only="TRUE";;
@@ -224,6 +228,7 @@ echo "    proxy_path: \"$proxy_path\""
 echo "    prefix_path:\"$prefix_path\""
 echo "    brisk_conf: \"$brisk_conf\""
 echo "    usock_path_pfx: \"$usock_path_pfx\""
+echo "    http_direct: \"$http_direct\""
 echo "    sys_user:   \"$sys_user\""
 echo "    web_only:   \"$web_only\""
 echo "    test_add:   \"$test_add\""
@@ -249,6 +254,7 @@ if [ ! -z "$outconf" ]; then
     echo "prefix_path=\"$prefix_path\""
     echo "brisk_conf=\"$brisk_conf\""
     echo "usock_path_pfx=\"$usock_path_pfx\""
+    echo "http_direct=\"$http_direct\""
     echo "sys_user=\"$sys_user\""
     echo "web_only=\"$web_only\""
     echo "test_add=\"$test_add\""
@@ -308,6 +314,11 @@ if [ $players_n -ne 3 -a $players_n -ne 5 ]; then
     exit 1
 fi
 
+if [ "$http_direct" != "TRUE" -a "$http_direct" != "FALSE" ]; then
+    echo "http_direct ($http_direct) out of range (TRUE|FALSE)"
+    exit 1
+fi
+
 if [ "$web_only" = "FALSE" ]; then
     if [ ! -d "$ftok_path" -a ! -d "$ftokk_path" ]; then
        echo "ftok_path (\"$ftok_path\") not exists"
@@ -425,7 +436,8 @@ sed -i "s@define *( *'FTOK_PATH',[^)]*)@define('FTOK_PATH', \"$ftok_path\")@g" $
 sed -i "s@define *( *'SITE_PREFIX',[^)]*)@define('SITE_PREFIX', \"$prefix_path\")@g;
 s@define *( *'SITE_PREFIX_LEN',[^)]*)@define('SITE_PREFIX_LEN', $prefix_path_len)@g" ${web_path}__/Obj/sac-a-push.phh
 
-sed -i "s@define *( *'USOCK_PATH_PFX',[^)]*)@define('USOCK_PATH_PFX', \"$usock_path_pfx\")@g" ${web_path}__/spush/brisk-spush.phh
+sed -i "s@define *( *'USOCK_PATH_PFX',[^)]*)@define('USOCK_PATH_PFX', \"$usock_path_pfx\")@g;
+s@define *( *'SPU_HTTP_DIRECT',[^)]*)@define('SPU_HTTP_DIRECT', $http_direct)@g" ${web_path}__/spush/brisk-spush.phh
 
 sed -i "s@define *( *'TABLES_N',[^)]*)@define('TABLES_N', $tables_n)@g;
 s@define *( *'TABLES_APPR_N',[^)]*)@define('TABLES_APPR_N', $tables_appr_n)@g;
@@ -468,7 +480,7 @@ else
     fi
 fi
 if [ -z "$document_root" ]; then
-    echo "Impossibile determinare la radice del sito: usa -R <document_root>"
+    echo "Cannot determine the root of the site: use -R <document_root>"
     exit 1
 fi
 echo "    document_root: \"$document_root\""
index 74b0e7c2fee1cf6435cd92bceb445a4f2c588113..5b738289dd7e7fb172366ee677e638015cbcd40f 100644 (file)
@@ -1,7 +1,51 @@
-APACHE2 CONFIGURATION IS CHANGED ! THIS IS A SAMPLE:
+THE WEB SERVER CONFIGURATION HAS CHANGED! READ BEFORE UPGRADING.
 
-Replace <USOCK_PATH_PFX> with the value passed to INSTALL.sh with -U
-(default: /home/nastasi/brisk-priv/brisk).
+The brisk-spush daemon can get the player connection in two ways, chosen
+with SPU_HTTP_DIRECT in spush/brisk-spush.phh (INSTALL.sh -D). From this
+version on, the default is the first one.
+
+
+==== 1. DIRECT MODE (default, -D TRUE) ====
+
+nginx opens an ordinary http connection on one of the unix sockets of the
+pool and the daemon reads the request from it. Nothing compiled by hand is
+needed: neither the php ancillary extension, nor mod_proxy_fdpass2 for
+apache, nor ngx_http_fdpass_module for nginx, nor kTLS. The debian nginx
+package plus php-fpm for the pages the daemon does not serve is enough.
+
+Sample configuration, ready to be adapted:
+
+    system/nginx/brisk.conf        -> /etc/nginx/sites-available/brisk
+    system/nginx/brisk-daemon.inc  -> /etc/nginx/brisk-daemon.inc
+
+The values to keep aligned with those passed to INSTALL.sh are listed at the
+top of brisk.conf: the root of the site, the url prefix and the path of the
+sockets (-U). The sockets are USOCK_POOL_N of them (10 by default), named
+<prefix><N>.sock.
+
+Three points that are easy to get wrong:
+
+  - max_fails=0 on every socket of the upstream: without it, at the first
+    restart of the daemon nginx marks all the sockets as dead and keeps
+    answering 502 "no live upstreams" even after the daemon is back.
+
+  - proxy_buffering off: brisk is a comet application, with buffering on the
+    response of index_rd.php stays inside nginx and the game never starts.
+
+  - nginx does not read the .htaccess files of the tree. The rules that
+    denied access to Etc/, Obj/, spush/ and to the .phh/.pho files have to be
+    rewritten in the server configuration (they are in brisk.conf). Without
+    them the configuration file holding the database credentials can be
+    downloaded as plain text, because the .pho extension is not associated
+    with php.
+
+
+==== 2. HISTORIC MODE, descriptor handover (-D FALSE) ====
+
+apache reads the request and hands the live client descriptor to the daemon
+with SCM_RIGHTS, stepping aside. It requires the php ancillary extension and
+mod_proxy_fdpass2. Replace <USOCK_PATH_PFX> with the value passed to
+INSTALL.sh with -U (default: /home/nastasi/brisk-priv/brisk).
 
 ProxyPassMatch ^/brisk/.*\.(png|gif|jpg|css|js|mp3|swf)$ !
     ProxyPass /brisk/briskin5/index_rd.php fd://localhost<USOCK_PATH_PFX> keepalive=Off connectiontimeout=5 timeout=30
index 945b60a5de50af41f4d949561414c558d7e5c883..a726a4670fd2c74ee55017506cfffe71cf0774a5 100644 (file)
@@ -2,7 +2,7 @@
 --    INSERT INTO #PFX#bin5_tournaments (code, active, name) VALUES (1, 1, 'normal match');
 --    INSERT INTO #PFX#bin5_tournaments (code, active, name) VALUES (2, 1, 'special match');
 
--- pg10+: "SET (col) = (val)" su una sola colonna non e' piu' valido
+-- pg10+: "SET (col) = (val)" on a single column is no longer valid
 UPDATE #PFX#bin5_tournaments SET name = 'old rules: with draw' WHERE code = 1;
 UPDATE #PFX#bin5_tournaments SET name = 'new rules: without draw' WHERE code = 2;
 INSERT INTO #PFX#bin5_tournaments (code, active, name) VALUES (3, 1, 'special match');
diff --git a/system/nginx/brisk-daemon.inc b/system/nginx/brisk-daemon.inc
new file mode 100644 (file)
index 0000000..5edb8b7
--- /dev/null
@@ -0,0 +1,31 @@
+# Directives common to every url served by the brisk-spush daemon.
+# To be installed as /etc/nginx/brisk-daemon.inc and included inside each
+# "location =" of the list (see brisk.conf).
+#
+# None of these lines is decorative: brisk is a comet application, and the
+# response of index_rd.php is a stream that stays open for hours.
+
+proxy_pass              http://brisk_daemon;
+
+# 1.1 is needed both for the websocket upgrade and for chunked
+proxy_http_version      1.1;
+
+# without this nginx buffers the response and the comet stream never arrives
+proxy_buffering         off;
+# the daemon reads the POST body by itself, with no rebuilt Content-Length
+proxy_request_buffering off;
+
+# a read channel stays open as long as the player is connected: the default
+# timeout (60s) would keep closing it
+proxy_read_timeout      3600s;
+proxy_send_timeout      3600s;
+
+# the daemon already compresses by itself when the client says so
+gzip                    off;
+
+proxy_set_header        Host              $host;
+# the daemon uses X-Real-Ip for the player address (see pproxy_realip)
+proxy_set_header        X-Real-Ip         $remote_addr;
+proxy_set_header        X-Forwarded-Proto $scheme;
+proxy_set_header        Upgrade           $http_upgrade;
+proxy_set_header        Connection        $connection_upgrade;
diff --git a/system/nginx/brisk.conf b/system/nginx/brisk.conf
new file mode 100644 (file)
index 0000000..065152e
--- /dev/null
@@ -0,0 +1,102 @@
+# Brisk - nginx configuration (debian 13, packaged nginx)
+#
+# To be installed as /etc/nginx/sites-available/brisk and linked from
+# sites-enabled/. The directives common to the daemon urls live in
+# brisk-daemon.inc, to be copied into /etc/nginx/.
+#
+# This configuration is for SPU_HTTP_DIRECT mode (see
+# web/spush/brisk-spush.phh): nginx speaks ordinary http with the daemon over
+# the unix sockets of the pool. No additional module is needed, nor the php
+# ancillary extension: the debian nginx package is enough.
+#
+# The values to keep aligned with INSTALL.sh:
+#   root            <- the root of the site (-R, or -w minus -P)
+#   /brisk/         <- prefix_path (-P)
+#   the unix sockets <- usock_path_pfx (-U) + <N> + ".sock", N from 0 to
+#                      USOCK_POOL_N-1 (10 by default, in brisk-spush.phh)
+
+# The pool of daemon sockets. max_fails=0 matters: when the daemon restarts
+# nginx would mark every socket as dead and answer 502 "no live upstreams"
+# even after the daemon is back.
+upstream brisk_daemon {
+    server unix:/home/nastasi/brisk-priv/brisk0.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk1.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk2.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk3.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk4.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk5.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk6.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk7.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk8.sock max_fails=0;
+    server unix:/home/nastasi/brisk-priv/brisk9.sock max_fails=0;
+}
+
+# Needed to propagate the websocket upgrade. If another configuration in the
+# same nginx already defines it, this block has to go (nginx refuses a
+# duplicate "map" on the same name).
+map $http_upgrade $connection_upgrade {
+    default upgrade;
+    ''      close;
+}
+
+server {
+    listen      80;
+    listen      443 ssl;
+    server_name brisk.example.org;
+
+    ssl_certificate     /etc/ssl/certs/brisk.crt;
+    ssl_certificate_key /etc/ssl/private/brisk.key;
+    ssl_protocols       TLSv1.2 TLSv1.3;
+
+    root  /home/nastasi/web;
+    index index.php;
+
+    # --- what the .htaccess files used to protect ---
+    # nginx does not read .htaccess files: these rules replace the
+    # "Deny from All" scattered through the tree. Etc/ holds the database
+    # credentials in clear, and .pho/.phh are not associated with php:
+    # without these lines they can be downloaded as plain text.
+    location ^~ /Etc/                { return 404; }
+    location ^~ /brisk/Obj/          { return 404; }
+    location ^~ /brisk/spush/        { return 404; }
+    location ^~ /brisk/briskin5/Obj/ { return 404; }
+    location ~ \.(phh|pho)$          { return 404; }
+    location ~ /\.                   { return 404; }
+
+    # --- the nine urls served by the daemon ---
+    # Exact match ("location ="), not a prefix: everything else in the tree
+    # (static pages, admin.php, usermgmt.php, the .css, the .js, the images)
+    # does NOT go through the daemon.
+    location = /brisk/index.php                   { include brisk-daemon.inc; }
+    location = /brisk/index_wr.php                { include brisk-daemon.inc; }
+    location = /brisk/index_rd.php                { include brisk-daemon.inc; }
+    location = /brisk/index_rd_wss.php            { include brisk-daemon.inc; }
+    location = /brisk/briskin5/index.php          { include brisk-daemon.inc; }
+    location = /brisk/briskin5/index_wr.php       { include brisk-daemon.inc; }
+    location = /brisk/briskin5/index_rd.php       { include brisk-daemon.inc; }
+    location = /brisk/briskin5/index_rd_wss.php   { include brisk-daemon.inc; }
+    location = /brisk/briskin5/briskin5/index.php { include brisk-daemon.inc; }
+
+    # --- every other php page: php-fpm ---
+    location ~ \.php$ {
+        include       fastcgi_params;
+        fastcgi_pass  unix:/run/php/php8.4-fpm.sock;
+        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+        fastcgi_param DOCUMENT_ROOT   $document_root;
+    }
+
+    # --- static files, with the cache headers that lived in the .htaccess ---
+    location ~* \.(js|css)$ {
+        expires 1d;
+        add_header Cache-Control "public, must-revalidate";
+    }
+    location ~* \.(png|jpg|gif|mp3|swf)$ {
+        expires 4d;
+    }
+
+    location / { try_files $uri $uri/ =404; }
+
+    # error.php is the daemon courtesy page (it was ErrorDocument 500 in the
+    # apache configuration)
+    error_page 500 502 503 504 /error.php;
+}
index 1a9a413aed1eea9ef159836dc9998ff500dca97b..9002acaf202b18851641fdccd70544d60749395c 100644 (file)
@@ -698,7 +698,12 @@ class Sac_a_push {
             fprintf(STDERR, "WARNING: pendpage not found\n");
         }
         $this->socks_unset($sock);
-        fprintf(STDERR, "PP_REM: %d\n", intval($sock));
+        /* in direct http mode every request goes through a pending page:
+           this trace, occasional before, would become one log line for
+           every request of every player */
+        if ($this->debug > 1) {
+            fprintf(STDERR, "PP_REM: %d\n", intval($sock));
+        }
     }
 
 
@@ -1123,7 +1128,14 @@ class Sac_a_push {
                                         */
                                     }
                                 }
-                                else {
+                                else if (!isset($this->s2p[$id]) && !isset($this->s2c[$id])) {
+                                    /* the message only makes sense if the socket belongs
+                                       to none of the known lists. Pending pages
+                                       (s2p) and postcloses (s2c) are handled
+                                       further down and have no user attached: in
+                                       direct http mode the head of EVERY request
+                                       goes through here, and this branch filled
+                                       the log with errors that were not. */
                                     fprintf(STDERR, "User associated with ID: %s not found\n", $id);
                                 }
 
index 39bcee3bdab11c63abc3e433c0c594bb71928c0e..5fdb783022b635e6c6b7f69df7a018f7552b139e 100644 (file)
@@ -27,10 +27,26 @@ $HTTP_HOST="dodo.birds.lan";
 define('USOCK_PATH_PFX', "/tmp/brisk");
 define('USOCK_POOL_N', 10);
 
-/* TRUE  = nginx speaks http directly on the unix socket (no ancillary
-           extension, no descriptor handover modules, no kTLS)
-   FALSE = historic behaviour: the frontend hands over the client descriptor */
-define('SPU_HTTP_DIRECT', FALSE);
+/* How the daemon gets the player connection.
+
+   TRUE  (direct mode, the default): the frontend opens an ordinary http
+         connection on one of the unix sockets of the pool and the daemon
+         reads the request from it. Neither the php ancillary extension nor a
+         descriptor handover module in the frontend (mod_proxy_fdpass2 for
+         apache, ngx_http_fdpass_module for nginx) are needed, nor kTLS: the
+         packaged nginx is enough. The frontend however stays in the middle
+         for the whole life of the connection, which in a comet application
+         is long. Sample configuration in system/nginx/.
+
+   FALSE (historic mode): the frontend reads the request, then hands the live
+         client descriptor to the daemon with SCM_RIGHTS and steps aside.
+         It requires the C components mentioned above. If the frontend
+         terminates the TLS and hands over the encrypted socket, kTLS is
+         needed as well.
+
+   The code for both modes is in Obj/sac-a-push.phh, in the branch that
+   accepts new connections. */
+define('SPU_HTTP_DIRECT', TRUE);
 define('SOCK_SHARD_N', 2);
 
 define('PENDINGPAGE_CONTINUE', 0);