From: Matteo Nastasi Date: Sun, 13 Sep 2026 10:35:02 +0000 (+0200) Subject: direct http mode becomes the default X-Git-Url: https://mop.ddnsfree.com/gitweb/?a=commitdiff_plain;h=68ebb225a4781f0323e8fc1c9ec6ee7ae4a8a5f8;p=brisk.git direct http mode becomes the default 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) Claude-Session: https://claude.ai/code/session_014M1jiEq9cHdE5SE5j6vFuE --- diff --git a/INSTALL.sh b/INSTALL.sh index 6d1478c..fe9bbdf 100755 --- a/INSTALL.sh +++ b/INSTALL.sh @@ -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 ] [-r ] [-G ] [-A ] [-a ] [-f ] [-p ] [-U ] [-u ] [-d ] [-w ] [-k ] [-l ] [-y ] [-P ] [-x]" + echo "$1 [-W] [-n 3|5] [-c 2|8] [-t <(n>=4)>] [-T ] [-r ] [-G ] [-A ] [-a ] [-f ] [-p ] [-U ] [-D ] [-u ] [-d ] [-w ] [-k ] [-l ] [-y ] [-P ] [-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 " + echo "Cannot determine the root of the site: use -R " exit 1 fi echo " document_root: \"$document_root\"" diff --git a/WARNING.txt b/WARNING.txt index 74b0e7c..5b73828 100644 --- a/WARNING.txt +++ b/WARNING.txt @@ -1,7 +1,51 @@ -APACHE2 CONFIGURATION IS CHANGED ! THIS IS A SAMPLE: +THE WEB SERVER CONFIGURATION HAS CHANGED! READ BEFORE UPGRADING. -Replace 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 +.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 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 keepalive=Off connectiontimeout=5 timeout=30 diff --git a/sql/sql.d/085-tourn-update.sql b/sql/sql.d/085-tourn-update.sql index 945b60a..a726a46 100644 --- a/sql/sql.d/085-tourn-update.sql +++ b/sql/sql.d/085-tourn-update.sql @@ -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 index 0000000..5edb8b7 --- /dev/null +++ b/system/nginx/brisk-daemon.inc @@ -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 index 0000000..065152e --- /dev/null +++ b/system/nginx/brisk.conf @@ -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) + + ".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; +} diff --git a/web/Obj/sac-a-push.phh b/web/Obj/sac-a-push.phh index 1a9a413..9002aca 100644 --- a/web/Obj/sac-a-push.phh +++ b/web/Obj/sac-a-push.phh @@ -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); } diff --git a/web/spush/brisk-spush.phh b/web/spush/brisk-spush.phh index 39bcee3..5fdb783 100644 --- a/web/spush/brisk-spush.phh +++ b/web/spush/brisk-spush.phh @@ -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);