[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1106212: marked as done (unblock: uwsgi/2.0.29-1)



Your message dated Fri, 30 May 2025 12:33:44 +0200
with message-id <ed865a90-edab-4b76-b36a-a8508a8e9632@debian.org>
and subject line Re: Bug#1106212: unblock: uwsgi/2.0.29-1
has caused the Debian Bug report #1106212,
regarding unblock: uwsgi/2.0.29-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1106212: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1106212
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: uwsgi@packages.debian.org
Control: affects -1 + src:uwsgi
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package uwsgi

[ Reason ]
This was the course of action discussed in #1106184. Summary is:

    reverting to older release will only miss a few bugfixes and
    look ugly and cost more time

[ Impact ]
Some reverse dependencies will require a new upload and a new unblock
if the unblock is not granted.

[ Tests ]
uwsgi and plugins have autopktests covering "hello world" apps.

[ Risks ]
As uwsgi has been in maintenance mode for some years, new upstream versions
only include bugfixes and maintenance fixes (e.g. adapting to new external
API). Therefore, risks are low for regressions. Upstream changelog for further
reference:

    https://github.com/unbit/uwsgi/compare/2.0.28...2.0.29

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
The new 2.0.29 upstream version diff seems big. However, this is because 8
patches were upstreamed. Attached is the source diff with patches applied
and ignoring whitespace changes.

unblock uwsgi/2.0.29-1
diff -NruwB uwsgi-2.0.28-9/contrib/emperormon.ru uwsgi-2.0.29-1/contrib/emperormon.ru
--- uwsgi-2.0.28-9/contrib/emperormon.ru	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/contrib/emperormon.ru	2025-05-21 11:50:57.343560130 +0200
@@ -23,13 +23,13 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta name="description" content="">
     <meta name="author" content="unbit">
-    <link href="http://unbit.github.com/bootstrap/css/bootstrap.css"; rel="stylesheet">
+    <link href="https://unbit.github.com/bootstrap/css/bootstrap.css"; rel="stylesheet">
     <style>
       body {
         padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
       }
     </style>
-    <link href="http://unbit.github.com/bootstrap/css/bootstrap-responsive.css"; rel="stylesheet">
+    <link href="https://unbit.github.com/bootstrap/css/bootstrap-responsive.css"; rel="stylesheet">
   </head>
   <body>
 
@@ -99,8 +99,8 @@
 
 
 
-    <script src="http://unbit.github.com/jquery-1.7.2.min.js"; type="text/javascript"></script>
-    <script src="http://unbit.github.com/bootstrap/js/bootstrap.min.js"; type="text/javascript"></script>
+    <script src="https://unbit.github.com/jquery-1.7.2.min.js"; type="text/javascript"></script>
+    <script src="https://unbit.github.com/bootstrap/js/bootstrap.min.js"; type="text/javascript"></script>
   </body>
 </html>
 eof
diff -NruwB uwsgi-2.0.28-9/contrib/pypy/uwsgi_pypy_greenlets.py uwsgi-2.0.29-1/contrib/pypy/uwsgi_pypy_greenlets.py
--- uwsgi-2.0.28-9/contrib/pypy/uwsgi_pypy_greenlets.py	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/contrib/pypy/uwsgi_pypy_greenlets.py	2025-05-21 11:50:57.343560130 +0200
@@ -15,10 +16,11 @@
 def uwsgi_pypy_greenlet_wrapper():
     lib.async_schedule_to_req_green()
 
+
 @ffi.callback("void()")
 def uwsgi_pypy_greenlet_schedule():
     id = lib.uwsgi.wsgi_req.async_id
-    modifier1 = lib.uwsgi.wsgi_req.uh.modifier1;
+    modifier1 = lib.uwsgi.wsgi_req.uh.modifier1
 
     # generate a new greenlet
     if not lib.uwsgi.wsgi_req.suspended:
@@ -36,10 +38,11 @@
     if lib.uwsgi.p[modifier1].resume:
         lib.uwsgi.p[modifier1].resume(ffi.NULL)
 
+
 @ffi.callback("void(struct wsgi_request *)")
 def uwsgi_pypy_greenlet_switch(wsgi_req):
-    id = wsgi_req.async_id
-    modifier1 = wsgi_req.uh.modifier1;
+    wsgi_req.async_id
+    modifier1 = wsgi_req.uh.modifier1
 
     # this is called in the current greenlet
     if lib.uwsgi.p[modifier1].suspend:
@@ -54,7 +57,7 @@
     # update current running greenlet
     lib.uwsgi.wsgi_req = wsgi_req
 
-if lib.uwsgi.async <= 1:
+if lib.uwsgi.async < 1:
     raise Exception("pypy greenlets require async mode !!!")
 lib.uwsgi.schedule_to_main = uwsgi_pypy_greenlet_switch
 lib.uwsgi.schedule_to_req = uwsgi_pypy_greenlet_schedule
diff -NruwB uwsgi-2.0.28-9/contrib/runuwsgi.py uwsgi-2.0.29-1/contrib/runuwsgi.py
--- uwsgi-2.0.28-9/contrib/runuwsgi.py	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/contrib/runuwsgi.py	2025-05-21 11:50:57.343560130 +0200
@@ -57,7 +58,7 @@
         # exec the uwsgi binary
         os.execvp('uwsgi', ('uwsgi',))
 
-    def usage(self, subcomand):
+    def usage(self, subcommand):
         return r"""
 run this project on the uWSGI server
 
diff -NruwB uwsgi-2.0.28-9/contrib/spoolqueue/tasksconsumer.py uwsgi-2.0.29-1/contrib/spoolqueue/tasksconsumer.py
--- uwsgi-2.0.28-9/contrib/spoolqueue/tasksconsumer.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/contrib/spoolqueue/tasksconsumer.py	2025-05-21 11:50:57.343560130 +0200
@@ -1,4 +1,4 @@
-from uwsgidecorators import *
+from uwsgidecorators import spool
 from threading import Thread
 
 from six.moves import queue
diff -NruwB uwsgi-2.0.28-9/contrib/spoolqueue/tasks.py uwsgi-2.0.29-1/contrib/spoolqueue/tasks.py
--- uwsgi-2.0.28-9/contrib/spoolqueue/tasks.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/contrib/spoolqueue/tasks.py	2025-05-21 11:50:57.343560130 +0200
@@ -1,6 +1,7 @@
 from __future__ import print_function
 
-from tasksconsumer import *
+from tasksconsumer import queueconsumer
+
 
 @queueconsumer('fast', 4)
 def fast_queue(arguments):
diff -NruwB uwsgi-2.0.28-9/contrib/twuwsgi.py uwsgi-2.0.29-1/contrib/twuwsgi.py
--- uwsgi-2.0.28-9/contrib/twuwsgi.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/contrib/twuwsgi.py	2025-05-21 11:50:57.343560130 +0200
@@ -1,7 +1,6 @@
 from twisted.internet import defer, protocol, reactor
 from twisted.protocols import basic
-from twisted.web2 import server, http, resource, responsecode, stream, channel
-from twisted.application import service, strports
+from twisted.web2 import http, resource, responsecode, stream
 
 import struct
 
diff -NruwB uwsgi-2.0.28-9/contrib/uwsgi-cache-monitor.py uwsgi-2.0.29-1/contrib/uwsgi-cache-monitor.py
--- uwsgi-2.0.28-9/contrib/uwsgi-cache-monitor.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/contrib/uwsgi-cache-monitor.py	2025-05-21 11:50:57.343560130 +0200
@@ -70,8 +71,13 @@
         self.samples += 1
         block_sizes = sum([x[3] for x in data])
         self.block_sizes += block_sizes
-        self.history.append({'full': full, 'empty': empty, 'data': data, \
-            'items': items, 'block_sizes': block_sizes})
+        self.history.append({
+            'full': full,
+            'empty': empty,
+            'data': data,
+            'items': items,
+            'block_sizes': block_sizes
+        })
 
     def dump(self):
         return {
@@ -88,14 +94,22 @@
     def show_dump(self):
         d = self.dump()
         print()
-        print("Recorded %d samples (%d second(s) sleep between samples)" % \
-            (d['samples'], d['sample_sleep']))
-        print("Cache empty %d times, full %d times, %.2f items on average" % \
-            (d['cache_empty'], d['cache_full'], d['cache_items'] / d['samples']))
-        print("Block size average size: %d bytes" % \
-            (d['block_sizes'] / d['cache_items'] * 8))
-        print("Data in cache average: %d bytes" % \
-            (d['block_sizes'] / d['samples'] * 8))
+        print("Recorded %d samples (%d second(s) sleep between samples)" % (
+            d['samples'],
+            d['sample_sleep']
+        ))
+        print("Cache empty %d times, full %d times, %.2f items on average" % (
+            d['cache_empty'],
+            d['cache_full'],
+            d['cache_items'] / d['samples']
+        ))
+        print("Block size average size: %d bytes" % (
+            d['block_sizes'] / d['cache_items'] * 8
+        ))
+        print("Data in cache average: %d bytes" % (
+            d['block_sizes'] / d['samples'] * 8
+        ))
+
 
 def main(options):
     cache = Cache(options.cache_store, options.cache_slots, options.block_size,
@@ -103,21 +117,29 @@
     print("Recording...")
     while True:
         try:
-            data = cache.read()
+            cache.read()
         except KeyboardInterrupt:
             cache.show_dump()
             sys.exit(0)
 
 if __name__ == '__main__':
     parser = OptionParser()
-    parser.add_option("-s", "--cache-slots", dest="cache_slots", type="int",
-        help="Slots available in the cache, uwsgi cache option")
-    parser.add_option("-c", "--cache-store", dest="cache_store", default="uwsgi.cache",
-        help="The filename of the cache store, uwsgi cache-store option. Default: uwsgi.cache")
-    parser.add_option("-b", "--block-size", dest="block_size", default=65536, type="int",
-        help="The size of the cache block, uwsgi cache-blocksize option. Default: 65536")
-    parser.add_option("-t", "--sleep-time", dest="sleep_time", default=1, type="int",
-        help="The time to sleep between each sample. Default: 1")
+    parser.add_option(
+        "-s", "--cache-slots", dest="cache_slots", type="int",
+        help="Slots available in the cache, uwsgi cache option"
+    )
+    parser.add_option(
+        "-c", "--cache-store", dest="cache_store", default="uwsgi.cache",
+        help="The filename of the cache store, uwsgi cache-store option. Default: uwsgi.cache"
+    )
+    parser.add_option(
+        "-b", "--block-size", dest="block_size", default=65536, type="int",
+        help="The size of the cache block, uwsgi cache-blocksize option. Default: 65536"
+    )
+    parser.add_option(
+        "-t", "--sleep-time", dest="sleep_time", default=1, type="int",
+        help="The time to sleep between each sample. Default: 1"
+    )
 
     (options, args) = parser.parse_args()
     if not options.cache_slots:
diff -NruwB uwsgi-2.0.28-9/contrib/uwsgisubscribers.ru uwsgi-2.0.29-1/contrib/uwsgisubscribers.ru
--- uwsgi-2.0.28-9/contrib/uwsgisubscribers.ru	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/contrib/uwsgisubscribers.ru	2025-05-21 11:50:57.343560130 +0200
@@ -23,13 +23,13 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta name="description" content="">
     <meta name="author" content="unbit">
-    <link href="http://unbit.github.com/bootstrap/css/bootstrap.css"; rel="stylesheet">
+    <link href="https://unbit.github.com/bootstrap/css/bootstrap.css"; rel="stylesheet">
     <style>
       body {
         padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
       }
     </style>
-    <link href="http://unbit.github.com/bootstrap/css/bootstrap-responsive.css"; rel="stylesheet">
+    <link href="https://unbit.github.com/bootstrap/css/bootstrap-responsive.css"; rel="stylesheet">
   </head>
   <body>
 
@@ -75,8 +75,8 @@
 
     <% end %>
 
-    <script src="http://unbit.github.com/jquery-1.7.2.min.js"; type="text/javascript"></script>
-    <script src="http://unbit.github.com/bootstrap/js/bootstrap.min.js"; type="text/javascript"></script>
+    <script src="https://unbit.github.com/jquery-1.7.2.min.js"; type="text/javascript"></script>
+    <script src="https://unbit.github.com/bootstrap/js/bootstrap.min.js"; type="text/javascript"></script>
   </body>
 </html>
 eof
diff -NruwB uwsgi-2.0.28-9/core/emperor.c uwsgi-2.0.29-1/core/emperor.c
--- uwsgi-2.0.28-9/core/emperor.c	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/core/emperor.c	2025-05-21 11:50:57.343560130 +0200
@@ -598,7 +598,7 @@
 }
 
 
-static void emperor_stats() {
+static void emperor_stats(int signum) {
 
 	struct uwsgi_instance *c_ui = ui->ui_next;
 
diff -NruwB uwsgi-2.0.28-9/core/fifo.c uwsgi-2.0.29-1/core/fifo.c
--- uwsgi-2.0.28-9/core/fifo.c	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/core/fifo.c	2025-05-21 11:50:57.343560130 +0200
@@ -26,18 +26,18 @@
 
 #define announce_fifo uwsgi_log_verbose("active master fifo is now %s\n", uwsgi_fifo_by_slot())
 
-static void uwsgi_fifo_set_slot_zero() { uwsgi.master_fifo_slot = 0; announce_fifo; }
-static void uwsgi_fifo_set_slot_one() { uwsgi.master_fifo_slot = 1; announce_fifo; }
-static void uwsgi_fifo_set_slot_two() { uwsgi.master_fifo_slot = 2; announce_fifo; }
-static void uwsgi_fifo_set_slot_three() { uwsgi.master_fifo_slot = 3; announce_fifo; }
-static void uwsgi_fifo_set_slot_four() { uwsgi.master_fifo_slot = 4; announce_fifo; }
-static void uwsgi_fifo_set_slot_five() { uwsgi.master_fifo_slot = 5; announce_fifo; }
-static void uwsgi_fifo_set_slot_six() { uwsgi.master_fifo_slot = 6; announce_fifo; }
-static void uwsgi_fifo_set_slot_seven() { uwsgi.master_fifo_slot = 7; announce_fifo; }
-static void uwsgi_fifo_set_slot_eight() { uwsgi.master_fifo_slot = 8; announce_fifo; }
-static void uwsgi_fifo_set_slot_nine() { uwsgi.master_fifo_slot = 9; announce_fifo; }
+static void uwsgi_fifo_set_slot_zero(int signum) { uwsgi.master_fifo_slot = 0; announce_fifo; }
+static void uwsgi_fifo_set_slot_one(int signum) { uwsgi.master_fifo_slot = 1; announce_fifo; }
+static void uwsgi_fifo_set_slot_two(int signum) { uwsgi.master_fifo_slot = 2; announce_fifo; }
+static void uwsgi_fifo_set_slot_three(int signum) { uwsgi.master_fifo_slot = 3; announce_fifo; }
+static void uwsgi_fifo_set_slot_four(int signum) { uwsgi.master_fifo_slot = 4; announce_fifo; }
+static void uwsgi_fifo_set_slot_five(int signum) { uwsgi.master_fifo_slot = 5; announce_fifo; }
+static void uwsgi_fifo_set_slot_six(int signum) { uwsgi.master_fifo_slot = 6; announce_fifo; }
+static void uwsgi_fifo_set_slot_seven(int signum) { uwsgi.master_fifo_slot = 7; announce_fifo; }
+static void uwsgi_fifo_set_slot_eight(int signum) { uwsgi.master_fifo_slot = 8; announce_fifo; }
+static void uwsgi_fifo_set_slot_nine(int signum) { uwsgi.master_fifo_slot = 9; announce_fifo; }
 
-static void subscriptions_blocker() {
+static void subscriptions_blocker(int signum) {
 	if (uwsgi.subscriptions_blocked) {
 		uwsgi_log_verbose("subscriptions re-enabled\n");
 		uwsgi.subscriptions_blocked = 0;
@@ -48,7 +48,7 @@
 	}
 }
 
-static void emperor_rescan() {
+static void emperor_rescan(int signum) {
 	if (uwsgi.emperor_pid > 0) {
 		if (kill(uwsgi.emperor_pid, SIGWINCH)) {
 			uwsgi_error("emperor_rescan()/kill()");
@@ -78,25 +78,25 @@
 	uwsgi_fifo_table['8'] = uwsgi_fifo_set_slot_eight;
 	uwsgi_fifo_table['9'] = uwsgi_fifo_set_slot_nine;
 
-	uwsgi_fifo_table['-'] = uwsgi_cheaper_decrease;
-	uwsgi_fifo_table['+'] = uwsgi_cheaper_increase;
-	uwsgi_fifo_table['B'] = vassal_sos; 
-	uwsgi_fifo_table['c'] = uwsgi_chain_reload;
-	uwsgi_fifo_table['C'] = uwsgi_go_cheap;
+	uwsgi_fifo_table['-'] = (void (*)(int))uwsgi_cheaper_decrease;
+	uwsgi_fifo_table['+'] = (void (*)(int))uwsgi_cheaper_increase;
+	uwsgi_fifo_table['B'] = (void (*)(int))vassal_sos;
+	uwsgi_fifo_table['c'] = (void (*)(int))uwsgi_chain_reload;
+	uwsgi_fifo_table['C'] = (void (*)(int))uwsgi_go_cheap;
 	uwsgi_fifo_table['E'] = emperor_rescan;
-	uwsgi_fifo_table['f'] = uwsgi_refork_master;
-	uwsgi_fifo_table['l'] = uwsgi_log_reopen;
-	uwsgi_fifo_table['L'] = uwsgi_log_rotate;
+	uwsgi_fifo_table['f'] = (void (*)(int))uwsgi_refork_master;
+	uwsgi_fifo_table['l'] = (void (*)(int))uwsgi_log_reopen;
+	uwsgi_fifo_table['L'] = (void (*)(int))uwsgi_log_rotate;
 	uwsgi_fifo_table['p'] = suspend_resume_them_all;
-	uwsgi_fifo_table['P'] = uwsgi_update_pidfiles;
+	uwsgi_fifo_table['P'] = (void (*)(int))uwsgi_update_pidfiles;
 	uwsgi_fifo_table['q'] = gracefully_kill_them_all;
 	uwsgi_fifo_table['Q'] = kill_them_all;
 	uwsgi_fifo_table['r'] = grace_them_all;
 	uwsgi_fifo_table['R'] = reap_them_all;
 	uwsgi_fifo_table['s'] = stats;
 	uwsgi_fifo_table['S'] = subscriptions_blocker;
-	uwsgi_fifo_table['w'] = uwsgi_reload_workers;
-	uwsgi_fifo_table['W'] = uwsgi_brutally_reload_workers;
+	uwsgi_fifo_table['w'] = (void (*)(int))uwsgi_reload_workers;
+	uwsgi_fifo_table['W'] = (void (*)(int))uwsgi_brutally_reload_workers;
 
 }
 
diff -NruwB uwsgi-2.0.28-9/core/init.c uwsgi-2.0.29-1/core/init.c
--- uwsgi-2.0.28-9/core/init.c	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/core/init.c	2025-05-21 11:50:57.347560209 +0200
@@ -3,7 +3,7 @@
 extern struct uwsgi_server uwsgi;
 
 struct http_status_codes {
-        const char      key[3];
+        const char      key[4];
         const char      *message;
         int             message_size;
 };
diff -NruwB uwsgi-2.0.28-9/core/master.c uwsgi-2.0.29-1/core/master.c
--- uwsgi-2.0.28-9/core/master.c	2025-05-21 11:50:49.935416594 +0200
+++ uwsgi-2.0.29-1/core/master.c	2025-05-21 11:51:54.696671342 +0200
@@ -232,6 +232,13 @@
 
 #ifdef UNBIT
 #define SIOBKLGQ 0x8908
+#else
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/unix_diag.h>
+#include <linux/sock_diag.h>
+
 #endif
 
 #ifdef SIOBKLGQ
@@ -245,7 +252,141 @@
 		uwsgi_sock->max_queue = (uint64_t) uwsgi.listen_queue;
 	}
 }
+#else
+
+static int get_socket_inode_from_fd(int fd) {
+	int inode = -1;
+	char source_link_path[32];
+	char link_target[32];
+	sprintf(source_link_path, "/proc/self/fd/%i", fd);
+	if (readlink(source_link_path, link_target, 32) > 0)
+		sscanf(link_target, "socket:[%i]", &inode);
+	return inode;
+}
+
+static void send_netlink_query_for_inode(int fd, int target_ino) {
+	struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
+	struct {
+		struct nlmsghdr nlh;
+		struct unix_diag_req udr;
+	} req = {
+		.nlh = {
+			.nlmsg_len = sizeof(req),
+			.nlmsg_type = SOCK_DIAG_BY_FAMILY,
+			.nlmsg_flags = NLM_F_REQUEST
+		},
+		.udr = {
+			.sdiag_family = AF_UNIX,
+			.udiag_show = UDIAG_SHOW_RQLEN,
+			.udiag_ino = target_ino,
+			.udiag_cookie = {-1, -1}
+		}
+	};
+	struct iovec iov = {.iov_base = &req, .iov_len = sizeof(req)};
+	struct msghdr msg = {
+		.msg_name = &nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1
+	};
+
+	for (;;) {
+		if (sendmsg(fd, &msg, 0) < 0) {
+			if (errno == EINTR)
+				continue;
+
+			perror("sendmsg");
+			return;
+		}
+
+		return;
+	}
+}
+
+static int receive_netlink_answers(int fd, uint64_t *current_queue, uint64_t *max_queue) {
+	long buf[8192 / sizeof(long)];
+	struct sockaddr_nl nladdr;
+	struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
+	int flags = 0;
+	
+	for (;;) {
+		struct msghdr msg = {
+			.msg_name = &nladdr,
+			.msg_namelen = sizeof(nladdr),
+			.msg_iov = &iov,
+			.msg_iovlen = 1
+		};
+		
+		ssize_t ret = recvmsg(fd, &msg, flags);
+		
+		if (ret < 0) {
+			if (errno == EINTR)
+				continue;
+		
+			perror("recvmsg");
+			return -1;
+		}
+		if (ret == 0)
+			return 0;
+		
+		if (nladdr.nl_family != AF_NETLINK) {
+			fputs("!AF_NETLINK\n", stderr);
+			return -1;
+		}
+		
+		const struct nlmsghdr *h = (struct nlmsghdr *)buf;
+		
+		if (!NLMSG_OK(h, ret)) {
+			fputs("!NLMSG_OK\n", stderr);
+			return -1;
+		}
+		
+		for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
+			if (h->nlmsg_type == NLMSG_DONE)
+				return 0;
+			if (h->nlmsg_type == NLMSG_ERROR) {
+				const struct nlmsgerr *err = NLMSG_DATA(h);
+				if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) {
+					fputs("NLMSG_ERROR\n", stderr);
+				} else {
+					errno = -err->error;
+					perror("NLMSG_ERROR");
+				}
+				return -1;
+			}
+			if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY) {
+				fprintf(stderr, "unexpected nlmsg_type %u\n", (unsigned)h->nlmsg_type);
+		  		return -1;
+			}
+
+			// Now extract queue len from results
+			const struct unix_diag_msg *diag = NLMSG_DATA(h);
+			unsigned int rta_len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*diag));
+
+			for (struct rtattr *attr = (struct rtattr *)(diag + 1); RTA_OK(attr, rta_len); attr = RTA_NEXT(attr, rta_len)) {
+				switch (attr->rta_type) {
+				case UNIX_DIAG_RQLEN:
+					*current_queue = ((struct unix_diag_rqlen *) RTA_DATA(attr))->udiag_rqueue;
+					*max_queue = ((struct unix_diag_rqlen *) RTA_DATA(attr))->udiag_wqueue;
+					break;
+				}
+			}
+		}
+		return 0;
+	}
+}
+
+static void get_linux_unix_socket_queue(struct uwsgi_socket *uwsgi_sock) {
+	int fd = uwsgi_sock->fd;
+	int inode = get_socket_inode_from_fd(fd);
+	int diag_socket = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SOCK_DIAG);
+	send_netlink_query_for_inode(diag_socket, inode);
+	receive_netlink_answers(diag_socket, &uwsgi_sock->queue, &uwsgi_sock->max_queue);
+	close(diag_socket);
+}
+
 #endif
+
 #endif
 
 static void master_check_listen_queue() {
@@ -256,14 +397,15 @@
 		if (uwsgi_sock->family == AF_INET || uwsgi_sock->family == AF_INET6) {
 			get_tcp_info(uwsgi_sock);
                 }
+		else if (uwsgi_sock->family == AF_UNIX) {
 #ifdef __linux__
 #ifdef SIOBKLGQ
-                else if (uwsgi_sock->family == AF_UNIX) {
                 	get_linux_unbit_SIOBKLGQ(uwsgi_sock);
-                }
+#else
+			get_linux_unix_socket_queue(uwsgi_sock);
 #endif
 #endif
-
+		}
 		if (uwsgi_sock->queue > backlog) {
 			backlog = uwsgi_sock->queue;
 		}
diff -NruwB uwsgi-2.0.28-9/core/master_utils.c uwsgi-2.0.29-1/core/master_utils.c
--- uwsgi-2.0.28-9/core/master_utils.c	2025-05-21 11:51:19.147982584 +0200
+++ uwsgi-2.0.29-1/core/master_utils.c	2025-05-21 11:51:54.724671885 +0200
@@ -2,7 +2,7 @@
 
 extern struct uwsgi_server uwsgi;
 
-void worker_wakeup() {
+void worker_wakeup(int sig) {
 }
 
 uint64_t uwsgi_worker_exceptions(int wid) {
diff -NruwB uwsgi-2.0.28-9/core/spooler.c uwsgi-2.0.29-1/core/spooler.c
--- uwsgi-2.0.28-9/core/spooler.c	2025-05-21 11:51:12.867860902 +0200
+++ uwsgi-2.0.29-1/core/spooler.c	2025-05-21 11:50:57.347560209 +0200
@@ -12,7 +12,7 @@
 static uint64_t wakeup = 0;
 
 // function to allow waking up the spooler if blocked in event_wait
-void spooler_wakeup() {
+void spooler_wakeup(int signum) {
 	wakeup++;
 }
 
diff -NruwB uwsgi-2.0.28-9/core/utils.c uwsgi-2.0.29-1/core/utils.c
--- uwsgi-2.0.28-9/core/utils.c	2025-05-21 11:51:12.867860902 +0200
+++ uwsgi-2.0.29-1/core/utils.c	2025-05-21 11:50:57.351560285 +0200
@@ -1189,7 +1189,7 @@
 	// yes, this is pretty useless but we cannot ensure all of the plugin have the same behaviour
 	uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 0;
 
-	if (uwsgi.max_requests > 0 && uwsgi.workers[uwsgi.mywid].delta_requests >= uwsgi.max_requests
+	if (uwsgi.max_requests > 0 && uwsgi.workers[uwsgi.mywid].delta_requests >= (uwsgi.max_requests + ((uwsgi.mywid-1) * uwsgi.max_requests_delta))
 	    && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
 		goodbye_cruel_world();
 	}
diff -NruwB uwsgi-2.0.28-9/core/uwsgi.c uwsgi-2.0.29-1/core/uwsgi.c
--- uwsgi-2.0.28-9/core/uwsgi.c	2025-05-21 11:51:12.871860980 +0200
+++ uwsgi-2.0.29-1/core/uwsgi.c	2025-05-21 11:50:57.351560285 +0200
@@ -277,6 +277,7 @@
 
 	{"reaper", no_argument, 'r', "call waitpid(-1,...) after each request to get rid of zombies", uwsgi_opt_true, &uwsgi.reaper, 0},
 	{"max-requests", required_argument, 'R', "reload workers after the specified amount of managed requests", uwsgi_opt_set_64bit, &uwsgi.max_requests, 0},
+	{"max-requests-delta", required_argument, 0, "add (worker_id * delta) to the max_requests value of each worker", uwsgi_opt_set_64bit, &uwsgi.max_requests_delta, 0},
 	{"min-worker-lifetime", required_argument, 0, "number of seconds worker must run before being reloaded (default is 10)", uwsgi_opt_set_64bit, &uwsgi.min_worker_lifetime, 0},
 	{"max-worker-lifetime", required_argument, 0, "reload workers after the specified amount of seconds (default is disabled)", uwsgi_opt_set_64bit, &uwsgi.max_worker_lifetime, 0},
 	{"max-worker-lifetime-delta", required_argument, 0, "add (worker_id * delta) seconds to the max_worker_lifetime value of each worker", uwsgi_opt_set_int, &uwsgi.max_worker_lifetime_delta, 0},
diff -NruwB uwsgi-2.0.28-9/debian/changelog uwsgi-2.0.29-1/debian/changelog
--- uwsgi-2.0.28-9/debian/changelog	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/changelog	2025-05-21 11:50:57.351560285 +0200
@@ -1,3 +1,19 @@
+uwsgi (2.0.29-1) unstable; urgency=medium
+
+  [ upstream ]
+  * new release
+
+  [ Alexandre Rossi ]
+  * add patch to handle backlog of UNIX socket too
+  * add patch to fix build with gcc-15 (Closes: #1098052)
+  * remove patches which have been integrated upstream
+  * add patch to use python3
+
+  [ Jonas Smedegaard ]
+  * update copyright info: update coverage
+
+ -- Jonas Smedegaard <dr@jones.dk>  Sat, 26 Apr 2025 22:24:52 +0200
+
 uwsgi (2.0.28-9) unstable; urgency=medium
 
   [ Alexandre Rossi ]
diff -NruwB uwsgi-2.0.28-9/debian/copyright uwsgi-2.0.29-1/debian/copyright
--- uwsgi-2.0.28-9/debian/copyright	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/copyright	2025-05-21 11:50:57.351560285 +0200
@@ -79,7 +79,7 @@
 Copyright:
   2024-2025  Alexandre Rossi <niol@zincube.net>
   2012-2013  Janos Guljas <janos@debian.org>
-  2013-2026  Jonas Smedegaard <dr@jones.dk>
+  2013-2025  Jonas Smedegaard <dr@jones.dk>
   2010       Leonid Borisenko <leo.borisenko@gmail.com>
 License-grant:
  This program is free software;
diff -NruwB uwsgi-2.0.28-9/debian/copyright_hints uwsgi-2.0.29-1/debian/copyright_hints
--- uwsgi-2.0.28-9/debian/copyright_hints	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/copyright_hints	2025-05-21 11:50:57.351560285 +0200
@@ -171,18 +171,14 @@
  debian/patches/1001_avoid_setting_RPATH.patch
  debian/patches/1002_fix-reload-process-name.patch
  debian/patches/1003_remove-php-libs.patch
- debian/patches/1004_rack3.patch
  debian/patches/1005_avoid_auto_ptr.patch
  debian/patches/1006_avoid_xml2-config.patch
- debian/patches/1007_fix_php_pypy_tests.patch
- debian/patches/1008_fix_pypy_tests.patch
  debian/patches/1009_fix_java_paths.patch
  debian/patches/1010_support_java_pass_includes.patch
- debian/patches/1012_noexecstack.patch
- debian/patches/1016_readline.patch
- debian/patches/1017_python3-compat.patch
+ debian/patches/1011_backlog_uds.patch
  debian/patches/2001_ensure_verbose_build.patch
  debian/patches/2002_rename_py_plugins.patch
+ debian/patches/2003_plugin_builder_py3.patch
  debian/patches/README
  debian/patches/series
  debian/rules
@@ -623,7 +619,11 @@
  t/clojure/myapp.clj
  t/core/apps/read_body_and_send.pl
  t/core/read_body_and_send.pl
+ t/core/readline/app.py
+ t/core/readline/client.py
+ t/core/readline/requirements.txt
  t/core/url_sanitize.pl
+ t/cron.ini
  t/go/cachetest.go
  t/go/complextest.go
  t/go/uploadtest.go
@@ -634,7 +634,12 @@
  t/mules/reload.py
  t/mules/signal_management.py
  t/perl/active_workers_signal.pl
+ t/perl/apps/body-types.psgi
+ t/perl/apps/env.psgi
  t/perl/apps/input_with_offset.pl
+ t/perl/body-types.t
+ t/perl/env.t
+ t/perl/run
  t/perl/test.psgi
  t/perl/test_benchmark.pl
  t/perl/test_harakiri.psgi
@@ -644,6 +649,7 @@
  t/perl/test_sleepy.psgi
  t/perl/test_streaming.psgi
  t/php/config.ini
+ t/php/test.php
  t/pypy/config.ini
  t/pypy/t_continulet1.py
  t/pypy/t_continulet2.py
@@ -654,7 +660,15 @@
  t/python/spooler_decorators/spooler_decorator_test.ini
  t/python/spooler_decorators/spooler_decorator_tests.py
  t/python/spooler_decorators/spooler_handlers.py
+ t/python/spooler_handler.py
+ t/python/spooler_priority/spooler_priority_constants.py
+ t/python/spooler_priority/spooler_priority_handler.py
+ t/python/spooler_priority/spooler_priority_test.ini
+ t/python/spooler_priority/spooler_priority_test.py
  t/python/testba.py
+ t/python/testmultipleenv.py
+ t/python/timers.py
+ t/python/wsgi_chunked.py
  t/rack/app.ru
  t/ring/README.md
  t/ring/config.ini
@@ -672,6 +686,7 @@
  t/sharedarea/bigranges.py
  t/sharedarea/sharedarea_incdec.ini
  t/sharedarea/sharedarea_incdec.py
+ t/spooler/cheap.py
  t/spooler/read.py
  t/spooler/reload.py
  t/static/config.ini
@@ -695,6 +710,7 @@
  tests/deadlocks/nomaster-threads-1worker.ini
  tests/deadlocks/sitecustomize.py
  tests/decoratortest.py
+ tests/else_test.ini
  tests/fileserve_async.py
  tests/gevent_spool.py
  tests/gh-deadlocks.sh
@@ -708,6 +724,7 @@
  tests/iobound_green.py
  tests/logger.py
  tests/mako_ugreen.py
+ tests/mule_file.py
  tests/mulefunc.py
  tests/multiapp.txt
  tests/myadmin.py
@@ -730,6 +747,7 @@
  tests/sleeping_green.py
  tests/sleepthreadasync.py
  tests/slow.py
+ tests/spooler_dir.py
  tests/spoolme.py
  tests/static/test.txt
  tests/static/test2.txt
@@ -744,7 +762,6 @@
  tests/testrpc.py
  tests/testsignals.py
  tests/testworkers.py
- tests/testyieldnone.py
  tests/threads.py
  tests/threads_atexit.py
  tests/threads_heavy.py
@@ -754,6 +771,8 @@
  tests/websockets.py
  tests/websockets_chat.pl
  tests/websockets_chat.py
+ tests/websockets_chat_2.py
+ tests/websockets_chat_async.lua
  tests/websockets_chat_async.py
  tests/websockets_chat_asyncio.py
  tests/websockets_echo.lua
diff -NruwB uwsgi-2.0.28-9/debian/patches/1004_rack3.patch uwsgi-2.0.29-1/debian/patches/1004_rack3.patch
--- uwsgi-2.0.28-9/debian/patches/1004_rack3.patch	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/patches/1004_rack3.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,57 +0,0 @@
-Description: plugins/rack: add support for rack 3
-Author: Alexandre Rossi <alexandre.rossi@gmail.com>
-Source: upstream, https://github.com/unbit/uwsgi/commit/ceb027f
-Bug: https://github.com/unbit/uwsgi/pull/2704
-Last-Update: 2025-03-02
----
-This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
-diff --git a/plugins/rack/rack_plugin.c b/plugins/rack/rack_plugin.c
-index 1611deecd..792cc4dc7 100644
---- a/plugins/rack/rack_plugin.c
-+++ b/plugins/rack/rack_plugin.c
-@@ -1055,18 +1055,22 @@ VALUE init_rack_app( VALUE script ) {
- 	}
- #endif
- 
--        VALUE rackup = rb_funcall( rb_const_get(rack, rb_intern("Builder")), rb_intern("parse_file"), 1, script);
--        if (TYPE(rackup) != T_ARRAY) {
--        	uwsgi_log("unable to parse %s file\n", RSTRING_PTR(script));
--                return Qnil;
--        }
--
--        if (RARRAY_LEN(rackup) < 1) {
--        	uwsgi_log("invalid rack config file: %s\n", RSTRING_PTR(script));
-+	VALUE rackup = rb_funcall( rb_const_get(rack, rb_intern("Builder")), rb_intern("parse_file"), 1, script);
-+	if (TYPE(rackup) == T_OBJECT) { // rack +3
-+		return rackup;
-+	}
-+	else if (TYPE(rackup) == T_ARRAY) { // rack << 3
-+		if (RARRAY_LEN(rackup) < 1) {
-+			uwsgi_log("invalid rack config file: %s\n", RSTRING_PTR(script));
-+			return Qnil;
-+		}
-+		return RARRAY_PTR(rackup)[0] ;
-+	}
-+	else {
-+		uwsgi_log("unable to parse %s file %d\n", RSTRING_PTR(script), TYPE(rackup));
- 		return Qnil;
--        }
-+	}
- 
--        return RARRAY_PTR(rackup)[0] ;
- }
- 
- int uwsgi_rack_magic(char *mountpoint, char *lazy) {
-diff --git a/t/rack/app.ru b/t/rack/app.ru
-index 6a4323e18..4603375e7 100644
---- a/t/rack/app.ru
-+++ b/t/rack/app.ru
-@@ -1,7 +1,7 @@
- class App
- 
-   def call(environ)
--    [200, {'Content-Type' => 'text/html'}, ['Hello']]
-+    [200, {"content-type" => "text/plain"}, ['Hello']]
-   end
- 
- end
diff -NruwB uwsgi-2.0.28-9/debian/patches/1007_fix_php_pypy_tests.patch uwsgi-2.0.29-1/debian/patches/1007_fix_php_pypy_tests.patch
--- uwsgi-2.0.28-9/debian/patches/1007_fix_php_pypy_tests.patch	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/patches/1007_fix_php_pypy_tests.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,846 +0,0 @@
-Description: fix php and pypy tests
- fixes the php (missing file)
- .
- fixes the pypy (inconsistent API for set_user_harakiri())
-Author: Alexandre Rossi <niol@zincube.net>
-Bug: https://github.com/unbit/uwsgi/pull/2691
-Last-Update: 2025-03-02
----
-This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
---- a/Makefile
-+++ b/Makefile
-@@ -22,3 +22,5 @@
- 
- %:
- 	$(PYTHON) uwsgiconfig.py --build $@
-+
-+.PHONY: all clean check tests
---- a/plugins/pypy/pypy_setup.py
-+++ b/plugins/pypy/pypy_setup.py
-@@ -24,13 +24,13 @@
- ssize_t write(int, const void *, size_t);
- int close(int);
- 
--void (*uwsgi_pypy_hook_execute_source)(char *);
--void (*uwsgi_pypy_hook_loader)(char *);
--void (*uwsgi_pypy_hook_file_loader)(char *);
--void (*uwsgi_pypy_hook_paste_loader)(char *);
--void (*uwsgi_pypy_hook_pythonpath)(char *);
--void (*uwsgi_pypy_hook_request)(struct wsgi_request *);
--void (*uwsgi_pypy_post_fork_hook)(void);
-+extern void (*uwsgi_pypy_hook_execute_source)(char *);
-+extern void (*uwsgi_pypy_hook_loader)(char *);
-+extern void (*uwsgi_pypy_hook_file_loader)(char *);
-+extern void (*uwsgi_pypy_hook_paste_loader)(char *);
-+extern void (*uwsgi_pypy_hook_pythonpath)(char *);
-+extern void (*uwsgi_pypy_hook_request)(struct wsgi_request *);
-+extern void (*uwsgi_pypy_post_fork_hook)(void);
- '''
- 
- # here we load CFLAGS and uwsgi.h from the binary
-@@ -53,7 +53,7 @@
-         if '=' in line:
-             (key, value) = line.split('=', 1)
-             uwsgi_cdef.append('#define %s ...' % key)
--            uwsgi_defines.append('#define %s %s' % (key, value.replace('\\"','"').replace('""','"')))
-+            uwsgi_defines.append('#define %s %s' % (key, value.replace('\\"', '"').replace('""', '"')))
-         else:
-             uwsgi_cdef.append('#define %s ...' % line)
-             uwsgi_defines.append('#define %s 1' % line)
-@@ -64,107 +64,107 @@
- %s
- 
- struct iovec {
--	void *iov_base;
--	size_t iov_len;
--	...;
-+        void *iov_base;
-+        size_t iov_len;
-+        ...;
- };
- 
- struct uwsgi_header {
--	uint8_t modifier1;
--	...;
-+        uint8_t modifier1;
-+        ...;
- };
- 
- struct wsgi_request {
--	int fd;
--	int async_id;
--	uint16_t var_cnt;
--	struct iovec *hvec;
-+        int fd;
-+        int async_id;
-+        uint16_t var_cnt;
-+        struct iovec *hvec;
- 
--	int async_ready_fd;
--	int async_last_ready_fd;
-+        int async_ready_fd;
-+        int async_last_ready_fd;
- 
--	int suspended;
-+        int suspended;
- 
--	struct uwsgi_header *uh;
--	...;
-+        struct uwsgi_header *uh;
-+        ...;
- };
- 
- struct uwsgi_opt {
--	char *key;
--	char *value;
--	...;
-+        char *key;
-+        char *value;
-+        ...;
- };
- 
- struct uwsgi_worker {
--	int id;
--	int pid;
--	uint64_t requests;
--	uint64_t delta_requests;
--	uint64_t signals;
--
--	int cheaped;
--	int suspended;
--	int sig;
--	uint8_t signum;
--
--	uint64_t running_time;
--	uint64_t avg_response_time;
--	uint64_t tx;
--	...;
-+        int id;
-+        int pid;
-+        uint64_t requests;
-+        uint64_t delta_requests;
-+        uint64_t signals;
-+
-+        int cheaped;
-+        int suspended;
-+        int sig;
-+        uint8_t signum;
-+
-+        uint64_t running_time;
-+        uint64_t avg_response_time;
-+        uint64_t tx;
-+        ...;
- };
- 
- struct uwsgi_plugin {
--	uint8_t modifier1;
-+        uint8_t modifier1;
- 
--	void (*suspend) (struct wsgi_request *);
-+        void (*suspend) (struct wsgi_request *);
-         void (*resume) (struct wsgi_request *);
--	...;
-+        ...;
- };
- 
- struct uwsgi_buffer {
--	char *buf;
--	size_t pos;
--	...;
-+        char *buf;
-+        size_t pos;
-+        ...;
- };
- 
- struct uwsgi_lock_item {
--	...;
-+        ...;
- };
- 
- struct uwsgi_cache {
--	struct uwsgi_lock_item *lock;
--	...;
-+        struct uwsgi_lock_item *lock;
-+        ...;
- };
- 
- struct uwsgi_cache_item {
--	uint64_t keysize;
--	...;
-+        uint64_t keysize;
-+        ...;
- };
- 
- struct uwsgi_server {
--	char hostname[];
--	int mywid;
--	int muleid;
--	int master_process;
-+        char hostname[];
-+        int mywid;
-+        int muleid;
-+        int master_process;
- 
--	struct uwsgi_opt **exported_opts;
--	int exported_opts_cnt;	
-+        struct uwsgi_opt **exported_opts;
-+        int exported_opts_cnt;
- 
--	struct uwsgi_worker *workers;
-+        struct uwsgi_worker *workers;
- 
--	int signal_socket;
--	int numproc;
--	int async;
-+        int signal_socket;
-+        int numproc;
-+        int async;
- 
--	void (*schedule_to_main) (struct wsgi_request *);
-+        void (*schedule_to_main) (struct wsgi_request *);
-         void (*schedule_to_req) (void);
- 
--	struct wsgi_request *(*current_wsgi_req) (void);
--	
--	struct wsgi_request *wsgi_req;
-+        struct wsgi_request *(*current_wsgi_req) (void);
- 
--	struct uwsgi_plugin *p[];
--	...;
-+        struct wsgi_request *wsgi_req;
-+
-+        struct uwsgi_plugin *p[];
-+        ...;
- };
- extern struct uwsgi_server uwsgi;
- 
-@@ -248,7 +248,7 @@
- 
- int uwsgi_ready_fd(struct wsgi_request *);
- 
--void set_user_harakiri(struct wsgi_request *, int);
-+void set_user_harakiri(int);
- 
- int uwsgi_metric_set(char *, char *, int64_t);
- int uwsgi_metric_inc(char *, char *, int64_t);
-@@ -288,20 +288,22 @@
- if len(sys.argv) == 0:
-     sys.argv.insert(0, ffi.string(lib.uwsgi_binary_path()).decode())
- 
--"""
--execute source, we expose it as cffi callback to avoid deadlocks
--after GIL initialization
--"""
-+
- @ffi.callback("void(char *)")
- def uwsgi_pypy_execute_source(s):
-+    """
-+    execute source, we expose it as cffi callback to avoid deadlocks
-+    after GIL initialization
-+    """
-     source = ffi.string(s)
-     exec(source)
- 
--"""
--load a wsgi module
--"""
-+
- @ffi.callback("void(char *)")
- def uwsgi_pypy_loader(module):
-+    """
-+    load a wsgi module
-+    """
-     global wsgi_application
-     m = ffi.string(module).decode()
-     c = 'application'
-@@ -313,22 +315,24 @@
-         mod = __import__(m)
-     wsgi_application = getattr(mod, c)
- 
--"""
--load a mod_wsgi compliant .wsgi file
--"""
-+
- @ffi.callback("void(char *)")
- def uwsgi_pypy_file_loader(filename):
-+    """
-+    load a mod_wsgi compliant .wsgi file
-+    """
-     global wsgi_application
-     w = ffi.string(filename)
-     c = 'application'
-     mod = imp.load_source('uwsgi_file_wsgi', w.decode())
-     wsgi_application = getattr(mod, c)
- 
--"""
--load a .ini paste app
--"""
-+
- @ffi.callback("void(char *)")
- def uwsgi_pypy_paste_loader(config):
-+    """
-+    load a .ini paste app
-+    """
-     global wsgi_application
-     c = ffi.string(config).decode()
-     if c.startswith('config:'):
-@@ -343,29 +347,31 @@
-     from paste.deploy import loadapp
-     wsgi_application = loadapp('config:%s' % c)
- 
--"""
--.post_fork_hook
--"""
-+
- @ffi.callback("void()")
- def uwsgi_pypy_post_fork_hook():
-+    """
-+    .post_fork_hook
-+    """
-     import uwsgi
-     if hasattr(uwsgi, 'post_fork_hook'):
-         uwsgi.post_fork_hook()
- 
--"""
--add an item to the pythonpath
--"""
-+
- @ffi.callback("void(char *)")
- def uwsgi_pypy_pythonpath(item):
-+    """
-+    add an item to the pythonpath
-+    """
-     path = ffi.string(item).decode()
-     sys.path.append(path)
-     print("added %s to pythonpath" % path)
- 
- 
--"""
--class implementing wsgi.file_wrapper
--"""
- class WSGIfilewrapper(object):
-+    """
-+    class implementing wsgi.file_wrapper
-+    """
-     def __init__(self, wsgi_req, f, chunksize=0):
-         self.wsgi_req = wsgi_req
-         self.f = f
-@@ -376,6 +382,17 @@
-     def __iter__(self):
-         return self
- 
-+    def __next__(self):
-+        if self.chunksize:
-+            data = self.f.read(self.chunksize)
-+        else:
-+            data = self.f.read()
-+        if data:
-+            return data
-+        raise StopIteration()
-+
-+    next = __next__
-+
-     def sendfile(self):
-         if hasattr(self.f, 'fileno'):
-             lib.uwsgi_response_sendfile_do_can_close(self.wsgi_req, self.f.fileno(), 0, 0, 0)
-@@ -392,10 +409,10 @@
-                 lib.uwsgi_response_write_body_do(self.wsgi_req, ffi.new("char[]", chunk), len(chunk))
- 
- 
--"""
--class implementing wsgi.input
--"""
- class WSGIinput(object):
-+    """
-+    class implementing wsgi.input
-+    """
-     def __init__(self, wsgi_req):
-         self.wsgi_req = wsgi_req
- 
-@@ -439,11 +456,11 @@
-         return chunk
- 
- 
--"""
--the WSGI request handler
--"""
- @ffi.callback("void(struct wsgi_request *)")
- def uwsgi_pypy_wsgi_handler(wsgi_req):
-+    """
-+    the WSGI request handler
-+    """
-     import uwsgi
-     global wsgi_application
- 
-@@ -518,6 +535,7 @@
- uwsgi.version = ffi.string(lib.uwsgi_pypy_version)
- uwsgi.hostname = ffi.string(lib.uwsgi.hostname)
- 
-+
- def uwsgi_pypy_uwsgi_register_signal(signum, kind, handler):
-     cb = ffi.callback('void(int)', handler)
-     uwsgi_gc.append(cb)
-@@ -550,6 +568,7 @@
-         raise Exception("unable to register rpc func %s" % name)
- uwsgi.register_rpc = uwsgi_pypy_uwsgi_register_rpc
- 
-+
- def uwsgi_pypy_rpc(node, func, *args):
-     argc = 0
-     argv = ffi.new('char*[256]')
-@@ -570,7 +589,7 @@
-     else:
-         c_node = ffi.NULL
- 
--    response = lib.uwsgi_do_rpc(c_node, ffi.new("char[]",func), argc, argv, argvs, rsize)
-+    response = lib.uwsgi_do_rpc(c_node, ffi.new("char[]", func), argc, argv, argvs, rsize)
-     if response:
-         ret = ffi.buffer(response, rsize[0])[:]
-         lib.free(response)
-@@ -578,13 +597,14 @@
-     return None
- uwsgi.rpc = uwsgi_pypy_rpc
- 
-+
- def uwsgi_pypy_call(func, *args):
-     node = None
-     if b'@' in func:
-         (func, node) = func.split(b'@')
-     return uwsgi_pypy_rpc(node, func, *args)
- uwsgi.call = uwsgi_pypy_call
--    
-+
- uwsgi.signal = lambda x: lib.uwsgi_signal_send(lib.uwsgi.signal_socket, x)
- 
- uwsgi.metric_get = lambda x: lib.uwsgi_metric_get(x, ffi.NULL)
-@@ -594,6 +614,7 @@
- uwsgi.metric_mul = lambda x, y=1: lib.uwsgi_metric_mul(x, ffi.NULL, y)
- uwsgi.metric_div = lambda x, y=1: lib.uwsgi_metric_div(x, ffi.NULL, y)
- 
-+
- def uwsgi_pypy_uwsgi_cache_get(key, cache=ffi.NULL):
-     vallen = ffi.new('uint64_t*')
-     value = lib.uwsgi_cache_magic_get(key, len(key), vallen, ffi.NULL, cache)
-@@ -604,21 +625,25 @@
-     return ret
- uwsgi.cache_get = uwsgi_pypy_uwsgi_cache_get
- 
-+
- def uwsgi_pypy_uwsgi_cache_set(key, value, expires=0, cache=ffi.NULL):
-     if lib.uwsgi_cache_magic_set(key, len(key), value, len(value), expires, 0, cache) < 0:
-         raise Exception('unable to store item in the cache')
- uwsgi.cache_set = uwsgi_pypy_uwsgi_cache_set
- 
-+
- def uwsgi_pypy_uwsgi_cache_update(key, value, expires=0, cache=ffi.NULL):
-     if lib.uwsgi_cache_magic_set(key, len(key), value, len(value), expires, 1 << 1, cache) < 0:
-         raise Exception('unable to store item in the cache')
- uwsgi.cache_update = uwsgi_pypy_uwsgi_cache_update
- 
-+
- def uwsgi_pypy_uwsgi_cache_del(key, cache=ffi.NULL):
-     if lib.uwsgi_cache_magic_del(key, len(key), cache) < 0:
-         raise Exception('unable to delete item from the cache')
- uwsgi.cache_del = uwsgi_pypy_uwsgi_cache_del
- 
-+
- def uwsgi_pypy_uwsgi_cache_keys(cache=ffi.NULL):
-     uc = lib.uwsgi_cache_by_name(cache)
-     if uc == ffi.NULL:
-@@ -636,11 +661,13 @@
-     return l
- uwsgi.cache_keys = uwsgi_pypy_uwsgi_cache_keys
- 
-+
- def uwsgi_pypy_uwsgi_add_timer(signum, secs):
-     if lib.uwsgi_add_timer(signum, secs) < 0:
-         raise Exception("unable to register timer")
- uwsgi.add_timer = uwsgi_pypy_uwsgi_add_timer
- 
-+
- def uwsgi_pypy_uwsgi_add_rb_timer(signum, secs):
-     if lib.uwsgi_signal_add_rb_timer(signum, secs, 0) < 0:
-         raise Exception("unable to register redblack timer")
-@@ -652,16 +679,19 @@
-         raise Exception("unable to register file monitor")
- uwsgi.add_file_monitor = uwsgi_pypy_uwsgi_add_file_monitor
- 
-+
- def uwsgi_pypy_lock(num=0):
-     if lib.uwsgi_user_lock(num) < 0:
-         raise Exception("invalid lock")
- uwsgi.lock = uwsgi_pypy_lock
- 
-+
- def uwsgi_pypy_unlock(num=0):
-     if lib.uwsgi_user_unlock(num) < 0:
-         raise Exception("invalid lock")
- uwsgi.unlock = uwsgi_pypy_unlock
- 
-+
- def uwsgi_pypy_masterpid():
-     if lib.uwsgi.master_process:
-         return lib.uwsgi.workers[0].pid
-@@ -672,18 +702,21 @@
- 
- uwsgi.mule_id = lambda: lib.uwsgi.muleid
- 
-+
- def uwsgi_pypy_signal_registered(signum):
-     if lib.uwsgi_signal_registered(signum) > 0:
-         return True
-     return False
- uwsgi.signal_registered = uwsgi_pypy_signal_registered
- 
-+
- def uwsgi_pypy_alarm(alarm, msg):
-     lib.uwsgi_alarm_trigger(ffi.new('char[]', alarm), ffi.new('char[]', msg), len(msg))
- uwsgi.alarm = uwsgi_pypy_alarm
- 
- uwsgi.setprocname = lambda name: lib.uwsgi_set_processname(ffi.new('char[]', name))
- 
-+
- def uwsgi_pypy_add_cron(signum, minute, hour, day, month, week):
-     if lib.uwsgi_signal_add_cron(signum, minute, hour, day, month, week) < 0:
-         raise Exception("unable to register cron")
-@@ -708,25 +741,28 @@
-     else:
-         uwsgi.opt[k] = v
- 
-+
- def uwsgi_pypy_current_wsgi_req():
-     wsgi_req = lib.uwsgi.current_wsgi_req()
-     if wsgi_req == ffi.NULL:
-         raise Exception("unable to get current wsgi_request, check your setup !!!")
-     return wsgi_req
- 
--"""
--uwsgi.suspend()
--"""
-+
- def uwsgi_pypy_suspend():
-+    """
-+    uwsgi.suspend()
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     if lib.uwsgi.schedule_to_main:
-         lib.uwsgi.schedule_to_main(wsgi_req)
- uwsgi.suspend = uwsgi_pypy_suspend
- 
--"""
--uwsgi.workers()
--"""
-+
- def uwsgi_pypy_workers():
-+    """
-+    uwsgi.workers()
-+    """
-     workers = []
-     for i in range(1, lib.uwsgi.numproc+1):
-         worker = {}
-@@ -745,31 +781,33 @@
-             if lib.uwsgi.workers[i].sig:
-                 worker['status'] = 'sig%d' % lib.uwsgi.workers[i].signum
-             elif lib.uwsgi_worker_is_busy(i):
--                worker['status'] = 'busy' 
-+                worker['status'] = 'busy'
-             else:
-                 worker['status'] = 'idle'
-         worker['running_time'] = lib.uwsgi.workers[i].running_time
-         worker['avg_rt'] = lib.uwsgi.workers[i].avg_response_time
-         worker['tx'] = lib.uwsgi.workers[i].tx
--            
-+
-         workers.append(worker)
-     return workers
--    
-+
- uwsgi.workers = uwsgi_pypy_workers
- 
--"""
--uwsgi.async_sleep(timeout)
--"""
-+
- def uwsgi_pypy_async_sleep(timeout):
-+    """
-+    uwsgi.async_sleep(timeout)
-+    """
-     if timeout > 0:
-         wsgi_req = uwsgi_pypy_current_wsgi_req()
-         lib.async_add_timeout(wsgi_req, timeout)
- uwsgi.async_sleep = uwsgi_pypy_async_sleep
- 
--"""
--uwsgi.async_connect(addr)
--"""
-+
- def uwsgi_pypy_async_connect(addr):
-+    """
-+    uwsgi.async_connect(addr)
-+    """
-     fd = lib.uwsgi_connect(ffi.new('char[]', addr), 0, 1)
-     if fd < 0:
-         raise Exception("unable to connect to %s" % addr)
-@@ -778,37 +816,40 @@
- 
- uwsgi.connection_fd = lambda: uwsgi_pypy_current_wsgi_req().fd
- 
--"""
--uwsgi.wait_fd_read(fd, timeout=0)
--"""
-+
- def uwsgi_pypy_wait_fd_read(fd, timeout=0):
-+    """
-+    uwsgi.wait_fd_read(fd, timeout=0)
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     if lib.async_add_fd_read(wsgi_req, fd, timeout) < 0:
-         raise Exception("unable to add fd %d to the event queue" % fd)
- uwsgi.wait_fd_read = uwsgi_pypy_wait_fd_read
- 
--"""
--uwsgi.wait_fd_write(fd, timeout=0)
--"""
-+
- def uwsgi_pypy_wait_fd_write(fd, timeout=0):
-+    """
-+    uwsgi.wait_fd_write(fd, timeout=0)
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     if lib.async_add_fd_write(wsgi_req, fd, timeout) < 0:
-         raise Exception("unable to add fd %d to the event queue" % fd)
- uwsgi.wait_fd_write = uwsgi_pypy_wait_fd_write
- 
--"""
--uwsgi.ready_fd()
--"""
-+
- def uwsgi_pypy_ready_fd():
-+    """
-+    uwsgi.ready_fd()
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     return lib.uwsgi_ready_fd(wsgi_req)
- uwsgi.ready_fd = uwsgi_pypy_ready_fd
--    
- 
--"""
--uwsgi.send(fd=None,data)
--"""
-+
- def uwsgi_pypy_send(*args):
-+    """
-+    uwsgi.send(fd=None,data)
-+    """
-     if len(args) == 0:
-         raise ValueError("uwsgi.send() takes at least 1 argument")
-     elif len(args) == 1:
-@@ -824,10 +865,11 @@
-     return rlen
- uwsgi.send = uwsgi_pypy_send
- 
--"""
--uwsgi.recv(fd=None,len)
--"""
-+
- def uwsgi_pypy_recv(*args):
-+    """
-+    uwsgi.recv(fd=None,len)
-+    """
-     if len(args) == 0:
-         raise ValueError("uwsgi.recv() takes at least 1 argument")
-     elif len(args) == 1:
-@@ -843,7 +885,7 @@
-         raise IOError("unable to receive data")
-     return ffi.string(data[0:rlen])
- uwsgi.recv = uwsgi_pypy_recv
--    
-+
- """
- uwsgi.close(fd)
- """
-@@ -854,10 +896,11 @@
- """
- uwsgi.disconnect = lambda: lib.uwsgi_disconnect(uwsgi_pypy_current_wsgi_req())
- 
--"""
--uwsgi.websocket_recv()
--"""
-+
- def uwsgi_pypy_websocket_recv():
-+    """
-+    uwsgi.websocket_recv()
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     ub = lib.uwsgi_websocket_recv(wsgi_req)
-     if ub == ffi.NULL:
-@@ -867,10 +910,11 @@
-     return ret
- uwsgi.websocket_recv = uwsgi_pypy_websocket_recv
- 
--"""
--uwsgi.websocket_recv_nb()
--"""
-+
- def uwsgi_pypy_websocket_recv_nb():
-+    """
-+    uwsgi.websocket_recv_nb()
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     ub = lib.uwsgi_websocket_recv_nb(wsgi_req)
-     if ub == ffi.NULL:
-@@ -880,10 +924,11 @@
-     return ret
- uwsgi.websocket_recv_nb = uwsgi_pypy_websocket_recv_nb
- 
--"""
--uwsgi.websocket_handshake(key, origin)
--"""
-+
- def uwsgi_pypy_websocket_handshake(key='', origin='', proto=''):
-+    """
-+    uwsgi.websocket_handshake(key, origin)
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     c_key = ffi.new('char[]', key)
-     c_origin = ffi.new('char[]', origin)
-@@ -892,19 +937,21 @@
-         raise IOError("unable to complete websocket handshake")
- uwsgi.websocket_handshake = uwsgi_pypy_websocket_handshake
- 
--"""
--uwsgi.websocket_send(msg)
--"""
-+
- def uwsgi_pypy_websocket_send(msg):
-+    """
-+    uwsgi.websocket_send(msg)
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     if lib.uwsgi_websocket_send(wsgi_req, ffi.new('char[]', msg), len(msg)) < 0:
-         raise IOError("unable to send websocket message")
- uwsgi.websocket_send = uwsgi_pypy_websocket_send
- 
--"""
--uwsgi.chunked_read(timeout=0)
--"""
-+
- def uwsgi_pypy_chunked_read(timeout=0):
-+    """
-+    uwsgi.chunked_read(timeout=0)
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     rlen = ffi.new("size_t*")
-     chunk = lib.uwsgi_chunked_read(wsgi_req, rlen, timeout, 0)
-@@ -913,10 +960,11 @@
-     return ffi.buffer(chunk, rlen[0])[:]
- uwsgi.chunked_read = uwsgi_pypy_chunked_read
- 
--"""
--uwsgi.chunked_read_nb()
--"""
-+
- def uwsgi_pypy_chunked_read_nb():
-+    """
-+    uwsgi.chunked_read_nb()
-+    """
-     wsgi_req = uwsgi_pypy_current_wsgi_req()
-     rlen = ffi.new("size_t*")
-     chunk = lib.uwsgi_chunked_read(wsgi_req, rlen, 0, 1)
-@@ -924,17 +972,15 @@
-         if lib.uwsgi_is_again() > 0:
-             return None
-         raise IOError("unable to receive chunked part")
-+
-     return ffi.buffer(chunk, rlen[0])[:]
- uwsgi.chunked_read_nb = uwsgi_pypy_chunked_read_nb
- 
- 
--def uwsgi_pypy_set_user_harakiri(x):
--    """
--    uwsgi.set_user_harakiri(sec)
--    """
--    wsgi_req = uwsgi_pypy_current_wsgi_req()
--    lib.set_user_harakiri(wsgi_req, x)
--uwsgi.set_user_harakiri = uwsgi_pypy_set_user_harakiri
-+"""
-+uwsgi.set_user_harakiri(sec)
-+"""
-+uwsgi.set_user_harakiri = lambda x: lib.set_user_harakiri(x)
- 
- 
- def uwsgi_pypy_get_logvar(key):
-@@ -976,6 +1022,7 @@
- def uwsgi_pypy_continulet_wrapper(cont):
-     lib.async_schedule_to_req_green()
- 
-+
- @ffi.callback("void()")
- def uwsgi_pypy_continulet_schedule():
-     id = lib.uwsgi.wsgi_req.async_id
-@@ -989,14 +1036,15 @@
- 
-     # this is called in the main stack
-     if lib.uwsgi.p[modifier1].suspend:
--        lib.uwsgi.p[modifier1].suspend(ffi.NULL)    
-+        lib.uwsgi.p[modifier1].suspend(ffi.NULL)
- 
-     # let's switch
-     uwsgi_pypy_continulets[id].switch()
- 
-     # back to the main stack
-     if lib.uwsgi.p[modifier1].resume:
--        lib.uwsgi.p[modifier1].resume(ffi.NULL) 
-+        lib.uwsgi.p[modifier1].resume(ffi.NULL)
-+
- 
- @ffi.callback("void(struct wsgi_request *)")
- def uwsgi_pypy_continulet_switch(wsgi_req):
-@@ -1005,17 +1053,18 @@
- 
-     # this is called in the current continulet
-     if lib.uwsgi.p[modifier1].suspend:
--        lib.uwsgi.p[modifier1].suspend(wsgi_req)    
-+        lib.uwsgi.p[modifier1].suspend(wsgi_req)
- 
-     uwsgi_pypy_continulets[id].switch()
- 
-     # back to the continulet
-     if lib.uwsgi.p[modifier1].resume:
--        lib.uwsgi.p[modifier1].resume(wsgi_req) 
-+        lib.uwsgi.p[modifier1].resume(wsgi_req)
- 
-     # update current running continulet
-     lib.uwsgi.wsgi_req = wsgi_req
--    
-+
-+
- def uwsgi_pypy_setup_continulets():
-     if lib.uwsgi["async"] < 1:
-         raise Exception("pypy continulets require async mode !!!")
---- /dev/null
-+++ b/t/php/test.php
-@@ -0,0 +1,23 @@
-+<?php
-+
-+# execute with:
-+# uwsgi --ini t/php/config.ini &
-+# curl http://localhost:8080/test.php
-+
-+set_error_handler(function() {
-+	var_export(func_get_args());
-+	echo "\nFAIL\n";
-+	die;
-+});
-+
-+session_start();
-+$_SESSION['t'] = 't';
-+session_commit();
-+
-+session_start();
-+session_regenerate_id();
-+session_commit();
-+
-+session_start();
-+session_destroy();
-+echo "PASS\n";
diff -NruwB uwsgi-2.0.28-9/debian/patches/1008_fix_pypy_tests.patch uwsgi-2.0.29-1/debian/patches/1008_fix_pypy_tests.patch
--- uwsgi-2.0.28-9/debian/patches/1008_fix_pypy_tests.patch	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/patches/1008_fix_pypy_tests.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,17 +0,0 @@
-Description: fix pypy tests when run from another dir
-Author: Alexandre Rossi <alexandre.rossi@gmail.com>
-Source: upstream, https://github.com/unbit/uwsgi/commit/4616b8f
-Bug: https://github.com/unbit/uwsgi/pull/2701
-Last-Update: 2025-03-02
----
-This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
---- a/t/pypy/config.ini
-+++ b/t/pypy/config.ini
-@@ -3,5 +3,5 @@
- need-app = false
- pypy-lib = /usr/lib/x86_64-linux-gnu/libpypy3-c.so
- pypy-home = /usr/lib/pypy3
--pypy-setup = plugins/pypy/pypy_setup.py
--pypy-wsgi-file = t/python/helloapp.py
-+pypy-setup = %d../../plugins/pypy/pypy_setup.py
-+pypy-wsgi-file = %d../python/helloapp.py
diff -NruwB uwsgi-2.0.28-9/debian/patches/1011_backlog_uds.patch uwsgi-2.0.29-1/debian/patches/1011_backlog_uds.patch
--- uwsgi-2.0.28-9/debian/patches/1011_backlog_uds.patch	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/debian/patches/1011_backlog_uds.patch	2025-05-21 11:50:57.351560285 +0200
@@ -0,0 +1,191 @@
+Description: Handle backlog of UNIX socket too
+Author: Pierre Ducroquet <pducroquet@entrouvert.com>
+Source: upstream, https://github.com/unbit/uwsgi/pull/2493
+Forwarded: https://github.com/unbit/uwsgi/pull/2493
+
+The cheaper subsystem specifies that "backlog is only available on Linux
+and only on TCP sockets (not UNIX domain sockets)."
+This commit specifically implement this: UNIX domain socket support for
+backlog on Linux, using Netlink to call the kernel and get the queue status.
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- uwsgi.git.orig/core/master.c	2025-02-07 14:18:16.922375818 +0100
++++ uwsgi.git/core/master.c	2025-02-07 14:18:16.918375745 +0100
+@@ -232,6 +232,13 @@
+ 
+ #ifdef UNBIT
+ #define SIOBKLGQ 0x8908
++#else
++
++#include <linux/netlink.h>
++#include <linux/rtnetlink.h>
++#include <linux/unix_diag.h>
++#include <linux/sock_diag.h>
++
+ #endif
+ 
+ #ifdef SIOBKLGQ
+@@ -245,7 +252,141 @@
+ 		uwsgi_sock->max_queue = (uint64_t) uwsgi.listen_queue;
+ 	}
+ }
++#else
++
++static int get_socket_inode_from_fd(int fd) {
++	int inode = -1;
++	char source_link_path[32];
++	char link_target[32];
++	sprintf(source_link_path, "/proc/self/fd/%i", fd);
++	if (readlink(source_link_path, link_target, 32) > 0)
++		sscanf(link_target, "socket:[%i]", &inode);
++	return inode;
++}
++
++static void send_netlink_query_for_inode(int fd, int target_ino) {
++	struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
++	struct {
++		struct nlmsghdr nlh;
++		struct unix_diag_req udr;
++	} req = {
++		.nlh = {
++			.nlmsg_len = sizeof(req),
++			.nlmsg_type = SOCK_DIAG_BY_FAMILY,
++			.nlmsg_flags = NLM_F_REQUEST
++		},
++		.udr = {
++			.sdiag_family = AF_UNIX,
++			.udiag_show = UDIAG_SHOW_RQLEN,
++			.udiag_ino = target_ino,
++			.udiag_cookie = {-1, -1}
++		}
++	};
++	struct iovec iov = {.iov_base = &req, .iov_len = sizeof(req)};
++	struct msghdr msg = {
++		.msg_name = &nladdr,
++		.msg_namelen = sizeof(nladdr),
++		.msg_iov = &iov,
++		.msg_iovlen = 1
++	};
++
++	for (;;) {
++		if (sendmsg(fd, &msg, 0) < 0) {
++			if (errno == EINTR)
++				continue;
++
++			perror("sendmsg");
++			return;
++		}
++
++		return;
++	}
++}
++
++static int receive_netlink_answers(int fd, uint64_t *current_queue, uint64_t *max_queue) {
++	long buf[8192 / sizeof(long)];
++	struct sockaddr_nl nladdr;
++	struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
++	int flags = 0;
++	
++	for (;;) {
++		struct msghdr msg = {
++			.msg_name = &nladdr,
++			.msg_namelen = sizeof(nladdr),
++			.msg_iov = &iov,
++			.msg_iovlen = 1
++		};
++		
++		ssize_t ret = recvmsg(fd, &msg, flags);
++		
++		if (ret < 0) {
++			if (errno == EINTR)
++				continue;
++		
++			perror("recvmsg");
++			return -1;
++		}
++		if (ret == 0)
++			return 0;
++		
++		if (nladdr.nl_family != AF_NETLINK) {
++			fputs("!AF_NETLINK\n", stderr);
++			return -1;
++		}
++		
++		const struct nlmsghdr *h = (struct nlmsghdr *)buf;
++		
++		if (!NLMSG_OK(h, ret)) {
++			fputs("!NLMSG_OK\n", stderr);
++			return -1;
++		}
++		
++		for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
++			if (h->nlmsg_type == NLMSG_DONE)
++				return 0;
++			if (h->nlmsg_type == NLMSG_ERROR) {
++				const struct nlmsgerr *err = NLMSG_DATA(h);
++				if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) {
++					fputs("NLMSG_ERROR\n", stderr);
++				} else {
++					errno = -err->error;
++					perror("NLMSG_ERROR");
++				}
++				return -1;
++			}
++			if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY) {
++				fprintf(stderr, "unexpected nlmsg_type %u\n", (unsigned)h->nlmsg_type);
++		  		return -1;
++			}
++
++			// Now extract queue len from results
++			const struct unix_diag_msg *diag = NLMSG_DATA(h);
++			unsigned int rta_len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*diag));
++
++			for (struct rtattr *attr = (struct rtattr *)(diag + 1); RTA_OK(attr, rta_len); attr = RTA_NEXT(attr, rta_len)) {
++				switch (attr->rta_type) {
++				case UNIX_DIAG_RQLEN:
++					*current_queue = ((struct unix_diag_rqlen *) RTA_DATA(attr))->udiag_rqueue;
++					*max_queue = ((struct unix_diag_rqlen *) RTA_DATA(attr))->udiag_wqueue;
++					break;
++				}
++			}
++		}
++		return 0;
++	}
++}
++
++static void get_linux_unix_socket_queue(struct uwsgi_socket *uwsgi_sock) {
++	int fd = uwsgi_sock->fd;
++	int inode = get_socket_inode_from_fd(fd);
++	int diag_socket = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SOCK_DIAG);
++	send_netlink_query_for_inode(diag_socket, inode);
++	receive_netlink_answers(diag_socket, &uwsgi_sock->queue, &uwsgi_sock->max_queue);
++	close(diag_socket);
++}
++
+ #endif
++
+ #endif
+ 
+ static void master_check_listen_queue() {
+@@ -255,15 +396,16 @@
+ 	while(uwsgi_sock) {
+ 		if (uwsgi_sock->family == AF_INET || uwsgi_sock->family == AF_INET6) {
+ 			get_tcp_info(uwsgi_sock);
+-                }
++		}
++		else if (uwsgi_sock->family == AF_UNIX) {
+ #ifdef __linux__
+ #ifdef SIOBKLGQ
+-                else if (uwsgi_sock->family == AF_UNIX) {
+-                	get_linux_unbit_SIOBKLGQ(uwsgi_sock);
+-                }
++			get_linux_unbit_SIOBKLGQ(uwsgi_sock);
++#else
++			get_linux_unix_socket_queue(uwsgi_sock);
+ #endif
+ #endif
+-
++		}
+ 		if (uwsgi_sock->queue > backlog) {
+ 			backlog = uwsgi_sock->queue;
+ 		}
diff -NruwB uwsgi-2.0.28-9/debian/patches/1012_noexecstack.patch uwsgi-2.0.29-1/debian/patches/1012_noexecstack.patch
--- uwsgi-2.0.28-9/debian/patches/1012_noexecstack.patch	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/patches/1012_noexecstack.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,62 +0,0 @@
-Description: Disable executable stack
- Starting with glibc 2.41,
- the dlopen and dlmopen functions no longer make the stack executable
- if a shared library requires it and instead just fail.
- This change aims to improve security,
- as the previous behaviour was used as a vector for RCE
- (CVE-2023-38408).
- .
- ld: warning: plugins/pypy/pypy_setup.py.o:
- missing .note.GNU-stack section implies executable stack
-Author: Martin Liška <martin.liska@hey.com>
-Bug: https://github.com/unbit/uwsgi/issues/2436
-Last-Update: 2025-03-02
----
-This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
---- uwsgi.git.orig/uwsgiconfig.py	2025-02-16 15:04:43.415708437 +0100
-+++ uwsgi.git/uwsgiconfig.py	2025-02-16 15:04:43.399708138 +0100
-@@ -535,7 +535,7 @@
-                         gcc_list.append('%s/%s' % (path, cfile))
-                 for bfile in up.get('BINARY_LIST', []):
-                     try:
--                        binary_link_cmd = "ld -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1])
-+                        binary_link_cmd = "ld -z noexecstack -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1])
-                         print(binary_link_cmd)
-                         if subprocess.call(binary_link_cmd, shell=True) != 0:
-                             raise Exception('unable to link binary file')
-@@ -1150,7 +1150,7 @@
-             if not self.embed_config:
-                 self.embed_config = self.get('embed_config')
-             if self.embed_config:
--                binary_link_cmd = "ld -r -b binary -o %s.o %s" % (binarize(self.embed_config), self.embed_config)
-+                binary_link_cmd = "ld -z noexecstack -r -b binary -o %s.o %s" % (binarize(self.embed_config), self.embed_config)
-                 print(binary_link_cmd)
-                 subprocess.call(binary_link_cmd, shell=True)
-                 self.cflags.append("-DUWSGI_EMBED_CONFIG=_binary_%s_start" % binarize(self.embed_config))
-@@ -1169,7 +1169,7 @@
-                         for directory, directories, files in os.walk(ef):
-                             for f in files:
-                                 fname = "%s/%s" % (directory, f)
--                                binary_link_cmd = "ld -r -b binary -o %s.o %s" % (binarize(fname), fname)
-+                                binary_link_cmd = "ld -z noexecstack -r -b binary -o %s.o %s" % (binarize(fname), fname)
-                                 print(binary_link_cmd)
-                                 subprocess.call(binary_link_cmd, shell=True)
-                                 if symbase:
-@@ -1179,7 +1179,7 @@
-                                         subprocess.call(objcopy_cmd, shell=True)
-                                 binary_list.append(binarize(fname))
-                     else:
--                        binary_link_cmd = "ld -r -b binary -o %s.o %s" % (binarize(ef), ef)
-+                        binary_link_cmd = "ld -z noexecstack -r -b binary -o %s.o %s" % (binarize(ef), ef)
-                         print(binary_link_cmd)
-                         subprocess.call(binary_link_cmd, shell=True)
-                         binary_list.append(binarize(ef))
-@@ -1467,7 +1467,7 @@
-             gcc_list.append(path + '/' + cfile)
-     for bfile in up.get('BINARY_LIST', []):
-         try:
--            binary_link_cmd = "ld -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1])
-+            binary_link_cmd = "ld -z noexecstack -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1])
-             print(binary_link_cmd)
-             if subprocess.call(binary_link_cmd, shell=True) != 0:
-                 raise Exception('unable to link binary file')
diff -NruwB uwsgi-2.0.28-9/debian/patches/1016_readline.patch uwsgi-2.0.29-1/debian/patches/1016_readline.patch
--- uwsgi-2.0.28-9/debian/patches/1016_readline.patch	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/patches/1016_readline.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,18 +0,0 @@
-Description: Fix limit readline output to buffer size
-Author: Juho Heikkinen <juho.heikkinen@f-secure.com>
-Bug: https://github.com/unbit/uwsgi/issues/1412
-Bug-Debian: https://bugs.debian.org/846362
-Last-Update: 2024-09-17
----
-This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
---- a/core/reader.c
-+++ b/core/reader.c
-@@ -283,7 +283,7 @@
-                 }
- 
- 	// no line found, let's return all
--        *rlen = wsgi_req->post_readline_size - wsgi_req->post_readline_pos;
-+        *rlen = wsgi_req->post_readline_watermark - wsgi_req->post_readline_pos;
-         char *buf = wsgi_req->post_readline_buf + wsgi_req->post_readline_pos;
- 	wsgi_req->post_readline_pos = 0;
- 	return buf;
diff -NruwB uwsgi-2.0.28-9/debian/patches/1017_python3-compat.patch uwsgi-2.0.29-1/debian/patches/1017_python3-compat.patch
--- uwsgi-2.0.28-9/debian/patches/1017_python3-compat.patch	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/patches/1017_python3-compat.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,960 +0,0 @@
-Description: Python 3 compat
-Author: Thomas Goirand <zigo@debian.org>
-Bug: https://github.com/unbit/uwsgi/pull/2062
-Last-Update: 2024-09-17
----
-This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
---- a/contrib/spoolqueue/tasks.py
-+++ b/contrib/spoolqueue/tasks.py
-@@ -1,9 +1,11 @@
-+from __future__ import print_function
-+
- from tasksconsumer import *
- 
- @queueconsumer('fast', 4)
- def fast_queue(arguments):
--    print "fast", arguments
-+    print("fast", arguments)
- 
- @queueconsumer('slow')
- def slow_queue(arguments):
--    print "foobar", arguments
-+    print("foobar", arguments)
---- a/contrib/spoolqueue/tasksconsumer.py
-+++ b/contrib/spoolqueue/tasksconsumer.py
-@@ -1,7 +1,9 @@
- from uwsgidecorators import *
--import Queue
- from threading import Thread
- 
-+from six.moves import queue
-+
-+
- queues = {}
- 
- class queueconsumer(object):
-@@ -9,7 +11,7 @@
-     def __init__(self, name, num=1, **kwargs):
-         self.name = name
- 	self.num = num
--        self.queue = Queue.Queue()
-+        self.queue = queue.Queue()
- 	self.threads = []
- 	self.func = None
- 	queues[self.name] = self
-@@ -19,7 +21,7 @@
-     def consumer(self):
-         while True:
- 	    req = self.queue.get()
--            print req
-+            print(req)
- 	    self.func(req)
- 	    self.queue.task_done()
- 
---- a/contrib/twuwsgi.py
-+++ b/contrib/twuwsgi.py
-@@ -39,7 +39,7 @@
- 	return struct.pack('<H',len(key)) + key + struct.pack('<H',len(value)) + value
- 
-     def connectionMade(self):
--	print self.request.__dict__
-+	print(self.request.__dict__)
- 	# reset response parser
- 	self.status_parsed = None
- 	# build header and vars
---- a/contrib/uwsgi-cache-monitor.py
-+++ b/contrib/uwsgi-cache-monitor.py
-@@ -1,3 +1,5 @@
-+from __future__ import print_function
-+
- import mmap
- import os
- import struct
-@@ -85,20 +87,20 @@
- 
-     def show_dump(self):
-         d = self.dump()
--        print
--        print "Recorded %d samples (%d second(s) sleep between samples)" % \
--            (d['samples'], d['sample_sleep'])
--        print "Cache empty %d times, full %d times, %.2f items on average" % \
--            (d['cache_empty'], d['cache_full'], d['cache_items'] / d['samples'])
--        print "Block size average size: %d bytes" % \
--            (d['block_sizes'] / d['cache_items'] * 8)
--        print "Data in cache average: %d bytes" % \
--            (d['block_sizes'] / d['samples'] * 8)
-+        print()
-+        print("Recorded %d samples (%d second(s) sleep between samples)" % \
-+            (d['samples'], d['sample_sleep']))
-+        print("Cache empty %d times, full %d times, %.2f items on average" % \
-+            (d['cache_empty'], d['cache_full'], d['cache_items'] / d['samples']))
-+        print("Block size average size: %d bytes" % \
-+            (d['block_sizes'] / d['cache_items'] * 8))
-+        print("Data in cache average: %d bytes" % \
-+            (d['block_sizes'] / d['samples'] * 8))
- 
- def main(options):
-     cache = Cache(options.cache_store, options.cache_slots, options.block_size,
-         options.sleep_time)
--    print "Recording..."
-+    print("Recording...")
-     while True:
-         try:
-             data = cache.read()
---- a/examples/bootstrap5.py
-+++ b/examples/bootstrap5.py
-@@ -1,3 +1,3 @@
- import uwsgi
- 
--print uwsgi.extract("data://0")
-+print(uwsgi.extract("data://0"))
---- a/examples/mjpeg_stream.py
-+++ b/examples/mjpeg_stream.py
-@@ -18,7 +18,7 @@
- 
- 	while 1:
- 		yield "Content-Type: image/jpeg\r\n\r\n"
--		print os.system('screencapture -t jpg -m -T 1 screenshot.jpg')
-+		print(os.system('screencapture -t jpg -m -T 1 screenshot.jpg'))
- 		f = open('screenshot.jpg')
-                 yield env['wsgi.file_wrapper'](f)
- 		yield "\r\n--%s\r\n" % boundary
---- a/examples/simple_app.py
-+++ b/examples/simple_app.py
-@@ -8,14 +8,14 @@
- 
- def ciao2():
-     print("nuovo uwsgi_server")
--    print os.getpid()
-+    print(os.getpid())
- 
- counter = 0
- 
- #if uwsgi.load_plugin(0, 'plugins/example/example_plugin.so', 'ciao'):
--#    print "example plugin loaded"
-+#    print("example plugin loaded")
- #else:
--#    print "unable to load example plugin"
-+#    print("unable to load example plugin")
- 
- #uwsgi.event_add(uwsgi.EVENT_FILE, "/tmp", ciao)
- #uwsgi.event_add(uwsgi.EVENT_DNSSD, "_uwsgi._tcp", ciao2)
---- a/examples/simple_app_wsgi2.py
-+++ b/examples/simple_app_wsgi2.py
-@@ -1,6 +1,9 @@
-+from six.moves import range
-+
-+
- 
- def mygen(uri):
--    for i in xrange(1,100):
-+    for i in range(1,100):
-         yield "ciao %s<br/>" % uri
- 
- 
---- a/examples/taskqueue.py
-+++ b/examples/taskqueue.py
-@@ -1,7 +1,9 @@
--import Queue
- from threading import Thread
- import uwsgi
- 
-+from six.moves import queue
-+
-+
- CONSUMERS = 4
- 
- def consumer(q):
-@@ -13,7 +15,7 @@
- 
- def spawn_consumers():
-     global q
--    q = Queue.Queue()
-+    q = queue.Queue()
-     for i in range(CONSUMERS):
-         t = Thread(target=consumer,args=(q,))
-         t.daemon = True
---- a/examples/uwsgirouter.py
-+++ b/examples/uwsgirouter.py
-@@ -10,13 +10,13 @@
- 
- 	# has timed out ?
- 	if env['x-wsgiorg.fdevent.timeout']:
--		print "connection timed out !!!"
-+		print("connection timed out !!!")
- 		uwsgi.close(fd)
- 		raise StopIteration
- 
- 	# connection refused ?
- 	if not uwsgi.is_connected(fd):
--		print "unable to connect"
-+		print("unable to connect")
- 		uwsgi.close(fd)
- 		raise StopIteration
- 	
-@@ -42,7 +42,7 @@
- 			bufsize = min(cl, 4096)
- 			yield uwsgi.wait_fd_read(input, 30)
- 			if env['x-wsgiorg.fdevent.timeout']:
--				print "connection timed out !!!"
-+				print("connection timed out !!!")
- 				uwsgi.close(fd)
- 				raise StopIteration
- 			body = uwsgi.recv(input, bufsize)
-@@ -58,7 +58,7 @@
- 
- 	# has timed out ?
- 	if env['x-wsgiorg.fdevent.timeout']:
--		print "connection timed out !!!"
-+		print("connection timed out !!!")
- 		uwsgi.close(fd)
- 		raise StopIteration
- 
-@@ -69,7 +69,7 @@
- 		# wait for response
- 		yield uwsgi.wait_fd_read(fd, 30)
- 		if env['x-wsgiorg.fdevent.timeout']:
--			print "connection timed out !!!"
-+			print("connection timed out !!!")
- 			uwsgi.close(fd)
- 			raise StopIteration
- 		data = uwsgi.recv(fd)
---- a/examples/uwsgirouter3.py
-+++ b/examples/uwsgirouter3.py
-@@ -8,10 +8,10 @@
- 	global current_node
- 
- 	nodes = uwsgi.cluster_nodes()
--	print nodes
-+	print(nodes)
- 
- 	if len(nodes) == 0:
--		print "no cluster node available"
-+		print("no cluster node available")
- 		raise StopIteration
- 
- 	if current_node >= len(nodes):
---- a/examples/uwsgirouter4.py
-+++ b/examples/uwsgirouter4.py
-@@ -4,10 +4,10 @@
- def application(e,s):
- 
- 	node = uwsgi.cluster_best_node()
--	print node
-+	print(node)
- 
- 	if not node:
--		print "sorry node unavailable"
-+		print("sorry node unavailable")
- 		raise StopIteration
- 
- 	for part in uwsgi.send_message(node, 0, 0, e, 0, e['wsgi.input'].fileno(), uwsgi.cl()):
---- a/examples/uwsgistatus.py
-+++ b/examples/uwsgistatus.py
-@@ -6,7 +6,7 @@
- 
- 
- def application(env, start_response):
--    print env
-+    print(env)
-     start_response('200 OK', [('Content-Type', 'text/html')])
- 
-     yield '<h1>uWSGI %s status</h1>' % uwsgi.version
-@@ -50,11 +50,11 @@
-     yield '<h2>workers</h2>'
- 
-     for w in workers:
--        #print w
--        #print w['running_time']
-+        #print(w)
-+        #print(w['running_time'])
-         if w is not None:
-             yield '<tr><td>'+ str(w['id']) +'</td><td>' + str(w['pid']) + '</td><td>' + str(w['pid']) + '</td><td>' + str(w['requests']) + '</td><td>' + str(w['running_time']) + '</td><td>' + str(w['vsz']) + '</td><td>' + str(w['rss']) + '</td></tr>'
--            print w
-+            print(w)
- 
-     yield '</table>'
- 
---- a/t/cachetest.py
-+++ b/t/cachetest.py
-@@ -11,7 +11,7 @@
- def gen_rand_s(size):
-     return ''.join( [ random.choice(string.letters) for i in range(size) ])
- 
--print 'filling cache...'
-+print('filling cache...')
- for i in range(0, 1000):
-    kl = gen_rand_n(200)
-    key = gen_rand_s(kl)
-@@ -20,14 +20,14 @@
-    items[key] = val
-    uwsgi.cache_set(key, val)
- 
--print 'checking cache...'
-+print('checking cache...')
- count = 0
- for key in items.keys():
-     val = uwsgi.cache_get(key)
-     count += 1
-     if val != items[key]:
--         print len(val), val
--         print len(items[key]), items[key]
-+         print(len(val), val)
-+         print(len(items[key]), items[key])
-          raise Exception('CACHE TEST FAILED AFTER %d ITERATIONS !!!' % count)
- 
--print "TEST PASSED"
-+print("TEST PASSED")
---- a/t/pypy/t_continulet1.py
-+++ b/t/pypy/t_continulet1.py
-@@ -12,7 +12,7 @@
- 
-     # call suspend 10 times and yield some value
-     for i in range(0,10):
--        print i
-+        print(i)
-         uwsgi.suspend()
-         yield str(i)
- 
-@@ -34,7 +34,7 @@
-     finally:
-         uwsgi.close(fd)
- 
--    print "sleeping for 3 seconds..."
-+    print("sleeping for 3 seconds...")
-     uwsgi.async_sleep(3)
-     uwsgi.suspend()
-     yield "done"
---- a/t/pypy/t_continulet2.py
-+++ b/t/pypy/t_continulet2.py
-@@ -12,7 +12,7 @@
- 
-     # suspend 10 times and yield a value
-     for i in range(1,10):
--        print i
-+        print(i)
-         uwsgi.suspend()
-         yield str(i)
- 
-@@ -41,7 +41,7 @@
-         # always ensure sockets are closed
-         uwsgi.close(fd)
- 
--    print "sleeping for 3 seconds..."
-+    print("sleeping for 3 seconds...")
-     uwsgi.async_sleep(3)
-     uwsgi.suspend()
-     yield "done"
---- a/t/sharedarea/bigranges.py
-+++ b/t/sharedarea/bigranges.py
-@@ -4,12 +4,12 @@
- class SharedareaTest(unittest.TestCase):
- 
-     def test_32(self):
--        pos = 2L * (1024L ** 3)
-+        pos = 2 * (1024 ** 3)
-         uwsgi.sharedarea_write32(0, pos, 17)
-         self.assertEqual(uwsgi.sharedarea_read32(0, pos), 17)
- 
-     def test_64(self):
--        pos = 2L * (1024L ** 3)
-+        pos = 2 * (1024 ** 3)
-         uwsgi.sharedarea_write64(0, pos, 30)
-         self.assertEqual(uwsgi.sharedarea_read64(0, pos), 30)
- 
---- a/tests/cpubound_stackless.py
-+++ b/tests/cpubound_stackless.py
-@@ -3,12 +3,12 @@
- 
- def application(env, start_response):
- 	start_response( '200 OK', [ ('Content-Type','text/html') ])
--	#print env
-+	#print(env)
- 	for i in range(1,100000):
--		#print i
-+		#print(i)
- 		yield "<h1>%s at %s</h1>\n" % (i, str(time.time()))
- 		#schedule every 2
- 		if i % 2 == 0:
- 			stackless.schedule()
- 
--	print "DONE AT %d" % i
-+	print("DONE AT %d" % i)
---- a/tests/decoratortest.py
-+++ b/tests/decoratortest.py
-@@ -8,6 +8,9 @@
- import time
- 
- 
-+from six.moves import range
-+
-+
- # register rpc function helloworld
- @rpc("helloworld")
- def hello2():
-@@ -43,7 +46,7 @@
- # spool a long running task
- @spool
- def a_long_task(args):
--    for i in xrange(1,10):
-+    for i in range(1,10):
-         print("%s = %d" % ( str(args), i))
-         print(uwsgi.call('helloworld'))
-         time.sleep(1)
-@@ -51,7 +54,7 @@
- # continuosly spool a long running task
- @spoolforever
- def an_infinite_task(args):
--    for i in xrange(1,4):
-+    for i in range(1,4):
-         print("infinite: %d %s" % (i, str(args)))
-         print(uwsgi.call('helloworld'))
-         uwsgi.signal(100)
-@@ -114,7 +117,7 @@
- @lock
- def locked_func():
-     print("starting locked function on worker %d" % uwsgi.worker_id())
--    for i in xrange(1, 5):
-+    for i in range(1, 5):
-         time.sleep(1)
-         print("[locked %d] waiting..." % uwsgi.worker_id())
-     print("done with locked function on worker %d" % uwsgi.worker_id())
---- a/tests/gevent_spool.py
-+++ b/tests/gevent_spool.py
-@@ -3,7 +3,7 @@
- 
- @spool
- def longtask(*args):
--    print args
-+    print(args)
-     return uwsgi.SPOOL_OK
- 
- def level2():
---- a/tests/grunter.py
-+++ b/tests/grunter.py
-@@ -1,6 +1,9 @@
- import uwsgi
- import time
- 
-+from six.moves import range
-+
-+
- def application(env, start_response):
- 
- 	start_response('200 Ok', [ ('Content-Type','text/html') ] )
-@@ -10,12 +13,12 @@
- 	grunt = uwsgi.grunt()
- 	
- 	if grunt is None:
--		print "worker %d detached" % uwsgi.worker_id()
-+		print("worker %d detached" % uwsgi.worker_id())
- 	else:
- 		yield "And now i am the grunt with a fix worker id of %d<br/>" % uwsgi.worker_id()
- 		time.sleep(2)
- 		yield "Now, i will start a very slow task...<br/>"
--		for i in xrange(1,10):
-+		for i in range(1,10):
- 			yield "waiting for %d seconds<br/>" % i
- 			time.sleep(i)
- 	
---- a/tests/iobound_async_unix.py
-+++ b/tests/iobound_async_unix.py
-@@ -44,7 +44,7 @@
- 		for r in send_request(env, s):
- 			yield r
- 	else:
--		print c
-+		print(c)
- 		start_response( '500 Internal Server Error', [ ('Content-Type', 'text/plain')])
- 		yield "Internal Server Error"
- 
---- a/tests/mulefunc.py
-+++ b/tests/mulefunc.py
-@@ -2,20 +2,20 @@
- 
- @timer(3, target='mule1')
- def hello_timer(signum):
--    print "3 seconds elapsed"
-+    print("3 seconds elapsed")
- 
- 
- @mulefunc
- def conto_fino_a_dieci(uno, due, tre):
--    print "MULE ID %d: conto_fino_a_dieci" % uwsgi.mule_id(),uno,due,tre
-+    print("MULE ID %d: conto_fino_a_dieci" % uwsgi.mule_id(),uno,due,tre)
- 
- @mulefunc(2)
- def conto_fino_a_venti(uno, due, tre):
--    print "MULE ID %d: conto_fino_a_venti" % uwsgi.mule_id(),uno,due,tre
-+    print("MULE ID %d: conto_fino_a_venti" % uwsgi.mule_id(),uno,due,tre)
- 
- @mulefunc('topogigio')
- def conto_fino_a_trenta(uno, due, tre):
--    print "MULE ID %d: conto_fino_a_trenta" % uwsgi.mule_id(),uno,due,tre
-+    print("MULE ID %d: conto_fino_a_trenta" % uwsgi.mule_id(),uno,due,tre)
- 
- 
- 
---- a/tests/myadmin.py
-+++ b/tests/myadmin.py
-@@ -2,7 +2,7 @@
- import struct
- import sys
- 
--print sys.argv
-+print(sys.argv)
- if len(sys.argv) == 3:
- 	chunks = uwsgi.send_message(sys.argv[1], 10, int(sys.argv[2]), '')
- 
-@@ -11,7 +11,7 @@
- 	for chunk in chunks:
-     		pkt += chunk
- 
--	print "%d = %d" % (int(sys.argv[2]), struct.unpack("I", pkt)[0])
-+	print("%d = %d" % (int(sys.argv[2]), struct.unpack("I", pkt)[0]))
- elif len(sys.argv) == 4:
- 	uwsgi.send_message(sys.argv[1], 10, int(sys.argv[2]), struct.pack("I", int(sys.argv[3])))
- 
---- a/tests/pgbound_async.py
-+++ b/tests/pgbound_async.py
-@@ -22,7 +22,7 @@
-         for i in pg_wait(connection, env, 3):
-                 yield i
- 
--        print "connected"
-+        print("connected")
-         cursor = connection.cursor()
- 
-         cursor.execute("SELECT * FROM foo")
-@@ -30,7 +30,7 @@
-         for i in pg_wait(cursor.connection, env, 3):
-                 yield i
- 
--        print "query result available"
-+        print("query result available")
- 
-         for record in cursor:
-                 yield str(record)
---- a/tests/picazzo.py
-+++ b/tests/picazzo.py
-@@ -9,7 +9,7 @@
-   return "<form method='post'><input type='submit' name='foo' value='login' /></form>"
- 
- def login_post(req):
--  print req
-+  print(req)
-   req["session"]["user"] = "James"
-   return redirect("/")
- 
---- a/tests/refcount.py
-+++ b/tests/refcount.py
-@@ -2,5 +2,5 @@
- 
- def application(e, sr):
-     sr('200 OK', [('Content-Type','text/html')])
--    print sys.gettotalrefcount()
-+    print(sys.gettotalrefcount())
-     yield '%s' % sys.gettotalrefcount()
---- a/tests/rpc.py
-+++ b/tests/rpc.py
-@@ -4,7 +4,7 @@
- def hello():
-     return "Hello World"
- 
--print uwsgi.register_rpc("hello", hello)
-+print(uwsgi.register_rpc("hello", hello))
- 
- 
--print uwsgi.rpc(None, "hello")
-+print(uwsgi.rpc(None, "hello"))
---- a/tests/runningthread.py
-+++ b/tests/runningthread.py
-@@ -4,7 +4,7 @@
- 
- def mess():
-     while True:
--        for i in xrange(0, 100):
-+        for i in range(0, 100):
-             if uwsgi.ready():
-                 uwsgi.signal(17)
-             print(i)
---- a/tests/sendchunked.py
-+++ b/tests/sendchunked.py
-@@ -1,6 +1,9 @@
- import socket
- import sys
- 
-+from six.moves import input
-+
-+
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 
- (addr, port) = sys.argv[1].split(':')
-@@ -10,5 +13,5 @@
- s.send("Transfer-Encoding: chunked\r\n\r\n")
- 
- while True:
--    msg = raw_input("msg >> ")
-+    msg = input("msg >> ")
-     s.send("%X\r\n%s\r\n" % (len(msg), msg))
---- a/tests/signals.py
-+++ b/tests/signals.py
-@@ -2,16 +2,16 @@
- 
- 
- def hello_signal(num, payload):
--	print "i am the signal %d" % num
-+	print("i am the signal %d" % num)
- 
- def hello_signal2(num, payload):
--	print "i am the signal %d with payload: %s" % (num, payload)
-+	print("i am the signal %d with payload: %s" % (num, payload))
- 
- def hello_file(num, filename):
--	print "file %s has been modified !!!" % filename
-+	print("file %s has been modified !!!" % filename)
- 
- def hello_timer(num, secs):
--	print "%s seconds elapsed" % secs
-+	print("%s seconds elapsed" % secs)
- 
- #uwsgi.register_signal(30, uwsgi.SIGNAL_KIND_WORKER, hello_signal)
- uwsgi.register_signal(30, "workers", hello_signal)
---- a/tests/sleeping_async.py
-+++ b/tests/sleeping_async.py
-@@ -5,5 +5,5 @@
- def application(env, start_response):
-     start_response('200 Ok', [('Content-type', 'text/html')])
-     yield uwsgi.async_sleep(sleepvalue)
--    #print "TIMEOUT: ", env['x-wsgiorg.fdevent.timeout']
-+    #print("TIMEOUT: ", env['x-wsgiorg.fdevent.timeout'])
-     yield "<h1>Hello World after %d seconds</h1>" % sleepvalue
---- a/tests/sleeping_green.py
-+++ b/tests/sleeping_green.py
-@@ -4,11 +4,11 @@
- 
- def application(env, start_response):
-     sleepvalue = 5
--    if env.has_key('QUERY_STRING'):
-+    if 'QUERY_STRING' in env:
-         if env['QUERY_STRING'] != '':
-             sleepvalue = int(env['QUERY_STRING'])
-         start_response('200 Ok', [('Content-type', 'text/html')])
-     start_at = time.time()
-     uwsgi.green_sleep(sleepvalue)
--    #print "TIMEOUT: ", env['x-wsgiorg.fdevent.timeout']
-+    #print("TIMEOUT: ", env['x-wsgiorg.fdevent.timeout'])
-     yield "<h1>Hello World after %s seconds</h1>" % str(time.time() - start_at)
---- a/tests/sleepthreadasync.py
-+++ b/tests/sleepthreadasync.py
-@@ -5,7 +5,7 @@
- def foo():
-     while True:
-         time.sleep(1)
--        print "ciao, sono un thread"
-+        print("ciao, sono un thread")
- 
- t = threading.Thread(target=foo)
- t.daemon=True
---- a/tests/slow.py
-+++ b/tests/slow.py
-@@ -1,12 +1,12 @@
- import time
- import uwsgi
- def application(e,s):
--    print "locking"
-+    print("locking")
-     uwsgi.lock()
--    print "locked"
-+    print("locked")
-     time.sleep(3)
-     uwsgi.unlock()
--    print "UN-locked"
-+    print("UN-locked")
-     s('200 OK', [('Content-Type','text/html')])
-     return "slow"
- 
---- a/tests/testapp.py
-+++ b/tests/testapp.py
-@@ -1,5 +1,6 @@
- import uwsgi
- 
-+from six.moves import reload_module
- import time
- import sys
- import os
-@@ -18,7 +19,7 @@
-     def run(self):
-         while 1:
-             time.sleep(2)
--            print "i am a terrible python thread of the uWSGI master process", uwsgi.applications
-+            print("i am a terrible python thread of the uWSGI master process", uwsgi.applications)
- 
- 
- tthread = testthread()
-@@ -36,7 +37,7 @@
- #print uwsgi.send_uwsgi_message("127.0.0.1", 3033, 33, 17, ('makotest.txt', {'whattimeisit':time.time(), 'roberta':'serena'}), 17)
- 
- def myspooler(env):
--    print env
-+    print(env)
-     for i in range(1,100):
-         uwsgi.sharedarea_inclong(100)
-         #time.sleep(1)
-@@ -57,11 +58,11 @@
- 
- 
- def application(env, start_response):
--    print env
-+    print(env)
-     start_response('200 OK', [('Content-Type', 'text/plain')])
-     yield { '/': helloworld, '/sleep': force_harakiri, '/counter': increment, '/uwsgi/':helloworld }[env['PATH_INFO']]()
- 
--    print env
-+    print(env)
- 
- def gomako():
-     from mako.template import Template
-@@ -81,12 +82,12 @@
-     uwsgi.start_response('200 OK', [('Content-Type', 'text/html')])
-     t = Template("My name is {{ my_name }}.")
-     c = Context({"my_name": "Serena"})
--    print t,c
-+    print(t,c)
-     a = t.render(c)
--    print "ciao", a
-+    print("ciao", a)
-     yield str(a)
- 
--def reload(env, start_response):
-+def reload_module(env, start_response):
- 
-     start_response('200 OK', [('Content-Type', 'text/html')])
- 
-@@ -97,7 +98,7 @@
- 
- #    print str(uwsgi.masterpid()) + "\n"
- 
--#    print "i am python"
-+#    print("i am python")
-     #yo()
- 
- #    yield "python"
-@@ -106,17 +107,17 @@
- 
- #    yield str(uwsgi.masterpid())
- 
--    #print uwsgi.pippo
-+    #print(uwsgi.pippo)
- 
-     #print 4/0
- #    try:
- #        print 4/0
- #
--#        print uwsgi.pippo
-+#        print(uwsgi.pippo)
- #    except:
--#        print "bah"
-+#        print("bah")
- 
--#    print "ok"
-+#    print("ok")
- 
- #    yield 4/0
- 
-@@ -139,16 +140,16 @@
-     yield '<h2>workers</h2>'
- 
-     for w in workers:
--        #print w
--        #print w['running_time']
-+        #print(w)
-+        #print(w['running_time'])
-         if w is not None:
-             yield '<tr><td>'+ str(w['id']) +'</td><td>' + str(w['pid']) + '</td><td>' + str(w['pid']) + '</td><td>' + str(w['requests']) + '</td><td>' + str(w['running_time']) + '</td><td>' + str(w['vsz']) + '</td><td>' + str(w['rss']) + '</td></tr>'
--            print w
-+            print(w)
- 
-     yield '</table>'
- 
-     #yield out
--    #print "FATTOfattoFATTO"
-+    #print("FATTOfattoFATTO")
- 
- def remotemako(env, start_response):
-     start_response('200 OK', [('Content-Type', 'text/html')])
-@@ -158,9 +159,9 @@
-             ('192.168.173.5', 3434, [9001, 12000] ),
-             ('192.168.173.5', 3435, [12001, 15000] )
-         );
--    print clusters
-+    print(clusters)
-     all_values = uwsgi.send_multi_uwsgi_message(clusters, 33, 17, 40);
--    print all_values
-+    print(all_values)
-     return mako('makotest.txt', {'whattimeisit':time.time(), 'roberta':'serena', 'cluster_values': all_values})
- 
- 
-@@ -173,5 +174,5 @@
- #applications = { '/':django.core.handlers.wsgi.WSGIHandler() }
- uwsgi.applications = { '/':reload, '/pippo':reload }
- 
--print uwsgi.applications
--print uwsgi.applist
-+print(uwsgi.applications)
-+print(uwsgi.applist)
---- a/tests/testgevent.py
-+++ b/tests/testgevent.py
-@@ -5,19 +5,19 @@
- import time
- 
- def microtask(wid):
--    print "i am a gevent task"
-+    print("i am a gevent task")
-     gevent.sleep(10)
--    print "10 seconds elapsed in worker id %d" % wid
-+    print("10 seconds elapsed in worker id %d" % wid)
- 
- def athread():
-     while True:
-         time.sleep(1)
--        print "i am the thread 1"
-+        print("i am the thread 1")
- 
- def athread2():
-     while True:
-         time.sleep(1)
--        print "i am the thread 2"
-+        print("i am the thread 2")
- 
- t1 = Thread(target=athread)
- t1.daemon = True
---- a/tests/threads.py
-+++ b/tests/threads.py
-@@ -3,6 +3,9 @@
- import time
- import sys
- 
-+from six.moves import reload_module
-+
-+
- def monitor1():
- 	while 1:
- 		time.sleep(1)
-@@ -18,7 +21,7 @@
- 	while 1:
- 		time.sleep(5)
- 		print("5 seconds elapsed")
--		#reload(fake)
-+		#reload_module(fake)
-     
- def spawn_my_magic_threads():
- 	print("^^^ spawning magic threads ^^^")
---- a/tests/ugevent.py
-+++ b/tests/ugevent.py
-@@ -11,25 +11,25 @@
- 
- @signal(17)
- def hello(signum):
--    print "hello i am signal %d, i am here because the background job is finished" % signum
-+    print("hello i am signal %d, i am here because the background job is finished" % signum)
-     if REFCNT:
--        print sys.gettotalrefcount()
-+        print(sys.gettotalrefcount())
- 
- @timer(10)
- def ten_seconds(signum):
--    print "10 seconds elapsed, signal %d raised" % signum
-+    print("10 seconds elapsed, signal %d raised" % signum)
-     if REFCNT:
--        print sys.gettotalrefcount()
-+        print(sys.gettotalrefcount())
- 
- @filemon('/tmp')
- def tmp_modified(signum):
--    print "/tmp has been touched, i am the greenlet %s running on worker %d" % (gevent.getcurrent(), uwsgi.worker_id())
-+    print("/tmp has been touched, i am the greenlet %s running on worker %d" % (gevent.getcurrent(), uwsgi.worker_id()))
-     if REFCNT:
--        print sys.gettotalrefcount()
-+        print(sys.gettotalrefcount())
- 
- def bg_task():
-     for i in range(1,10):
--        print "background task", i
-+        print("background task", i)
-         gevent.sleep(1)
- 
-     # task ended raise a signal !!!
-@@ -37,7 +37,7 @@
- 
- def long_task():
-     for i in range(1,10):
--        print i
-+        print(i)
-         gevent.sleep()
- 
- def application(e, sr):
-@@ -64,7 +64,7 @@
-         yield "ip = %s<br/>" % j.value
- 
-     if REFCNT:
--        print sys.gettotalrefcount()
-+        print(sys.gettotalrefcount())
-         yield "%d" % sys.gettotalrefcount()
- 
-     # this task will goes on after request end
---- a/tests/websockets.py
-+++ b/tests/websockets.py
-@@ -54,7 +54,7 @@
-         """ % (ws_scheme, env['HTTP_HOST'])
-     elif env['PATH_INFO'] == '/foobar/':
- 	uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'], env.get('HTTP_ORIGIN', ''))
--        print "websockets..."
-+        print("websockets...")
-         while True:
-             msg = uwsgi.websocket_recv_nb()
-             if msg:
---- a/tests/websockets_chat.py
-+++ b/tests/websockets_chat.py
-@@ -54,7 +54,7 @@
-         return ""
-     elif env['PATH_INFO'] == '/foobar/':
- 	uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'], env.get('HTTP_ORIGIN', ''))
--        print "websockets..."
-+        print("websockets...")
-         r = redis.StrictRedis(host='localhost', port=6379, db=0)
-         channel = r.pubsub()
-         channel.subscribe('foobar')
---- a/tests/websockets_echo.py
-+++ b/tests/websockets_echo.py
-@@ -50,7 +50,7 @@
-         """ % (ws_scheme, env['HTTP_HOST'])
-     elif env['PATH_INFO'] == '/foobar/':
-         uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'], env.get('HTTP_ORIGIN', ''))
--        print "websockets..."
-+        print("websockets...")
-         while True:
-             msg = uwsgi.websocket_recv()
-             uwsgi.websocket_send("[%s] %s" % (time.time(), msg))
---- a/core/plugins_builder.c
-+++ b/core/plugins_builder.c
-@@ -90,7 +90,7 @@
- 	char *argv[6];
- 
- 	argv[0] = getenv("PYTHON");
--	if (!argv[0]) argv[0] = "python";
-+	if (!argv[0]) argv[0] = "python3";
- 
- 	argv[1] = UWSGI_BUILD_DIR "/uwsgiconfig.py";
- 	argv[2] = "--extra-plugin";
---- a/uwsgi.gemspec
-+++ b/uwsgi.gemspec
-@@ -1,7 +1,7 @@
- Gem::Specification.new do |s|
-   s.name        = 'uwsgi'
-   s.license     = 'GPL-2'
--  s.version     = `python -c "import uwsgiconfig as uc; print uc.uwsgi_version"`.sub(/-dev-.*/,'')
-+  s.version     = `python3 -c "import uwsgiconfig as uc; print uc.uwsgi_version"`.sub(/-dev-.*/,'')
-   s.date        = '2024-10-26'
-   s.summary     = "uWSGI"
-   s.description = "The uWSGI server for Ruby/Rack"
diff -NruwB uwsgi-2.0.28-9/debian/patches/2003_plugin_builder_py3.patch uwsgi-2.0.29-1/debian/patches/2003_plugin_builder_py3.patch
--- uwsgi-2.0.28-9/debian/patches/2003_plugin_builder_py3.patch	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/debian/patches/2003_plugin_builder_py3.patch	2025-05-21 11:50:57.351560285 +0200
@@ -0,0 +1,16 @@
+Description: Use python3 to build plugins as default
+Forwarded: not-needed
+
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- uwsgi.git.orig/core/plugins_builder.c	2025-02-07 14:12:20.000000000 +0100
++++ uwsgi.git/core/plugins_builder.c	2025-04-14 23:17:22.486424305 +0200
+@@ -90,7 +90,7 @@
+ 	char *argv[6];
+ 
+ 	argv[0] = getenv("PYTHON");
+-	if (!argv[0]) argv[0] = "python";
++	if (!argv[0]) argv[0] = "python3";
+ 
+ 	argv[1] = UWSGI_BUILD_DIR "/uwsgiconfig.py";
+ 	argv[2] = "--extra-plugin";
diff -NruwB uwsgi-2.0.28-9/debian/patches/series uwsgi-2.0.29-1/debian/patches/series
--- uwsgi-2.0.28-9/debian/patches/series	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/debian/patches/series	2025-05-21 11:50:57.351560285 +0200
@@ -1,7 +1,4 @@
-1012_noexecstack.patch
-1004_rack3.patch
-1008_fix_pypy_tests.patch
-1007_fix_php_pypy_tests.patch
+1011_backlog_uds.patch
 1001_avoid_setting_RPATH.patch
 1002_fix-reload-process-name.patch
 1003_remove-php-libs.patch
@@ -9,7 +6,6 @@
 1006_avoid_xml2-config.patch
 1009_fix_java_paths.patch
 1010_support_java_pass_includes.patch
-1016_readline.patch
-1017_python3-compat.patch
 2001_ensure_verbose_build.patch
 2002_rename_py_plugins.patch
+2003_plugin_builder_py3.patch
diff -NruwB uwsgi-2.0.28-9/examples/heavytest.py uwsgi-2.0.29-1/examples/heavytest.py
--- uwsgi-2.0.28-9/examples/heavytest.py	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/examples/heavytest.py	2025-05-21 11:50:57.355560364 +0200
@@ -7,8 +8,13 @@
     start_response('200 OK', [('Content-Type', 'text/html')])
     return "PATH_INFO=%s" % env['PATH_INFO']
 
+
 def app002(env, start_response):
     start_response('200 OK', [('Content-Type', 'text/html')])
     return "requests: %d" % uwsgi.total_requests()
 
-uwsgi.applications = { '':werkzeug.testapp.test_app, '/app001':app001, '/app002':app002 }
+uwsgi.applications = {
+    '': werkzeug.testapp.test_app,
+    '/app001': app001,
+    '/app002': app002
+}
diff -NruwB uwsgi-2.0.28-9/examples/info_uwsgi.php uwsgi-2.0.29-1/examples/info_uwsgi.php
--- uwsgi-2.0.28-9/examples/info_uwsgi.php	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/examples/info_uwsgi.php	2025-05-21 11:50:57.355560364 +0200
@@ -1,5 +1,5 @@
 uWSGI version <b><?=uwsgi_version()?></b><br/>
-workerd id: <b><?=uwsgi_worker_id()?></b><br/>
+worker id: <b><?=uwsgi_worker_id()?></b><br/>
 master pid: <b><?=uwsgi_masterpid()?></b><br/>
 
 uri: <b><?= $_SERVER['REQUEST_URI'] ?></b><br/>
diff -NruwB uwsgi-2.0.28-9/examples/mjpeg_stream.py uwsgi-2.0.29-1/examples/mjpeg_stream.py
--- uwsgi-2.0.28-9/examples/mjpeg_stream.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/examples/mjpeg_stream.py	2025-05-21 11:50:57.355560364 +0200
@@ -1,5 +1,6 @@
-import uwsgi
 import os
+import subprocess
+
 
 def application(env, start_response):
 
@@ -6,20 +7,18 @@
 	boundary = 'uwsgi_mjpeg_frame'
 
 	start_response('200 Ok', [
-
 				('Cache-Control', 'no-cache'),
 				('Cache-Control', 'private'),
 				('Pragma', 'no-cache'),
 				('Content-Type', 'multipart/x-mixed-replace; boundary=' + boundary),
-				]
-			)
+    ])
 
 	yield "--%s\r\n" % boundary
 
 	while 1:
 		yield "Content-Type: image/jpeg\r\n\r\n"
-		print(os.system('screencapture -t jpg -m -T 1 screenshot.jpg'))
+        print(subprocess.call('screencapture -t jpg -m -T 1 screenshot.jpg', shell=True))
 		f = open('screenshot.jpg')
                 yield env['wsgi.file_wrapper'](f)
 		yield "\r\n--%s\r\n" % boundary
-		#os.system('./isightcapture -w 640 -h 480 screenshot.jpg')
+        # subprocess.call('./isightcapture -w 640 -h 480 screenshot.jpg', shell=True)
diff -NruwB uwsgi-2.0.28-9/examples/multiapp.py uwsgi-2.0.29-1/examples/multiapp.py
--- uwsgi-2.0.28-9/examples/multiapp.py	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/examples/multiapp.py	2025-05-21 11:50:57.355560364 +0200
@@ -19,4 +21,10 @@
     s('500 Internal Server Error', [ ('Content-Type', 'text/plain') ])
     return ""
 
-uwsgi.applications = {'/wsgi':wsgi_app, '/test':werkzeug.test_app, '/zero':zero_app, '/notfound':not_found, '/ise':internal_server_error}
+uwsgi.applications = {
+    '/wsgi': wsgi_app,
+    '/test': werkzeug.test_app,
+    '/zero': zero_app,
+    '/notfound': not_found,
+    '/ise': internal_server_error
+}
diff -NruwB uwsgi-2.0.28-9/examples/simple_app.py uwsgi-2.0.29-1/examples/simple_app.py
--- uwsgi-2.0.28-9/examples/simple_app.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/examples/simple_app.py	2025-05-21 11:50:57.355560364 +0200
@@ -23,12 +25,10 @@
 
 uwsgi.post_fork_hook = ciao2
 
-def application(env, start_response):
 
+def application(env, start_response):
     global counter
 
-
-    #print(env)
     start_response('200 Ok', [('Content-type', 'text/plain')])
     yield "hello world"
     yield "hello world2"
diff -NruwB uwsgi-2.0.28-9/examples/staticfilesnmp.py uwsgi-2.0.29-1/examples/staticfilesnmp.py
--- uwsgi-2.0.28-9/examples/staticfilesnmp.py	2025-05-21 11:51:12.875861059 +0200
+++ uwsgi-2.0.29-1/examples/staticfilesnmp.py	2025-05-21 11:50:57.355560364 +0200
@@ -4,10 +4,11 @@
 uwsgi.snmp_set_counter64(1, 0)  # Number of requests
 uwsgi.snmp_set_counter64(2, 0)  # Number of bytes
 
+
 def application(environ, start_response):
     size = path.getsize('logo_uWSGI.png')
     start_response('200 OK', [('Content-Type', 'image/png'), ('Content-Length', str(size))] )
-    fd = open('logo_uWSGI.png','r')
+    fd = open('logo_uWSGI.png')
     uwsgi.snmp_incr_counter64(1)
     uwsgi.snmp_incr_counter64(2, size)
     return environ['wsgi.file_wrapper'](fd, 4096)
diff -NruwB uwsgi-2.0.28-9/examples/uwsgistatus.py uwsgi-2.0.29-1/examples/uwsgistatus.py
--- uwsgi-2.0.28-9/examples/uwsgistatus.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/examples/uwsgistatus.py	2025-05-21 11:50:57.355560364 +0200
@@ -26,12 +26,12 @@
 
     try:
         yield "mode: <b>%s</b><br/>" % uwsgi.mode
-    except:
+    except Exception:
         pass
 
     try:
         yield "pidfile: <b>%s</b><br/>" % uwsgi.pidfile
-    except:
+    except Exception:
         pass
 
     yield "<h2>Hooks</h2>"
@@ -45,7 +45,7 @@
     yield '<table border="1">'
     yield '<th>worker id</th><th>pid</th><th>in request</th><th>requests</th><th>running time</th><th>address space</th><th>rss</th>'
 
-    workers = uwsgi.workers();
+    workers = uwsgi.workers()
 
     yield '<h2>workers</h2>'
 
diff -NruwB uwsgi-2.0.28-9/examples/welcome3.py uwsgi-2.0.29-1/examples/welcome3.py
--- uwsgi-2.0.28-9/examples/welcome3.py	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/examples/welcome3.py	2025-05-21 11:50:57.355560364 +0200
@@ -1,31 +1,50 @@
 import uwsgi
 import os
 
+from os.path import abspath, dirname, join
+
+logo_png = abspath(join(dirname(__file__), "../logo_uWSGI.png"))
+
+
 def xsendfile(e, sr):
-    sr('200 OK', [('Content-Type', 'image/png'), ('X-Sendfile', os.path.abspath('logo_uWSGI.png'))])
-    return b''
+    sr(
+        "200 OK", [("Content-Type", "image/png"), ("X-Sendfile", logo_png),],
+    )
+    return b""
+
 
 def serve_logo(e, sr):
-    sr('200 OK', [('Content-Type', 'image/png')])
-    return uwsgi.sendfile('logo_uWSGI.png')
+    sr("200 OK", [("Content-Type", "image/png")])
+    return uwsgi.sendfile(logo_png)
+
 
 def serve_config(e, sr):
-    sr('200 OK', [('Content-Type', 'text/html')])
+    sr("200 OK", [("Content-Type", "text/html")])
     for opt in uwsgi.opt.keys():
-        body = "{opt} = {optvalue}<br/>".format(opt=opt, optvalue=uwsgi.opt[opt].decode('ascii'))
-        yield bytes(body.encode('ascii'))
+
+        def decode_if_bytes(val):
+            if isinstance(val, bytes):
+                return val.decode("ascii")
+            return val
+
+        body = "{opt} = {optvalue}<br/>".format(
+            opt=opt.decode("ascii"), optvalue=decode_if_bytes(uwsgi.opt[opt])
+        )
+        yield bytes(body.encode("ascii"))
+
 
 routes = {}
-routes['/xsendfile'] = xsendfile
-routes['/logo'] = serve_logo
-routes['/config'] = serve_config
+routes["/xsendfile"] = xsendfile
+routes["/logo"] = serve_logo
+routes["/config"] = serve_config
+
 
 def application(env, start_response):
 
-    if env['PATH_INFO'] in routes:
-        return routes[env['PATH_INFO']](env, start_response)
+    if env["PATH_INFO"] in routes:
+        return routes[env["PATH_INFO"]](env, start_response)
 
-    start_response('200 OK', [('Content-Type', 'text/html')])
+    start_response("200 OK", [("Content-Type", "text/html")])
 
     body = """
 <img src="/logo"/> version {version}<br/>
@@ -36,12 +55,8 @@
 
 <br/>
 
-    """.format(version=uwsgi.version.decode('ascii'))
-
-    return bytes(body.encode('ascii'))
-
-
-
-
-
+    """.format(
+        version=uwsgi.version
+    )
 
+    return [bytes(body.encode("ascii"))]
diff -NruwB uwsgi-2.0.28-9/examples/welcome.py uwsgi-2.0.29-1/examples/welcome.py
--- uwsgi-2.0.28-9/examples/welcome.py	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/examples/welcome.py	2025-05-21 11:50:57.355560364 +0200
@@ -2,10 +2,15 @@
 import os
 import gc
 import sys
-from uwsgidecorators import *
+from uwsgidecorators import rpc, signal, postfork
+
+from os.path import abspath, dirname, join
+
+logo_png = abspath(join(dirname(__file__), "../logo_uWSGI.png"))
+
 print(sys.version)
 print(sys.version_info)
-if 'set_debug' in gc.__dict__:
+if "set_debug" in gc.__dict__:
     gc.set_debug(gc.DEBUG_SAVEALL)
 
 print(os.environ)
@@ -13,11 +18,8 @@
 print(sys.argv)
 
 try:
-    if sys.argv[1] == 'debug':
-        DEBUG = True
-    else:
-        raise
-except:
+    DEBUG = sys.argv[1] == "debug"
+except IndexError:
     DEBUG = False
 
 
@@ -24,9 +26,11 @@
 def after_request_hook():
     print("request finished")
 
+
 uwsgi.after_req_hook = after_request_hook
 
-@rpc('hello')
+
+@rpc(b"hello")
 def hello_rpc(one, two, three):
     arg0 = one[::-1]
     arg1 = two[::-1]
@@ -37,81 +42,107 @@
 def ciao_mondo(signum):
     print("Hello World")
 
+
 def xsendfile(e, sr):
-    sr('200 OK', [('Content-Type', 'image/png'), ('X-Sendfile', os.path.abspath('logo_uWSGI.png'))])
-    return ''
+    sr(
+        "200 OK", [("Content-Type", "image/png"), ("X-Sendfile", logo_png),],
+    )
+    return []
+
 
 def serve_logo(e, sr):
     # use raw facilities (status will not be set...)
-    uwsgi.send("%s 200 OK\r\nContent-Type: image/png\r\n\r\n" % e['SERVER_PROTOCOL'])
-    uwsgi.sendfile('logo_uWSGI.png')
-    return ''
+    uwsgi.send(
+        b"%s 200 OK\r\nContent-Type: image/png\r\n\r\n"
+        % e["SERVER_PROTOCOL"].encode("latin1")
+    )
+    uwsgi.sendfile(logo_png)
+    return []
+
 
 def serve_config(e, sr):
-    sr('200 OK', [('Content-Type', 'text/html')])
-    for opt in uwsgi.opt.keys():
-        yield "<b>%s</b> = %s<br/>" % (opt, uwsgi.opt[opt])
+    sr("200 OK", [("Content-Type", "text/html")])
+    for key in uwsgi.opt.keys():
+        opt = uwsgi.opt[key]
+        if not isinstance(opt, bytes):
+            opt = str(opt).encode("utf-8")
+        yield b"<b>%s</b> = %s<br/>" % (key, opt)
+
 
 routes = {}
-routes['/xsendfile'] = xsendfile
-routes['/logo'] = serve_logo
-routes['/config'] = serve_config
+routes["/xsendfile"] = xsendfile
+routes["/logo"] = serve_logo
+routes["/config"] = serve_config
+
 
 @postfork
 def setprocname():
     if uwsgi.worker_id() > 0:
-        uwsgi.setprocname("i am the worker %d" % uwsgi.worker_id())
+        uwsgi.setprocname(b"i am the worker %d" % uwsgi.worker_id())
+
 
 def application(env, start_response):
     try:
-        uwsgi.mule_msg(env['REQUEST_URI'], 1)
-    except:
+        uwsgi.mule_msg(env["REQUEST_URI"], 1)
+    except Exception:
         pass
 
-    req = uwsgi.workers()[uwsgi.worker_id()-1]['requests']
+    req = uwsgi.workers()[uwsgi.worker_id() - 1]["requests"]
 
-    uwsgi.setprocname("worker %d managed %d requests" % (uwsgi.worker_id(), req))
+    uwsgi.setprocname(b"worker %d managed %d requests" % (uwsgi.worker_id(), req))
 
     try:
         gc.collect(2)
-    except:
+    except Exception:
         pass
     if DEBUG:
-        print(env['wsgi.input'].fileno())
-
-    if env['PATH_INFO'] in routes:
-        return routes[env['PATH_INFO']](env, start_response)
+        print(env["wsgi.input"].fileno())
 
+    if env["PATH_INFO"] in routes:
+        return routes[env["PATH_INFO"]](env, start_response)
 
     if DEBUG:
-        print(env['wsgi.input'].fileno())
+        print(env["wsgi.input"].fileno())
 
     try:
         gc.collect(2)
-    except:
+    except Exception:
         pass
 
     if DEBUG:
         print(len(gc.get_objects()))
 
-    workers = ''
+    workers = ""
     for w in uwsgi.workers():
         apps = '<table border="1"><tr><th>id</th><th>mountpoint</th><th>startup time</th><th>requests</th></tr>'
-        for app in w['apps']:
-            apps += '<tr><td>%d</td><td>%s</td><td>%d</td><td>%d</td></tr>' % (app['id'], app['mountpoint'], app['startup_time'], app['requests']) 
-        apps += '</table>'
+        for app in w["apps"]:
+            apps += "<tr><td>%d</td><td>%s</td><td>%d</td><td>%d</td></tr>" % (
+                app["id"],
+                app["mountpoint"],
+                app["startup_time"],
+                app["requests"],
+            )
+        apps += "</table>"
         workers += """
 <tr>
 <td>%d</td><td>%d</td><td>%s</td><td>%d</td><td>%d</td><td>%d</td><td>%s</td>
 </tr>
-        """ % (w['id'], w['pid'], w['status'], w['running_time']/1000, w['avg_rt']/1000, w['tx'], apps)
+        """ % (
+            w["id"],
+            w["pid"],
+            w["status"],
+            w["running_time"] / 1000,
+            w["avg_rt"] / 1000,
+            w["tx"],
+            apps,
+        )
 
     output = """
-<img src="/logo"/> version %s running on %s (remote user: %s)<br/>
+<img src="{script_name}/logo"/> version %s running on %s (remote user: %s)<br/>
 <hr size="1"/>
 
 Configuration<br/>
-<iframe src="/config"></iframe><br/>
+<iframe src="{script_name}/config"></iframe><br/>
 
 <br/>
 Workers and applications<br/>
@@ -122,14 +153,15 @@
 %s
 </table>
 
-    """ % (uwsgi.version, uwsgi.hostname, env.get('REMOTE_USER','None'), workers)
-
-    start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', str(len(output)) )])
-
-    #return bytes(output.encode('latin1'))
-    return output
-
-
-
-
+    """ % (
+        uwsgi.version,
+        uwsgi.hostname,
+        env.get("REMOTE_USER", "None"),
+        workers,
+    )
+
+    start_response(
+        "200 OK", [("Content-Type", "text/html"), ("Content-Length", str(len(output)))]
+    )
 
+    return [output.format(script_name=env["SCRIPT_NAME"]).encode("utf-8")]
diff -NruwB uwsgi-2.0.28-9/PKG-INFO uwsgi-2.0.29-1/PKG-INFO
--- uwsgi-2.0.28-9/PKG-INFO	2025-05-21 11:51:12.863860826 +0200
+++ uwsgi-2.0.29-1/PKG-INFO	2025-05-21 11:50:57.343560130 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: uWSGI
-Version: 2.0.28
+Version: 2.0.29
 Summary: The uWSGI server
 Home-page: https://uwsgi-docs.readthedocs.io/en/latest/
 Author: Unbit
diff -NruwB uwsgi-2.0.28-9/plugins/fiber/fiber.c uwsgi-2.0.29-1/plugins/fiber/fiber.c
--- uwsgi-2.0.28-9/plugins/fiber/fiber.c	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/plugins/fiber/fiber.c	2025-05-21 11:50:57.359560440 +0200
@@ -15,12 +15,12 @@
 };
 
 
-VALUE uwsgi_fiber_request() {
+VALUE uwsgi_fiber_request(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)) {
 	async_schedule_to_req_green();
 	return Qnil;
 }
 
-VALUE rb_fiber_schedule_to_req() {
+VALUE rb_fiber_schedule_to_req(VALUE v) {
 	int id = uwsgi.wsgi_req->async_id;
 
         if (!uwsgi.wsgi_req->suspended) {
diff -NruwB uwsgi-2.0.28-9/plugins/pypy/pypy_plugin.c uwsgi-2.0.29-1/plugins/pypy/pypy_plugin.c
--- uwsgi-2.0.28-9/plugins/pypy/pypy_plugin.c	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/plugins/pypy/pypy_plugin.c	2025-05-21 11:50:57.363560519 +0200
@@ -294,7 +294,7 @@
 	}
 }
 
-static void uwsgi_pypy_init_thread() {
+static void uwsgi_pypy_init_thread(int sig) {
 	if (u_pypy_thread_attach) {
 		pthread_mutex_lock(&upypy.attach_thread_lock);
 		u_pypy_thread_attach();
diff -NruwB uwsgi-2.0.28-9/plugins/python/pyloader.c uwsgi-2.0.29-1/plugins/python/pyloader.c
--- uwsgi-2.0.28-9/plugins/python/pyloader.c	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/plugins/python/pyloader.c	2025-05-21 11:50:57.363560519 +0200
@@ -129,13 +129,21 @@
                         		p = strchr(*e, '=');
                         		if (p == NULL) continue;
 
+#ifdef PYTHREE
+					k = PyUnicode_FromStringAndSize(*e, (int)(p-*e));
+#else
 					k = PyString_FromStringAndSize(*e, (int)(p-*e));
+#endif
 					if (k == NULL) {
                                 		PyErr_Print();
                                 		continue;
 					}
 
+#ifdef PYTHREE
+					env_value = PyUnicode_FromString(p+1);
+#else
                         		env_value = PyString_FromString(p+1);
+#endif
                         		if (env_value == NULL) {
                                 		PyErr_Print();
 						Py_DECREF(k);
@@ -143,8 +151,12 @@
                         		}
 	
 #ifdef UWSGI_DEBUG
+#ifdef PYTHREE
+					uwsgi_log("%s = %s\n", PyUnicode_AsUTF8(k), PyUnicode_AsUTF8(env_value));
+#else
 					uwsgi_log("%s = %s\n", PyString_AsString(k), PyString_AsString(env_value));
 #endif
+#endif
 
                         		if (PyObject_SetItem(py_environ, k, env_value)) {
                                 		PyErr_Print();
diff -NruwB uwsgi-2.0.28-9/t/cachebitmap.py uwsgi-2.0.29-1/t/cachebitmap.py
--- uwsgi-2.0.28-9/t/cachebitmap.py	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/t/cachebitmap.py	2025-05-21 11:50:57.367560595 +0200
@@ -3,9 +3,20 @@
 import random
 import string
 
+
 class BitmapTest(unittest.TestCase):
 
-    __caches__ = ['items_1', 'items_2', 'items_3', 'items_4', 'items_17', 'items_4_10', 'items_1_100000', 'items_non_bitmap', 'items_lru']
+    __caches__ = [
+        'items_1',
+        'items_2',
+        'items_3',
+        'items_4',
+        'items_17',
+        'items_4_10',
+        'items_1_100000',
+        'items_non_bitmap',
+        'items_lru'
+    ]
 
     def setUp(self):
         for cache in self.__caches__:
@@ -86,7 +97,7 @@
         self.assertIsNone(uwsgi.cache_set('K' * 2049, 'X' * 50 , 0, 'items_4_10'))
 
     def rand_blob(self, n=32):
-        return ''.join([random.choice(string.ascii_letters + string.digits) for n in range(n)])
+        return ''.join(random.choice(string.ascii_letters + string.digits) for x in range(n))
 
     def test_big_random(self):
         blob = self.rand_blob(100000)
@@ -108,7 +119,7 @@
         self.assertTrue(uwsgi.cache_set('KEY2', 'X' * 20, 0, 'items_lru'))
         self.assertTrue(uwsgi.cache_set('KEY3', 'Y' * 20, 0, 'items_lru'))
         self.assertIsNone(uwsgi.cache_get('KEY1', 'items_lru'))
-        second_item = uwsgi.cache_get('KEY3', 'items_lru')
+        uwsgi.cache_get('KEY3', 'items_lru')
         for i in range(4,100):
             self.assertTrue(uwsgi.cache_set('KEY%d' % i, 'Y' * 20, 0, 'items_lru'))
             self.assertIsNone(uwsgi.cache_get('KEY%d' % (i-2), 'items_lru'))
diff -NruwB uwsgi-2.0.28-9/t/cachetest.py uwsgi-2.0.29-1/t/cachetest.py
--- uwsgi-2.0.28-9/t/cachetest.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/t/cachetest.py	2025-05-21 11:50:57.367560595 +0200
@@ -8,8 +9,9 @@
 def gen_rand_n(max_n):
     return random.randint(8, max_n)
 
+
 def gen_rand_s(size):
-    return ''.join( [ random.choice(string.letters) for i in range(size) ])
+    return ''.join(random.choice(string.letters) for i in range(size))
 
 print('filling cache...')
 for i in range(0, 1000):
diff -NruwB uwsgi-2.0.28-9/t/core/readline/app.py uwsgi-2.0.29-1/t/core/readline/app.py
--- uwsgi-2.0.28-9/t/core/readline/app.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/core/readline/app.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,16 @@
+#!/usr/bin/python3
+# uwsgi --plugin python,http --http 0.0.0.0:8000 -w app
+
+from werkzeug.wrappers import Request, Response
+
+
+def application(env, start_response):
+    request = Request(env)
+    lines = b""
+    while True:
+        line = request.stream.readline()
+        if not line:
+            break;
+        lines += line
+    response = Response(lines)
+    return response(env, start_response)
diff -NruwB uwsgi-2.0.28-9/t/core/readline/client.py uwsgi-2.0.29-1/t/core/readline/client.py
--- uwsgi-2.0.28-9/t/core/readline/client.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/core/readline/client.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,8 @@
+import requests
+
+headers = {'Content-Type': 'application/octet-stream'}
+data = '\n'.join(['{:04}'.format(i) for i in range(1001)] + ['final'])
+
+r = requests.post("http://127.0.0.1:8000";, data=data, headers=headers)
+
+assert r.text == data
diff -NruwB uwsgi-2.0.28-9/t/core/readline/requirements.txt uwsgi-2.0.29-1/t/core/readline/requirements.txt
--- uwsgi-2.0.28-9/t/core/readline/requirements.txt	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/core/readline/requirements.txt	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,2 @@
+requests
+Werkzeug
diff -NruwB uwsgi-2.0.28-9/t/cron.ini uwsgi-2.0.29-1/t/cron.ini
--- uwsgi-2.0.28-9/t/cron.ini	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/cron.ini	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,3 @@
+[uwsgi]
+cron = -1 -1 -1 -1 -1 /bin/echo minus one
+cron = 14 -1 -1 -1 -1 /bin/echo zero
diff -NruwB uwsgi-2.0.28-9/t/perl/apps/body-types.psgi uwsgi-2.0.29-1/t/perl/apps/body-types.psgi
--- uwsgi-2.0.28-9/t/perl/apps/body-types.psgi	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/perl/apps/body-types.psgi	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,43 @@
+use strict;
+use warnings;
+
+use FileHandle;
+use IO::File;
+use IO::String;
+
+my $code = do { local ( @ARGV, $/ ) = __FILE__; <> };
+
+sub ObjectPath::path { __FILE__ }
+
+sub {
+    my $path = shift->{PATH_INFO};
+
+    my $body = $path eq '/Array'      ? [ split //, $code ]
+             : $path eq '/Code'       ? sub {}
+             : $path eq '/DATA'       ? \*DATA
+             : $path eq '/DIRHANDLE'  ? do { opendir my $fh, '.'; $fh }
+             : $path eq '/FILEHANDLE' ? do { open my $fh, __FILE__; $fh }
+             : $path eq '/FileHandle' ? FileHandle->new(__FILE__)
+             : $path eq '/Float'      ? 3.14
+             : $path eq '/FloatRef'   ? \3.14
+             : $path eq '/Format'     ? *STDOUT{FORMAT}
+             : $path eq '/FormatRef'  ? \*STDOUT{FORMAT}
+             : $path eq '/IO::File'   ? IO::File->new(__FILE__)
+             : $path eq '/Hash'       ? { foo => 'bar' }
+             : $path eq '/Int'        ? 3
+             : $path eq '/IntRef'     ? \3
+             : $path eq '/IO::String' ? IO::String->new($code)
+             : $path eq '/Object'     ? bless({})
+             : $path eq '/ObjectPath' ? bless( {}, 'ObjectPath' )
+             : $path eq '/Regexp'     ? qr/foo/
+             : $path eq '/String'     ? 'foo'
+             : $path eq '/StringRef'  ? \'bar'
+             : $path eq '/Undef'      ? undef
+             : $path eq '/UndefRef'   ? \undef
+             : return [ 404, [], [] ];
+
+    [ 200, [ 'X-ref' => ref $body ], $body ];
+};
+
+__DATA__
+data data data
diff -NruwB uwsgi-2.0.28-9/t/perl/apps/env.psgi uwsgi-2.0.29-1/t/perl/apps/env.psgi
--- uwsgi-2.0.28-9/t/perl/apps/env.psgi	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/perl/apps/env.psgi	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+
+sub {
+    my $env = shift;
+
+    my $body = join "\n", map "$_\n$env->{$_}", sort keys %$env;
+
+    [ 200, [ 'Content-type' => 'text/plain' ], [$body] ];
+};
diff -NruwB uwsgi-2.0.28-9/t/perl/body-types.t uwsgi-2.0.29-1/t/perl/body-types.t
--- uwsgi-2.0.28-9/t/perl/body-types.t	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/perl/body-types.t	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,57 @@
+use strict;
+use warnings;
+
+BEGIN {
+    exec qw(
+        ./uwsgi
+        --disable-logging
+        --http-socket :5000
+        --perl-no-die-catch
+        --perl-no-plack
+        --psgi t/perl/apps/body-types.psgi
+    ) unless my $pid = fork;
+
+    END { kill 15, $pid }
+}
+
+use HTTP::Tiny;
+use Test::More tests => 22;
+
+my $http = HTTP::Tiny->new;
+my $code = do { local ( @ARGV, $/ ) = 't/perl/apps/body-types.psgi'; <> };
+
+for (
+    [ Array        => 1, 'ARRAY'      ],
+    [ Code         => 0, 'CODE'       ],
+    [ DATA         => 1, 'GLOB'       ],
+    [ DIRHANDLE    => 0, 'GLOB'       ],
+    [ FILEHANDLE   => 1, 'GLOB'       ],
+    [ FileHandle   => 1, 'FileHandle' ],
+    [ Float        => 0, ''           ],
+    [ FloatRef     => 0, 'SCALAR'     ],
+    [ Format       => 0, ''           ],
+    [ FormatRef    => 0, 'SCALAR'     ],
+    [ Hash         => 0, 'HASH'       ],
+    [ Int          => 0, ''           ],
+    [ IntRef       => 0, 'SCALAR'     ],
+    [ 'IO::File'   => 1, 'IO::File'   ],
+    [ 'IO::String' => 1, 'IO::String' ],
+    [ Object       => 0, 'main'       ],
+    [ ObjectPath   => 1, 'ObjectPath' ],
+    [ Regexp       => 0, 'Regexp'     ],
+    [ String       => 0, ''           ],
+    [ StringRef    => 0, 'SCALAR'     ],
+    [ Undef        => 0, ''           ],
+    [ UndefRef     => 0, 'SCALAR'     ],
+) {
+    my ( $path, $has_content, $ref ) = @$_;
+
+    my $got = $http->get( 'http://localhost:5000/' . $path );
+
+    delete @$got{qw/protocol reason success status url/};
+
+    is_deeply $got, {
+        content => $code x $has_content,
+        headers => { 'x-ref' => $ref },
+    }, $path;
+}
diff -NruwB uwsgi-2.0.28-9/t/perl/env.t uwsgi-2.0.29-1/t/perl/env.t
--- uwsgi-2.0.28-9/t/perl/env.t	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/perl/env.t	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,79 @@
+use strict;
+use warnings;
+
+use Config;
+use HTTP::Tiny;
+use Test::Deep;
+use Test::More;
+
+chomp( my $host = `hostname` );
+
+my ( $t, $f ) = ( bool(1), bool(0) );
+
+my $http = HTTP::Tiny->new;
+my %exp  = (
+    HTTP_HOST                => 'localhost:5000',
+    HTTP_USER_AGENT          => $http->agent,
+    PATH_INFO                => '/',
+    QUERY_STRING             => '',
+    REMOTE_ADDR              => '127.0.0.1',
+    REQUEST_METHOD           => 'GET',
+    REQUEST_URI              => '/',
+    SCRIPT_NAME              => '',
+    SERVER_NAME              => $host,
+    SERVER_PORT              => 5000,
+    SERVER_PROTOCOL          => 'HTTP/1.1',
+    'psgi.errors'            => re(qr/^uwsgi::error=SCALAR\(0x[\da-f]+\)$/),
+    'psgi.input'             => re(qr/^uwsgi::input=SCALAR\(0x[\da-f]+\)$/),
+    'psgi.multiprocess'      => $f,
+    'psgi.multithread'       => $f,
+    'psgi.nonblocking'       => $f,
+    'psgi.run_once'          => $f,
+    'psgi.streaming'         => $t,
+    'psgi.url_scheme'        => 'http',
+    'psgi.version'           => re(qr/^ARRAY\(0x[\da-f]+\)$/),
+    'psgix.cleanup'          => $t,
+    'psgix.cleanup.handlers' => re(qr/^ARRAY\(0x[\da-f]+\)$/),
+    'psgix.harakiri'         => $f,
+    'psgix.input.buffered'   => $f,
+    'psgix.logger'           => re(qr/^CODE\(0x[\da-f]+\)$/),
+);
+
+my @tests = (
+    [ 'Defaults', {} ],
+    [ 'Master', { 'psgix.harakiri' => $t }, '--master' ],
+    [ 'Async', { 'psgi.nonblocking' => $t }, '--async' => 1 ],
+    [
+        'Workers',
+        { 'psgix.harakiri' => $t, 'psgi.multiprocess' => $t },
+        '--master', '--workers' => 2,
+    ],
+);
+
+push @tests, [ 'Threads', { 'psgi.multithread' => $t }, '--threads' => 2 ]
+    if $ENV{UWSGI_PERL} =~ /-thread$/;
+
+plan tests => scalar @tests;
+
+for (@tests) {
+    my ( $name, $exp, @opts ) = @$_;
+
+    exec qw(
+        ./uwsgi
+        --disable-logging
+        --http-socket :5000
+        --perl-no-die-catch
+        --perl-no-plack
+        --psgi t/perl/apps/env.psgi
+    ), @opts unless my $pid = fork;
+
+    sleep 1;    # Let uWSGI start.
+
+    my %got = split /\n/, $http->get('http://localhost:5000')->{content};
+
+    cmp_deeply \%got, { %exp, %$exp }, $name;
+
+    kill 15, $pid;
+
+    sleep 1;    # Let uWSGI kill it's workers.
+}
diff -NruwB uwsgi-2.0.28-9/t/perl/run uwsgi-2.0.29-1/t/perl/run
--- uwsgi-2.0.28-9/t/perl/run	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/perl/run	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use TAP::Harness;
+
+my $cpus  = `nproc`;
+my @perls = qw/
+    5.20.2
+    5.18.4
+    5.16.3
+    5.14.4
+    5.12.4
+    5.10.1
+    5.8.9
+/;
+
+my %installed;
+@installed{ map /(uwsgi-perl-[-\w.]+)/ ? $1 : (), `perlbrew list` } = ();
+
+my $tap = TAP::Harness->new( { verbosity => 1 } );
+
+for my $perl (@perls) {
+    for my $thread (0, 1) {
+        my $name = $ENV{UWSGI_PERL} =
+            'uwsgi-perl-' . $perl . ( '-thread' x $thread );
+
+        warn "\n# $name\n\n";
+
+        # Install required perlbrew, unless already installed.
+        system 'perlbrew', 'install', $perl,
+            '--as', $name, '-D', 'useshrplib', '-j', $cpus, '-n', '--noman',
+            ('--thread') x $thread and die $!
+            unless exists $installed{$name};
+
+        # Clean previously compiled uWSGI.
+        system 'python', 'uwsgiconfig.py', '-c' and die $!;
+
+        # Compile uWSGI, only show STDERR.
+        `perlbrew exec --with $name python uwsgiconfig.py -b plonly`;
+        exit if $?;
+
+        my $res = $tap->runtests( @ARGV ? @ARGV : <t/perl/*.t> );
+
+        exit $res->exit if $res->exit;
+    }
+}
diff -NruwB uwsgi-2.0.28-9/t/perl/test_harakiri.psgi uwsgi-2.0.29-1/t/perl/test_harakiri.psgi
--- uwsgi-2.0.28-9/t/perl/test_harakiri.psgi	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/t/perl/test_harakiri.psgi	2025-05-21 11:50:57.367560595 +0200
@@ -6,6 +6,12 @@
     sub DESTROY { print STDERR "$$: Calling DESTROY\n" }
 }
 
+uwsgi::atexit(
+    sub {
+        print STDERR "$$: Calling the atexit hook\n";
+    }
+);
+
 sub {
     my $env = shift;
 
diff -NruwB uwsgi-2.0.28-9/t/perl/test_post.psgi uwsgi-2.0.29-1/t/perl/test_post.psgi
--- uwsgi-2.0.28-9/t/perl/test_post.psgi	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/t/perl/test_post.psgi	2025-05-21 11:50:57.367560595 +0200
@@ -19,7 +19,7 @@
 
 __END__
 
-This is a trival test that prints out a POST request, it's here to
+This is a trivial test that prints out a POST request, it's here to
 test a regression introduced in 2.0-103-gf041d10 where doing reads
 without offsets, e.g.:
 
diff -NruwB uwsgi-2.0.28-9/t/python/spooler_decorators/spooler_decorator_test.ini uwsgi-2.0.29-1/t/python/spooler_decorators/spooler_decorator_test.ini
--- uwsgi-2.0.28-9/t/python/spooler_decorators/spooler_decorator_test.ini	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/t/python/spooler_decorators/spooler_decorator_test.ini	2025-05-21 11:50:57.367560595 +0200
@@ -1,7 +1,8 @@
 [uwsgi]
-socket = /tmp/temporary-socket
+socket = /tmp/temporary_socket
 
 ; Spooler!
+
 spooler-import = %d/spooler_handlers.py
 ; Specify the spooler
 spooler = $(SPOOLER_DIR)
@@ -9,7 +10,7 @@
 spooler-processes = 1
 ; Spooler ordered scanning (only works with "numbered" dirs)
 spooler-ordered = 1
-
+; Spooler frequency
 spooler-frequency = 1
 
 pyrun = %d/spooler_decorator_tests.py
diff -NruwB uwsgi-2.0.28-9/t/python/spooler_decorators/spooler_handlers.py uwsgi-2.0.29-1/t/python/spooler_decorators/spooler_handlers.py
--- uwsgi-2.0.28-9/t/python/spooler_decorators/spooler_handlers.py	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/t/python/spooler_decorators/spooler_handlers.py	2025-05-21 11:50:57.367560595 +0200
@@ -1,6 +1,6 @@
 # See spooler_decorator_tests
 
-from uwsgidecorators import *
+from uwsgidecorators import spool, spoolraw
 import uwsgi
 
 ghostpath = "/tmp/ghost"
diff -NruwB uwsgi-2.0.28-9/t/python/spooler_handler.py uwsgi-2.0.29-1/t/python/spooler_handler.py
--- uwsgi-2.0.28-9/t/python/spooler_handler.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/spooler_handler.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,34 @@
+#! /usr/bin/env python2
+# coding = utf-8
+
+from __future__ import print_function
+from constants import tasks, LOGFILE
+from os import remove
+import uwsgi
+
+counter = 0
+
+
+def spoolerHandler(env):
+    global counter
+    # Spooler is handling a task
+    with open(LOGFILE, "a") as log:
+        print("%s" % (env['name']), file=log)
+
+    counter += 1
+
+    if counter == len(tasks):
+        # Each task has been processed.
+        uwsgi.signal(17)
+
+    # Spooler has done handling the task
+    return uwsgi.SPOOL_OK
+
+uwsgi.spooler = spoolerHandler
+
+# Clear the logfile
+try:
+    remove(LOGFILE)
+except OSError, e:  # log does not exist
+    pass
+    # print(e)
diff -NruwB uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_constants.py uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_constants.py
--- uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_constants.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_constants.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,22 @@
+#! /usr/bin/env python2
+# coding = utf-8
+
+tasks = [
+    (101, "101"),
+    (101, "101Bis"),
+    (2, "2"),
+    (1, "1"),
+    (0, "0"),
+    (None, "NoPriority")
+]
+
+ordered_tasks = [
+    "0",
+    "1",
+    "2",
+    "101",
+    "101Bis",
+    "NoPriority"
+]
+
+LOGFILE = "/tmp/spoolerlog"
diff -NruwB uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_handler.py uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_handler.py
--- uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_handler.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_handler.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,33 @@
+#! /usr/bin/env python
+# coding = utf-8
+
+from __future__ import print_function
+from spooler_priority_constants import tasks, LOGFILE
+from os import remove
+import uwsgi
+
+counter = 0
+
+
+def spoolerHandler(env):
+    global counter
+    # Spooler is handling a task
+    with open(LOGFILE, "a") as log:
+        print("%s" % (env['name']), file=log)
+
+    counter += 1
+
+    if counter == len(tasks):
+        # Each task has been processed.
+        uwsgi.signal(17)
+
+    # Spooler has done handling the task
+    return uwsgi.SPOOL_OK
+
+uwsgi.spooler = spoolerHandler
+
+# Clear the logfile
+try:
+    remove(LOGFILE)
+except OSError, e:  # log does not exist
+    print(e)
diff -NruwB uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_test.ini uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_test.ini
--- uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_test.ini	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_test.ini	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,17 @@
+[uwsgi]
+socket = /tmp/temporary-socket
+
+; Specify the spooler
+spooler = %d/temporary_spooler
+; Spooler handler
+spooler-import = %d/spooler_priority_handler.py
+; And the number of processes
+spooler-processes = 1
+; Spooler ordered scanning (only works with "numbered" dirs)
+spooler-ordered = 1
+; Spooler scans folder each second
+spooler-frequency = 1
+
+pyrun = %d/spooler_priority_test.py
+master = 1
+
diff -NruwB uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_test.py uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_test.py
--- uwsgi-2.0.28-9/t/python/spooler_priority/spooler_priority_test.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/spooler_priority/spooler_priority_test.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,81 @@
+#! /usr/bin/env python
+# coding = utf-8
+
+import uwsgi
+import unittest
+import os
+import fcntl
+from shutil import rmtree
+import time
+from signal import signal, SIGINT
+import spooler_priority_constants
+
+
+def spoolersTaskList():
+    # Get the list of tasks
+    tasks = [
+        os.path.join(s, fn)
+        for s in uwsgi.spoolers
+        for fn in os.listdir(s)
+    ]
+
+    for t in tasks[:]:
+        if os.path.isdir(t):
+            tasks += [os.path.join(t, fn) for fn in os.listdir(t)]
+            tasks.remove(t)
+
+    return tasks
+
+
+def is_locked(filepath):
+    # Check if file is locked
+    with open(filepath, "a+") as f:
+        try:
+            fcntl.lockf(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+            is_locked = False
+        except IOError:
+            is_locked = True
+    return is_locked
+
+
+def lockedTasks(tasklist):
+    # List of locked tasks
+    return [fp for fp in spoolersTaskList() if is_locked(fp)]
+
+
+def taskParameters(filepath):
+    # Retrieve parameters
+    return uwsgi.parsefile(filepath)
+
+
+def cleanTasks():
+    # Clean any remaining task
+    for s in uwsgi.spoolers:
+        for f in os.listdir(s):
+            path = os.path.join(s, f)
+            if os.path.isdir(path):
+                rmtree(os.path.join(s, f))
+            else:
+                os.remove(path)
+
+
+class BitmapTest(unittest.TestCase):
+
+    def setUp(self):
+        for priority, name in spooler_priority_constants.tasks:
+            task = {'name': name, 'at': int(time.time() + 10)}
+            if priority is not None:
+                task['priority'] = str(priority)
+            uwsgi.spool(task)
+
+    def test_priority(self):
+        uwsgi.signal_wait(17)
+        print("Signal received.")
+
+        with open(spooler_priority_constants.LOGFILE) as log:
+            # Check logging ordering.
+            loglines = [line.rstrip() for line in log]
+            self.assertEqual(loglines, spooler_priority_constants.ordered_tasks)
+
+signal(SIGINT, cleanTasks)
+unittest.main()
diff -NruwB uwsgi-2.0.28-9/t/python/testmultipleenv.py uwsgi-2.0.29-1/t/python/testmultipleenv.py
--- uwsgi-2.0.28-9/t/python/testmultipleenv.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/testmultipleenv.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,12 @@
+# uwsgi --env foo=bar --env foo2=bar --http :8080 --wsgi-file t/python/testmultipleenv.py
+import os
+
+assert os.getenv('foo') == 'bar'
+assert os.getenv('foo2') == 'bar'
+
+print os.getenv('foo')
+print os.getenv('foo2')
+
+def application(e, sr):
+    sr('200 OK', [('Content-Type', 'text/html')])
+    return b'Hello world'
diff -NruwB uwsgi-2.0.28-9/t/python/timers.py uwsgi-2.0.29-1/t/python/timers.py
--- uwsgi-2.0.28-9/t/python/timers.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/timers.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,6 @@
+# uwsgi --wsgi-file t/python/timers.py --http :8080 --master
+from uwsgidecorators import mstimer
+
+@mstimer(500)
+def ms_timer(signum):
+    print("500 ms timer")
diff -NruwB uwsgi-2.0.28-9/t/python/wsgi_chunked.py uwsgi-2.0.29-1/t/python/wsgi_chunked.py
--- uwsgi-2.0.28-9/t/python/wsgi_chunked.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/python/wsgi_chunked.py	2025-05-21 11:50:57.367560595 +0200
@@ -0,0 +1,16 @@
+def application(environ, start_response):
+    print(environ)
+    start_response('200 OK', [])
+    if not environ['wsgi.input_terminated']:
+        return []
+#    print(environ['wsgi.input'].read())
+    data = environ['wsgi.input'].read(2)
+    print(data)
+    data = environ['wsgi.input'].read(2)
+    print(data)
+    data = environ['wsgi.input'].read(2)
+    print(data)
+    data = environ['wsgi.input'].read(6)
+    print(data)
+    print(environ['wsgi.input'].read())
+    return [data]
diff -NruwB uwsgi-2.0.28-9/t/ring/src/uwsgi/ring/tests/body.clj uwsgi-2.0.29-1/t/ring/src/uwsgi/ring/tests/body.clj
--- uwsgi-2.0.28-9/t/ring/src/uwsgi/ring/tests/body.clj	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/t/ring/src/uwsgi/ring/tests/body.clj	2025-05-21 11:50:57.367560595 +0200
@@ -2,7 +2,7 @@
   (:use [compojure.core]))
 
 ; generating primary numbers
-; http://clojuredocs.org/clojure_core/clojure.core/lazy-seq#example_1000
+; https://clojuredocs.org/clojure.core/lazy-seq#example_1000
 (defn sieve [s]
   (cons (first s)
         (lazy-seq (sieve (filter #(not= 0 (mod % (first s)))
diff -NruwB uwsgi-2.0.28-9/t/spooler/cheap.py uwsgi-2.0.29-1/t/spooler/cheap.py
--- uwsgi-2.0.28-9/t/spooler/cheap.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/t/spooler/cheap.py	2025-05-21 11:50:57.371560673 +0200
@@ -0,0 +1,21 @@
+# uwsgi --spooler spool1 --spooler spool2 --spooler-cheap --spooler-frequency 5 --spooler-processes 4 --mule --shared-py-import=t/spooler/cheap.py --stats :5000
+from uwsgidecorators import *
+import time
+import random
+import os
+
+def fake(args):
+    time.sleep(6)
+    return uwsgi.SPOOL_OK
+
+uwsgi.spooler = fake
+
+base = os.getcwd()
+spoolers = [base + '/spool1', base + '/spool2']
+
+@mule(1)
+def spooler_enqueuer():
+    while True:
+        print("enqueuing task...")
+        uwsgi.spool({'one':'two', 'spooler': random.choice(spoolers)})
+        time.sleep(random.randrange(1, 15))
diff -NruwB uwsgi-2.0.28-9/tests/badwrites.py uwsgi-2.0.29-1/tests/badwrites.py
--- uwsgi-2.0.28-9/tests/badwrites.py	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/tests/badwrites.py	2025-05-21 11:50:57.371560673 +0200
@@ -1,5 +1,5 @@
 import time
-import sys
+
 
 def application(e, sr):
     time.sleep(3)
@@ -13,7 +13,7 @@
 
     try:
         time.sleep(2)
-    except:
+    except Exception:
         print("CLIENT DISCONNECTED !!!")
     print("2 seconds elapsed")
 
@@ -21,7 +21,7 @@
 
     try:
         time.sleep(2)
-    except:
+    except Exception:
         print("CLIENT DISCONNECTED !!!")
     print("2 seconds elapsed")
 
diff -NruwB uwsgi-2.0.28-9/tests/cpubound_stackless.py uwsgi-2.0.29-1/tests/cpubound_stackless.py
--- uwsgi-2.0.28-9/tests/cpubound_stackless.py	2025-05-21 11:51:19.287985297 +0200
+++ uwsgi-2.0.29-1/tests/cpubound_stackless.py	2025-05-21 11:50:57.371560673 +0200
@@ -1,11 +1,11 @@
 import time
 import stackless
 
+
 def application(env, start_response):
 	start_response( '200 OK', [ ('Content-Type','text/html') ])
-	#print(env)
+
 	for i in range(1,100000):
-		#print(i)
 		yield "<h1>%s at %s</h1>\n" % (i, str(time.time()))
 		#schedule every 2
 		if i % 2 == 0:
diff -NruwB uwsgi-2.0.28-9/tests/decoratortest.py uwsgi-2.0.29-1/tests/decoratortest.py
--- uwsgi-2.0.28-9/tests/decoratortest.py	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/tests/decoratortest.py	2025-05-21 11:50:57.371560673 +0200
@@ -3,7 +3,7 @@
 
 from uwsgidecorators import *
 
-from uwsgicc import app as application
+from uwsgicc import app as application  # NOQA
 
 import time
 
@@ -51,7 +55,8 @@
         print(uwsgi.call('helloworld'))
         time.sleep(1)
 
-# continuosly spool a long running task
+
+# continuously spool a long running task
 @spoolforever
 def an_infinite_task(args):
     for i in range(1,4):
@@ -131,6 +144,6 @@
 delayed_task.spool(foo2='bar2', at=when)
 a_running_thread()
 a_running_thread_with_args("uWSGI")
-uwsgi_source_file = open('uwsgi.c','r')
+uwsgi_source_file = open('uwsgi.c')
 print(big_body_task.spool(priority=9,filename='uwsgi.c',body=uwsgi_source_file.read()))
 uwsgi_source_file.close()
diff -NruwB uwsgi-2.0.28-9/tests/else_test.ini uwsgi-2.0.29-1/tests/else_test.ini
--- uwsgi-2.0.28-9/tests/else_test.ini	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/tests/else_test.ini	2025-05-21 11:50:57.371560673 +0200
@@ -0,0 +1,18 @@
+[uwsgi]
+if-hostname = foobar
+  print = ok1
+  print = ok2
+  print = ok3
+else =
+  print = no1
+  print = no2
+  print = no3
+  socket = 127.0.0.1:0
+  else_executed = 1
+endif =
+
+print = done
+
+if-not-opt = else_executed
+  exit = 17
+end =
diff -NruwB uwsgi-2.0.28-9/tests/fileserve_async.py uwsgi-2.0.29-1/tests/fileserve_async.py
--- uwsgi-2.0.28-9/tests/fileserve_async.py	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/tests/fileserve_async.py	2025-05-21 11:50:57.371560673 +0200
@@ -13,5 +13,5 @@
 		content_type = 'text/plain'
 
 	start_response('200 OK', [('Content-Type', content_type)])
-	fd = open(filename,'r')
+    fd = open(filename)
 	yield environ['wsgi.file_wrapper'](fd, 32*1024)
diff -NruwB uwsgi-2.0.28-9/tests/iobound_async_unix.py uwsgi-2.0.29-1/tests/iobound_async_unix.py
--- uwsgi-2.0.28-9/tests/iobound_async_unix.py	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/tests/iobound_async_unix.py	2025-05-21 11:50:57.371560673 +0200
@@ -1,5 +1,4 @@
 import socket
-import select
 import errno
 import struct
 
diff -NruwB uwsgi-2.0.28-9/tests/mule_file.py uwsgi-2.0.29-1/tests/mule_file.py
--- uwsgi-2.0.28-9/tests/mule_file.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/tests/mule_file.py	2025-05-21 11:50:57.371560673 +0200
@@ -0,0 +1,3 @@
+# uwsgi --master --plugins=python --mule=mule_file.py -s:0 
+import uwsgi
+print(uwsgi.mule_file())
diff -NruwB uwsgi-2.0.28-9/tests/picazzo.py uwsgi-2.0.29-1/tests/picazzo.py
--- uwsgi-2.0.28-9/tests/picazzo.py	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/tests/picazzo.py	2025-05-21 11:50:57.371560673 +0200
@@ -17,6 +20,7 @@
   GET("/", home),
   GET("/login", login),
   POST("/login", login_post),
-  routing.not_found("<h1>Not Found</h1>"))
+    routing.not_found("<h1>Not Found</h1>")
+)
 
 app = setup_app(routes)
diff -NruwB uwsgi-2.0.28-9/tests/psycogreen_green.py uwsgi-2.0.29-1/tests/psycogreen_green.py
--- uwsgi-2.0.28-9/tests/psycogreen_green.py	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/tests/psycogreen_green.py	2025-05-21 11:50:57.371560673 +0200
@@ -37,7 +36,8 @@
 
 	while True:
 		row = curs.fetchone()
-		if not row: break
+        if not row:
+            break
 		yield "<tr><td>%s</td></tr>" % str(row)
 
 	yield "</table>"
diff -NruwB uwsgi-2.0.28-9/tests/psycopg2_green.py uwsgi-2.0.29-1/tests/psycopg2_green.py
--- uwsgi-2.0.28-9/tests/psycopg2_green.py	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/psycopg2_green.py	2025-05-21 11:50:57.371560673 +0200
@@ -45,7 +43,8 @@
 
 	while True:
 		row = curs.fetchone()
-		if not row: break
+        if not row:
+            break
 		yield "<tr><td>%s</td></tr>" % str(row)
 
 	yield "</table>"
diff -NruwB uwsgi-2.0.28-9/tests/pump.py uwsgi-2.0.29-1/tests/pump.py
--- uwsgi-2.0.28-9/tests/pump.py	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/pump.py	2025-05-21 11:50:57.371560673 +0200
@@ -1,7 +1,12 @@
 def app(req):
   print(req)
-  ret = {"status": 200,
-          "headers": {"content_type": "text/html", "foo":['bar0','bar1','bar2']},
-          "body": "<h1>Hello!</h1>"}
+    ret = {
+        "status": 200,
+        "headers": {
+            "content_type": "text/html",
+            "foo": ['bar0', 'bar1', 'bar2']
+        },
+        "body": "<h1>Hello!</h1>",
+    }
   print(ret)
   return ret
diff -NruwB uwsgi-2.0.28-9/tests/Responder.pm uwsgi-2.0.29-1/tests/Responder.pm
--- uwsgi-2.0.28-9/tests/Responder.pm	2025-05-21 11:51:12.879861135 +0200
+++ uwsgi-2.0.29-1/tests/Responder.pm	2025-05-21 11:50:57.371560673 +0200
@@ -27,7 +27,7 @@
 	}
 	elsif ($self->{_counter} == 4) {
 		$self->{_counter}++;
-		return "connected to http://projects.unbit.it<br/>";
+		return "connected to https://projects.unbit.it<br/>";
 	}
 	elsif ($self->{_counter} == 7) {
 		$self->{_counter}++;
diff -NruwB uwsgi-2.0.28-9/tests/spooler_dir.py uwsgi-2.0.29-1/tests/spooler_dir.py
--- uwsgi-2.0.28-9/tests/spooler_dir.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/tests/spooler_dir.py	2025-05-21 11:50:57.371560673 +0200
@@ -0,0 +1,10 @@
+# uwsgi --master --plugins=python27 --spooler=/var/spool/uwsgi/ --spooler-import spooler_dir.py
+import uwsgi
+
+
+def spooler_func(env):
+    print(uwsgi.spooler_dir())
+    return uwsgi.SPOOL_RETRY
+
+uwsgi.spooler = spooler_func
+uwsgi.spool({"foo": "bar"})
diff -NruwB uwsgi-2.0.28-9/tests/spoolme.py uwsgi-2.0.29-1/tests/spoolme.py
--- uwsgi-2.0.28-9/tests/spoolme.py	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/spoolme.py	2025-05-21 11:50:57.371560673 +0200
@@ -8,12 +9,17 @@
 uwsgi.spooler = slow_task
 
 
-
 def application(env, start_response):
 
-    name = uwsgi.spool({'Hello':'World', 'I am a':'long running task'})
+    name = uwsgi.spool({
+        'Hello': 'World',
+        'I am a': 'long running task'
+    })
     print("spooled as %s" % name)
 
-    start_response('200 Ok', [('Content-Type','text/plain'),('uWSGI-Status', 'spooled')])
+    start_response('200 Ok', [
+        ('Content-Type', 'text/plain'),
+        ('uWSGI-Status', 'spooled'),
+    ])
 
     return "task spooled"
diff -NruwB uwsgi-2.0.28-9/tests/staticfile.py uwsgi-2.0.29-1/tests/staticfile.py
--- uwsgi-2.0.28-9/tests/staticfile.py	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/staticfile.py	2025-05-21 11:50:57.371560673 +0200
@@ -1,20 +1,20 @@
 import sys
-import uwsgi
 
 content_type = 'image/png'
 filename = 'logo_uWSGI.png'
 
 try:
     filename = sys.argv[1]
-except:
+except IndexError:
     pass
 
 try:
     content_type = sys.argv[2]
-except:
+except IndexError:
     pass
 
+
 def application(environ, start_response):
     start_response('200 OK', [('Content-Type', content_type)])
-    fd = open(filename,'r')
+    fd = open(filename)
     yield environ['wsgi.file_wrapper'](fd, 32*1024)
diff -NruwB uwsgi-2.0.28-9/tests/testapp.py uwsgi-2.0.29-1/tests/testapp.py
--- uwsgi-2.0.28-9/tests/testapp.py	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/tests/testapp.py	2025-05-21 11:50:57.371560673 +0200
@@ -29,12 +30,14 @@
 p = "serena"
 
 #while 1:
-#print "MARSHALLED OUT: ",uwsgi.send_uwsgi_message("127.0.0.1", 3033, 33, 17, {'prodotto':p, 'tempo': time.time(), 'pippo':'pluto', 'topolino':'paperino', 'callable':4+1, 'nullo': None, 'embedded': {'a':1} }, 17)
+#print("MARSHALLED OUT: ",uwsgi.send_uwsgi_message("127.0.0.1", 3033, 33, 17, {'prodotto':p, 'tempo': time.time(), 'pippo':'pluto', 'topolino':'paperino', 'callable':4+1, 'nullo': None, 'embedded': {'a':1} }, 17))
+
 
 def mako(filename, vars):
     return uwsgi.send_uwsgi_message("127.0.0.1", 3033, 33, 17, (filename, vars), 17)
 
-#print uwsgi.send_uwsgi_message("127.0.0.1", 3033, 33, 17, ('makotest.txt', {'whattimeisit':time.time(), 'roberta':'serena'}), 17)
+#print(uwsgi.send_uwsgi_message("127.0.0.1", 3033, 33, 17, ('makotest.txt', {'whattimeisit':time.time(), 'roberta':'serena'}), 17))
+
 
 def myspooler(env):
     print(env)
@@ -44,7 +47,8 @@
 
 uwsgi.spooler = myspooler
 
-#print "SPOOLER: ", uwsgi.send_to_spooler({'TESTKEY':'TESTVALUE', 'APPNAME':'uWSGI'})
+#print("SPOOLER: ", uwsgi.send_to_spooler({'TESTKEY':'TESTVALUE', 'APPNAME':'uWSGI'}))
+
 
 def helloworld():
     return 'Hello World'
@@ -56,11 +62,15 @@
     time.sleep(60)
 
 
-
 def application(env, start_response):
     print(env)
     start_response('200 OK', [('Content-Type', 'text/plain')])
-    yield { '/': helloworld, '/sleep': force_harakiri, '/counter': increment, '/uwsgi/':helloworld }[env['PATH_INFO']]()
+    yield {
+        '/': helloworld,
+        '/sleep': force_harakiri,
+        '/counter': increment,
+        '/uwsgi/': helloworld
+    }[env['PATH_INFO']]()
 
     print(env)
 
@@ -87,7 +100,8 @@
     print("ciao", a)
     yield str(a)
 
-def reload_module(env, start_response):
+
+def reload(env, start_response):
 
     start_response('200 OK', [('Content-Type', 'text/html')])
 
@@ -96,7 +110,7 @@
 
     #uwsgi.reload()
 
-#    print str(uwsgi.masterpid()) + "\n"
+#    print(str(uwsgi.masterpid()) + "\n")
 
 #    print("i am python")
     #yo()
@@ -113,15 +127,15 @@
 #    try:
 #        print 4/0
 #
-#        print(uwsgi.pippo)
-#    except:
-#        print("bah")
+#        print uwsgi.pippo
+#    except Exception:
+#        print "bah"
 
 #    print("ok")
 
 #    yield 4/0
 
-    yield '<h1>uWSGI status ('+env['SCRIPT_NAME']+')</h1>';
+    yield '<h1>uWSGI status ('+env['SCRIPT_NAME']+')</h1>'
     yield 'masterpid: <b>' + str(uwsgi.masterpid()) + '</b><br/>'
 
     yield 'started on: <b>' + time.ctime(uwsgi.started_on) + '</b><br/>'
@@ -135,7 +149,7 @@
     yield '<table border="1">'
     yield '<th>worker id</th><th>pid</th><th>in request</th><th>requests</th><th>running time</th><th>address space</th><th>rss</th>'
 
-    workers = uwsgi.workers();
+    workers = uwsgi.workers()
 
     yield '<h2>workers</h2>'
 
@@ -151,18 +165,24 @@
     #yield out
     #print("FATTOfattoFATTO")
 
+
 def remotemako(env, start_response):
     start_response('200 OK', [('Content-Type', 'text/html')])
-    clusters = (    ('192.168.173.5', 3431, [0,3000] ),
+    clusters = (
+        ('192.168.173.5', 3431, [0, 3000]),
             ('192.168.173.5', 3432, [3001, 6000] ),
             ('192.168.173.5', 3433, [6001, 9000] ),
             ('192.168.173.5', 3434, [9001, 12000] ),
             ('192.168.173.5', 3435, [12001, 15000] )
-        );
+    )
     print(clusters)
-    all_values = uwsgi.send_multi_uwsgi_message(clusters, 33, 17, 40);
+    all_values = uwsgi.send_multi_uwsgi_message(clusters, 33, 17, 40)
     print(all_values)
-    return mako('makotest.txt', {'whattimeisit':time.time(), 'roberta':'serena', 'cluster_values': all_values})
+    return mako('makotest.txt', {
+        'whattimeisit': time.time(),
+        'roberta': 'serena',
+        'cluster_values': all_values
+    })
 
 
 uwsgi.fastfuncs.insert(10, gomako)
@@ -172,7 +192,10 @@
 #djangoapp = django.core.handlers.wsgi.WSGIHandler()
 
 #applications = { '/':django.core.handlers.wsgi.WSGIHandler() }
-uwsgi.applications = { '/':reload, '/pippo':reload }
+uwsgi.applications = {
+    '/': reload,
+    '/pippo': reload
+}
 
 print(uwsgi.applications)
 print(uwsgi.applist)
diff -NruwB uwsgi-2.0.28-9/tests/testworkers.py uwsgi-2.0.29-1/tests/testworkers.py
--- uwsgi-2.0.28-9/tests/testworkers.py	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/testworkers.py	2025-05-21 11:50:57.371560673 +0200
@@ -10,7 +10,10 @@
     start_objs = len(gc.get_objects())
 
     for i in range(200):
-        uwsgi.workers()
+        workers = uwsgi.workers()
+        assert workers, "none/empty uwsgi.workers() - " + repr(workers)
+        for w in workers:
+            assert w["apps"], "none/empty apps in worker dict: " + repr(w)
 
     gc.collect()
     end_objs = len(gc.get_objects())
@@ -19,7 +22,7 @@
     # Sometimes there is a spurious diff of 4 objects or so.
     if diff_objs > 10:
         start_response('500 Leaking', [('Content-Type', 'text/plain')])
-        yield "Leaking objects...\n".format(diff_objs).encode("utf-8")
+        yield "Leaking objects...\n".encode("utf-8")
     else:
         start_response('200 OK', [('Content-Type', 'text/plain')])
 
diff -NruwB uwsgi-2.0.28-9/tests/testyieldnone.py uwsgi-2.0.29-1/tests/testyieldnone.py
--- uwsgi-2.0.28-9/tests/testyieldnone.py	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/testyieldnone.py	1970-01-01 01:00:00.000000000 +0100
@@ -1,13 +0,0 @@
-"""
-Regression test for #2185.
-
-This started to rightfully log the following line:
-
-    [ERROR] Unhandled object from iterator: None (0x7f7e552eac30)
-
-But in 2.0.18 this was silently swallowed and that's the expectation
-for 2.0.19 as well.
-"""
-def application(env, start_response):
-    start_response('200', [])
-    yield None
diff -NruwB uwsgi-2.0.28-9/tests/threads.py uwsgi-2.0.29-1/tests/threads.py
--- uwsgi-2.0.28-9/tests/threads.py	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/tests/threads.py	2025-05-21 11:50:57.371560673 +0200
@@ -23,11 +26,12 @@
 		print("5 seconds elapsed")
 		#reload_module(fake)
     
+
 def spawn_my_magic_threads():
 	print("^^^ spawning magic threads ^^^")
-	t = threading.Thread(target=monitor1).start()
-	t2 = threading.Thread(target=monitor2).start()
-	t3 = threading.Thread(target=monitor3).start()
+    threading.Thread(target=monitor1).start()
+    threading.Thread(target=monitor2).start()
+    threading.Thread(target=monitor3).start()
 
 uwsgi.post_fork_hook = spawn_my_magic_threads
 
diff -NruwB uwsgi-2.0.28-9/tests/travis.sh uwsgi-2.0.29-1/tests/travis.sh
--- uwsgi-2.0.28-9/tests/travis.sh	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/travis.sh	2025-05-21 11:50:57.371560673 +0200
@@ -45,17 +45,10 @@
         if [ $RET != 0 ]; then
             die "${bldred}>>> Error during curl run${txtrst}"
             ERROR=$((ERROR+1))
-        elif fgrep '[ERROR]' uwsgi.log > /dev/null ; then
-            # Blindly report any [ERROR] string in the log file as failure.
-            # This obviously won't work when for tests purposely generating
-            # [ERROR] messages.
-            die "${bldred}>>> ERROR in uwsgi.log!${txtrst}"
-            ERROR=$((ERROR+1))
         else
-            die "${bldyel}>>> SUCCESS: Done${txtrst}"
             SUCCESS=$((SUCCESS+1))
         fi
-
+        die "${bldyel}>>> SUCCESS: Done${txtrst}"
     else
         die "${bldred}>>> ERROR: uWSGI did not start${txtrst}"
         ERROR=$((ERROR+1))
@@ -109,7 +102,7 @@
 
 
 while read PV ; do
-    for WSGI_FILE in tests/staticfile.py tests/testworkers.py tests/testrpc.py tests/testyieldnone.py ; do
+    for WSGI_FILE in tests/staticfile.py tests/testworkers.py tests/testrpc.py ; do
         test_python $PV $WSGI_FILE
     done
 done < <(cat "$CI_CONFIG" | grep "plugins/python base" | sed s_".*plugins/python base "_""_g)
diff -NruwB uwsgi-2.0.28-9/tests/ugevent.py uwsgi-2.0.29-1/tests/ugevent.py
--- uwsgi-2.0.28-9/tests/ugevent.py	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/tests/ugevent.py	2025-05-21 11:50:57.371560673 +0200
@@ -2,7 +2,7 @@
 import gevent.socket
 import sys
 import uwsgi
-from uwsgidecorators import *
+from uwsgidecorators import timer, signal, filemon
 
 if 'gettotalrefcount' in sys.__dict__:
     REFCNT = True
diff -NruwB uwsgi-2.0.28-9/tests/websockets_chat_2.py uwsgi-2.0.29-1/tests/websockets_chat_2.py
--- uwsgi-2.0.28-9/tests/websockets_chat_2.py	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/tests/websockets_chat_2.py	2025-05-21 11:50:57.371560673 +0200
@@ -0,0 +1,145 @@
+#!./uwsgi --http-socket :9090  --http-raw-body --gevent 100 --module tests.websockets_chat_2
+import uwsgi
+import time
+
+import gevent
+from gevent.queue import Queue
+
+class ClientManager(object):
+    clients = set()
+
+    @classmethod
+    def add(cls, client):
+        cls.clients.add(client)
+
+    @classmethod
+    def remove(cls, client):
+        cls.clients.remove(client)
+
+    @classmethod
+    def count(cls):
+        return len(cls.clients)
+
+    @classmethod
+    def broadcast(cls, data):
+        data = "{0} {1}".format(time.time(), data)
+        def do_broadcast():
+            for c in cls.clients:
+                c.send(data)
+
+        gevent.spawn(do_broadcast)
+
+
+class Client(object):
+    def __init__(self):
+        self.ctx = None
+        self.send_queue = Queue()
+        self.jobs = []
+
+
+    def _recv_job(self):
+        while True:
+            data = uwsgi.websocket_recv(request_context=self.ctx)
+            self.on_data(data)
+
+    def _send_job(self):
+        while True:
+            data = self.send_queue.get()
+            uwsgi.websocket_send(data, request_context=self.ctx)
+
+    def _exit(self, *args):
+        for j in self.jobs:
+            j.unlink(self._exit)
+
+        gevent.killall(self.jobs)
+        ClientManager.remove(self)
+        self.on_exit()
+
+
+    def on_data(self, data):
+        print "GOT: {0}".format(data)
+        ClientManager.broadcast(data)
+
+
+    def on_exit(self):
+        print "bye bye..."
+
+
+    def send(self, data):
+        self.send_queue.put(data)
+
+
+    def start(self):
+        uwsgi.websocket_handshake()
+        self.ctx = uwsgi.request_context()
+
+        ClientManager.add(self)
+
+        self.jobs.extend([
+            gevent.spawn(self._recv_job),
+            gevent.spawn(self._send_job),
+        ])
+
+        for j in self.jobs:
+            j.link(self._exit)
+
+        gevent.joinall(self.jobs)
+
+
+
+
+def application(env, sr):
+
+    ws_scheme = 'ws'
+    if 'HTTPS' in env or env['wsgi.url_scheme'] == 'https':
+        ws_scheme = 'wss'
+
+    if env['PATH_INFO'] == '/':
+        sr('200 OK', [('Content-Type', 'text/html')])
+        return """
+    <html>
+      <head>
+          <script language="Javascript">
+            var s = new WebSocket("%s://%s/foobar/");
+            s.onopen = function() {
+              alert("connected !!!");
+              s.send("ciao");
+            };
+            s.onmessage = function(e) {
+                var bb = document.getElementById('blackboard')
+                var html = bb.innerHTML;
+                bb.innerHTML = html + '<br/>' + e.data;
+            };
+
+            s.onerror = function(e) {
+                        alert(e);
+                }
+
+        s.onclose = function(e) {
+                alert("connection closed");
+        }
+
+            function invia() {
+              var value = document.getElementById('testo').value;
+              s.send(value);
+            }
+          </script>
+     </head>
+    <body>
+        <h1>WebSocket</h1>
+        <input type="text" id="testo"/>
+        <input type="button" value="invia" onClick="invia();"/>
+        <div id="blackboard" style="width:640px;height:480px;background-color:black;color:white;border: solid 2px red;overflow:auto">
+        </div>
+    </body>
+    </html>
+        """ % (ws_scheme, env['HTTP_HOST'])
+    elif env['PATH_INFO'] == '/favicon.ico':
+        return ""
+    elif env['PATH_INFO'] == '/foobar/':
+        print "websockets..."
+        client = Client()
+        client.start()
+
+        return ""
+
diff -NruwB uwsgi-2.0.28-9/tests/websockets_chat_async.lua uwsgi-2.0.29-1/tests/websockets_chat_async.lua
--- uwsgi-2.0.28-9/tests/websockets_chat_async.lua	1970-01-01 01:00:00.000000000 +0100
+++ uwsgi-2.0.29-1/tests/websockets_chat_async.lua	2025-05-21 11:50:57.371560673 +0200
@@ -0,0 +1,108 @@
+#!./uwsgi --http :9090 --http-modifier1 6 --http-raw-body --async 256 --ugreen --master --lua tests/websockets_chat_async.lua
+
+-- Same worker = Same luaState = Same chat room
+
+local PAGE_STATIC = [[<html>
+<head>
+  <script language="Javascript">
+    var s = new WebSocket("%s://%s/foobar/", ["chat","foo","bar"]);
+    s.onopen = function() {
+      alert("connected !!!");
+      s.send("ciao");
+    };
+    s.onmessage = function(e) {
+var bb = document.getElementById('blackboard')
+var html = bb.innerHTML;
+bb.innerHTML = html + '<br/>' + e.data;
+    };
+
+s.onerror = function(e) {
+    alert(e);
+}
+
+s.onclose = function(e) {
+alert("connection closed");
+}
+
+    function invia() {
+      var value = document.getElementById('testo').value;
+      s.send(value);
+    }
+  </script>
+</head>
+<body>
+<h1>WebSocket</h1>
+<input type="text" id="testo"/>
+<input type="button" value="invia" onClick="invia();"/>
+<div id="blackboard" style="width:640px;height:480px;background-color:black;color:white;border: solid 2px red;overflow:auto">
+</div>
+</body>
+</html>
+]];
+
+local PAGE_STATIC_HEADERS = { ["Content-type"] = "text/html" };
+
+local MSG_FORMAT = "[%s][%s]: %s";
+local MSG_DATE_FORMAT = "%H:%M:%S";
+
+local subs = {};
+
+local send_to_subs = function(msg)
+    uwsgi.log(msg);
+
+    for id, handler in next, subs do
+        if not handler:send(msg) then
+            subs[id] = nil;
+        end
+    end
+end
+
+local say = function(who, msg)
+    if msg:len() > 0 then
+        send_to_subs(string.format(MSG_FORMAT, os.date(MSG_DATE_FORMAT), who or "System", msg));
+    end
+end
+
+local loop = function(my_name)
+    local wait_fd = uwsgi.connection_fd();
+
+    while true do
+        uwsgi.wait_fd_read(wait_fd, 30); -- 2th arg for ping/pong
+        coroutine.yield();
+        say(my_name, uwsgi.websocket_recv_nb());
+    end
+end
+
+local gogo_websockets = function(env)
+
+    uwsgi.websocket_handshake(nil, nil, "chat");
+
+    local handler = assert(uwsgi.websocket_handler(), "no handler");
+    local id = assert(handler:async_id(), "handler is dead");
+
+    subs[id] = handler; -- add to subs
+
+    local my_name = "User" .. id;
+
+    say(nil, my_name .. " Has been Connected"); -- say hallo
+
+    pcall(loop, my_name); -- start listen from user
+
+    subs[id] = nil; -- remove from subs
+
+    say(nil, my_name .. " Has been Disconnected"); -- say bye
+
+end
+
+return function(env)
+
+    if env['PATH_INFO'] == '/' then
+        return "200", PAGE_STATIC_HEADERS, string.format(PAGE_STATIC, env['HTTPS'] and 'wss' or 'ws', env['HTTP_HOST']);
+    end
+
+    if env['PATH_INFO'] == '/foobar/' then
+        return nil, nil, coroutine.wrap(gogo_websockets), env;
+    end
+
+    return "404";
+end
diff -NruwB uwsgi-2.0.28-9/tests/websockets_chat.pl uwsgi-2.0.29-1/tests/websockets_chat.pl
--- uwsgi-2.0.28-9/tests/websockets_chat.pl	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/websockets_chat.pl	2025-05-21 11:50:57.371560673 +0200
@@ -66,7 +66,7 @@
 		# when defined there is a redis message available
 		my $redis_message = undef;
 
-		# do the hadnshake
+		# do the handshake
 		uwsgi::websocket_handshake($env->{'HTTP_SEC_WEBSOCKET_KEY'}, $env->{'HTTP_ORIGIN'});
         	print "websockets...\n" ;
 
diff -NruwB uwsgi-2.0.28-9/tests/websockets.py uwsgi-2.0.29-1/tests/websockets.py
--- uwsgi-2.0.28-9/tests/websockets.py	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/tests/websockets.py	2025-05-21 11:50:57.371560673 +0200
@@ -66,6 +67,6 @@
                     try:
                         msg = queue.get_nowait()
                         uwsgi.websocket_send(msg)
-                    except:
+                    except Exception:
                         pass
     return ""
diff -NruwB uwsgi-2.0.28-9/tests/werkzeug_app.py uwsgi-2.0.29-1/tests/werkzeug_app.py
--- uwsgi-2.0.28-9/tests/werkzeug_app.py	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/tests/werkzeug_app.py	2025-05-21 11:50:57.371560673 +0200
@@ -2,4 +2,4 @@
 
 print(uwsgi.opt)
 print(uwsgi.magic_table)
-from werkzeug.testapp import test_app as application
+from werkzeug.testapp import test_app as application  # NOQA
diff -NruwB uwsgi-2.0.28-9/uwsgiconfig.py uwsgi-2.0.29-1/uwsgiconfig.py
--- uwsgi-2.0.28-9/uwsgiconfig.py	2025-05-21 11:51:19.307985685 +0200
+++ uwsgi-2.0.29-1/uwsgiconfig.py	2025-05-21 11:51:54.812673591 +0200
@@ -1,6 +1,6 @@
 # uWSGI build system
 
-uwsgi_version = '2.0.28'
+uwsgi_version = '2.0.29'
 
 import os
 import re
@@ -1467,7 +1467,7 @@
             gcc_list.append(path + '/' + cfile)
     for bfile in up.get('BINARY_LIST', []):
         try:
-            binary_link_cmd = "ld -z noexecstack -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1])
+            binary_link_cmd = "ld -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1])
             print(binary_link_cmd)
             if subprocess.call(binary_link_cmd, shell=True) != 0:
                 raise Exception('unable to link binary file')
diff -NruwB uwsgi-2.0.28-9/uwsgi.gemspec uwsgi-2.0.29-1/uwsgi.gemspec
--- uwsgi-2.0.28-9/uwsgi.gemspec	2025-05-21 11:51:19.291985376 +0200
+++ uwsgi-2.0.29-1/uwsgi.gemspec	2025-05-21 11:50:57.371560673 +0200
@@ -1,8 +1,8 @@
 Gem::Specification.new do |s|
   s.name        = 'uwsgi'
   s.license     = 'GPL-2'
-  s.version     = `python3 -c "import uwsgiconfig as uc; print uc.uwsgi_version"`.sub(/-dev-.*/,'')
-  s.date        = '2024-10-26'
+  s.version     = `python -c "import uwsgiconfig as uc; print uc.uwsgi_version"`.sub(/-dev-.*/,'')
+  s.date        = '2025-04-11'
   s.summary     = "uWSGI"
   s.description = "The uWSGI server for Ruby/Rack"
   s.authors     = ["Unbit"]
diff -NruwB uwsgi-2.0.28-9/uwsgi.h uwsgi-2.0.29-1/uwsgi.h
--- uwsgi-2.0.28-9/uwsgi.h	2025-05-21 11:51:12.883861214 +0200
+++ uwsgi-2.0.29-1/uwsgi.h	2025-05-21 11:50:57.371560673 +0200
@@ -2874,6 +2874,9 @@
 	// uWSGI 2.0.27
 	// This pipe is used to stop event_queue_wait() in threaded workers.
 	int loop_stop_pipe[2];
+
+	// uWSGI 2.0.29
+	uint64_t max_requests_delta;
 };
 
 struct uwsgi_rpc {

--- End Message ---
--- Begin Message ---
Hi,

On 21-05-2025 12:10, Alexandre Rossi wrote:
The new 2.0.29 upstream version diff seems big. However, this is because 8
patches were upstreamed. Attached is the source diff with patches applied
and ignoring whitespace changes.


This is still ridiculous to review:
 95 files changed, 1554 insertions(+), 2215 deletions(-)

Let's keep the version we have in trixie in trixie and the version we have in unstable in unstable and hope we don't run into issues.

Paul

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature


--- End Message ---

Reply to: