From 6944c28fb098c2b666fb3e5662d6612d446214a5 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 12 Jul 2018 10:06:11 +0200 Subject: [PATCH 001/207] plugins/redislog: fix redislog compilation on 32bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In function ‘uwsgi_redis_logger’: plugins/redislog/redislog_plugin.c:162:38: error: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 4 has type ‘size_t {aka unsigned int}’ [-Werror=format=] Fix #1828 --- plugins/redislog/redislog_plugin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/redislog/redislog_plugin.c b/plugins/redislog/redislog_plugin.c index 2669368fec..6c19eb75c9 100644 --- a/plugins/redislog/redislog_plugin.c +++ b/plugins/redislog/redislog_plugin.c @@ -159,7 +159,7 @@ ssize_t uwsgi_redis_logger(struct uwsgi_logger *ul, char *message, size_t len) { uredislog->fd = uwsgi_connect(uredislog->address, uwsgi.socket_timeout, 0); if (uredislog->password) { setup_iov.iov_len = snprintf( - setup_buf, sizeof (setup_buf), "*2\r\n$4\r\nauth\r\n$%lu\r\n%*s\r\n", + setup_buf, sizeof (setup_buf), "*2\r\n$4\r\nauth\r\n$%zu\r\n%*s\r\n", strlen(uredislog->password), (int)strlen(uredislog->password), uredislog->password); setup_iov.iov_base = setup_buf; ret = writev(uredislog->fd, &setup_iov, 1); @@ -172,7 +172,7 @@ ssize_t uwsgi_redis_logger(struct uwsgi_logger *ul, char *message, size_t len) { } if (uredislog->id) { setup_iov.iov_len = snprintf( - setup_buf, sizeof (setup_buf), "*2\r\n$6\r\nselect\r\n$%lu\r\n%*s\r\n", + setup_buf, sizeof (setup_buf), "*2\r\n$6\r\nselect\r\n$%zu\r\n%*s\r\n", strlen(uredislog->id), (int)strlen(uredislog->id), uredislog->id); setup_iov.iov_base = setup_buf; ret = writev(uredislog->fd, &setup_iov, 1); From c0c119a2be8bce8962d7de2f5757f4d35d118832 Mon Sep 17 00:00:00 2001 From: Timi Date: Thu, 16 Aug 2018 13:43:15 +0200 Subject: [PATCH 002/207] Added support for continuation frames --- core/websockets.c | 37 ++++++++++++++++++++++++++++++++++++- uwsgi.h | 2 ++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/core/websockets.c b/core/websockets.c index 60ef20c185..8f8bce42fd 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -144,6 +144,7 @@ int uwsgi_websocket_send_binary_from_sharedarea(struct wsgi_request *wsgi_req, i static void uwsgi_websocket_parse_header(struct wsgi_request *wsgi_req) { uint8_t byte1 = wsgi_req->websocket_buf->buf[0]; uint8_t byte2 = wsgi_req->websocket_buf->buf[1]; + wsgi_req->websocket_is_fin = byte1 >> 7; wsgi_req->websocket_opcode = byte1 & 0xf; wsgi_req->websocket_has_mask = byte2 >> 7; wsgi_req->websocket_size = byte2 & 0x7f; @@ -161,14 +162,38 @@ static struct uwsgi_buffer *uwsgi_websockets_parse(struct wsgi_request *wsgi_req } } - struct uwsgi_buffer *ub = uwsgi_buffer_new(wsgi_req->websocket_size); + struct uwsgi_buffer *ub = NULL; + if (wsgi_req->websocket_opcode == 0) { + if (uwsgi.websockets_continuation_buffer == NULL) { + uwsgi_log("Error continuation with empty previous buffer"); + goto error; + } + ub = uwsgi.websockets_continuation_buffer; + } + else { + ub = uwsgi_buffer_new(wsgi_req->websocket_size); + } if (uwsgi_buffer_append(ub, (char *) ptr, wsgi_req->websocket_size)) goto error; if (uwsgi_buffer_decapitate(wsgi_req->websocket_buf, wsgi_req->websocket_pktsize)) goto error; wsgi_req->websocket_phase = 0; wsgi_req->websocket_need = 2; + + if (wsgi_req->websocket_is_fin) { + uwsgi.websockets_continuation_buffer = NULL; + /// Freeing websockets_continuation_buffer is done by the caller + return ub; + } + uwsgi.websockets_continuation_buffer = ub; + /// Message is not complete, send empty dummy buffer to signal waiting for full message + ub = uwsgi_buffer_new(1); + uwsgi_buffer_append(ub, "\0", 1); return ub; error: uwsgi_buffer_destroy(ub); + if (uwsgi.websockets_continuation_buffer != NULL && ub != uwsgi.websockets_continuation_buffer) { + uwsgi_buffer_destroy(uwsgi.websockets_continuation_buffer); + } + uwsgi.websockets_continuation_buffer = NULL; return NULL; } @@ -338,12 +363,20 @@ static struct uwsgi_buffer *uwsgi_websocket_recv_do(struct wsgi_request *wsgi_re return NULL; } +static void clear_continuation_buffer() { + if (uwsgi.websockets_continuation_buffer != NULL) { + uwsgi_buffer_destroy(uwsgi.websockets_continuation_buffer); + uwsgi.websockets_continuation_buffer = NULL; + } +} + struct uwsgi_buffer *uwsgi_websocket_recv(struct wsgi_request *wsgi_req) { if (wsgi_req->websocket_closed) { return NULL; } struct uwsgi_buffer *ub = uwsgi_websocket_recv_do(wsgi_req, 0); if (!ub) { + clear_continuation_buffer(); wsgi_req->websocket_closed = 1; } return ub; @@ -355,6 +388,7 @@ struct uwsgi_buffer *uwsgi_websocket_recv_nb(struct wsgi_request *wsgi_req) { } struct uwsgi_buffer *ub = uwsgi_websocket_recv_do(wsgi_req, 1); if (!ub) { + clear_continuation_buffer(); wsgi_req->websocket_closed = 1; } return ub; @@ -432,4 +466,5 @@ void uwsgi_websockets_init() { uwsgi.websockets_ping_freq = 30; uwsgi.websockets_pong_tolerance = 3; uwsgi.websockets_max_size = 1024; + uwsgi.websockets_continuation_buffer = NULL; } diff --git a/uwsgi.h b/uwsgi.h index 540ee3c7f3..863ff613d5 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -1569,6 +1569,7 @@ struct wsgi_request { size_t websocket_need; int websocket_phase; uint8_t websocket_opcode; + uint8_t websocket_is_fin; size_t websocket_has_mask; size_t websocket_size; size_t websocket_pktsize; @@ -2765,6 +2766,7 @@ struct uwsgi_server { struct uwsgi_buffer *websockets_ping; struct uwsgi_buffer *websockets_pong; + struct uwsgi_buffer *websockets_continuation_buffer; int websockets_ping_freq; int websockets_pong_tolerance; uint64_t websockets_max_size; From 1474eb878679a89f900fa84eda5d9329d6b81391 Mon Sep 17 00:00:00 2001 From: Timi Date: Thu, 16 Aug 2018 14:06:24 +0000 Subject: [PATCH 003/207] Fixed indent and moved members --- core/websockets.c | 2 +- uwsgi.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/websockets.c b/core/websockets.c index 8f8bce42fd..0b9b034329 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -186,7 +186,7 @@ static struct uwsgi_buffer *uwsgi_websockets_parse(struct wsgi_request *wsgi_req uwsgi.websockets_continuation_buffer = ub; /// Message is not complete, send empty dummy buffer to signal waiting for full message ub = uwsgi_buffer_new(1); - uwsgi_buffer_append(ub, "\0", 1); + uwsgi_buffer_append(ub, "\0", 1); return ub; error: uwsgi_buffer_destroy(ub); diff --git a/uwsgi.h b/uwsgi.h index 863ff613d5..9a84a2c1e1 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -1569,7 +1569,6 @@ struct wsgi_request { size_t websocket_need; int websocket_phase; uint8_t websocket_opcode; - uint8_t websocket_is_fin; size_t websocket_has_mask; size_t websocket_size; size_t websocket_pktsize; @@ -1649,6 +1648,8 @@ struct wsgi_request { char * if_range; uint16_t if_range_len; + + uint8_t websocket_is_fin; }; @@ -2766,7 +2767,6 @@ struct uwsgi_server { struct uwsgi_buffer *websockets_ping; struct uwsgi_buffer *websockets_pong; - struct uwsgi_buffer *websockets_continuation_buffer; int websockets_ping_freq; int websockets_pong_tolerance; uint64_t websockets_max_size; @@ -2920,6 +2920,8 @@ struct uwsgi_server { size_t environ_len; int dynamic_apps; + + struct uwsgi_buffer *websockets_continuation_buffer; }; struct uwsgi_rpc { From 98aace4c6467ddef48ad05b910888a07492e28b6 Mon Sep 17 00:00:00 2001 From: Jacek Grzechnik Date: Tue, 21 Aug 2018 12:00:57 +0200 Subject: [PATCH 004/207] Disable logrotate when logging into UDP socket If host:port is specified in logto parameter instead of a file, the destination log receiver(fluentd -> elasticsearch in my case) is flooded with messages: uwsgi_check_logrotate()/lseek(): Illegal seek [core/logging.c line 494] This commit disables log file related checks if log file descriptor is a socket. --- core/logging.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/logging.c b/core/logging.c index 9afdc0b730..d997df72b1 100644 --- a/core/logging.c +++ b/core/logging.c @@ -502,6 +502,10 @@ void uwsgi_check_logrotate(void) { return; } + if (logstat.st_mode & S_IFSOCK) { + return; + } + logsize = lseek(logfd, 0, SEEK_CUR); if (logsize < 0) { uwsgi_error("uwsgi_check_logrotate()/lseek()"); From b6308cae818dab78da5f51eae8c903b6e2122b7a Mon Sep 17 00:00:00 2001 From: Unbit Date: Thu, 7 Mar 2019 19:13:25 +0100 Subject: [PATCH 005/207] implemented #670 --- plugins/python/python_plugin.c | 3 +++ plugins/python/pyutils.c | 6 ++++-- plugins/python/uwsgi_python.h | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 25657986f9..698bdf212a 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -207,6 +207,9 @@ struct uwsgi_option uwsgi_python_options[] = { {"py-master-check-signals", no_argument, 0, "enable python signal handlers in master", uwsgi_opt_true, &up.master_check_signals, 0}, + {"py-executable", required_argument, 0, "override sys.executable value", uwsgi_opt_set_str, &up.executable, 0}, + {"py-sys-executable", required_argument, 0, "override sys.executable value", uwsgi_opt_set_str, &up.executable, 0}, + {0, 0, 0, 0, 0, 0, 0}, }; diff --git a/plugins/python/pyutils.c b/plugins/python/pyutils.c index 4c984ce82f..8a3da0438b 100644 --- a/plugins/python/pyutils.c +++ b/plugins/python/pyutils.c @@ -395,10 +395,12 @@ void init_pyargv() { uwsgi_log("unable to load python sys module !!!\n"); exit(1); } + if (!up.executable) + up.executable = uwsgi.binary_path; #ifdef PYTHREE - PyDict_SetItemString(sys_dict, "executable", PyUnicode_FromString(uwsgi.binary_path)); + PyDict_SetItemString(sys_dict, "executable", PyUnicode_FromString(up.executable)); #else - PyDict_SetItemString(sys_dict, "executable", PyString_FromString(uwsgi.binary_path)); + PyDict_SetItemString(sys_dict, "executable", PyString_FromString(up.executable)); #endif diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 1d7d1caa11..e97c0bab60 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -232,6 +232,8 @@ struct uwsgi_python { int wsgi_manage_chunked_input; int master_check_signals; + + char *executable; }; From 300f230491871ec9e23b5891898cf47e7dcbb5cb Mon Sep 17 00:00:00 2001 From: Unbit Date: Mon, 11 Mar 2019 14:55:51 +0100 Subject: [PATCH 006/207] Update README --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index 46bea5dc17..a5a521696f 100644 --- a/README +++ b/README @@ -10,3 +10,4 @@ http://unbit.com https://www.pythonanywhere.com/ https://lincolnloop.com/ https://yourlabs.io/oss +https://fili.com From d5d6beba7860f3dbbf3f5c16d0cd468666bb4eba Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 1 May 2019 12:44:48 -0700 Subject: [PATCH 007/207] core/uwsgi: Fix compilation with uClibc-ng uClibc-ng by default does not compile in execinfo support. --- core/uwsgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/uwsgi.c b/core/uwsgi.c index c7ff72e967..55964e0b4d 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1889,7 +1889,7 @@ void uwsgi_plugins_atexit(void) { void uwsgi_backtrace(int depth) { -#if defined(__GLIBC__) || (defined(__APPLE__) && !defined(NO_EXECINFO)) || defined(UWSGI_HAS_EXECINFO) +#if (!defined(__UCLIBC__) && defined(__GLIBC__)) || (defined(__APPLE__) && !defined(NO_EXECINFO)) || defined(UWSGI_HAS_EXECINFO) #include From 9b2f96977cf7b9bc2075a3a432fa03ff9b7525d7 Mon Sep 17 00:00:00 2001 From: "Terence D. Honles" Date: Tue, 7 May 2019 18:18:41 -0700 Subject: [PATCH 008/207] fix travis - build on xenial rather than trusty - trusty is EOL and the default is going to change to xenial anyways. See: https://blog.travis-ci.com/2019-04-15-xenial-default-build-environment - PPA "ppa:ondrej/php" does not support EOL Ubuntu (trusty) and PHP packages are not found anymore. See: https://launchpad.net/~ondrej/+archive/ubuntu/php - update to jdk-8 instead of using outdated PPA "ppa:openjdk-r/ppa" Last release was in 2016, See: https://launchpad.net/~openjdk-r/+archive/ubuntu/ppa?field.series_filter=xenial --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ece46d63a6..c4e9c75867 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +dist: xenial + language: c compiler: @@ -35,6 +37,8 @@ script: - ./tests/travis.sh before_install: + - sudo apt-get update -qq + - sudo apt-get install -qqyf software-properties-common - sudo add-apt-repository ppa:deadsnakes/ppa -y - sudo add-apt-repository ppa:ondrej/php -y - sudo apt-get update -qq @@ -49,6 +53,6 @@ before_install: - sudo apt-get install -qqyf libwrap0-dev libgeoip-dev libv8-dev libxslt1-dev - sudo apt-get install -qqyf libboost-thread-dev libboost-filesystem-dev - sudo apt-get install -qqyf libssl-dev libacl1-dev python-greenlet-dev - - sudo apt-get install -qqyf openjdk-7-jdk libgloox-dev gccgo + - sudo apt-get install -qqyf openjdk-8-jdk libgloox-dev gccgo - sudo apt-get install -qqyf cli-common-dev mono-devel mono-mcs uuid-dev - sudo apt-get install -qqyf curl From d642e635b3d558ce91e80442c74f4d16b9d81146 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 12 May 2019 12:14:34 +0200 Subject: [PATCH 009/207] plugins/logsocket: always initialize ul->count Otherwise in case there's no ul->data we segfault. Fix #2010 --- plugins/logsocket/logsocket_plugin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/logsocket/logsocket_plugin.c b/plugins/logsocket/logsocket_plugin.c index a603b3adc8..9ac8f456b6 100644 --- a/plugins/logsocket/logsocket_plugin.c +++ b/plugins/logsocket/logsocket_plugin.c @@ -43,6 +43,7 @@ ssize_t uwsgi_socket_logger(struct uwsgi_logger *ul, char *message, size_t len) else { ul->msg.msg_iov = uwsgi_malloc(sizeof(struct iovec)); ul->msg.msg_iovlen = 1; + ul->count = 0; } if (comma) { From 9d6f81d5280c743582dd1d87de5582e1ec179a1a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 26 Jun 2019 15:11:36 +0200 Subject: [PATCH 010/207] check: fixup uwsgi_strncmp tests --- check/check_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check/check_core.c b/check/check_core.c index 14d0b16ac6..6efc40677b 100644 --- a/check/check_core.c +++ b/check/check_core.c @@ -15,10 +15,10 @@ START_TEST(test_uwsgi_strncmp) ck_assert(result == 1); result = uwsgi_strncmp("aaa", 3, "bbb", 3); - ck_assert_msg(result == -1, "result: %d", result); + ck_assert_msg(result < 0, "result: %d", result); result = uwsgi_strncmp("bbb", 3, "aaa", 3); - ck_assert_msg(result == 1, "result: %d", result); + ck_assert_msg(result > 0, "result: %d", result); } END_TEST From 65d982e28c98da8aa9db7d4664603f96986e5fa7 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 26 Jun 2019 15:12:43 +0200 Subject: [PATCH 011/207] check: add basic tests for uwsgi_opt_set_int --- check/check_core.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/check/check_core.c b/check/check_core.c index 6efc40677b..75dda7e70b 100644 --- a/check/check_core.c +++ b/check/check_core.c @@ -32,11 +32,38 @@ Suite *check_core_strings(void) return s; } +START_TEST(test_uwsgi_opt_set_int) +{ + int result; + uwsgi_opt_set_int("", "true", &result); + ck_assert(result == 0); + + uwsgi_opt_set_int("", "false", &result); + ck_assert(result == 0); + + uwsgi_opt_set_int("", "0", &result); + ck_assert(result == 0); + + uwsgi_opt_set_int("", "60", &result); + ck_assert(result == 60); +} +END_TEST + +Suite *check_core_opt_parsing(void) +{ + Suite *s = suite_create("uwsgi opt parsing"); + TCase *tc = tcase_create("opt_parsing"); + + suite_add_tcase(s, tc); + tcase_add_test(tc, test_uwsgi_opt_set_int); + return s; +} + int main(void) { int nf; - Suite *s = check_core_strings(); - SRunner *r = srunner_create(s); + SRunner *r = srunner_create(check_core_strings()); + srunner_add_suite(r, check_core_opt_parsing()); srunner_run_all(r, CK_NORMAL); nf = srunner_ntests_failed(r); srunner_free(r); From d856a6b99875c949e7a962a1b6666b068599913a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 26 Jun 2019 15:13:28 +0200 Subject: [PATCH 012/207] check: link against jansson if available --- check/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/check/Makefile b/check/Makefile index 0f0b447865..14a7e109b0 100644 --- a/check/Makefile +++ b/check/Makefile @@ -5,6 +5,7 @@ LDFLAGS += -ldl -lz LDFLAGS += $(shell xml2-config --libs) LDFLAGS += $(shell pkg-config --libs openssl) LDFLAGS += $(shell pcre-config --libs) +LDFLAGS += $(shell pkg-config --libs jansson) objects = check_core From 7c699d1ef8ba79e69a1990a60cdc833bc8c2eb57 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 26 Jun 2019 15:22:19 +0200 Subject: [PATCH 013/207] uwsgiconfig: avoid gnu or linux specifics for check config As I don't want to go grazy with ifdefery in check/Makefile. --- uwsgiconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 51348320e0..6558060ed6 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1130,12 +1130,12 @@ def get_gcll(self): self.cflags.append("-DUWSGI_ROUTING") report['routing'] = True - if self.has_include('sys/capability.h') and uwsgi_os == 'Linux': + if self.has_include('sys/capability.h') and uwsgi_os == 'Linux' and not self.get('check'): self.cflags.append("-DUWSGI_CAP") self.libs.append('-lcap') report['capabilities'] = True - if self.has_include('uuid/uuid.h'): + if self.has_include('uuid/uuid.h') and not self.get('check'): self.cflags.append("-DUWSGI_UUID") if uwsgi_os in ('Linux', 'GNU', 'GNU/kFreeBSD') or uwsgi_os.startswith('CYGWIN') or os.path.exists('/usr/lib/libuuid.so') or os.path.exists('/usr/local/lib/libuuid.so') or os.path.exists('/usr/lib64/libuuid.so') or os.path.exists('/usr/local/lib64/libuuid.so'): self.libs.append('-luuid') From d7af6345cd69655032352359f9ec1a5e5b7f1298 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 26 Jun 2019 15:33:54 +0200 Subject: [PATCH 014/207] travis: run unit tests --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c4e9c75867..330f3ed8c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,8 @@ compiler: - gcc script: + - echo -e "\n\n>>> Running uWSGI unit tests" + - make tests - echo -e "\n\n>>> Building kitchensink uWSGI binary" - UWSGICONFIG_PHPPATH=php-config7.2 /usr/bin/python uwsgiconfig.py --build travis - echo -e "\n\n>>> Building uWSGI binary" @@ -55,4 +57,4 @@ before_install: - sudo apt-get install -qqyf libssl-dev libacl1-dev python-greenlet-dev - sudo apt-get install -qqyf openjdk-8-jdk libgloox-dev gccgo - sudo apt-get install -qqyf cli-common-dev mono-devel mono-mcs uuid-dev - - sudo apt-get install -qqyf curl + - sudo apt-get install -qqyf curl check From fcaeb887b21d9050a3a3995add5e195d72db9ff8 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sun, 23 Jun 2019 18:23:52 +0200 Subject: [PATCH 015/207] uwsgi_opt_set_int: use strtol() and warn on non-numeric data This should not change the existing behavior of using 0 when a non-numeric value is provided, but loudly logs: $ uwsgi test.ini --processes 2x [uWSGI] getting INI configuration from test.ini [WARNING] non-numeric value "2x" for option "processes" - using 2 ! [WARNING] non-numeric value "true" for option "http-keepalive" - using 0 ! *** Starting uWSGI 2.1-dev-ade7d170 (64bit) on [Sun Jun 23 18:36:29 2019] *** compiled with version: 8.3.0 on 23 June 2019 16:22:05 ... It might be saner to not even start with invalid options. However, hypothetical setups with http-keepalive=off or http-keepalive=false may then fail to start at all and people might get unahppy. --- core/uwsgi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/uwsgi.c b/core/uwsgi.c index 55964e0b4d..a6fbfd856d 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -4138,7 +4138,11 @@ void uwsgi_opt_safe_fd(char *opt, char *value, void *foobar) { void uwsgi_opt_set_int(char *opt, char *value, void *key) { int *ptr = (int *) key; if (value) { - *ptr = atoi((char *) value); + char *endptr; + *ptr = (int)strtol(value, &endptr, 10); + if (*endptr) { + uwsgi_log("[WARNING] non-numeric value \"%s\" for option \"%s\" - using %d !\n", value, opt, *ptr); + } } else { *ptr = 1; From bb74161d530449b6ce2f13ec7561d5d63a62e0fa Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Wed, 26 Jun 2019 19:31:52 +0200 Subject: [PATCH 016/207] check: add test for uwsgi_opt_set_int with when used with optional_argument --- check/check_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/check/check_core.c b/check/check_core.c index 75dda7e70b..35b88ce783 100644 --- a/check/check_core.c +++ b/check/check_core.c @@ -46,6 +46,10 @@ START_TEST(test_uwsgi_opt_set_int) uwsgi_opt_set_int("", "60", &result); ck_assert(result == 60); + + // When used with "optional_argument", value will be passed as NULL + uwsgi_opt_set_int("", NULL, &result); + ck_assert(result == 1); } END_TEST From 2491b9d76b3514992ec4b16dbfd3e3f9e4cf59cf Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sat, 29 Jun 2019 09:16:22 +0200 Subject: [PATCH 017/207] check: add proper ifdefery for linux capabilities So at least we can avoid an hack in uwsgiconfig.py --- check/Makefile | 5 +++++ uwsgiconfig.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/check/Makefile b/check/Makefile index 14a7e109b0..ac69b37891 100644 --- a/check/Makefile +++ b/check/Makefile @@ -7,6 +7,11 @@ LDFLAGS += $(shell pkg-config --libs openssl) LDFLAGS += $(shell pcre-config --libs) LDFLAGS += $(shell pkg-config --libs jansson) +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + LDFLAGS += -lcap +endif + objects = check_core diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 6558060ed6..210d9d016b 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1130,7 +1130,7 @@ def get_gcll(self): self.cflags.append("-DUWSGI_ROUTING") report['routing'] = True - if self.has_include('sys/capability.h') and uwsgi_os == 'Linux' and not self.get('check'): + if self.has_include('sys/capability.h') and uwsgi_os == 'Linux': self.cflags.append("-DUWSGI_CAP") self.libs.append('-lcap') report['capabilities'] = True From 79d14fbbf4a505ae6c70fd64e7b071206ca7ffeb Mon Sep 17 00:00:00 2001 From: Nick Wilkens Date: Wed, 17 Jul 2019 12:20:04 +0200 Subject: [PATCH 018/207] uwsgi: fixup name clash on solaris MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Solaris defines sun to 1 which results in a compilation error: ./uwsgi.h:1646:22: error: expected identifier or ‘(’ before numeric constant struct sockaddr_un sun; Fix #1933 --- uwsgi.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/uwsgi.h b/uwsgi.h index ba3986e794..3bf473bd0e 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -1368,6 +1368,9 @@ enum uwsgi_range { UWSGI_RANGE_INVALID, }; +// avoid name clashes on solaris +#undef sun + struct wsgi_request { int fd; struct uwsgi_header *uh; From 34cc53d0899f874ea1dc1d13d49c054ca1f1757c Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Thu, 18 Jul 2019 23:47:41 +0200 Subject: [PATCH 019/207] python: use consistent key type for opt dict manipulations - fixes #1374 PyString_FromString is defined as PyBytes_FromString in uwsgi_python.h What happens in Python 3 during the population of the opt_dict is that we first check if a byte object, representing the key is in the dict. If there is none, we use PyDict_SetItemString to set it. However, as the docs say, PyDict_SetItemString will convert the key using PyUnicode_FromString and we actually put a unicode key in the dict[1]. Therefore, when we check the "same" key again, we check again for the "same" key as bytes object we don't find it and end up overwriting it instead of doing the list promotion dance. Attached patch fixes this by using PyDict_SetItem and PyDict_GetItem with a consistent key type. For Python 3, a unicode object is used as key as this is the backwards compatible thing to do. Mini tester: import uwsgi def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) for k, v in uwsgi.opt.items(): yield "{} {!r} ({})\n".format(k, v, type(v)).encode("utf-8") yield "{} {}\n".format(uwsgi.version, type(uwsgi.version)).encode("utf-8") yield "{} {}\n".format(uwsgi.hostname, type(uwsgi.hostname)).encode("utf-8") yield b"END" What is a bit rough is that in Python 3 the actual values for uwsgi.opt entries end-up being all bytes, but that has always been like this... [1] https://docs.python.org/3.5/c-api/dict.html#c.PyDict_SetItemString --- plugins/python/python_plugin.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 698bdf212a..7977359132 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -819,8 +819,13 @@ void init_uwsgi_embedded_module() { PyObject *py_opt_dict = PyDict_New(); for (i = 0; i < uwsgi.exported_opts_cnt; i++) { - if (PyDict_Contains(py_opt_dict, PyString_FromString(uwsgi.exported_opts[i]->key))) { - PyObject *py_opt_item = PyDict_GetItemString(py_opt_dict, uwsgi.exported_opts[i]->key); +#ifdef PYTHREE + PyObject *key = PyUnicode_FromString(uwsgi.exported_opts[i]->key); +#else + PyObject *key = PyString_FromString(uwsgi.exported_opts[i]->key); +#endif + if (PyDict_Contains(py_opt_dict, key)) { + PyObject *py_opt_item = PyDict_GetItem(py_opt_dict, key); if (PyList_Check(py_opt_item)) { if (uwsgi.exported_opts[i]->value == NULL) { PyList_Append(py_opt_item, Py_True); @@ -839,15 +844,15 @@ void init_uwsgi_embedded_module() { PyList_Append(py_opt_list, PyString_FromString(uwsgi.exported_opts[i]->value)); } - PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, py_opt_list); + PyDict_SetItem(py_opt_dict, key, py_opt_list); } } else { if (uwsgi.exported_opts[i]->value == NULL) { - PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, Py_True); + PyDict_SetItem(py_opt_dict, key, Py_True); } else { - PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, PyString_FromString(uwsgi.exported_opts[i]->value)); + PyDict_SetItem(py_opt_dict, key, PyString_FromString(uwsgi.exported_opts[i]->value)); } } } From 341a848d86294bcb08a368d5590e80c9cdcd066e Mon Sep 17 00:00:00 2001 From: Adam Duskett Date: Wed, 7 Aug 2019 15:03:51 -0400 Subject: [PATCH 020/207] plugins/router_basicauth: Fix building with uClibc The struct crypt_data does not exist with uClibc, so a check must be put in place to make sure uClibc does not try to use this struct. --- plugins/router_basicauth/router_basicauth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/router_basicauth/router_basicauth.c b/plugins/router_basicauth/router_basicauth.c index 429bade659..0b7161ea60 100644 --- a/plugins/router_basicauth/router_basicauth.c +++ b/plugins/router_basicauth/router_basicauth.c @@ -3,7 +3,7 @@ #ifdef UWSGI_ROUTING // TODO: Add more crypt_r supported platfroms here -#if defined(__linux__) && defined(__GLIBC__) +#if defined(__linux__) && defined(__GLIBC__) && !defined(__UCLIBC__) #include #elif defined(__CYGWIN__) #include @@ -67,7 +67,7 @@ static uint16_t htpasswd_check(char *filename, char *auth) { if (clen > 13) cpwd[13] = 0; -#if defined(__linux__) && defined(__GLIBC__) +#if defined(__linux__) && defined(__GLIBC__) && !defined(__UCLIBC__) struct crypt_data cd; memset(&cd, 0, sizeof(struct crypt_data)); /* work around glibc-2.2.5 bug, From ad61cca29d270cd5fc269db3794da8b839ab0c2b Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Mon, 12 Aug 2019 21:25:05 +0200 Subject: [PATCH 021/207] python: fix object leak in uwsgi.workers() resolves #2056. --- plugins/python/uwsgi_pymodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/python/uwsgi_pymodule.c b/plugins/python/uwsgi_pymodule.c index f9a4377796..7a6e776f60 100644 --- a/plugins/python/uwsgi_pymodule.c +++ b/plugins/python/uwsgi_pymodule.c @@ -2384,8 +2384,6 @@ PyObject *py_uwsgi_workers(PyObject * self, PyObject * args) { goto clear; } - apps_tuple = PyDict_GetItemString(worker_dict, "apps"); - PyDict_Clear(worker_dict); zero = PyInt_FromLong(uwsgi.workers[i + 1].id); @@ -2539,9 +2537,10 @@ PyObject *py_uwsgi_workers(PyObject * self, PyObject * args) { PyTuple_SetItem(apps_tuple, j, apps_dict); } - + PyDict_SetItemString(worker_dict, "apps", apps_tuple); + Py_DECREF(apps_tuple); } From 9a422e70db2b1a72466d6a812c9a4cbac6132a21 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Mon, 12 Aug 2019 21:35:31 +0200 Subject: [PATCH 022/207] tests: regression "test" for #2056 --- tests/testworkers.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/testworkers.py diff --git a/tests/testworkers.py b/tests/testworkers.py new file mode 100644 index 0000000000..e5038bf1b0 --- /dev/null +++ b/tests/testworkers.py @@ -0,0 +1,26 @@ +""" +Regression test for #2056 - uwsgi.workers() leaking objects. +""" +import uwsgi +import gc + + +def application(env, start_response): + gc.collect() + start_objs = len(gc.get_objects()) + + for i in range(200): + uwsgi.workers() + + gc.collect() + end_objs = len(gc.get_objects()) + diff_objs = end_objs - start_objs + + # 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") + else: + start_response('200 OK', [('Content-Type', 'text/plain')]) + + yield "{} {} {}\n".format(start_objs, end_objs, diff_objs).encode("utf-8") From 390461949c1cd395ace1dbc2db9df9503acfe2ba Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Mon, 12 Aug 2019 22:47:14 +0200 Subject: [PATCH 023/207] travis.sh: allow for running multiple apps for testing For each plugin, allow to run multiple apps instead of just the hard-coded tests/staticfile.py or examples/config2.ru apps. This helps to add tiny apps for very basic regression testing. Only a single request is made to / - the app should return 500 on error, 200 OK in the success case. NOTE: Anything more complicated would probably justify rewriting the whole script in Python. --- tests/travis.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/travis.sh b/tests/travis.sh index 993b1a2534..02e104e45f 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -u txtund=$(tput sgr 0 1) # underline @@ -57,13 +58,13 @@ http_test() { test_python() { date > reload.txt rm -f uwsgi.log - echo -e "${bldyel}================== TESTING $1 =====================${txtrst}" + echo -e "${bldyel}================== TESTING $1 $2 =====================${txtrst}" echo -e "${bldyel}>>> Spawning uWSGI python app${txtrst}" echo -en "${bldred}" - ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --wsgi-file tests/staticfile.py --daemonize uwsgi.log + ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --wsgi-file $2 --daemonize uwsgi.log echo -en "${txtrst}" http_test "http://localhost:8080/" - echo -e "${bldyel}===================== DONE $1 =====================${txtrst}\n\n" + echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" } @@ -71,31 +72,35 @@ test_rack() { date > reload.txt rm -f uwsgi.log # the code assumes that ruby environment is activated by `rvm use` - echo -e "${bldyel}================== TESTING $1 =====================${txtrst}" + echo -e "${bldyel}================== TESTING $1 $2 =====================${txtrst}" echo -e "${bldyel}>>> Installing sinatra gem using gem${txtrst}" gem install sinatra || die echo -e "${bldyel}>>> Spawning uWSGI rack app${txtrst}" echo -en "${bldred}" - ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --rack examples/config2.ru --daemonize uwsgi.log + ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --rack $2 --daemonize uwsgi.log echo -en "${txtrst}" http_test "http://localhost:8080/hi" - echo -e "${bldyel}===================== DONE $1 =====================${txtrst}\n\n" + echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" } while read PV ; do - test_python $PV + for WSGI_FILE in tests/staticfile.py tests/testworkers.py ; do + test_python $PV $WSGI_FILE + done done < <(cat .travis.yml | grep "plugins/python base" | sed s_".*plugins/python base "_""_g) while read RV ; do - test_rack $RV + for RACK in examples/config2.ru ; do + test_rack $RV $RACK + done done < <(cat .travis.yml | grep "plugins/rack base" | sed s_".*plugins/rack base "_""_g) -echo "${bldgre}>>> $SUCCESS SUCCESSFUL PLUGIN(S)${txtrst}" +echo "${bldgre}>>> $SUCCESS SUCCESSFUL TEST(S)${txtrst}" if [ $ERROR -ge 1 ]; then - echo "${bldred}>>> $ERROR FAILED PLUGIN(S)${txtrst}" + echo "${bldred}>>> $ERROR FAILED TEST(S)${txtrst}" exit 1 fi From 6b611c4f4325a1b129f37418eac215af83d16b73 Mon Sep 17 00:00:00 2001 From: Thomas Goirand Date: Sun, 25 Aug 2019 19:13:14 +0200 Subject: [PATCH 024/207] Python 3 compat --- contrib/spoolqueue/tasks.py | 4 +-- contrib/spoolqueue/tasksconsumer.py | 7 +++-- contrib/uwsgi-cache-monitor.py | 2 ++ examples/bootstrap5.py | 2 +- examples/mjpeg_stream.py | 2 +- examples/simple_app.py | 6 ++-- examples/simple_app_wsgi2.py | 3 +- examples/taskqueue.py | 6 ++-- examples/uwsgirouter.py | 10 +++---- examples/uwsgirouter3.py | 4 +-- examples/uwsgirouter4.py | 4 +-- examples/uwsgistatus.py | 8 +++--- plugins/coroae/uwsgiplugin.py | 2 +- t/cachetest.py | 10 +++---- t/pypy/t_continulet1.py | 4 +-- t/pypy/t_continulet2.py | 4 +-- t/sharedarea/bigranges.py | 4 +-- tests/cpubound_stackless.py | 2 +- tests/decoratortest.py | 9 ++++-- tests/gevent_spool.py | 2 +- tests/grunter.py | 7 +++-- tests/iobound_async_unix.py | 2 +- tests/mulefunc.py | 8 +++--- tests/myadmin.py | 4 +-- tests/pgbound_async.py | 4 +-- tests/picazzo.py | 2 +- tests/refcount.py | 2 +- tests/runningthread.py | 2 +- tests/sendchunked.py | 5 +++- tests/signals.py | 8 +++--- tests/sleeping_async.py | 2 +- tests/sleeping_green.py | 2 +- tests/sleepthreadasync.py | 2 +- tests/slow.py | 6 ++-- tests/testapp.py | 43 +++++++++++++++-------------- tests/testgevent.py | 8 +++--- tests/threads.py | 5 +++- tests/ugevent.py | 18 ++++++------ tests/websockets.py | 2 +- tests/websockets_chat.py | 2 +- tests/websockets_echo.py | 2 +- 41 files changed, 125 insertions(+), 106 deletions(-) diff --git a/contrib/spoolqueue/tasks.py b/contrib/spoolqueue/tasks.py index e9048ee89b..fbc89b2175 100644 --- a/contrib/spoolqueue/tasks.py +++ b/contrib/spoolqueue/tasks.py @@ -3,9 +3,9 @@ @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) diff --git a/contrib/spoolqueue/tasksconsumer.py b/contrib/spoolqueue/tasksconsumer.py index f2b6a77976..b3987fef2a 100644 --- a/contrib/spoolqueue/tasksconsumer.py +++ b/contrib/spoolqueue/tasksconsumer.py @@ -1,7 +1,8 @@ from uwsgidecorators import spool -import Queue from threading import Thread +from six.moves import queue + queues = {} @@ -10,7 +11,7 @@ class queueconsumer(object): 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 +20,7 @@ def __init__(self, name, num=1, **kwargs): def consumer(self): while True: req = self.queue.get() - print req + print(req) self.func(req) self.queue.task_done() diff --git a/contrib/uwsgi-cache-monitor.py b/contrib/uwsgi-cache-monitor.py index ee15dab36d..ec543f7b7d 100644 --- 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 diff --git a/examples/bootstrap5.py b/examples/bootstrap5.py index 340e6889eb..f8dc3136b5 100644 --- a/examples/bootstrap5.py +++ b/examples/bootstrap5.py @@ -1,3 +1,3 @@ import uwsgi -print uwsgi.extract("data://0") +print(uwsgi.extract("data://0")) diff --git a/examples/mjpeg_stream.py b/examples/mjpeg_stream.py index 9ed082c175..b99a8c4a73 100644 --- a/examples/mjpeg_stream.py +++ b/examples/mjpeg_stream.py @@ -16,7 +16,7 @@ def application(env, start_response): 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 diff --git a/examples/simple_app.py b/examples/simple_app.py index 7a5afbb9bf..f6a565e874 100644 --- a/examples/simple_app.py +++ b/examples/simple_app.py @@ -10,14 +10,14 @@ def ciao(): 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) diff --git a/examples/simple_app_wsgi2.py b/examples/simple_app_wsgi2.py index ba176e433e..e18a8dc03e 100644 --- a/examples/simple_app_wsgi2.py +++ b/examples/simple_app_wsgi2.py @@ -1,6 +1,7 @@ +from six.moves import range def mygen(uri): - for i in xrange(1, 100): + for i in range(1, 100): yield "ciao %s
" % uri diff --git a/examples/taskqueue.py b/examples/taskqueue.py index 71131759d3..02f7b26c84 100644 --- 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 @@ -15,7 +17,7 @@ def consumer(q): 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 diff --git a/examples/uwsgirouter.py b/examples/uwsgirouter.py index 541238e4f9..630913d367 100644 --- a/examples/uwsgirouter.py +++ b/examples/uwsgirouter.py @@ -11,13 +11,13 @@ def application(env, start_response): # 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 @@ def application(env, start_response): 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) @@ -57,7 +57,7 @@ def application(env, start_response): # has timed out ? if env['x-wsgiorg.fdevent.timeout']: - print "connection timed out !!!" + print("connection timed out !!!") uwsgi.close(fd) raise StopIteration @@ -68,7 +68,7 @@ def application(env, start_response): # 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) diff --git a/examples/uwsgirouter3.py b/examples/uwsgirouter3.py index 37b4b894cb..07c8358248 100644 --- a/examples/uwsgirouter3.py +++ b/examples/uwsgirouter3.py @@ -9,10 +9,10 @@ def application(e, s): 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): diff --git a/examples/uwsgirouter4.py b/examples/uwsgirouter4.py index 7786ff6ebf..e7b5c97d93 100644 --- a/examples/uwsgirouter4.py +++ b/examples/uwsgirouter4.py @@ -5,10 +5,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()): diff --git a/examples/uwsgistatus.py b/examples/uwsgistatus.py index 1123b6d909..9581349b26 100644 --- 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 '

uWSGI %s status

' % uwsgi.version @@ -50,11 +50,11 @@ def application(env, start_response): yield '

workers

' for w in workers: - # print w - # print w['running_time'] + # print(w) + # print(w['running_time']) if w is not None: yield '' + str(w['id']) + '' + str(w['pid']) + '' + str(w['pid']) + '' + str(w['requests']) + '' + str(w['running_time']) + '' + str(w['vsz']) + '' + str(w['rss']) + '' - print w + print(w) yield '' diff --git a/plugins/coroae/uwsgiplugin.py b/plugins/coroae/uwsgiplugin.py index 83c2b25e7c..16da85bdb6 100644 --- a/plugins/coroae/uwsgiplugin.py +++ b/plugins/coroae/uwsgiplugin.py @@ -11,7 +11,7 @@ coroapi = p if not coroapi: - print "unable to find the Coro perl module !!!" + print("unable to find the Coro perl module !!!") sys.exit(1) CFLAGS = os.popen('perl -MExtUtils::Embed -e ccopts').read().rstrip().split() diff --git a/t/cachetest.py b/t/cachetest.py index b717e8d3d6..8dc93946d5 100644 --- a/t/cachetest.py +++ b/t/cachetest.py @@ -13,7 +13,7 @@ def gen_rand_n(max_n): 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) @@ -22,14 +22,14 @@ def gen_rand_s(size): 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") diff --git a/t/pypy/t_continulet1.py b/t/pypy/t_continulet1.py index d4ad90c453..668797dfbc 100644 --- a/t/pypy/t_continulet1.py +++ b/t/pypy/t_continulet1.py @@ -14,7 +14,7 @@ def application(e, sr): # call suspend 10 times and yield some value for i in range(0, 10): - print i + print(i) uwsgi.suspend() yield str(i) @@ -36,7 +36,7 @@ def application(e, sr): finally: uwsgi.close(fd) - print "sleeping for 3 seconds..." + print("sleeping for 3 seconds...") uwsgi.async_sleep(3) uwsgi.suspend() yield "done" diff --git a/t/pypy/t_continulet2.py b/t/pypy/t_continulet2.py index 0d7ab6d1e6..df700f2fa9 100644 --- a/t/pypy/t_continulet2.py +++ b/t/pypy/t_continulet2.py @@ -14,7 +14,7 @@ def application(e, sr): # suspend 10 times and yield a value for i in range(1, 10): - print i + print(i) uwsgi.suspend() yield str(i) @@ -43,7 +43,7 @@ def application(e, sr): # 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" diff --git a/t/sharedarea/bigranges.py b/t/sharedarea/bigranges.py index 8543221ec9..4ac5fdcc41 100644 --- 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) diff --git a/tests/cpubound_stackless.py b/tests/cpubound_stackless.py index a4c397246a..5934a02d43 100644 --- a/tests/cpubound_stackless.py +++ b/tests/cpubound_stackless.py @@ -11,4 +11,4 @@ def application(env, start_response): if i % 2 == 0: stackless.schedule() - print "DONE AT %d" % i + print("DONE AT %d" % i) diff --git a/tests/decoratortest.py b/tests/decoratortest.py index 8a2f41b95d..8117fc3572 100644 --- 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(): @@ -47,7 +50,7 @@ def tmpmodified(num): # 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) @@ -56,7 +59,7 @@ def a_long_task(args): # continuously 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) @@ -127,7 +130,7 @@ def fork_happened2(): @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()) diff --git a/tests/gevent_spool.py b/tests/gevent_spool.py index bdf90363c9..2742466f0c 100644 --- a/tests/gevent_spool.py +++ b/tests/gevent_spool.py @@ -4,7 +4,7 @@ @spool def longtask(*args): - print args + print(args) return uwsgi.SPOOL_OK diff --git a/tests/grunter.py b/tests/grunter.py index d3af2c77ce..1efbb12485 100644 --- a/tests/grunter.py +++ b/tests/grunter.py @@ -2,6 +2,9 @@ import time +from six.moves import range + + def application(env, start_response): start_response('200 Ok', [('Content-Type', 'text/html')]) @@ -11,11 +14,11 @@ def application(env, start_response): 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
" % uwsgi.worker_id() time.sleep(2) yield "Now, i will start a very slow task...
" - for i in xrange(1, 10): + for i in range(1, 10): yield "waiting for %d seconds
" % i time.sleep(i) diff --git a/tests/iobound_async_unix.py b/tests/iobound_async_unix.py index 8fb23e7855..81051bc810 100644 --- a/tests/iobound_async_unix.py +++ b/tests/iobound_async_unix.py @@ -44,7 +44,7 @@ def application(env, start_response): 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" diff --git a/tests/mulefunc.py b/tests/mulefunc.py index a05bebb2b9..24ae60239b 100644 --- a/tests/mulefunc.py +++ b/tests/mulefunc.py @@ -3,22 +3,22 @@ @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) def application(e, sr): diff --git a/tests/myadmin.py b/tests/myadmin.py index f05df27ce0..3d5ced0b2e 100644 --- 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,6 +11,6 @@ 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]))) diff --git a/tests/pgbound_async.py b/tests/pgbound_async.py index b7d6101cb8..b4728cc75f 100644 --- a/tests/pgbound_async.py +++ b/tests/pgbound_async.py @@ -23,7 +23,7 @@ def application(env, start_response): for i in pg_wait(connection, env, 3): yield i - print "connected" + print("connected") cursor = connection.cursor() cursor.execute("SELECT * FROM foo") @@ -31,7 +31,7 @@ def application(env, start_response): 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) diff --git a/tests/picazzo.py b/tests/picazzo.py index 87e197434d..39a9bb2105 100644 --- a/tests/picazzo.py +++ b/tests/picazzo.py @@ -12,7 +12,7 @@ def login(req): def login_post(req): - print req + print(req) req["session"]["user"] = "James" return redirect("/") diff --git a/tests/refcount.py b/tests/refcount.py index c83085f4d0..97dc4387cc 100644 --- a/tests/refcount.py +++ b/tests/refcount.py @@ -3,5 +3,5 @@ def application(e, sr): sr('200 OK', [('Content-Type', 'text/html')]) - print sys.gettotalrefcount() + print(sys.gettotalrefcount()) yield '%s' % sys.gettotalrefcount() diff --git a/tests/runningthread.py b/tests/runningthread.py index dfed522cfb..b222fe7e13 100644 --- a/tests/runningthread.py +++ b/tests/runningthread.py @@ -5,7 +5,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) diff --git a/tests/sendchunked.py b/tests/sendchunked.py index 99b36657f5..84f4fdfe66 100644 --- 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)) diff --git a/tests/signals.py b/tests/signals.py index 4a2e8592e0..49640bcd99 100644 --- a/tests/signals.py +++ b/tests/signals.py @@ -2,19 +2,19 @@ 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) diff --git a/tests/sleeping_async.py b/tests/sleeping_async.py index 7a5d536ae5..3a2af66d32 100644 --- a/tests/sleeping_async.py +++ b/tests/sleeping_async.py @@ -6,5 +6,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 "

Hello World after %d seconds

" % sleepvalue diff --git a/tests/sleeping_green.py b/tests/sleeping_green.py index df9ee8a5dd..355ac1aa2c 100644 --- a/tests/sleeping_green.py +++ b/tests/sleeping_green.py @@ -10,5 +10,5 @@ def application(env, start_response): 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 "

Hello World after %s seconds

" % str(time.time() - start_at) diff --git a/tests/sleepthreadasync.py b/tests/sleepthreadasync.py index 5795099f84..ef6d9da2e3 100644 --- a/tests/sleepthreadasync.py +++ b/tests/sleepthreadasync.py @@ -6,7 +6,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 diff --git a/tests/slow.py b/tests/slow.py index 61ca5d5b05..f82e328b2c 100644 --- a/tests/slow.py +++ b/tests/slow.py @@ -3,11 +3,11 @@ 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" diff --git a/tests/testapp.py b/tests/testapp.py index 99ec401877..fb3e1386a1 100644 --- 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 @@ -19,7 +20,7 @@ class testthread(Thread): 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() @@ -29,24 +30,24 @@ def run(self): 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 + print(env) for i in range(1, 100): uwsgi.sharedarea_inclong(100) # time.sleep(1) 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(): @@ -62,7 +63,7 @@ def force_harakiri(): def application(env, start_response): - print env + print(env) start_response('200 OK', [('Content-Type', 'text/plain')]) yield { '/': helloworld, @@ -71,7 +72,7 @@ def application(env, start_response): '/uwsgi/': helloworld }[env['PATH_INFO']]() - print env + print(env) def gomako(): @@ -94,9 +95,9 @@ def djangohomepage(): 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) @@ -109,9 +110,9 @@ def reload(env, start_response): #uwsgi.reload() -# print str(uwsgi.masterpid()) + "\n" +# print(str(uwsgi.masterpid()) + "\n") -# print "i am python" +# print("i am python") #yo() # yield "python" @@ -120,7 +121,7 @@ def reload(env, start_response): # yield str(uwsgi.masterpid()) - #print uwsgi.pippo + #print(uwsgi.pippo) #print 4/0 # try: @@ -130,7 +131,7 @@ def reload(env, start_response): # except Exception: # print "bah" -# print "ok" +# print("ok") # yield 4/0 @@ -153,16 +154,16 @@ def reload(env, start_response): yield '

workers

' for w in workers: - #print w - #print w['running_time'] + #print(w) + #print(w['running_time']) if w is not None: yield '' + str(w['id']) + '' + str(w['pid']) + '' + str(w['pid']) + '' + str(w['requests']) + '' + str(w['running_time']) + '' + str(w['vsz']) + '' + str(w['rss']) + '' - print w + print(w) yield '' #yield out - #print "FATTOfattoFATTO" + #print("FATTOfattoFATTO") def remotemako(env, start_response): @@ -174,9 +175,9 @@ def remotemako(env, start_response): ('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', @@ -196,5 +197,5 @@ def remotemako(env, start_response): '/pippo': reload } -print uwsgi.applications -print uwsgi.applist +print(uwsgi.applications) +print(uwsgi.applist) diff --git a/tests/testgevent.py b/tests/testgevent.py index 53849abff3..c0995d8aaf 100644 --- a/tests/testgevent.py +++ b/tests/testgevent.py @@ -6,21 +6,21 @@ 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 diff --git a/tests/threads.py b/tests/threads.py index be3dcffdf6..241a001d78 100644 --- a/tests/threads.py +++ b/tests/threads.py @@ -4,6 +4,9 @@ import sys +from six.moves import reload_module + + def monitor1(): while 1: time.sleep(1) @@ -21,7 +24,7 @@ def monitor3(): while 1: time.sleep(5) print("5 seconds elapsed") - # reload(fake) + # reload_module(fake) def spawn_my_magic_threads(): diff --git a/tests/ugevent.py b/tests/ugevent.py index bb03a1b2d2..7b847d77fd 100644 --- a/tests/ugevent.py +++ b/tests/ugevent.py @@ -12,28 +12,28 @@ @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 !!! @@ -42,7 +42,7 @@ def bg_task(): def long_task(): for i in range(1, 10): - print i + print(i) gevent.sleep() @@ -70,7 +70,7 @@ def application(e, sr): yield "ip = %s
" % j.value if REFCNT: - print sys.gettotalrefcount() + print(sys.gettotalrefcount()) yield "%d" % sys.gettotalrefcount() # this task will goes on after request end diff --git a/tests/websockets.py b/tests/websockets.py index a7a3b38a0d..106e350860 100644 --- a/tests/websockets.py +++ b/tests/websockets.py @@ -55,7 +55,7 @@ def application(env, sr): """ % (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: diff --git a/tests/websockets_chat.py b/tests/websockets_chat.py index de91abf8f5..34b354ff1d 100644 --- a/tests/websockets_chat.py +++ b/tests/websockets_chat.py @@ -55,7 +55,7 @@ def application(env, sr): 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') diff --git a/tests/websockets_echo.py b/tests/websockets_echo.py index 5db57cea55..83184afd0b 100644 --- a/tests/websockets_echo.py +++ b/tests/websockets_echo.py @@ -51,7 +51,7 @@ def application(env, sr): """ % (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)) From 9564b42be190c73bc1aa44dab2865003c0a83679 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Tue, 3 Sep 2019 20:10:55 +0200 Subject: [PATCH 025/207] testworkers: smoke check that uwsgi.workers() does in fact return something --- tests/testworkers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/testworkers.py b/tests/testworkers.py index e5038bf1b0..ff84428341 100644 --- a/tests/testworkers.py +++ b/tests/testworkers.py @@ -10,7 +10,10 @@ def application(env, start_response): 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 @@ def application(env, start_response): # 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')]) From 084faa79fcd578e01a408effa53e5b9377da1b05 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 7 Sep 2019 19:05:37 +0200 Subject: [PATCH 026/207] rename tests/werkzeug.py to tests/werkzeug_app.py ... flask doesn't like the fact that there's a werkzeug module. --- tests/{werkzeug.py => werkzeug_app.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{werkzeug.py => werkzeug_app.py} (100%) diff --git a/tests/werkzeug.py b/tests/werkzeug_app.py similarity index 100% rename from tests/werkzeug.py rename to tests/werkzeug_app.py From 10e3f0599280e245e121ce239fcf7b6ac657e89d Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 7 Sep 2019 13:58:04 +0200 Subject: [PATCH 027/207] python: support io.BytesIO with wsgi.file_wrapper Should solve #1126 This change proposes to move the `PyObject_AsFileDescriptor()` calls from the `wsgi.file_wrapper` into the response handler. In the response handler, we can sensibly fall back to the `read()` protocol using the existing `uwsgi_python_consume_file_wrapper_read()` if converting the file-like object to an fd fails. Previously, after the conversion to an fd failed, the `wsgi.file_wrapper` raised a SystemError. Running the added test application requires flask, start as follows: uwsgi --enable-threads --workers 1 --threads 1 \ --plugin ./python37_plugin.so --http-socket :5000 \ -H ./venv/ --wsgi-file ./tests/testfilewrapper.py $ for i in {1..3} ; do echo -n "test $i - "; curl localhost:5000/$i ; done test 1 - cookie test 2 - cookie test 3 - cookie2 $ for i in {1..3} ; do echo -n "test $i - "; curl localhost:5000/stream$i ; done test 1 - 0.10 cookie 0.20 cookie 0.30 cookie test 2 - cookie test 3 - cookie --- plugins/python/wsgi_handlers.c | 35 ++++----- plugins/python/wsgi_subhandler.c | 45 +++++++++--- tests/static/test.txt | 1 + tests/static/test2.txt | 1 + tests/testfilewrapper.py | 122 +++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 26 deletions(-) create mode 100644 tests/static/test.txt create mode 100644 tests/static/test2.txt create mode 100644 tests/testfilewrapper.py diff --git a/plugins/python/wsgi_handlers.c b/plugins/python/wsgi_handlers.c index b6a2b24101..e56ab4b652 100644 --- a/plugins/python/wsgi_handlers.c +++ b/plugins/python/wsgi_handlers.c @@ -475,31 +475,32 @@ void uwsgi_after_request_wsgi(struct wsgi_request *wsgi_req) { } PyObject *py_uwsgi_sendfile(PyObject * self, PyObject * args) { - + int chunk_size; + PyObject *filelike; struct wsgi_request *wsgi_req = py_current_wsgi_req(); - if (!PyArg_ParseTuple(args, "O|i:uwsgi_sendfile", &wsgi_req->async_sendfile, &wsgi_req->sendfile_fd_chunk)) { + if (!PyArg_ParseTuple(args, "O|i:uwsgi_sendfile", &filelike, &chunk_size)) { return NULL; } -#ifdef PYTHREE - wsgi_req->sendfile_fd = PyObject_AsFileDescriptor(wsgi_req->async_sendfile); - if (wsgi_req->sendfile_fd >= 0) { - Py_INCREF((PyObject *)wsgi_req->async_sendfile); - } -#else - if (PyFile_Check((PyObject *)wsgi_req->async_sendfile)) { - Py_INCREF((PyObject *)wsgi_req->async_sendfile); - wsgi_req->sendfile_fd = PyObject_AsFileDescriptor(wsgi_req->async_sendfile); + if (!PyObject_HasAttrString(filelike, "read")) { + PyErr_SetString(PyExc_AttributeError, "object has no attribute 'read'"); + return NULL; } -#endif - // PEP 333 hack - wsgi_req->sendfile_obj = wsgi_req->async_sendfile; - //wsgi_req->sendfile_obj = (void *) PyTuple_New(0); + // wsgi.file_wrapper called a second time? Forget the old reference. + if (wsgi_req->async_sendfile) { + Py_DECREF(wsgi_req->async_sendfile); + } - Py_INCREF((PyObject *) wsgi_req->sendfile_obj); - return (PyObject *) wsgi_req->sendfile_obj; + // XXX: Not 100% sure why twice. + // Maybe: We keep one at async_sendfile and transfer + // one to the caller (even though he gave it to us). + Py_INCREF(filelike); + Py_INCREF(filelike); + wsgi_req->async_sendfile = filelike; + wsgi_req->sendfile_fd_chunk = chunk_size; + return filelike; } void threaded_swap_ts(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) { diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index a8ad492ab8..d773f6c3ce 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -76,7 +76,18 @@ int uwsgi_python_send_body(struct wsgi_request *wsgi_req, PyObject *chunk) { return 1; } return 0; -} +} + +/* + * attempt to make a fd from a file-like PyObject - returns -1 on error. + */ +static int uwsgi_python_try_filelike_as_fd(PyObject *filelike) { + int fd; + if ((fd = PyObject_AsFileDescriptor(filelike)) < 0) { + PyErr_Clear(); + } + return fd; +} /* this is a hack for supporting non-file object passed to wsgi.file_wrapper @@ -262,7 +273,9 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { if (uwsgi_python_send_body(wsgi_req, (PyObject *)wsgi_req->async_result)) goto clear; } - if (wsgi_req->sendfile_obj == wsgi_req->async_result) { + // Check if wsgi.file_wrapper has been used. + if (wsgi_req->async_sendfile == wsgi_req->async_result) { + wsgi_req->sendfile_fd = uwsgi_python_try_filelike_as_fd((PyObject *)wsgi_req->async_sendfile); if (wsgi_req->sendfile_fd >= 0) { UWSGI_RELEASE_GIL uwsgi_response_sendfile_do(wsgi_req, wsgi_req->sendfile_fd, 0, 0); @@ -308,8 +321,14 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { Py_DECREF(pychunk); goto clear; } - } - else if (wsgi_req->sendfile_obj == pychunk) { + } else if (wsgi_req->async_sendfile == pychunk) { + // + // XXX: It's not clear whether this can ever be sensibly reached + // based on PEP 333/3333 - it would mean the iterator yielded + // the result of wsgi.file_wrapper. However, the iterator should + // only ever yield `bytes`... + // + wsgi_req->sendfile_fd = uwsgi_python_try_filelike_as_fd((PyObject *)wsgi_req->async_sendfile); if (wsgi_req->sendfile_fd >= 0) { UWSGI_RELEASE_GIL uwsgi_response_sendfile_do(wsgi_req, wsgi_req->sendfile_fd, 0, 0); @@ -319,22 +338,30 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { else if (PyObject_HasAttrString(pychunk, "read")) { uwsgi_python_consume_file_wrapper_read(wsgi_req, pychunk); } - uwsgi_py_check_write_errors { uwsgi_py_write_exception(wsgi_req); Py_DECREF(pychunk); goto clear; } + } else { + // The iterator returned something that we were not able to handle. + PyObject *pystr = PyObject_Repr(pychunk); +#ifdef PYTHREE + const char *cstr = PyUnicode_AsUTF8(pystr); +#else + const char *cstr = PyString_AsString(pystr); +#endif + uwsgi_log("[ERROR] Unhandled object from iterator: %s (%p)\n", cstr, pychunk); + Py_DECREF(pystr); } - Py_DECREF(pychunk); return UWSGI_AGAIN; clear: - - if (wsgi_req->sendfile_fd != -1) { - Py_DECREF((PyObject *)wsgi_req->async_sendfile); + // Release the reference that we took in py_uwsgi_sendfile. + if (wsgi_req->async_sendfile != NULL) { + Py_DECREF((PyObject *) wsgi_req->async_sendfile); } if (wsgi_req->async_placeholder) { diff --git a/tests/static/test.txt b/tests/static/test.txt new file mode 100644 index 0000000000..871bb87ecb --- /dev/null +++ b/tests/static/test.txt @@ -0,0 +1 @@ +cookie diff --git a/tests/static/test2.txt b/tests/static/test2.txt new file mode 100644 index 0000000000..8504e7eaeb --- /dev/null +++ b/tests/static/test2.txt @@ -0,0 +1 @@ +cookie2 diff --git a/tests/testfilewrapper.py b/tests/testfilewrapper.py new file mode 100644 index 0000000000..2575c1aed9 --- /dev/null +++ b/tests/testfilewrapper.py @@ -0,0 +1,122 @@ +from __future__ import print_function +import gc +import io +import os.path +import time + +import flask +import flask.helpers + + +application = flask.Flask(__name__) + +FILENAME = os.path.join(os.path.dirname(__file__), "static", "test.txt") +FILENAME2 = os.path.join(os.path.dirname(__file__), "static", "test2.txt") + +@application.after_request +def _after(response): + gc.collect() + fds = os.listdir("/proc/self/fd") + print("PY: objects:", len(gc.get_objects()), "fds:", len(fds)) + return response + +@application.route("/") +def index(): + return "HELLO\n" + +@application.route("/1") +def send_file_1(): + fp = open(FILENAME, "rb") + return flask.send_file(fp, attachment_filename="test.txt") + + +@application.route("/2") +def send_file_2(): + bio = io.BytesIO(b"cookie\n") + return flask.send_file(bio, attachment_filename="test.txt") + + +@application.route("/3") +def send_file_3(): + """ + What happens if we call the wsgi.file_wrapper twice? + + This should respond with cookie2 + """ + fp = open(FILENAME, "rb") + flask.send_file(fp, attachment_filename="test.txt") + fp = open(FILENAME2, "rb") + return flask.send_file(fp, attachment_filename="test.txt") + + +@application.route("/4") +def send_file_4(): + """ + Non-filelike object to send_file/wrap_file/wsgi.file_wrapper. + + AttributeError on the call to wsgi.file_wrapper. + """ + return flask.send_file(object(), attachment_filename="test.txt") + + +@application.route("/stream1") +def stream1(): + """ + Unrelated to wsgi.file_wrapper, just ensuring the iterator stuff still works. + """ + def _yield(): + start = time.time() + for i in range(3): + time.sleep(0.1) + yield " {:.2f} cookie".format(time.time() - start).encode("utf-8") + yield b"\n" + return flask.Response(_yield(), mimetype="text/plain") + + +@application.route("/stream2") +def stream2(): + """ + Yielding the result of a wrap_file call with a file object. + + gunicorn / werkzeug do not support this as it's not required. + """ + fp = open(FILENAME, "rb") + resp = flask.helpers.wrap_file(flask.request.environ, fp) + print("PY: resp after return", hex(id(resp))) + + def _yield(): + print("PY: _yield() run", hex(id(resp)), repr(resp)) + yield resp + return flask.Response(_yield()) + + +@application.route("/stream3") +def stream3(): + """ + Yielding the result of a wrap_file call with a BytesIO object. + + gunicorn / werkzeug do not support this as it's not required. + """ + bio = io.BytesIO(b"cookie\n") + resp = flask.helpers.wrap_file(flask.request.environ, bio) + + def _yield(): + yield resp + return flask.Response(_yield()) + + +@application.route("/stream4") +def stream4(): + """ + werkzeug logs: AssertionError: applications must write bytes + gunicorn logs: TypeError: is not a byte + uwsgi didn't log, should now.. + """ + fp = open(FILENAME, "rb") + resp = flask.send_file(fp, attachment_filename="test.txt") + print("PY: resp after return", hex(id(resp))) + + def _yield(): + print("PY: _yield() run", hex(id(resp)), repr(resp)) + yield resp + return flask.Response(_yield(), direct_passthrough=True) From fef4d6d5bfa61a9a0c721d2505f3df6710761994 Mon Sep 17 00:00:00 2001 From: Jacob Tolar Date: Fri, 13 Sep 2019 00:08:37 -0500 Subject: [PATCH 028/207] Fix: correct macros for checking fifo or socket --- core/logging.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/logging.c b/core/logging.c index d997df72b1..3d0559b954 100644 --- a/core/logging.c +++ b/core/logging.c @@ -498,11 +498,7 @@ void uwsgi_check_logrotate(void) { return; } - if (logstat.st_mode & S_IFIFO) { - return; - } - - if (logstat.st_mode & S_IFSOCK) { + if (S_ISFIFO(logstat.st_mode) || S_ISSOCK(logstat.st_mode)) { return; } From 399f79baff22a82f487278e44a0e10534e070b1a Mon Sep 17 00:00:00 2001 From: jacobtolar Date: Sun, 20 Oct 2019 20:22:55 -0500 Subject: [PATCH 029/207] Add 'all', 'clean', 'check' to phony targets The only one currently useful is 'check': since there's a directory named 'check', `make check` always just returns `make: `check' is up to date.`. But, `all` and `clean` are also phony. Actually the other rule based targets are phony too but .PHONY doesn't work with pattern rules: https://www.gnu.org/software/make/manual/make.html#Phony-Targets > The implicit rule search (see Implicit Rules) is skipped for .PHONY targets. This is why declaring a target as .PHONY is good for performance, even if you are not worried about the actual file existing. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2b05ef970a..6896b4245a 100644 --- a/Makefile +++ b/Makefile @@ -19,4 +19,4 @@ tests: %: $(PYTHON) uwsgiconfig.py --build $@ -.PHONY: tests +.PHONY: all clean check tests From 034ce10426f68f819b957fbdd67ba0416d4588b6 Mon Sep 17 00:00:00 2001 From: Janneke Janssen Date: Tue, 22 Oct 2019 19:04:39 +0200 Subject: [PATCH 030/207] Bit more friendly log messages --- core/signal.c | 2 +- core/uwsgi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/signal.c b/core/signal.c index a7b4452184..d5d9f8a0ca 100644 --- a/core/signal.c +++ b/core/signal.c @@ -500,7 +500,7 @@ int uwsgi_receive_signal(struct wsgi_request *wsgi_req, int fd, char *name, int destroy: // better to kill the whole worker... - uwsgi_log_verbose("uWSGI %s %d screams: UAAAAAAH my master disconnected: I will kill myself!!!\n", name, id); + uwsgi_log_verbose("uWSGI %s %d error: the master disconnected from this worker. Shutting down the worker.\n", name, id); end_me(0); // never here return 0; diff --git a/core/uwsgi.c b/core/uwsgi.c index a6fbfd856d..7a92b87f49 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1515,7 +1515,7 @@ void reap_them_all(int signum) { void harakiri() { - uwsgi_log("\nF*CK !!! i must kill myself (pid: %d app_id: %d)...\n", uwsgi.mypid, uwsgi.wsgi_req->app_id); + uwsgi_log("\nKilling the current process (pid: %d app_id: %d)...\n", uwsgi.mypid, uwsgi.wsgi_req->app_id); if (!uwsgi.master_process) { uwsgi_log("*** if you want your workers to be automatically respawned consider enabling the uWSGI master process ***\n"); From 6a62fc836e24849a6ea30b31fafe604c1924691c Mon Sep 17 00:00:00 2001 From: MRoci Date: Fri, 25 Oct 2019 09:53:17 +0200 Subject: [PATCH 031/207] return register_rpc if name is >= UMAX8 --- core/rpc.c | 5 +++++ tests/testrpc.py | 11 +++++++++++ tests/travis.sh | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/testrpc.py diff --git a/core/rpc.c b/core/rpc.c index ed98751812..efb8c5badc 100644 --- a/core/rpc.c +++ b/core/rpc.c @@ -17,6 +17,11 @@ int uwsgi_register_rpc(char *name, struct uwsgi_plugin *plugin, uint8_t args, vo return -1; } + if (strlen(name) >= UMAX8) { + uwsgi_log("the supplied RPC name string is too long, max size is %d\n", UMAX8-1); + return -1; + } + uwsgi_lock(uwsgi.rpc_table_lock); // first check if a function is already registered diff --git a/tests/testrpc.py b/tests/testrpc.py new file mode 100644 index 0000000000..ef768ca5d5 --- /dev/null +++ b/tests/testrpc.py @@ -0,0 +1,11 @@ +import uwsgi + +def hello(): + pass + +def application(env, start_response): + try: + uwsgi.register_rpc("A"*300, hello) + start_response('500 Buffer Overflow', [('Content-Type', 'text/plain')]) + except ValueError: + start_response('200 OK', [('Content-Type', 'text/plain')]) diff --git a/tests/travis.sh b/tests/travis.sh index 02e104e45f..3c61e206ba 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -85,7 +85,7 @@ test_rack() { while read PV ; do - for WSGI_FILE in tests/staticfile.py tests/testworkers.py ; do + for WSGI_FILE in tests/staticfile.py tests/testworkers.py tests/testrpc.py ; do test_python $PV $WSGI_FILE done done < <(cat .travis.yml | grep "plugins/python base" | sed s_".*plugins/python base "_""_g) From 1b959f1888d202714f46ac4f155f949cb03a96bc Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 26 Oct 2019 14:09:03 +0200 Subject: [PATCH 032/207] uwsgiconfig: quote bin_name when linking If the path to the binary contains a space, the link step fails: $ python3 -m venv have\ space $ . have\ space/bin/activate (have space) $ pip install uwsgi .... *** uWSGI linking *** x86_64-linux-gnu-gcc -pthread -o /home/awelzel/projects/uwsgi/have space/bin/uwsgi -L/usr/lib -Wl,-rpat x86_64-linux-gnu-gcc: error: space/bin/uwsgi: No such file or directory Further, if one builds into a venv called `;uptime;`, `uptime` is executed. This is a minimal fix quoting `bin_name` and not considering any other cases. Fixes #1939. --- uwsgiconfig.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 210d9d016b..387fb323b8 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -27,6 +27,10 @@ except ImportError: import configparser as ConfigParser +try: + from shlex import quote +except ImportError: + from pipes import quote PY3 = sys.version_info[0] == 3 @@ -566,13 +570,13 @@ def build_uwsgi(uc, print_only=False, gcll=None): print("*** uWSGI linking ***") if '--static' in ldflags: ldline = 'ar cru %s %s' % ( - bin_name, + quote(bin_name), ' '.join(map(add_o, gcc_list)) ) else: ldline = "%s -o %s %s %s %s" % ( GCC, - bin_name, + quote(bin_name), ' '.join(uniq_warnings(ldflags)), ' '.join(map(add_o, gcc_list)), ' '.join(uniq_warnings(libs)) From 0a9e4083e536bfc98050550786022263a4d635bc Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 26 Oct 2019 14:06:59 +0200 Subject: [PATCH 033/207] fifo: log non-ENOENT unlink() errors --- core/fifo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/fifo.c b/core/fifo.c index 1e8cd2e94f..dae74194d1 100644 --- a/core/fifo.c +++ b/core/fifo.c @@ -104,7 +104,9 @@ int uwsgi_master_fifo() { char *path = uwsgi_fifo_by_slot(); - unlink(path); + if (unlink(path) != 0 && errno != ENOENT) { + uwsgi_error("uwsgi_master_fifo()/unlink()"); + } if (mkfifo(path, S_IRUSR|S_IWUSR)) { uwsgi_error("uwsgi_master_fifo()/mkfifo()"); From c4a2ad579a2fe5d357f2d6486c1afe84ef18a029 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sun, 8 Dec 2019 12:59:45 +0100 Subject: [PATCH 034/207] php: Properly zero initialize zend_file_handle In the PHP bugtracker [1], the stacktrace included a bogus pointer value being passed to `_efree`. The ASCII value of the pointer is "02.11.81" which heavily pointed at usage of non-initialized data. #1 0x00007f8fe07e9e96 in _efree (ptr=0x30322e31312e3831) at /data/work/php-src-php-7.4.0RC6/Zend/zend_alloc.c:2549 The solution is to use an open-coded version of`zend_stream_init_filename()` [2]. Maybe we could actually use the above helper, but I'm not familiar enough with PHP/versions/compat, so the proposed change seems safer. Should fix #2096. [1] https://bugs.php.net/bug.php?id=78828 [2] https://github.com/php/php-src/blob/bc6e4b6c574261188519a1e83ba49998ffbcb12b/Zend/zend_stream.c#L70 --- plugins/php/php_plugin.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 72c3902234..1690fb60cc 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -1115,10 +1115,9 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { SG(request_info).path_translated = wsgi_req->file; + memset(&file_handle, 0, sizeof(zend_file_handle)); file_handle.type = ZEND_HANDLE_FILENAME; file_handle.filename = real_filename; - file_handle.free_filename = 0; - file_handle.opened_path = NULL; if (php_request_startup(TSRMLS_C) == FAILURE) { uwsgi_500(wsgi_req); From 588048ca5ac7547c63503e85f06abaeb9315c9db Mon Sep 17 00:00:00 2001 From: Thomas Goirand Date: Thu, 9 Jan 2020 13:46:02 +0100 Subject: [PATCH 035/207] Add from __future__ import print_function --- contrib/spoolqueue/tasks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/spoolqueue/tasks.py b/contrib/spoolqueue/tasks.py index fbc89b2175..bb5b57a353 100644 --- a/contrib/spoolqueue/tasks.py +++ b/contrib/spoolqueue/tasks.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from tasksconsumer import queueconsumer From 5e788adce7fc369a55487acdc9f7358cf5f71a98 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Tue, 24 Sep 2019 00:15:48 +0200 Subject: [PATCH 036/207] travis: Build plugins for Python 3.7 and 3.8 --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 330f3ed8c8..a471c56fd7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,12 @@ script: - echo -e "\n\n>>> Building python36 plugin" - /usr/bin/python3.6 -V - /usr/bin/python3.6 uwsgiconfig.py --plugin plugins/python base python36 + - echo -e "\n\n>>> Building python37 plugin" + - /usr/bin/python3.7 -V + - /usr/bin/python3.7 uwsgiconfig.py --plugin plugins/python base python37 + - echo -e "\n\n>>> Building python38 plugin" + - /usr/bin/python3.8 -V + - /usr/bin/python3.8 uwsgiconfig.py --plugin plugins/python base python38 - echo -e "\n\n>>> Building rack plugin" - rvm use 2.4 - ruby -v @@ -44,7 +50,7 @@ before_install: - sudo add-apt-repository ppa:deadsnakes/ppa -y - sudo add-apt-repository ppa:ondrej/php -y - sudo apt-get update -qq - - sudo apt-get install -qqyf python2.6-dev python3.4-dev python3.5-dev python3.6-dev + - sudo apt-get install -qqyf python2.6-dev python3.4-dev python3.5-dev python3.6-dev python3.7-dev python3.8-dev - sudo apt-get install -qqyf libxml2-dev libpcre3-dev libcap2-dev - sudo apt-get install -qqyf php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev - sudo apt-get install -qqyf liblua5.1-0-dev From 52be746c0e600ce6fb01abe24dc177e2629e3d07 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 11 Jan 2020 17:31:24 +0100 Subject: [PATCH 037/207] travis: Install python3.8-distutils explicitly The python3.8 package from ppa:deadsnakes/ppa does not depend on python3.8-distutils (this is the case for python3.7). Workaround this by installing it explicitly. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a471c56fd7..20c62d7182 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,7 @@ before_install: - sudo add-apt-repository ppa:deadsnakes/ppa -y - sudo add-apt-repository ppa:ondrej/php -y - sudo apt-get update -qq - - sudo apt-get install -qqyf python2.6-dev python3.4-dev python3.5-dev python3.6-dev python3.7-dev python3.8-dev + - sudo apt-get install -qqyf python2.6-dev python3.4-dev python3.5-dev python3.6-dev python3.7-dev python3.8-dev python3.8-distutils - sudo apt-get install -qqyf libxml2-dev libpcre3-dev libcap2-dev - sudo apt-get install -qqyf php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev - sudo apt-get install -qqyf liblua5.1-0-dev From 096b9cc4560cfbfc03dbeb50564295baee30edad Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 11 Jan 2020 17:43:29 +0100 Subject: [PATCH 038/207] travis: more compact version listing --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 20c62d7182..51c3134f05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,8 @@ before_install: - sudo add-apt-repository ppa:deadsnakes/ppa -y - sudo add-apt-repository ppa:ondrej/php -y - sudo apt-get update -qq - - sudo apt-get install -qqyf python2.6-dev python3.4-dev python3.5-dev python3.6-dev python3.7-dev python3.8-dev python3.8-distutils + - sudo apt-get install -qqyf python{2.6,3.4,3.5,3.6,3.7,3.8}-dev + - sudo apt-get install -qqyf python3.8-distutils - sudo apt-get install -qqyf libxml2-dev libpcre3-dev libcap2-dev - sudo apt-get install -qqyf php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev - sudo apt-get install -qqyf liblua5.1-0-dev From beeb8af48936abf40fb7a81c9825f52c0774ee64 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 11 Jan 2020 17:57:45 +0100 Subject: [PATCH 039/207] tests/testrpc: appications must return an iterable Removes `TypeError: 'NoneType' object is not iterable` logs when running tests. --- tests/testrpc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/testrpc.py b/tests/testrpc.py index ef768ca5d5..c3363d6ade 100644 --- a/tests/testrpc.py +++ b/tests/testrpc.py @@ -9,3 +9,5 @@ def application(env, start_response): start_response('500 Buffer Overflow', [('Content-Type', 'text/plain')]) except ValueError: start_response('200 OK', [('Content-Type', 'text/plain')]) + + return () From bd9340c2b0f8af3ce8b292e24f2a2cf511526972 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 19 Jan 2020 00:47:24 +0200 Subject: [PATCH 040/207] Add Trove classifiers for Python 3.7 and 3.8 --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index cc1868c7dc..0792801ae5 100644 --- a/setup.py +++ b/setup.py @@ -144,6 +144,8 @@ def get_extra_require(): 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ], install_requires=get_extra_require() ) From f8b4c28383592193b563761da16b3e12a6060962 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sat, 14 Dec 2019 00:54:19 +0100 Subject: [PATCH 041/207] php: Fix memory corruption for uwsgi_cache_* Ah the joys of variadic arguments in C... So, when using zend_parse_parameters(), PHP internally loops through the type specifiers and accordingly uses va_arg() to get the corresponding argument. Since the arguments are expected to be pointers to the corresponding values, the size of them *does* matter, because PHP simply writes to the corresponding address with a size of size_t. If we for example pass a pointer to a 32bit integer and PHP writes 64 bits, we have an overflow of 4 bytes. From README.PARAMETER_PARSING_API in the PHP source tree: > Please note that since version 7 PHP uses zend_long as integer type > and zend_string with size_t as length, so make sure you pass > zend_longs to "l" and size_t to strings length (i.e. for "s" you need > to pass char * and size_t), not the other way round! > > Both mistakes might cause memory corruptions and segfaults: > 1) > char *str; > long str_len; /* XXX THIS IS WRONG!! Use size_t instead. */ > zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len) > > 2) > int num; /* XXX THIS IS WRONG!! Use zend_long instead. */ > zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) To fix this, I changed the types accordingly to use size_t and zend_long if the PHP major version is >= 7. Signed-off-by: aszlig --- plugins/php/php_plugin.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 1690fb60cc..16d495c004 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -6,6 +6,14 @@ static sapi_module_struct uwsgi_sapi_module; static int uwsgi_php_init(void); +#ifdef UWSGI_PHP7 +typedef size_t php_strlen_size; +typedef zend_long php_long_size; +#else +typedef int php_strlen_size; +typedef uint64_t php_long_size; +#endif + struct uwsgi_php { struct uwsgi_string_list *allowed_docroot; struct uwsgi_string_list *allowed_ext; @@ -297,9 +305,9 @@ PHP_FUNCTION(uwsgi_masterpid) { PHP_FUNCTION(uwsgi_cache_exists) { char *key = NULL; - int keylen = 0; + php_strlen_size keylen = 0; char *cache = NULL; - int cachelen = 0; + php_strlen_size cachelen = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { RETURN_NULL(); @@ -315,7 +323,7 @@ PHP_FUNCTION(uwsgi_cache_exists) { PHP_FUNCTION(uwsgi_cache_clear) { char *cache = NULL; - int cachelen = 0; + php_strlen_size cachelen = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cache, &cachelen) == FAILURE) { RETURN_NULL(); @@ -330,11 +338,11 @@ PHP_FUNCTION(uwsgi_cache_clear) { PHP_FUNCTION(uwsgi_cache_del) { - + char *key = NULL; - int keylen = 0; + php_strlen_size keylen = 0; char *cache = NULL; - int cachelen = 0; + php_strlen_size cachelen = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { RETURN_NULL(); @@ -350,9 +358,9 @@ PHP_FUNCTION(uwsgi_cache_del) { PHP_FUNCTION(uwsgi_cache_get) { char *key = NULL; - int keylen = 0; + php_strlen_size keylen = 0; char *cache = NULL; - int cachelen = 0; + php_strlen_size cachelen = 0; uint64_t valsize; if (!uwsgi.caches) @@ -377,12 +385,12 @@ PHP_FUNCTION(uwsgi_cache_get) { PHP_FUNCTION(uwsgi_cache_set) { char *key = NULL; - int keylen; + php_strlen_size keylen = 0; char *value = NULL; - int vallen; - uint64_t expires = 0; + php_strlen_size vallen = 0; + php_long_size expires = 0; char *cache = NULL; - int cachelen = 0; + php_strlen_size cachelen = 0; if (!uwsgi.caches) RETURN_NULL(); @@ -400,12 +408,12 @@ PHP_FUNCTION(uwsgi_cache_set) { PHP_FUNCTION(uwsgi_cache_update) { char *key = NULL; - int keylen; + php_strlen_size keylen = 0; char *value = NULL; - int vallen; - uint64_t expires = 0; + php_strlen_size vallen = 0; + php_long_size expires = 0; char *cache = NULL; - int cachelen = 0; + php_strlen_size cachelen = 0; if (!uwsgi.caches) RETURN_NULL(); From e7c7c33c54432cd155265ec9f510e78a27cd5f00 Mon Sep 17 00:00:00 2001 From: Eduardo Felipe Castegnaro Date: Mon, 20 Jan 2020 11:58:51 -0300 Subject: [PATCH 042/207] Add smart-daemon2 option to notify it on reloads We had a use-case of a smart-daemon that needed to be notified when the uWSGI master was reloading. That option needs to be explicitly enabled and requires the smart-daemon to have a pidfile and a valid pid. It that is the case we send a signal to it notifying the uWSGI master is reloading. --- core/daemons.c | 14 ++++++++++++++ uwsgi.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/core/daemons.c b/core/daemons.c index 025dac5a9c..0e7889f81f 100644 --- a/core/daemons.c +++ b/core/daemons.c @@ -254,6 +254,16 @@ void uwsgi_detach_daemons() { // unregister daemon to prevent it from being respawned ud->registered = 0; } + + // smart daemons that have to be notified when master is reloading or stopping + if (ud->notifypid && ud->pid > 0 && ud->pidfile) { + if (uwsgi_instance_is_reloading) { + kill(-(ud->pid), ud->reload_signal > 0 ? ud->reload_signal : SIGHUP); + } + else { + kill(-(ud->pid), ud->stop_signal); + } + } ud = ud->next; } } @@ -520,6 +530,7 @@ void uwsgi_opt_add_daemon2(char *opt, char *value, void *none) { char *d_ns_pid = NULL; char *d_chdir = NULL; char *d_max_throttle = NULL; + char *d_notifypid = NULL; char *arg = uwsgi_str(value); @@ -543,6 +554,7 @@ void uwsgi_opt_add_daemon2(char *opt, char *value, void *none) { "ns_pid", &d_ns_pid, "chdir", &d_chdir, "max_throttle", &d_max_throttle, + "notifypid", &d_notifypid, NULL)) { uwsgi_log("invalid --%s keyval syntax\n", opt); exit(1); @@ -594,6 +606,8 @@ void uwsgi_opt_add_daemon2(char *opt, char *value, void *none) { uwsgi_ud->max_throttle = d_max_throttle ? atoi(d_max_throttle) : 0; + uwsgi_ud->notifypid = d_notifypid ? 1 : 0; + if (d_touch) { size_t i,rlen = 0; char **argv = uwsgi_split_quoted(d_touch, strlen(d_touch), ";", &rlen); diff --git a/uwsgi.h b/uwsgi.h index 3bf473bd0e..27bbbfbc8f 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -625,6 +625,8 @@ struct uwsgi_daemon { char *chdir; int max_throttle; + + int notifypid; }; struct uwsgi_logger { From 8db86221bf82f7ebc7050125b9b2585fdbc30be1 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 25 Jan 2020 11:58:44 +0100 Subject: [PATCH 043/207] yaml/libyaml: Fix keys ignored after sequence, support non-indented sequence entries This patch re-purposes the in_uwsgi_section variable to track the depth of the configuration. Previously, leaving a nested sequence would stop the parsing process, ignoring any keys following the sequence. Further, support non-indented sequences for which no YAML_BLOCK_SEQUENCE_START_TOKEN is emitted. The entries are preceded by the YAML_BLOCK_ENTRY_TOKEN, however, which can be used instead to recognize these entries. The following configuration is parsed sensibly after this patch when compiled with libyaml: uwsgi: http-socket: ':4040' wsgi: tests.hello static-map: - /static/js=/server/static/js - /static/css=/server/static/css cache2: [ "name=thumbnails,items=200", "name=videos,items=100" ] env: # this is missing the SEQUENCE_START_TOKEN - ENVIRONMENT=prod - "DATABASE_URL=mysql://localhost" workers: 4 thunder-lock: true # Verified with --show-config ./uwsgi -y ./config.yaml --show-config ... ;uWSGI instance configuration [uwsgi] yaml = ./config.yaml http-socket = :4040 wsgi = tests.hello static-map = /static/js=/server/static/js static-map = /static/css=/server/static/css cache2 = name=thumbnails,items=200 cache2 = name=videos,items=100 env = ENVIRONMENT=prod env = DATABASE_URL=mysql://localhost workers = 4 thunder-lock = true show-config = true ;end of configuration Fixes #2097 (but *only* if libyaml is used). --- core/yaml.c | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/core/yaml.c b/core/yaml.c index e1fd6736d1..edaad760ef 100644 --- a/core/yaml.c +++ b/core/yaml.c @@ -156,44 +156,49 @@ void uwsgi_yaml_config(char *file, char *magic_table[]) { break; case YAML_FLOW_SEQUENCE_START_TOKEN: case YAML_BLOCK_SEQUENCE_START_TOKEN: - status = 3; + if (in_uwsgi_section) + in_uwsgi_section++; + // fallthrough + case YAML_FLOW_ENTRY_TOKEN: + case YAML_BLOCK_ENTRY_TOKEN: + status = 3; // inside a sequence break; case YAML_BLOCK_MAPPING_START_TOKEN: - if (!in_uwsgi_section) { - if (key) { - if (!strcmp(section_asked, key)) { - in_uwsgi_section = 1; - } + if (in_uwsgi_section) { + in_uwsgi_section++; + break; + } + if (key) { + if (!strcmp(section_asked, key)) { + in_uwsgi_section = 1; } } break; case YAML_BLOCK_END_TOKEN: - if (in_uwsgi_section) { - parsing = 0; - break; - } + case YAML_FLOW_SEQUENCE_END_TOKEN: + if (in_uwsgi_section) + parsing = !!(--in_uwsgi_section); + key = NULL; + status = 0; break; case YAML_SCALAR_TOKEN: - case YAML_FLOW_ENTRY_TOKEN: - case YAML_BLOCK_ENTRY_TOKEN: if (status == 1) { key = (char *) token.data.scalar.value; } - else if (status == 2) { + else if (status == 2 || status == 3) { val = (char *) token.data.scalar.value; if (key && val && in_uwsgi_section) { add_exported_option(key, val, 0); } - status = 0; - } - else if (status == 3) { - val = (char *) token.data.scalar.value; - if (key && val && in_uwsgi_section) { - add_exported_option(key, val, 0); + + // If this was the scalar of a value token, forget the state. + if (status == 2) { + key = NULL; + status = 0; } } else { - uwsgi_log("unsupported YAML token in %s block\n", section_asked); + uwsgi_log("unsupported YAML token %d in %s block\n", token.type, section_asked); parsing = 0; break; } From fe496f6c3a2421a9e725fc41d846ad857b4c2045 Mon Sep 17 00:00:00 2001 From: triangularcover Date: Tue, 3 Mar 2020 12:09:51 -0800 Subject: [PATCH 044/207] Update uwsgi.c fixing typo Fix typo --- core/uwsgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/uwsgi.c b/core/uwsgi.c index 7a92b87f49..d991b217df 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -773,7 +773,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"alarm", required_argument, 0, "create a new alarm, syntax: ", uwsgi_opt_add_string_list, &uwsgi.alarm_list, UWSGI_OPT_MASTER}, {"alarm-cheap", required_argument, 0, "use main alarm thread rather than create dedicated threads for curl-based alarms", uwsgi_opt_true, &uwsgi.alarm_cheap, 0}, {"alarm-freq", required_argument, 0, "tune the anti-loop alarm system (default 3 seconds)", uwsgi_opt_set_int, &uwsgi.alarm_freq, 0}, - {"alarm-fd", required_argument, 0, "raise the specified alarm when an fd is read for read (by default it reads 1 byte, set 8 for eventfd)", uwsgi_opt_add_string_list, &uwsgi.alarm_fd_list, UWSGI_OPT_MASTER}, + {"alarm-fd", required_argument, 0, "raise the specified alarm when an fd is ready for read (by default it reads 1 byte, set 8 for eventfd)", uwsgi_opt_add_string_list, &uwsgi.alarm_fd_list, UWSGI_OPT_MASTER}, {"alarm-segfault", required_argument, 0, "raise the specified alarm when the segmentation fault handler is executed", uwsgi_opt_add_string_list, &uwsgi.alarm_segfault, UWSGI_OPT_MASTER}, {"segfault-alarm", required_argument, 0, "raise the specified alarm when the segmentation fault handler is executed", uwsgi_opt_add_string_list, &uwsgi.alarm_segfault, UWSGI_OPT_MASTER}, {"alarm-backlog", required_argument, 0, "raise the specified alarm when the socket backlog queue is full", uwsgi_opt_add_string_list, &uwsgi.alarm_backlog, UWSGI_OPT_MASTER}, From 24e6a92a4c2cd912c03aceb6829e57cde7900272 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Wed, 4 Mar 2020 17:07:51 +0100 Subject: [PATCH 045/207] plugins/cgi: adds dontresolve option This option permit to call the simbolic link instead of the file the simbolic link points. All the security check are still done as the simbolic path is passed at the end after all the checks are passed. This is useful if some cgi app are used for multiple function based on the name they are called by. Signed-off-by: Ansuel Smith --- plugins/cgi/cgi_plugin.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/cgi/cgi_plugin.c b/plugins/cgi/cgi_plugin.c index d032db17c3..d640b5e555 100644 --- a/plugins/cgi/cgi_plugin.c +++ b/plugins/cgi/cgi_plugin.c @@ -22,6 +22,7 @@ struct uwsgi_cgi { int do_not_kill_on_error; int async_max_attempts; int close_stdin_on_eof; + int dontresolve; } uc ; static void uwsgi_opt_add_cgi(char *opt, char *value, void *foobar) { @@ -75,6 +76,8 @@ struct uwsgi_option uwsgi_cgi_options[] = { {"cgi-safe", required_argument, 0, "skip security checks if the cgi file is under the specified path", uwsgi_opt_add_string_list, &uc.cgi_safe, 0}, + {"cgi-dontresolve", no_argument, 0 , "call symbolic link directly instead of the real path", uwsgi_opt_true,&uc.dontresolve, 0}, + {0, 0, 0, 0, 0, 0, 0}, }; @@ -475,6 +478,7 @@ static int uwsgi_cgi_request(struct wsgi_request *wsgi_req) { char full_path[PATH_MAX]; char tmp_path[PATH_MAX]; + char symbolic_path[PATH_MAX]; struct stat cgi_stat; int need_free = 0; int is_a_file = 0; @@ -533,6 +537,10 @@ static int uwsgi_cgi_request(struct wsgi_request *wsgi_req) { uwsgi_404(wsgi_req); return UWSGI_OK; } + if (uc.dontresolve) { + full_path_len = strlen(full_path); + memcpy(symbolic_path, full_path, full_path_len+1); + } full_path_len = strlen(tmp_path); // add +1 to copy the null byte @@ -639,6 +647,11 @@ static int uwsgi_cgi_request(struct wsgi_request *wsgi_req) { } } + if (uc.dontresolve) { + full_path_len = strlen(symbolic_path); + memcpy(full_path, symbolic_path, full_path_len+1); + } + int ret = uwsgi_cgi_run(wsgi_req, docroot, docroot_len, full_path, helper, path_info, script_name, is_a_file, discard_base); if (need_free) free(docroot); return ret; From 72a30c48ac388fc81c244093c7ba9a59ae511899 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Thu, 5 Mar 2020 12:59:23 +0100 Subject: [PATCH 046/207] ported --emperor-graceful-shutdown --- core/emperor.c | 15 ++++++++++++--- core/uwsgi.c | 1 + uwsgi.h | 3 +++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/emperor.c b/core/emperor.c index c878767449..eae7f901cd 100644 --- a/core/emperor.c +++ b/core/emperor.c @@ -918,7 +918,7 @@ void emperor_back_to_ondemand(struct uwsgi_instance *c_ui) { // remove uWSGI instance if (c_ui->pid != -1) { - if (write(c_ui->pipe[0], "\0", 1) != 1) { + if (write(c_ui->pipe[0], uwsgi.emperor_graceful_shutdown ? "\2" : "\0", 1) != 1) { uwsgi_error("emperor_stop()/write()"); } } @@ -941,7 +941,7 @@ void emperor_stop(struct uwsgi_instance *c_ui) { } if (c_ui->pid != -1) { - if (write(c_ui->pipe[0], "\0", 1) != 1) { + if (write(c_ui->pipe[0], uwsgi.emperor_graceful_shutdown ? "\2" : "\0", 1) != 1) { uwsgi_error("emperor_stop()/write()"); } } @@ -2970,8 +2970,9 @@ void uwsgi_master_manage_emperor() { if (byte == 0) { uwsgi_hooks_run(uwsgi.hook_emperor_stop, "emperor-stop", 0); close(uwsgi.emperor_fd); - if (!uwsgi.status.brutally_reloading) + if (!uwsgi.status.brutally_reloading && !uwsgi.status.brutally_destroying) { kill_them_all(0); + } } // reload me else if (byte == 1) { @@ -2982,6 +2983,14 @@ void uwsgi_master_manage_emperor() { grace_them_all(0); uwsgi_unblock_signal(SIGHUP); } + // remove me gracefully + else if (byte == 2) { + uwsgi_hooks_run(uwsgi.hook_emperor_stop, "emperor-stop", 0); + close(uwsgi.emperor_fd); + if (!uwsgi.status.brutally_reloading && !uwsgi.status.brutally_destroying) { + gracefully_kill_them_all(0); + } + } } #ifdef UWSGI_EVENT_USE_PORT // special cose for port event system diff --git a/core/uwsgi.c b/core/uwsgi.c index d991b217df..c221831402 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -255,6 +255,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"emperor-use-fork-server", required_argument, 0, "connect to the specified fork server instead of using plain fork() for new vassals", uwsgi_opt_set_str, &uwsgi.emperor_use_fork_server, 0}, {"vassal-fork-base", required_argument, 0, "use plain fork() for the specified vassal (instead of a fork-server)", uwsgi_opt_add_string_list, &uwsgi.vassal_fork_base, 0}, {"emperor-subreaper", no_argument, 0, "force the Emperor to be a sub-reaper (if supported)", uwsgi_opt_true, &uwsgi.emperor_subreaper, 0}, + {"emperor-graceful-shutdown", no_argument, 0, "use vassals graceful shutdown during ragnarok", uwsgi_opt_true, &uwsgi.emperor_graceful_shutdown, 0}, #ifdef UWSGI_CAP {"emperor-cap", required_argument, 0, "set vassals capability", uwsgi_opt_set_emperor_cap, NULL, 0}, {"vassals-cap", required_argument, 0, "set vassals capability", uwsgi_opt_set_emperor_cap, NULL, 0}, diff --git a/uwsgi.h b/uwsgi.h index 27bbbfbc8f..050b545d85 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2859,6 +2859,9 @@ struct uwsgi_server { int tlsv1; #endif + // uWSGI 2.0.19 + int emperor_graceful_shutdown; + size_t response_header_limit; // uWSGI 2.1 From 0e60a88f878c3bfa1009b29aab2a9336c41cddc3 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 7 Mar 2020 21:43:24 +0100 Subject: [PATCH 047/207] [RFC] chroot: Do not chroot multiple times when root From 2.0.15 to 2.0.16 the uwsgi_setup() sequence was modified such that uwsgi_as_root() may be called multiple times. If the `uid` option is used, then on later invocation most of this function is skipped. However, if run as root, then most everything in this function is run again. In #2128 it was reported that the second chroot() call fails. This patch attempts to fix this for `chroot()` only. However, this is a bit naive - the other sandboxing calls inside uwsgi_as_root() should probably not be run multiple times either (unshare() / capabilities), but I'm not 100% sure about the semantics, particularly concerning uwsgi.master_as_root, so just presenting this as an RFC. Related, uwsgi 2.0.18 + chroot + uid is not working: $ sudo uwsgi --uid 1000 --chroot / *** Starting uWSGI 2.0.18 (64bit) on [Sat Mar 7 22:05:18 2020] *** ... detected binary path: /home/awelzel/venv/bin/uwsgi uWSGI running as root, you can use --uid/--gid/--chroot options chroot() to / setuid() to 1000 ... thunder lock: disabled (you can enable it with --thunder-lock) cannot chroot() as non-root user ... This patch happens to fix the above issue. --- core/utils.c | 6 ++++-- uwsgi.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/utils.c b/core/utils.c index 2f6c974284..221af819e6 100644 --- a/core/utils.c +++ b/core/utils.c @@ -580,13 +580,15 @@ void uwsgi_as_root() { } } - if (uwsgi.chroot && !uwsgi.reloads) { + if (uwsgi.chroot && !uwsgi.is_chrooted && !uwsgi.reloads) { if (!uwsgi.master_as_root) uwsgi_log("chroot() to %s\n", uwsgi.chroot); + if (chroot(uwsgi.chroot)) { uwsgi_error("chroot()"); exit(1); } + uwsgi.is_chrooted = 1; #ifdef __linux__ if (uwsgi.logging_options.memory_report) { uwsgi_log("*** Warning, on linux system you have to bind-mount the /proc fs in your chroot to get memory debug/report.\n"); @@ -987,7 +989,7 @@ void uwsgi_as_root() { return; nonroot: - if (uwsgi.chroot && !uwsgi.is_a_reload) { + if (uwsgi.chroot && !uwsgi.is_chrooted && !uwsgi.is_a_reload) { uwsgi_log("cannot chroot() as non-root user\n"); exit(1); } diff --git a/uwsgi.h b/uwsgi.h index 050b545d85..db43de4dcc 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2861,6 +2861,7 @@ struct uwsgi_server { // uWSGI 2.0.19 int emperor_graceful_shutdown; + int is_chrooted; size_t response_header_limit; From 71464fbda59c5e3668bcc3fab8c5aa66935cf95c Mon Sep 17 00:00:00 2001 From: Thomas De Schampheleire Date: Tue, 31 Mar 2020 21:10:29 +0200 Subject: [PATCH 048/207] plugins/{pypy,python}: get rid of unnecessary paste.script dependency paste.script.util.logging_config is an old copy of logging.config from Python 2.5.1 (see [1]). There are no paste-specific details in it. By using logging.config directly, Python WSGI environments no longer need to have pastescript installed. [1] https://bitbucket.org/wilig/pastescript/commits/04d0db6b2f5ab360bdaa5b10511d969a24fde0ec --- plugins/pypy/pypy_setup.py | 4 ++-- plugins/python/pyloader.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/pypy/pypy_setup.py b/plugins/pypy/pypy_setup.py index badf54e446..ecfe14f650 100644 --- a/plugins/pypy/pypy_setup.py +++ b/plugins/pypy/pypy_setup.py @@ -340,10 +340,10 @@ def uwsgi_pypy_paste_loader(config): if c[0] != '/': c = os.getcwd() + '/' + c try: - from paste.script.util.logging_config import fileConfig + from logging.config import fileConfig fileConfig(c) except ImportError: - print("PyPy WARNING: unable to load paste.script.util.logging_config") + print("PyPy WARNING: unable to load logging.config") from paste.deploy import loadapp wsgi_application = loadapp('config:%s' % c) diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c index 5afcc8e7c4..7c380f4dbe 100644 --- a/plugins/python/pyloader.c +++ b/plugins/python/pyloader.c @@ -685,7 +685,7 @@ PyObject *uwsgi_paste_loader(void *arg1) { uwsgi_log( "Loading paste environment: %s\n", paste); if (up.paste_logger) { - PyObject *paste_logger_dict = get_uwsgi_pydict("paste.script.util.logging_config"); + PyObject *paste_logger_dict = get_uwsgi_pydict("logging.config"); if (paste_logger_dict) { PyObject *paste_logger_fileConfig = PyDict_GetItemString(paste_logger_dict, "fileConfig"); if (paste_logger_fileConfig) { From 21e0258c770310ef6fea0cda01a4c8fcba882955 Mon Sep 17 00:00:00 2001 From: Jorge Gallegos Date: Mon, 6 Apr 2020 19:33:29 -0500 Subject: [PATCH 049/207] fix psgi plugin for GCC 10 Following guide here https://gcc.gnu.org/gcc-10/porting_to.html --- plugins/psgi/psgi.h | 2 ++ plugins/psgi/psgi_loader.c | 1 - plugins/psgi/psgi_plugin.c | 4 ---- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/psgi/psgi.h b/plugins/psgi/psgi.h index 220eab334e..c96d8da681 100644 --- a/plugins/psgi/psgi.h +++ b/plugins/psgi/psgi.h @@ -93,3 +93,5 @@ void uwsgi_perl_check_auto_reload(void); void uwsgi_psgi_preinit_apps(void); int uwsgi_perl_add_app(struct wsgi_request *, char *, PerlInterpreter **, SV **, time_t); + +extern struct uwsgi_perl uperl; diff --git a/plugins/psgi/psgi_loader.c b/plugins/psgi/psgi_loader.c index cdcecc1d13..b28bbf2e9c 100644 --- a/plugins/psgi/psgi_loader.c +++ b/plugins/psgi/psgi_loader.c @@ -1,7 +1,6 @@ #include "psgi.h" extern struct uwsgi_server uwsgi; -struct uwsgi_perl uperl; extern struct uwsgi_plugin psgi_plugin; diff --git a/plugins/psgi/psgi_plugin.c b/plugins/psgi/psgi_plugin.c index 0b1ce190ed..d2703aacb2 100644 --- a/plugins/psgi/psgi_plugin.c +++ b/plugins/psgi/psgi_plugin.c @@ -3,11 +3,7 @@ extern char **environ; extern struct uwsgi_server uwsgi; -#ifdef __APPLE__ -extern struct uwsgi_perl uperl; -#else struct uwsgi_perl uperl; -#endif struct uwsgi_plugin psgi_plugin; From 1002f11ab74084b08616da4928959416e932b89a Mon Sep 17 00:00:00 2001 From: ols Date: Thu, 9 Apr 2020 12:48:16 +0300 Subject: [PATCH 050/207] At the end of the stream, the uwsgi_chunked_read function returns len 0 and a pointer to a buffer filled with old data. Based on the description: https://perldoc.perl.org/5.16.1/perlapi.html, The newSVpv function in the case when the len is zero uses the strlen. This makes it impossible to determine the end of the stream since the last chunk returns endlessly. --- plugins/psgi/uwsgi_plmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/psgi/uwsgi_plmodule.c b/plugins/psgi/uwsgi_plmodule.c index e875fbec04..7298a3ee8f 100644 --- a/plugins/psgi/uwsgi_plmodule.c +++ b/plugins/psgi/uwsgi_plmodule.c @@ -885,7 +885,7 @@ XS(XS_chunked_read) { XSRETURN_UNDEF; } - ST(0) = newSVpv(chunk, len); + ST(0) = newSVpvn(chunk, len); sv_2mortal(ST(0)); XSRETURN(1); } @@ -904,7 +904,7 @@ XS(XS_chunked_read_nb) { XSRETURN_UNDEF; } - ST(0) = newSVpv(chunk, len); + ST(0) = newSVpvn(chunk, len); sv_2mortal(ST(0)); XSRETURN(1); } From aa86def683c9143cd6fb386906b4e8827b40695a Mon Sep 17 00:00:00 2001 From: Denis Dowling Date: Wed, 15 Apr 2020 09:25:24 +1000 Subject: [PATCH 051/207] Ensure '-lrt' is included on the link line after linking to python library. Python depends on clock_gettime() and other functions in librt. Nothing else in uwsgi has a dependency on librt so if this library is only specified early on the link line then the library is dropped by the linker. --- plugins/python/uwsgiplugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/python/uwsgiplugin.py b/plugins/python/uwsgiplugin.py index 1b33972122..fae3bf5fba 100644 --- a/plugins/python/uwsgiplugin.py +++ b/plugins/python/uwsgiplugin.py @@ -69,6 +69,8 @@ def get_python_version(): # hack for messy linkers/compilers if '-lutil' in LIBS: LIBS.append('-lutil') + if '-lrt' in LIBS: + LIBS.append('-lrt') else: try: libdir = sysconfig.get_config_var('LIBDIR') From 9815bf5fd2c72b5621c882d36e53fd7e3d8b8beb Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Wed, 15 Apr 2020 14:57:02 +0200 Subject: [PATCH 052/207] gevent plugin: adapt to removal of deprecated interfaces in gevent 1.5.0 (#2149) Adapt to removal of deprecated interfaces in gevent 1.5.0 still keep compatibility with gevent 1.1 & 1.2 series. --- plugins/gevent/gevent.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/gevent/gevent.c b/plugins/gevent/gevent.c index 5ddf100ddf..1b91417cf3 100644 --- a/plugins/gevent/gevent.c +++ b/plugins/gevent/gevent.c @@ -451,8 +451,13 @@ static void gevent_loop() { ugevent.spawn = PyDict_GetItemString(gevent_dict, "spawn"); if (!ugevent.spawn) uwsgi_pyexit; - ugevent.signal = PyDict_GetItemString(gevent_dict, "signal"); - if (!ugevent.signal) uwsgi_pyexit; + ugevent.signal = PyDict_GetItemString(gevent_dict, "signal_handler"); + if (!ugevent.signal) { + // gevent.signal_handler appears in gevent 1.3. + // On older gevent, fall back to the deprecated gevent.signal. + ugevent.signal = PyDict_GetItemString(gevent_dict, "signal"); + if (!ugevent.signal) uwsgi_pyexit; + } ugevent.greenlet_switch = PyDict_GetItemString(gevent_dict, "sleep"); if (!ugevent.greenlet_switch) uwsgi_pyexit; From fd87120e4fd96071de52ecd989b064db4cce923e Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Mon, 20 Apr 2020 08:07:05 +0200 Subject: [PATCH 053/207] plugins/cgi: improve performance for santitizing file descriptors Only close file descriptors that are known to be open when /proc/self/fd or /dev/fd exists. Fall back and loop over all when those are missing. This improves performance significantly when number of max fds is high. fixes #2053 --- plugins/cgi/cgi_plugin.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/cgi/cgi_plugin.c b/plugins/cgi/cgi_plugin.c index d640b5e555..2b21c2f65a 100644 --- a/plugins/cgi/cgi_plugin.c +++ b/plugins/cgi/cgi_plugin.c @@ -793,8 +793,21 @@ static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t do close(cgi_pipe[1]); // close all the fd > 2 - for(i=3;i<(int)uwsgi.max_fd;i++) { - close(i); + DIR *dirp = opendir("/proc/self/fd"); + if (dirp == NULL) + dirp = opendir("/dev/fd"); + if (dirp != NULL) { + struct dirent *dent; + while ((dent = readdir(dirp)) != NULL) { + int fd = atoi(dent->d_name); + if ((fd > 2) && fd != dirfd(dirp)) + close(fd); + } + closedir(dirp); + } else { + for(i=3;i<(int)uwsgi.max_fd;i++) { + close(i); + } } // fill cgi env From 8ed288bfbc1313f4b6af9db1384e879464556f9d Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Sat, 9 May 2020 17:15:39 -0400 Subject: [PATCH 054/207] improve plugin errors with execfile --- uwsgiconfig.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 387fb323b8..b777db6b81 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1390,6 +1390,15 @@ def get_remote_plugin(path): return git_dir +try: + execfile +except NameError: + def execfile(path, up): + with open(path) as py: + code = __builtins__.compile(py.read(), path, 'exec') + exec(code, up) + + def get_plugin_up(path): up = {} if os.path.isfile(path): @@ -1401,12 +1410,7 @@ def get_plugin_up(path): if not path: path = '.' elif os.path.isdir(path): - try: - execfile('%s/uwsgiplugin.py' % path, up) - except Exception: - f = open('%s/uwsgiplugin.py' % path) - exec(f.read(), up) - f.close() + execfile('%s/uwsgiplugin.py' % path, up) else: print("Error: unable to find directory '%s'" % path) sys.exit(1) From 6716c4152fc402b923079984a3ce4ac4a0fc79b1 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 10 May 2020 16:18:24 +0200 Subject: [PATCH 055/207] Drop unused declarations in uwsgi.h Thanks dholth for spotting these. Refs #2160 --- core/uwsgi.c | 4 ---- uwsgi.h | 30 ------------------------------ 2 files changed, 34 deletions(-) diff --git a/core/uwsgi.c b/core/uwsgi.c index c221831402..2fcab242b9 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -3193,10 +3193,6 @@ int uwsgi_start(void *v_argv) { if (uwsgi.logformat) { uwsgi_build_log_format(uwsgi.logformat); uwsgi.logit = uwsgi_logit_lf; - // TODO check it - //if (uwsgi.logformat_strftime) { - //uwsgi.logit = uwsgi_logit_lf_strftime; - //} uwsgi.logvectors = uwsgi_malloc(sizeof(struct iovec *) * uwsgi.cores); for (j = 0; j < uwsgi.cores; j++) { uwsgi.logvectors[j] = uwsgi_malloc(sizeof(struct iovec) * uwsgi.logformat_vectors); diff --git a/uwsgi.h b/uwsgi.h index aa284d278a..c1a47d30bb 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -3239,7 +3239,6 @@ void uwsgi_405(struct wsgi_request *); void uwsgi_redirect_to_slash(struct wsgi_request *); void manage_snmp(int, uint8_t *, int, struct sockaddr_in *); -void snmp_init(void); void uwsgi_master_manage_snmp(int); @@ -3272,21 +3271,12 @@ uint32_t uwsgi_swap32(uint32_t); uint64_t uwsgi_swap64(uint64_t); #endif -int uwsgi_parse_request(int, struct wsgi_request *, int); int uwsgi_parse_vars(struct wsgi_request *); -int uwsgi_enqueue_message(char *, int, uint8_t, uint8_t, char *, int, int); - -void manage_opt(int, char *); - -int uwsgi_ping_node(int, struct wsgi_request *); - void uwsgi_async_init(void); void async_loop(); struct wsgi_request *find_first_available_wsgi_req(void); -struct wsgi_request *find_first_accepting_wsgi_req(void); struct wsgi_request *find_wsgi_req_by_fd(int); -struct wsgi_request *find_wsgi_req_by_id(int); void async_schedule_to_req_green(void); void async_schedule_to_req(void); @@ -3294,9 +3284,6 @@ int async_add_fd_write(struct wsgi_request *, int, int); int async_add_fd_read(struct wsgi_request *, int, int); void async_reset_request(struct wsgi_request *); -struct wsgi_request *next_wsgi_req(struct wsgi_request *); - - void async_add_timeout(struct wsgi_request *, int); void uwsgi_as_root(void); @@ -3378,29 +3365,18 @@ void *uwsgi_get_loop(char *); void add_exported_option(char *, char *, int); void add_exported_option_do(char *, char *, int, int); -ssize_t uwsgi_send_empty_pkt(int, char *, uint8_t, uint8_t); - int uwsgi_waitfd_event(int, int, int); #define uwsgi_waitfd(a, b) uwsgi_waitfd_event(a, b, POLLIN) #define uwsgi_waitfd_write(a, b) uwsgi_waitfd_event(a, b, POLLOUT) -int uwsgi_hooked_parse_dict_dgram(int, char *, size_t, uint8_t, uint8_t, void (*)(char *, uint16_t, char *, uint16_t, void *), void *); int uwsgi_hooked_parse(char *, size_t, void (*)(char *, uint16_t, char *, uint16_t, void *), void *); int uwsgi_hooked_parse_array(char *, size_t, void (*) (uint16_t, char *, uint16_t, void *), void *); -int uwsgi_get_dgram(int, struct wsgi_request *); - -int uwsgi_string_sendto(int, uint8_t, uint8_t, struct sockaddr *, socklen_t, char *, size_t); - -void uwsgi_stdin_sendto(char *, uint8_t, uint8_t); - char *generate_socket_name(char *); #define UMIN(a,b) ((a)>(b)?(b):(a)) #define UMAX(a,b) ((a)<(b)?(b):(a)) -ssize_t uwsgi_send_message(int, uint8_t, uint8_t, char *, uint16_t, int, ssize_t, int); - int uwsgi_cache_set2(struct uwsgi_cache *, char *, uint16_t, char *, uint64_t, uint64_t, uint64_t); int uwsgi_cache_del2(struct uwsgi_cache *, char *, uint16_t, uint64_t, uint16_t); char *uwsgi_cache_get2(struct uwsgi_cache *, char *, uint16_t, uint64_t *); @@ -3624,7 +3600,6 @@ int uwsgi_proto_base_writev(struct wsgi_request *, struct iovec *, size_t *); #ifdef UWSGI_SSL int uwsgi_proto_ssl_write(struct wsgi_request *, char *, size_t); #endif -int uwsgi_proto_base_write_header(struct wsgi_request *, char *, size_t); ssize_t uwsgi_proto_base_read_body(struct wsgi_request *, char *, size_t); ssize_t uwsgi_proto_noop_read_body(struct wsgi_request *, char *, size_t); #ifdef UWSGI_SSL @@ -3969,7 +3944,6 @@ void uwsgi_opt_set_rawint(char *, char *, void *); void uwsgi_opt_set_16bit(char *, char *, void *); void uwsgi_opt_set_64bit(char *, char *, void *); void uwsgi_opt_set_megabytes(char *, char *, void *); -void uwsgi_opt_set_dyn(char *, char *, void *); void uwsgi_opt_set_placeholder(char *, char *, void *); void uwsgi_opt_add_shared_socket(char *, char *, void *); void uwsgi_opt_add_socket(char *, char *, void *); @@ -4113,7 +4087,6 @@ int uwsgi_calc_cheaper(void); int uwsgi_cheaper_algo_spare(int); int uwsgi_cheaper_algo_spare2(int); int uwsgi_cheaper_algo_backlog(int); -int uwsgi_cheaper_algo_backlog2(int); int uwsgi_cheaper_algo_manual(int); int uwsgi_master_log(void); @@ -4284,7 +4257,6 @@ struct uwsgi_logchunk *uwsgi_register_logchunk(char *, ssize_t (*)(struct wsgi_r void uwsgi_logit_simple(struct wsgi_request *); void uwsgi_logit_lf(struct wsgi_request *); -void uwsgi_logit_lf_strftime(struct wsgi_request *); struct uwsgi_logvar *uwsgi_logvar_get(struct wsgi_request *, char *, uint8_t); void uwsgi_logvar_add(struct wsgi_request *, char *, uint8_t, char *, uint8_t); @@ -4583,7 +4555,6 @@ void uwsgi_loop_cores_run(void *(*)(void *)); int uwsgi_kvlist_parse(char *, size_t, char, int, ...); int uwsgi_send_http_stats(int); -ssize_t uwsgi_simple_request_read(struct wsgi_request *, char *, size_t); int uwsgi_plugin_modifier1(char *); void *cache_udp_server_loop(void *); @@ -4658,7 +4629,6 @@ int uwsgi_response_prepare_headers(struct wsgi_request *, char *, uint16_t); int uwsgi_response_prepare_headers_int(struct wsgi_request *, int); int uwsgi_response_add_header(struct wsgi_request *, char *, uint16_t, char *, uint16_t); int uwsgi_response_add_header_force(struct wsgi_request *, char *, uint16_t, char *, uint16_t); -int uwsgi_response_commit_headers(struct wsgi_request *); int uwsgi_response_sendfile_do(struct wsgi_request *, int, size_t, size_t); int uwsgi_response_sendfile_do_can_close(struct wsgi_request *, int, size_t, size_t, int); From e3bed6a7172a03c2f03bc1355634ad7e9994b260 Mon Sep 17 00:00:00 2001 From: jacobtolar Date: Mon, 18 May 2020 16:53:09 -0500 Subject: [PATCH 056/207] Fix possible deadlock in install When using`stderr=subprocess.PIPE, stdout=subprocess.PIPE`, you can (will) deadlock if one pipe fills up (in this case, stdout) while the other is being written too. The subprocess documentation is littered with warnings about this but somehow this has survived here for nearly 10 years. :) --- uwsgiconfig.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index b777db6b81..1712e54516 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -220,11 +220,12 @@ def has_usable_ucontext(): def spcall3(cmd): p = subprocess.Popen(cmd, shell=True, stdin=open('/dev/null'), stderr=subprocess.PIPE, stdout=subprocess.PIPE) + (out, err) = p.communicate() - if p.wait() == 0: + if p.returncode == 0: if sys.version_info[0] > 2: - return p.stderr.read().rstrip().decode() - return p.stderr.read().rstrip() + return err.rstrip().decode() + return err.rstrip() else: return None From 926786c32ec10987d7613fc2669de3efffc4e556 Mon Sep 17 00:00:00 2001 From: Huehueh Date: Tue, 19 May 2020 09:29:36 +0200 Subject: [PATCH 057/207] =?UTF-8?q?Fixed=20logging=20issue=20-=20size=20of?= =?UTF-8?q?=20packege=20shown=20as=20negative=20value=20when=20it=E2=80=A6?= =?UTF-8?q?=20(#1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed logging issue - size of packege shown as negative value when it was greater than INT_MAX * uwsgi_size2str(size_t) method refactored --- core/logging.c | 7 +++---- core/utils.c | 6 ++++++ uwsgi.h | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/logging.c b/core/logging.c index 3d0559b954..3cd216f96e 100644 --- a/core/logging.c +++ b/core/logging.c @@ -1128,19 +1128,18 @@ static ssize_t uwsgi_lf_status(struct wsgi_request *wsgi_req, char **buf) { return strlen(*buf); } - static ssize_t uwsgi_lf_rsize(struct wsgi_request *wsgi_req, char **buf) { - *buf = uwsgi_num2str(wsgi_req->response_size); + *buf = uwsgi_size2str(wsgi_req->response_size); return strlen(*buf); } static ssize_t uwsgi_lf_hsize(struct wsgi_request *wsgi_req, char **buf) { - *buf = uwsgi_num2str(wsgi_req->headers_size); + *buf = uwsgi_size2str(wsgi_req->headers_size); return strlen(*buf); } static ssize_t uwsgi_lf_size(struct wsgi_request *wsgi_req, char **buf) { - *buf = uwsgi_num2str(wsgi_req->headers_size+wsgi_req->response_size); + *buf = uwsgi_size2str(wsgi_req->headers_size+wsgi_req->response_size); return strlen(*buf); } diff --git a/core/utils.c b/core/utils.c index 221af819e6..1fecbfd675 100644 --- a/core/utils.c +++ b/core/utils.c @@ -1948,6 +1948,12 @@ char *uwsgi_64bit2str(int64_t num) { return str; } +char *uwsgi_size2str(size_t num) { + char *str = uwsgi_malloc(sizeof(UMAX64_STR) + 1); + snprintf(str, sizeof(UMAX64_STR) + 1, "%llu", (unsigned long long) num); + return str; +} + int uwsgi_num2str2(int num, char *ptr) { return snprintf(ptr, 11, "%d", num); diff --git a/uwsgi.h b/uwsgi.h index aa284d278a..0fa982573a 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -3487,6 +3487,7 @@ void emperor_loop(void); char *uwsgi_num2str(int); char *uwsgi_float2str(float); char *uwsgi_64bit2str(int64_t); +char *uwsgi_size2str(size_t); char *magic_sub(char *, size_t, size_t *, char *[]); void init_magic_table(char *[]); From ddd96b379fb2a1b87c360faef0950a61cd6c1d6a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sat, 23 May 2020 17:02:03 +0200 Subject: [PATCH 058/207] Guard platform specific declarations in uwsgi.h Thanks dholth for spotting these. Refs #2160 --- uwsgi.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uwsgi.h b/uwsgi.h index c1a47d30bb..891e49438a 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -5106,7 +5106,11 @@ char *uwsgi_subscription_algo_name(void *); int uwsgi_wait_for_fs(char *, int); int uwsgi_wait_for_mountpoint(char *); int uwsgi_wait_for_socket(char *); + +#if defined(__linux__) && !defined(OBSOLETE_LINUX_KERNEL) void uwsgi_hooks_setns_run(struct uwsgi_string_list *, pid_t, uid_t, gid_t); +#endif + char *vassal_attr_get(struct uwsgi_instance *, char *); int vassal_attr_get_multi(struct uwsgi_instance *, char *, int (*)(struct uwsgi_instance *, char *, void *), void *); From d9346b0b461ea14e234e465e742ee16ec46dacbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyril=20Ba=C3=BF?= Date: Mon, 1 Jun 2020 13:30:57 +0200 Subject: [PATCH 059/207] Correctly handle SERVER_PORT for IPv6 sockets. --- proto/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/http.c b/proto/http.c index 981401c06b..e70b77344e 100644 --- a/proto/http.c +++ b/proto/http.c @@ -311,7 +311,7 @@ static int http_parse(struct wsgi_request *wsgi_req, char *watermark) { } } else { - char *server_port = strchr(wsgi_req->socket->name, ':'); + char *server_port = strrchr(wsgi_req->socket->name, ':'); if (server_port) { wsgi_req->len += proto_base_add_uwsgi_var(wsgi_req, "SERVER_PORT", 11, server_port+1, strlen(server_port+1)); } From cdea4fb24b074b9b3cc52eb2cb13907cbb793750 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Sat, 6 Jun 2020 15:26:14 -0400 Subject: [PATCH 060/207] update welcome.py, welcome3.py examples --- examples/welcome.py | 99 +++++++++++++++++++++++++++++--------------- examples/welcome3.py | 47 ++++++++++++++------- 2 files changed, 98 insertions(+), 48 deletions(-) diff --git a/examples/welcome.py b/examples/welcome.py index 5aa4ca88b0..ea1ccbfb8b 100644 --- a/examples/welcome.py +++ b/examples/welcome.py @@ -4,9 +4,13 @@ import sys 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) @@ -14,7 +18,7 @@ print(sys.argv) try: - DEBUG = sys.argv[1] == 'debug' + DEBUG = sys.argv[1] == "debug" except IndexError: DEBUG = False @@ -22,10 +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] @@ -39,56 +44,65 @@ def ciao_mondo(signum): 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 "%s = %s
" % (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"%s = %s
" % (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) + 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 Exception: pass if DEBUG: - print(env['wsgi.input'].fileno()) + print(env["wsgi.input"].fileno()) - 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) if DEBUG: - print(env['wsgi.input'].fileno()) + print(env["wsgi.input"].fileno()) try: gc.collect(2) @@ -98,24 +112,37 @@ def application(env, start_response): if DEBUG: print(len(gc.get_objects())) - workers = '' + workers = "" for w in uwsgi.workers(): apps = '' - for app in w['apps']: - apps += '' % (app['id'], app['mountpoint'], app['startup_time'], app['requests']) - apps += '
idmountpointstartup timerequests
%d%s%d%d
' + for app in w["apps"]: + apps += "%d%s%d%d" % ( + app["id"], + app["mountpoint"], + app["startup_time"], + app["requests"], + ) + apps += "" workers += """ %d%d%s%d%d%d%s - """ % (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 = """ - version %s running on %s (remote user: %s)
+ version %s running on %s (remote user: %s)

Configuration
-
+

Workers and applications
@@ -126,9 +153,15 @@ def application(env, start_response): %s - """ % (uwsgi.version, uwsgi.hostname, env.get('REMOTE_USER', 'None'), workers) + """ % ( + uwsgi.version, + uwsgi.hostname, + env.get("REMOTE_USER", "None"), + workers, + ) - start_response('200 OK', [('Content-Type', 'text/html'), ('Content-Length', str(len(output)))]) + start_response( + "200 OK", [("Content-Type", "text/html"), ("Content-Length", str(len(output)))] + ) - # return bytes(output.encode('latin1')) - return output + return [output.format(script_name=env["SCRIPT_NAME"]).encode("utf-8")] diff --git a/examples/welcome3.py b/examples/welcome3.py index 4d308d17f4..6273ec89ef 100644 --- a/examples/welcome3.py +++ b/examples/welcome3.py @@ -1,35 +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}
".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}
".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 = """ version {version}
@@ -40,6 +55,8 @@ def application(env, start_response):
- """.format(version=uwsgi.version.decode('ascii')) + """.format( + version=uwsgi.version + ) - return bytes(body.encode('ascii')) + return [bytes(body.encode("ascii"))] From 60a878b73618a63631ad6b2fc3e64753717897f2 Mon Sep 17 00:00:00 2001 From: Robert Schindler Date: Fri, 12 Jun 2020 09:40:27 +0200 Subject: [PATCH 061/207] Add chunked request decoding to the CGI plugin When a request using 'Transfer-Encoding: chunked' is routed to the CGI backend, the request body is now read and sent to the script chunk by chunk, allowing for requests with dynamic body length to pass. The CGI process's stdin is always closed after the last chunk has been written, regardless of --cgi-close-stdin-on-eof. A caveat is that it's only started to read the CGI process's output after the full request body has been consumed, meaning a CGI script may not start writing a response before it has read the entire body to avoid getting blocked during the write operation to stdout. Closes #2140 --- plugins/cgi/cgi_plugin.c | 61 +++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/plugins/cgi/cgi_plugin.c b/plugins/cgi/cgi_plugin.c index 2b21c2f65a..89d36bed09 100644 --- a/plugins/cgi/cgi_plugin.c +++ b/plugins/cgi/cgi_plugin.c @@ -704,23 +704,50 @@ static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t do uwsgi_socket_nb(cgi_pipe[0]); uwsgi_socket_nb(post_pipe[1]); - // ok start sending post data... - size_t remains = wsgi_req->post_cl; - while(remains > 0) { - ssize_t rlen = 0; - char *buf = uwsgi_request_body_read(wsgi_req, 8192, &rlen); - if (!buf) { - goto clear2; - } - if (buf == uwsgi.empty) break; - // write data to the node - if (uwsgi_write_true_nb(post_pipe[1], buf, rlen, uc.timeout)) { - goto clear2; - } - remains -= rlen; - } - - if (uc.close_stdin_on_eof) { + // Start sending request body + if (wsgi_req->body_is_chunked) { + // Write through to process chunk by chunk + while (1) { + struct uwsgi_buffer *ubuf = uwsgi_chunked_read_smart(wsgi_req, 8192, uwsgi.socket_timeout); + if (!ubuf) { + uwsgi_log("error reading chunk from CGI request !!!\n"); + kill_on_error + goto clear2; + } + if (!ubuf->pos) { + // Last chunk received, go and close process's stdin + uwsgi_buffer_destroy(ubuf); + break; + } + // Write chunk to process's stdin + int err = uwsgi_write_true_nb(post_pipe[1], ubuf->buf, ubuf->pos, uc.timeout); + uwsgi_buffer_destroy(ubuf); + if (err) { + uwsgi_log("error writing chunk to CGI process !!!\n"); + kill_on_error + goto clear2; + } + } + } else { + // Normal request with content length set + size_t remains = wsgi_req->post_cl; + while(remains > 0) { + ssize_t rlen = 0; + char *buf = uwsgi_request_body_read(wsgi_req, 8192, &rlen); + if (!buf) { + goto clear2; + } + if (buf == uwsgi.empty) break; + // write data to the node + if (uwsgi_write_true_nb(post_pipe[1], buf, rlen, uc.timeout)) { + goto clear2; + } + remains -= rlen; + } + } + + // For chunked requests, close stdin to tell the script body has ended + if (uc.close_stdin_on_eof || wsgi_req->body_is_chunked) { close(post_pipe[1]); stdin_closed = 1; } From 43bd09d6ee3fc46dfc7795ac6a9b5ca34ef61f65 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 12 Jun 2020 21:50:08 -0400 Subject: [PATCH 062/207] use strrchr to get port Noticed an ipv6 --http-socket=[::]:80 had a bad port. --- plugins/corerouter/cr_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/corerouter/cr_common.c b/plugins/corerouter/cr_common.c index e66a162d52..55746d3eb1 100644 --- a/plugins/corerouter/cr_common.c +++ b/plugins/corerouter/cr_common.c @@ -62,7 +62,7 @@ void uwsgi_corerouter_setup_sockets(struct uwsgi_corerouter *ucr) { // fix SERVER_PORT if (!ugs->port || !ugs->port_len) { - ugs->port = strchr(ugs->name, ':'); + ugs->port = strrchr(ugs->name, ':'); if (ugs->port) { ugs->port++; ugs->port_len = strlen(ugs->port); From 95d1d7498bfb9010467edf43fdd00eeed60e4d59 Mon Sep 17 00:00:00 2001 From: Marcin Lulek Date: Wed, 15 May 2019 11:35:34 +0200 Subject: [PATCH 063/207] core: add max-worker-lifetime-delta To not reload workers at the same time, instead with a timeout that depends on their worker id. Fix #2020 --- CONTRIBUTORS | 1 + core/master_checks.c | 2 +- core/uwsgi.c | 1 + uwsgi.h | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 738110f9dc..b5deac8eb0 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -37,3 +37,4 @@ Vladimir Didenko Alexandre Bonnetain Darvame Hleran Sokolov Yura +Marcin Lulek diff --git a/core/master_checks.c b/core/master_checks.c index 5b0c1d1b64..f3d05764d0 100644 --- a/core/master_checks.c +++ b/core/master_checks.c @@ -224,7 +224,7 @@ int uwsgi_master_check_workers_deadline() { // check if worker was running longer than allowed lifetime if (uwsgi.workers[i].pid > 0 && uwsgi.workers[i].cheaped == 0 && uwsgi.max_worker_lifetime > 0) { uint64_t lifetime = uwsgi_now() - uwsgi.workers[i].last_spawn; - if (lifetime > uwsgi.max_worker_lifetime && uwsgi.workers[i].manage_next_request == 1) { + if (lifetime > (uwsgi.max_worker_lifetime + (i-1) * uwsgi.max_worker_lifetime_delta) && uwsgi.workers[i].manage_next_request == 1) { uwsgi_log("worker %d lifetime reached, it was running for %llu second(s)\n", i, (unsigned long long) lifetime); uwsgi.workers[i].manage_next_request = 0; kill(uwsgi.workers[i].pid, SIGWINCH); diff --git a/core/uwsgi.c b/core/uwsgi.c index 2fcab242b9..286f30d719 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -300,6 +300,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"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 60)", 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}, {"socket-timeout", required_argument, 'z', "set internal sockets timeout", uwsgi_opt_set_int, &uwsgi.socket_timeout, 0}, {"no-fd-passing", no_argument, 0, "disable file descriptor passing", uwsgi_opt_true, &uwsgi.no_fd_passing, 0}, diff --git a/uwsgi.h b/uwsgi.h index e203e66605..725d820d74 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2931,6 +2931,8 @@ struct uwsgi_server { int dynamic_apps; struct uwsgi_buffer *websockets_continuation_buffer; + + uint64_t max_worker_lifetime_delta; }; struct uwsgi_rpc { From 8f74f118fdfd583dca0447767882ab6ca4a26395 Mon Sep 17 00:00:00 2001 From: Nick Brachet Date: Mon, 22 Jun 2020 19:17:11 +0000 Subject: [PATCH 064/207] in raw_body mode don't overwrite hr->remains --- plugins/http/http.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/http/http.c b/plugins/http/http.c index 05e3832ea9..a2c9ce82f3 100644 --- a/plugins/http/http.c +++ b/plugins/http/http.c @@ -1224,8 +1224,9 @@ ssize_t http_parse(struct corerouter_peer *main_peer) { } if (hr->remains > 0) { - if (hr->content_length < hr->remains) { - hr->remains = hr->content_length; + if (hr->content_length < hr->remains) { + if (hr->content_length > 0 || !hr->raw_body) + hr->remains = hr->content_length; hr->content_length = 0; // we need to avoid problems with pipelined requests hr->session.can_keepalive = 0; From 4f9e64fddc96ab5bae12a9f2597557b2fa688158 Mon Sep 17 00:00:00 2001 From: Nick Brachet Date: Mon, 22 Jun 2020 19:18:55 +0000 Subject: [PATCH 065/207] (re)acquire GIL before calling any Python method --- plugins/python/wsgi_handlers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/python/wsgi_handlers.c b/plugins/python/wsgi_handlers.c index e56ab4b652..20c6dfeab7 100644 --- a/plugins/python/wsgi_handlers.c +++ b/plugins/python/wsgi_handlers.c @@ -65,6 +65,7 @@ static PyObject *uwsgi_Input_read(uwsgi_Input *self, PyObject *args) { char *buf = NULL; if (wsgi_req->body_is_chunked && up.wsgi_manage_chunked_input) { struct uwsgi_buffer *ubuf = uwsgi_chunked_read_smart(wsgi_req, arg_len, uwsgi.socket_timeout); + UWSGI_GET_GIL if (!ubuf) { return PyErr_Format(PyExc_IOError, "error during chunked read(%ld) on wsgi.input", arg_len); } From 0a9d4fc703552eb9602af2311239b73cd9691c6c Mon Sep 17 00:00:00 2001 From: Mack Date: Thu, 9 Jul 2020 09:18:16 +0800 Subject: [PATCH 066/207] [FEAT] Adding millis to `format` log --- core/logging.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/logging.c b/core/logging.c index 3cd216f96e..65b00c08a0 100644 --- a/core/logging.c +++ b/core/logging.c @@ -325,7 +325,7 @@ void logto(char *logfile) { void uwsgi_setup_log() { - + uwsgi_setup_log_encoders(); if (uwsgi.daemonize) { @@ -688,7 +688,7 @@ void uwsgi_logit_simple(struct wsgi_request *wsgi_req) { via = msg4; break; default: - break; + break; } #if defined(__sun__) && !defined(__clang__) @@ -1051,7 +1051,7 @@ void uwsgi_logit_lf(struct wsgi_request *wsgi_req) { if (uwsgi.logvectors[wsgi_req->async_id][pos].iov_len == 0 && logchunk->type != 0) { uwsgi.logvectors[wsgi_req->async_id][pos].iov_base = (char *) empty_var; - uwsgi.logvectors[wsgi_req->async_id][pos].iov_len = 1; + uwsgi.logvectors[wsgi_req->async_id][pos].iov_len = 1; } logchunk = logchunk->next; } @@ -1319,7 +1319,7 @@ struct uwsgi_logchunk *uwsgi_register_logchunk(char *name, ssize_t (*func)(struc logchunk->func = func; logchunk->free = need_free; logchunk->type = 3; - return logchunk; + return logchunk; } struct uwsgi_logchunk *uwsgi_get_logchunk_by_name(char *name, size_t name_len) { @@ -1664,7 +1664,7 @@ void uwsgi_setup_log_encoders() { exit(1); } struct uwsgi_log_encoder *ule2 = uwsgi_malloc(sizeof(struct uwsgi_log_encoder)); - memcpy(ule2, ule, sizeof(struct uwsgi_log_encoder)); + memcpy(ule2, ule, sizeof(struct uwsgi_log_encoder)); if (use_for) { ule2->use_for = uwsgi_str(use_for+1); *use_for = ':'; @@ -1857,7 +1857,7 @@ void uwsgi_log_encoder_parse_vars(struct uwsgi_log_encoder *ule) { strftime (strftime) */ static char *uwsgi_log_encoder_format(struct uwsgi_log_encoder *ule, char *msg, size_t len, size_t *rlen) { - + if (!ule->configured) { uwsgi_log_encoder_parse_vars(ule); ule->configured = 1; @@ -1885,6 +1885,9 @@ static char *uwsgi_log_encoder_format(struct uwsgi_log_encoder *ule, char *msg, else if (!uwsgi_strncmp(usl->value, usl->len, "micros", 6)) { if (uwsgi_buffer_num64(ub, uwsgi_micros())) goto end; } + else if (!uwsgi_strncmp(usl->value, usl->len, "millis", 6)) { + if (uwsgi_buffer_num64(ub, uwsgi_millis())) goto end; + } else if (!uwsgi_starts_with(usl->value, usl->len, "strftime:", 9)) { char sftime[64]; time_t now = uwsgi_now(); From 9b65b5a01376867421191f81f2e73ec395f3f1ad Mon Sep 17 00:00:00 2001 From: Taem Park Date: Tue, 25 Aug 2020 15:20:34 +0900 Subject: [PATCH 067/207] cherrypick from 0fb46601676f7ed2cceee6149bf02e00a9cdd149 --- core/websockets.c | 7 +++++++ uwsgi.h | 1 + 2 files changed, 8 insertions(+) diff --git a/core/websockets.c b/core/websockets.c index 0b9b034329..af7982b181 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -54,6 +54,10 @@ static int uwsgi_websockets_pong(struct wsgi_request *wsgi_req) { return uwsgi_response_write_body_do(wsgi_req, uwsgi.websockets_pong->buf, uwsgi.websockets_pong->pos); } +static int uwsgi_websockets_close(struct wsgi_request, *wsgi_req) { + return uwsgi_response_write_body_do(wsgi_req, uwsgi.websockets_close->buf, uwsgi.websockets_close->pos); +} + static int uwsgi_websockets_check_pingpong(struct wsgi_request *wsgi_req) { time_t now = uwsgi_now(); // first round @@ -318,6 +322,7 @@ static struct uwsgi_buffer *uwsgi_websocket_recv_do(struct wsgi_request *wsgi_re return uwsgi_websockets_parse(wsgi_req); // close case 0x8: + uwsgi_websockets_close(wsgi_req); return NULL; // ping case 0x9: @@ -463,6 +468,8 @@ void uwsgi_websockets_init() { uwsgi_buffer_append(uwsgi.websockets_pong, "\x8A\0", 2); uwsgi.websockets_ping = uwsgi_buffer_new(2); uwsgi_buffer_append(uwsgi.websockets_ping, "\x89\0", 2); + uwsgi.websockets_close = uwsgi_buffer_new(2); + uwsgi_buffer_append(uwsgi.websockets_close, "\x88\0", 2); uwsgi.websockets_ping_freq = 30; uwsgi.websockets_pong_tolerance = 3; uwsgi.websockets_max_size = 1024; diff --git a/uwsgi.h b/uwsgi.h index 725d820d74..2b32cad799 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2772,6 +2772,7 @@ struct uwsgi_server { struct uwsgi_buffer *websockets_ping; struct uwsgi_buffer *websockets_pong; + struct uwsgi_buffer *websockets_close; int websockets_ping_freq; int websockets_pong_tolerance; uint64_t websockets_max_size; From 35e7e4dc41a3878303358feabdb72c1d05becba2 Mon Sep 17 00:00:00 2001 From: Taem Park Date: Tue, 25 Aug 2020 19:00:04 +0900 Subject: [PATCH 068/207] typo fix --- core/websockets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/websockets.c b/core/websockets.c index af7982b181..8e7d769695 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -54,7 +54,7 @@ static int uwsgi_websockets_pong(struct wsgi_request *wsgi_req) { return uwsgi_response_write_body_do(wsgi_req, uwsgi.websockets_pong->buf, uwsgi.websockets_pong->pos); } -static int uwsgi_websockets_close(struct wsgi_request, *wsgi_req) { +static int uwsgi_websockets_close(struct wsgi_request *wsgi_req) { return uwsgi_response_write_body_do(wsgi_req, uwsgi.websockets_close->buf, uwsgi.websockets_close->pos); } From db0a7fa4226fe75fe73657904c56b4fd870c2361 Mon Sep 17 00:00:00 2001 From: Taem Park Date: Tue, 25 Aug 2020 19:33:21 +0900 Subject: [PATCH 069/207] change from space to tab --- core/websockets.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/websockets.c b/core/websockets.c index 8e7d769695..d24f610df5 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -55,7 +55,7 @@ static int uwsgi_websockets_pong(struct wsgi_request *wsgi_req) { } static int uwsgi_websockets_close(struct wsgi_request *wsgi_req) { - return uwsgi_response_write_body_do(wsgi_req, uwsgi.websockets_close->buf, uwsgi.websockets_close->pos); + return uwsgi_response_write_body_do(wsgi_req, uwsgi.websockets_close->buf, uwsgi.websockets_close->pos); } static int uwsgi_websockets_check_pingpong(struct wsgi_request *wsgi_req) { @@ -322,7 +322,7 @@ static struct uwsgi_buffer *uwsgi_websocket_recv_do(struct wsgi_request *wsgi_re return uwsgi_websockets_parse(wsgi_req); // close case 0x8: - uwsgi_websockets_close(wsgi_req); + uwsgi_websockets_close(wsgi_req); return NULL; // ping case 0x9: From 18baaac4bbb356ef931d145f32b69b11b1bb9b76 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 7 Oct 2020 19:23:54 +0300 Subject: [PATCH 070/207] python: Use new Py_SET_SIZE API introduced in Python 3.9 Fixes compile error using Python 3.10 while keeping backwards compatibility --- CONTRIBUTORS | 1 + plugins/python/uwsgi_pymodule.c | 2 +- plugins/python/uwsgi_python.h | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index b5deac8eb0..91c5eb08d9 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -38,3 +38,4 @@ Alexandre Bonnetain Darvame Hleran Sokolov Yura Marcin Lulek +Derzsi Dániel diff --git a/plugins/python/uwsgi_pymodule.c b/plugins/python/uwsgi_pymodule.c index 7a6e776f60..cc6948746a 100644 --- a/plugins/python/uwsgi_pymodule.c +++ b/plugins/python/uwsgi_pymodule.c @@ -2039,7 +2039,7 @@ PyObject *py_uwsgi_sharedarea_read(PyObject * self, PyObject * args) { } // HACK: we are safe as rlen can only be lower or equal to len - Py_SIZE(ret) = rlen; + Py_SET_SIZE((PyVarObject *) ret, rlen); return ret; } diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index e97c0bab60..6b2d75d59d 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -42,9 +42,9 @@ #define PYTHREE #endif -#if (PY_VERSION_HEX < 0x02060000) -#ifndef Py_SIZE -#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) +#if (PY_VERSION_HEX < 0x03090000) +#ifndef Py_SET_SIZE +#define Py_SET_SIZE(o, size) ((o)->ob_size = (size)) #endif #endif From d374d48d7073cbaf0ce58da887c0ad7a2ad33b46 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 12 Jul 2020 11:51:42 +0200 Subject: [PATCH 071/207] tests: make the tests CI config file a parameter So we don't need to duplicate it right now --- .travis.yml | 2 +- tests/travis.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51c3134f05..26585514e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ script: - echo -e "\n\n>>> Building dummy plugin" - /usr/bin/python uwsgiconfig.py --plugin plugins/dummy base - echo -e "\n\n>>> Building done, starting tests" - - ./tests/travis.sh + - ./tests/travis.sh .travis.yml before_install: - sudo apt-get update -qq diff --git a/tests/travis.sh b/tests/travis.sh index 3c61e206ba..3ec948c257 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -2,6 +2,7 @@ set -u +CI_CONFIG="$1" txtund=$(tput sgr 0 1) # underline txtbld=$(tput bold) # bold bldred=${txtbld}$(tput setaf 1) # red @@ -88,14 +89,14 @@ while read PV ; do for WSGI_FILE in tests/staticfile.py tests/testworkers.py tests/testrpc.py ; do test_python $PV $WSGI_FILE done -done < <(cat .travis.yml | grep "plugins/python base" | sed s_".*plugins/python base "_""_g) +done < <(cat "$CI_CONFIG" | grep "plugins/python base" | sed s_".*plugins/python base "_""_g) while read RV ; do for RACK in examples/config2.ru ; do test_rack $RV $RACK done -done < <(cat .travis.yml | grep "plugins/rack base" | sed s_".*plugins/rack base "_""_g) +done < <(cat "$CI_CONFIG" | grep "plugins/rack base" | sed s_".*plugins/rack base "_""_g) echo "${bldgre}>>> $SUCCESS SUCCESSFUL TEST(S)${txtrst}" From f813c281cf69216ea63df1060c47fc7f6e6c42d5 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 12 Jul 2020 14:10:59 +0200 Subject: [PATCH 072/207] tests: gem install with sudo As otherwise it would fail on github actions --- tests/travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/travis.sh b/tests/travis.sh index 3ec948c257..a8b54fe1c3 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -75,7 +75,7 @@ test_rack() { # the code assumes that ruby environment is activated by `rvm use` echo -e "${bldyel}================== TESTING $1 $2 =====================${txtrst}" echo -e "${bldyel}>>> Installing sinatra gem using gem${txtrst}" - gem install sinatra || die + sudo gem install sinatra || die echo -e "${bldyel}>>> Spawning uWSGI rack app${txtrst}" echo -en "${bldred}" ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --rack $2 --daemonize uwsgi.log From 0423249a98824da599c2115bca97c70aa1aa5f43 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 12 Jul 2020 10:09:52 +0200 Subject: [PATCH 073/207] ci: first stab at running as github action --- .github/workflows/test.yml | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..05b99c6f33 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,77 @@ +name: Test + +on: + push: + branches: [ master, uwsgi-2.0 ] + pull_request: + branches: [ master, uwsgi-2.0 ] + +jobs: + build: + + runs-on: ubuntu-18.04 + + steps: + - name: Add deadnakes ppa + run: | + sudo apt install -qqyf software-properties-common + sudo add-apt-repository ppa:deadsnakes/ppa -y + - name: Install dependencies + run: | + sudo apt update -qq + sudo apt install --no-install-recommends -qqyf python{2.7,3.5,3.6,3.7,3.8}-dev \ + python3.8-distutils \ + libxml2-dev libpcre3-dev libcap2-dev \ + php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev \ + liblua5.1-0-dev ruby2.5-dev \ + libjansson-dev libldap2-dev libpq-dev \ + libpam0g-dev libsqlite3-dev libyaml-dev \ + libzmq3-dev libmatheval-dev libperl-dev \ + libonig-dev libdb-dev libqdbm-dev libbz2-dev \ + libwrap0-dev libgeoip-dev libv8-dev libxslt1-dev \ + libboost-thread-dev libboost-filesystem-dev \ + libssl-dev libacl1-dev python-greenlet-dev \ + libcurl4-openssl-dev \ + openjdk-8-jdk libgloox-dev gccgo \ + cli-common-dev mono-devel mono-mcs uuid-dev \ + curl check + - uses: actions/checkout@v2 + - name: Run unit tests + run: make tests + - name: Build kitchensink uWSGI binary + run: UWSGICONFIG_PHPPATH=php-config7.2 /usr/bin/python uwsgiconfig.py --build travis + - name: Build uWSGI binary + run: make + - name: Build python2.7 plugin + run: | + /usr/bin/python2.7 -V + /usr/bin/python2.7 uwsgiconfig.py --plugin plugins/python base python27 + - name: Build python3.5 plugin + run: | + /usr/bin/python3.5 -V + /usr/bin/python3.5 uwsgiconfig.py --plugin plugins/python base python35 + - name: Build python3.6 plugin + run: | + /usr/bin/python3.6 -V + /usr/bin/python3.6 uwsgiconfig.py --plugin plugins/python base python36 + - name: Build python3.7 plugin + run: | + /usr/bin/python3.7 -V + /usr/bin/python3.7 uwsgiconfig.py --plugin plugins/python base python37 + - name: Build python3.8 plugin + run: | + /usr/bin/python3.8 -V + /usr/bin/python3.8 uwsgiconfig.py --plugin plugins/python base python38 + - name: Build rack plugin + run: | + ruby -v + UWSGICONFIG_RUBYPATH=ruby /usr/bin/python uwsgiconfig.py --plugin plugins/rack base rack251 + - name: Build cgi plugin + run: | + /usr/bin/python uwsgiconfig.py --plugin plugins/cgi base + - name: Build dummy plugin + run: | + /usr/bin/python uwsgiconfig.py --plugin plugins/dummy base + - name: Run smoke tests + run: | + ./tests/travis.sh .github/workflows/test.yml From a9f6a88281f6c0b641a5146a765ff0c97ed31506 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 12 Jul 2020 22:58:34 +0200 Subject: [PATCH 074/207] ci: split kitchensink build test from rest --- .github/workflows/compile-test.yml | 44 ++++++++++++++++++++++++++++++ .github/workflows/test.yml | 28 ++++--------------- 2 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/compile-test.yml diff --git a/.github/workflows/compile-test.yml b/.github/workflows/compile-test.yml new file mode 100644 index 0000000000..ebfdb68164 --- /dev/null +++ b/.github/workflows/compile-test.yml @@ -0,0 +1,44 @@ +name: Compile test + +on: + push: + branches: [ master, uwsgi-2.0 ] + pull_request: + branches: [ master, uwsgi-2.0 ] + +jobs: + build: + + runs-on: ubuntu-18.04 + + steps: + - name: Install dependencies + run: | + sudo apt update -qq + sudo apt install --no-install-recommends -qqyf python3.6-dev \ + libxml2-dev libpcre3-dev libcap2-dev \ + php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev \ + liblua5.1-0-dev ruby2.5-dev \ + libjansson-dev libldap2-dev libpq-dev \ + libpam0g-dev libsqlite3-dev libyaml-dev \ + libzmq3-dev libmatheval-dev libperl-dev \ + libonig-dev libdb-dev libqdbm-dev libbz2-dev \ + libwrap0-dev libgeoip-dev libv8-dev libxslt1-dev \ + libboost-thread-dev libboost-filesystem-dev \ + libssl-dev libacl1-dev python-greenlet-dev \ + libcurl4-openssl-dev \ + openjdk-8-jdk libgloox-dev gccgo \ + cli-common-dev mono-devel mono-mcs uuid-dev \ + curl check + - uses: actions/checkout@v2 + - name: Build kitchensink uWSGI binary + run: UWSGICONFIG_PHPPATH=php-config7.2 /usr/bin/python3 uwsgiconfig.py --build travis + - name: Build uWSGI binary + run: | + /usr/bin/python3 uwsgiconfig.py --build base + - name: Build cgi plugin + run: | + /usr/bin/python3 uwsgiconfig.py --plugin plugins/cgi base + - name: Build dummy plugin + run: | + /usr/bin/python3 uwsgiconfig.py --plugin plugins/dummy base diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 05b99c6f33..cd7d36bff3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,29 +19,19 @@ jobs: - name: Install dependencies run: | sudo apt update -qq - sudo apt install --no-install-recommends -qqyf python{2.7,3.5,3.6,3.7,3.8}-dev \ + sudo apt install --no-install-recommends -qqyf python{2.6,2.7,3.5,3.6,3.7,3.8}-dev \ python3.8-distutils \ - libxml2-dev libpcre3-dev libcap2-dev \ - php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev \ - liblua5.1-0-dev ruby2.5-dev \ - libjansson-dev libldap2-dev libpq-dev \ - libpam0g-dev libsqlite3-dev libyaml-dev \ - libzmq3-dev libmatheval-dev libperl-dev \ - libonig-dev libdb-dev libqdbm-dev libbz2-dev \ - libwrap0-dev libgeoip-dev libv8-dev libxslt1-dev \ - libboost-thread-dev libboost-filesystem-dev \ - libssl-dev libacl1-dev python-greenlet-dev \ - libcurl4-openssl-dev \ - openjdk-8-jdk libgloox-dev gccgo \ - cli-common-dev mono-devel mono-mcs uuid-dev \ + libpcre3-dev libjansson-dev libcap2-dev \ curl check - uses: actions/checkout@v2 - name: Run unit tests run: make tests - - name: Build kitchensink uWSGI binary - run: UWSGICONFIG_PHPPATH=php-config7.2 /usr/bin/python uwsgiconfig.py --build travis - name: Build uWSGI binary run: make + - name: Build python2.6 plugin + run: | + /usr/bin/python2.6 -V + /usr/bin/python2.6 uwsgiconfig.py --plugin plugins/python base python26 - name: Build python2.7 plugin run: | /usr/bin/python2.7 -V @@ -66,12 +56,6 @@ jobs: run: | ruby -v UWSGICONFIG_RUBYPATH=ruby /usr/bin/python uwsgiconfig.py --plugin plugins/rack base rack251 - - name: Build cgi plugin - run: | - /usr/bin/python uwsgiconfig.py --plugin plugins/cgi base - - name: Build dummy plugin - run: | - /usr/bin/python uwsgiconfig.py --plugin plugins/dummy base - name: Run smoke tests run: | ./tests/travis.sh .github/workflows/test.yml From cc2de3deb94da5153313bf7299ff5a0a52d46e1b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 12 Jul 2020 23:19:24 +0200 Subject: [PATCH 075/207] buildconf: drop pyuwsgi from travis.ini As it does not build with python3.6 --- buildconf/travis.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildconf/travis.ini b/buildconf/travis.ini index 68b3642b67..28653a5db6 100644 --- a/buildconf/travis.ini +++ b/buildconf/travis.ini @@ -1,3 +1,3 @@ [uwsgi] -main_plugin = psgi,rack,lua,python,gevent,php,cgi,pty,xslt,msgpack,geoip,v8,pam,ldap,mono,jvm,ring,jwsgi,servlet,pypy,airbrake,alarm_curl,alarm_xmpp,asyncio,cheaper_backlog2,clock_monotonic,clock_realtime,cplusplus,curl_cron,dumbloop,dummy,echo,emperor_amqp,emperor_pg,emperor_zeromq,example,exception_log,fiber,forkptyrouter,graylog2,legion_cache_fetch,libffi,logcrypto,logpipe,logzmq,matheval,notfound,pyuwsgi,rbthreads,router_access,router_radius,router_spnego,router_xmldir,sqlite3,ssi,stats_pusher_file,stats_pusher_statsd,tornado,transformation_toupper,tuntap,webdav,xattr,zabbix +main_plugin = psgi,rack,lua,python,gevent,php,cgi,pty,xslt,msgpack,geoip,v8,pam,ldap,mono,jvm,ring,jwsgi,servlet,pypy,airbrake,alarm_curl,alarm_xmpp,asyncio,cheaper_backlog2,clock_monotonic,clock_realtime,cplusplus,curl_cron,dumbloop,dummy,echo,emperor_amqp,emperor_pg,emperor_zeromq,example,exception_log,fiber,forkptyrouter,graylog2,legion_cache_fetch,libffi,logcrypto,logpipe,logzmq,matheval,notfound,rbthreads,router_access,router_radius,router_spnego,router_xmldir,sqlite3,ssi,stats_pusher_file,stats_pusher_statsd,tornado,transformation_toupper,tuntap,webdav,xattr,zabbix inherit = base From d63bd198a89aeac62a00b10bc6eab40008a428f1 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 22 Nov 2020 16:31:05 +0100 Subject: [PATCH 076/207] ci: so long travis CI It's broken for some reason and we have github actions. --- .travis.yml | 67 ----------------------------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 26585514e8..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,67 +0,0 @@ -dist: xenial - -language: c - -compiler: - - gcc - -script: - - echo -e "\n\n>>> Running uWSGI unit tests" - - make tests - - echo -e "\n\n>>> Building kitchensink uWSGI binary" - - UWSGICONFIG_PHPPATH=php-config7.2 /usr/bin/python uwsgiconfig.py --build travis - - echo -e "\n\n>>> Building uWSGI binary" - - make - - echo -e "\n\n>>> Building python26 plugin" - - /usr/bin/python2.6 -V - - /usr/bin/python2.6 uwsgiconfig.py --plugin plugins/python base python26 - - echo -e "\n\n>>> Building python27 plugin" - - /usr/bin/python2.7 -V - - /usr/bin/python2.7 uwsgiconfig.py --plugin plugins/python base python27 - - echo -e "\n\n>>> Building python34 plugin" - - /usr/bin/python3.4 -V - - /usr/bin/python3.4 uwsgiconfig.py --plugin plugins/python base python34 - - echo -e "\n\n>>> Building python35 plugin" - - /usr/bin/python3.5 -V - - /usr/bin/python3.5 uwsgiconfig.py --plugin plugins/python base python35 - - echo -e "\n\n>>> Building python36 plugin" - - /usr/bin/python3.6 -V - - /usr/bin/python3.6 uwsgiconfig.py --plugin plugins/python base python36 - - echo -e "\n\n>>> Building python37 plugin" - - /usr/bin/python3.7 -V - - /usr/bin/python3.7 uwsgiconfig.py --plugin plugins/python base python37 - - echo -e "\n\n>>> Building python38 plugin" - - /usr/bin/python3.8 -V - - /usr/bin/python3.8 uwsgiconfig.py --plugin plugins/python base python38 - - echo -e "\n\n>>> Building rack plugin" - - rvm use 2.4 - - ruby -v - - UWSGICONFIG_RUBYPATH=ruby /usr/bin/python uwsgiconfig.py --plugin plugins/rack base rack226 - - echo -e "\n\n>>> Building cgi plugin" - - /usr/bin/python uwsgiconfig.py --plugin plugins/cgi base - - echo -e "\n\n>>> Building dummy plugin" - - /usr/bin/python uwsgiconfig.py --plugin plugins/dummy base - - echo -e "\n\n>>> Building done, starting tests" - - ./tests/travis.sh .travis.yml - -before_install: - - sudo apt-get update -qq - - sudo apt-get install -qqyf software-properties-common - - sudo add-apt-repository ppa:deadsnakes/ppa -y - - sudo add-apt-repository ppa:ondrej/php -y - - sudo apt-get update -qq - - sudo apt-get install -qqyf python{2.6,3.4,3.5,3.6,3.7,3.8}-dev - - sudo apt-get install -qqyf python3.8-distutils - - sudo apt-get install -qqyf libxml2-dev libpcre3-dev libcap2-dev - - sudo apt-get install -qqyf php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev - - sudo apt-get install -qqyf liblua5.1-0-dev - - sudo apt-get install -qqyf libjansson-dev libldap2-dev libpq-dev - - sudo apt-get install -qqyf libpam0g-dev libsqlite3-dev libyaml-dev - - sudo apt-get install -qqyf libzmq-dev libmatheval-dev libperl-dev - - sudo apt-get install -qqyf libonig-dev libdb-dev libqdbm-dev libbz2-dev - - sudo apt-get install -qqyf libwrap0-dev libgeoip-dev libv8-dev libxslt1-dev - - sudo apt-get install -qqyf libboost-thread-dev libboost-filesystem-dev - - sudo apt-get install -qqyf libssl-dev libacl1-dev python-greenlet-dev - - sudo apt-get install -qqyf openjdk-8-jdk libgloox-dev gccgo - - sudo apt-get install -qqyf cli-common-dev mono-devel mono-mcs uuid-dev - - sudo apt-get install -qqyf curl check From a282bbf22540e5ff307b9cc93e1db62e43e63f58 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 22 Nov 2020 15:54:53 +0100 Subject: [PATCH 077/207] plugins: use PyObject_CallObject() instead of PyEval_CallObject() Reference: https://bugs.python.org/issue29548 --- plugins/gevent/gevent.c | 2 +- plugins/python/pyloader.c | 4 ++-- plugins/python/python_plugin.c | 10 +++++----- plugins/python/pyutils.c | 4 ++-- plugins/python/raw.c | 2 +- plugins/python/tracebacker.c | 8 ++++---- plugins/python/wsgi_subhandler.c | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/plugins/gevent/gevent.c b/plugins/gevent/gevent.c index 1b91417cf3..cc02e90b5a 100644 --- a/plugins/gevent/gevent.c +++ b/plugins/gevent/gevent.c @@ -33,7 +33,7 @@ PyObject *py_uwsgi_gevent_ctrl_gl(PyObject *self, PyObject *args) { for(;;) { PyObject *gevent_sleep_args = PyTuple_New(1); PyTuple_SetItem(gevent_sleep_args, 0, PyInt_FromLong(60)); - PyObject *gswitch = PyEval_CallObject(ugevent.greenlet_switch, gevent_sleep_args); + PyObject *gswitch = PyObject_CallObject(ugevent.greenlet_switch, gevent_sleep_args); // could be NULL on exception if (!gswitch) { // just for being paranoid diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c index 7c380f4dbe..a63c375b58 100644 --- a/plugins/python/pyloader.c +++ b/plugins/python/pyloader.c @@ -667,7 +667,7 @@ PyObject *uwsgi_pecan_loader(void *arg1) { exit(UWSGI_FAILED_APP_CODE); } - pecan_app = PyEval_CallObject(pecan_deploy, pecan_arg); + pecan_app = PyObject_CallObject(pecan_deploy, pecan_arg); if (!pecan_app) { PyErr_Print(); exit(UWSGI_FAILED_APP_CODE); @@ -742,7 +742,7 @@ PyObject *uwsgi_paste_loader(void *arg1) { } } - paste_app = PyEval_CallObject(paste_loadapp, paste_arg); + paste_app = PyObject_CallObject(paste_loadapp, paste_arg); if (!paste_app) { PyErr_Print(); exit(UWSGI_FAILED_APP_CODE); diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 7977359132..c5baf08cc7 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -356,7 +356,7 @@ void uwsgi_python_reset_random_seed() { PyObject *random_args = PyTuple_New(1); // pass no args PyTuple_SetItem(random_args, 0, Py_None); - PyEval_CallObject(random_seed, random_args); + PyObject_CallObject(random_seed, random_args); if (PyErr_Occurred()) { PyErr_Print(); } @@ -1372,7 +1372,7 @@ void uwsgi_python_set_thread_name(int core_id) { PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread"); #endif if (threading_current) { - PyObject *current_thread = PyEval_CallObject(threading_current, (PyObject *)NULL); + PyObject *current_thread = PyObject_CallObject(threading_current, (PyObject *)NULL); if (!current_thread) { // ignore the error PyErr_Clear(); @@ -1460,7 +1460,7 @@ PyObject *uwsgi_python_setup_thread(char *name, PyInterpreterState *interpreter) PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread"); #endif if (threading_current) { - PyObject *current_thread = PyEval_CallObject(threading_current, (PyObject *)NULL); + PyObject *current_thread = PyObject_CallObject(threading_current, (PyObject *)NULL); if (!current_thread) { // ignore the error PyErr_Clear(); @@ -1905,7 +1905,7 @@ int uwsgi_python_mule(char *opt) { PyObject *arglist = Py_BuildValue("()"); PyObject *callable = up.loaders[LOADER_MOUNT](opt); if (callable) { - result = PyEval_CallObject(callable, arglist); + result = PyObject_CallObject(callable, arglist); } Py_XDECREF(result); Py_XDECREF(arglist); @@ -2018,7 +2018,7 @@ static ssize_t uwsgi_python_logger(struct uwsgi_logger *ul, char *message, size_ py_getLogger_args = PyTuple_New(1); PyTuple_SetItem(py_getLogger_args, 0, UWSGI_PYFROMSTRING(ul->arg)); } - ul->data = (void *) PyEval_CallObject(py_getLogger, py_getLogger_args); + ul->data = (void *) PyObject_CallObject(py_getLogger, py_getLogger_args); if (PyErr_Occurred()) { PyErr_Clear(); } diff --git a/plugins/python/pyutils.c b/plugins/python/pyutils.c index 8a3da0438b..9cb111d0bc 100644 --- a/plugins/python/pyutils.c +++ b/plugins/python/pyutils.c @@ -75,7 +75,7 @@ struct uwsgi_buffer *uwsgi_python_backtrace(struct wsgi_request *wsgi_req) { PyObject *args = PyTuple_New(1); Py_INCREF(traceback); PyTuple_SetItem(args, 0, traceback); - PyObject *result = PyEval_CallObject(extract_tb, args); + PyObject *result = PyObject_CallObject(extract_tb, args); Py_DECREF(args); if (!result) goto end; @@ -276,7 +276,7 @@ PyObject *python_call(PyObject *callable, PyObject *args, int catch, struct wsgi //uwsgi_log("ready to call %p %p\n", callable, args); - PyObject *pyret = PyEval_CallObject(callable, args); + PyObject *pyret = PyObject_CallObject(callable, args); //uwsgi_log("called\n"); diff --git a/plugins/python/raw.c b/plugins/python/raw.c index 47f66d20bf..3cd50720df 100644 --- a/plugins/python/raw.c +++ b/plugins/python/raw.c @@ -64,7 +64,7 @@ int uwsgi_request_python_raw(struct wsgi_request *wsgi_req) { UWSGI_GET_GIL PyObject * args = PyTuple_New(1); PyTuple_SetItem(args, 0, PyInt_FromLong(wsgi_req->fd)); - wsgi_req->async_result = PyEval_CallObject(up.raw_callable, args); + wsgi_req->async_result = PyObject_CallObject(up.raw_callable, args); Py_DECREF(args); if (wsgi_req->async_result) { for (;;) { diff --git a/plugins/python/tracebacker.c b/plugins/python/tracebacker.c index e19e3e1c44..a91692f059 100644 --- a/plugins/python/tracebacker.c +++ b/plugins/python/tracebacker.c @@ -14,7 +14,7 @@ char *uwsgi_python_get_thread_name(PyObject *thread_id) { PyObject *threading_enumerate = PyDict_GetItemString(threading_dict, "enumerate"); if (!threading_enumerate) return NULL; - PyObject *threads_list = PyEval_CallObject(threading_enumerate, (PyObject *)NULL); + PyObject *threads_list = PyObject_CallObject(threading_enumerate, (PyObject *)NULL); if (!threads_list) return NULL; PyObject *threads_list_iter = PyObject_GetIter(threads_list); @@ -106,13 +106,13 @@ void *uwsgi_python_tracebacker_thread(void *foobar) { } UWSGI_GET_GIL; // here is the core of the tracebacker - PyObject *current_frames = PyEval_CallObject(_current_frames, (PyObject *)NULL); + PyObject *current_frames = PyObject_CallObject(_current_frames, (PyObject *)NULL); if (!current_frames) goto end2; PyObject *current_frames_items = PyObject_GetAttrString(current_frames, "items"); if (!current_frames_items) goto end; - PyObject *frames_ret = PyEval_CallObject(current_frames_items, (PyObject *)NULL); + PyObject *frames_ret = PyObject_CallObject(current_frames_items, (PyObject *)NULL); if (!frames_ret) goto end3; PyObject *frames_iter = PyObject_GetIter(frames_ret); @@ -134,7 +134,7 @@ void *uwsgi_python_tracebacker_thread(void *foobar) { PyObject *arg_tuple = PyTuple_New(1); PyTuple_SetItem(arg_tuple, 0, stack); Py_INCREF(stack); - PyObject *stacktrace = PyEval_CallObject( extract_stack, arg_tuple); + PyObject *stacktrace = PyObject_CallObject( extract_stack, arg_tuple); Py_DECREF(arg_tuple); if (!stacktrace) goto next2; diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index d773f6c3ce..8f5019c462 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -103,7 +103,7 @@ static void uwsgi_python_consume_file_wrapper_read(struct wsgi_request *wsgi_req read_method_args = PyTuple_New(0); } for(;;) { - PyObject *read_method_output = PyEval_CallObject(read_method, read_method_args); + PyObject *read_method_output = PyObject_CallObject(read_method, read_method_args); if (PyErr_Occurred()) { uwsgi_manage_exception(wsgi_req, 0); break; @@ -372,7 +372,7 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { #ifdef UWSGI_DEBUG uwsgi_log("calling close() for %.*s %p %p\n", wsgi_req->uri_len, wsgi_req->uri, close_method, close_method_args); #endif - PyObject *close_method_output = PyEval_CallObject(close_method, close_method_args); + PyObject *close_method_output = PyObject_CallObject(close_method, close_method_args); if (PyErr_Occurred()) { uwsgi_manage_exception(wsgi_req, 0); } From 6d86c033ed78e44213454978d5e6743b8fc25b4e Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 22 Nov 2020 18:11:04 +0100 Subject: [PATCH 078/207] plugins/python: don't call PyEval_InitThreads on python >= 3.7 As it's already called in Py_Initialize() there. --- plugins/python/python_plugin.c | 2 ++ plugins/python/uwsgi_python.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index c5baf08cc7..37d0b7bbf0 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -1328,7 +1328,9 @@ void uwsgi_python_master_fixup(int step) { void uwsgi_python_enable_threads() { +#ifdef UWSGI_SHOULD_CALL_PYEVAL_INITTHREADS PyEval_InitThreads(); +#endif if (pthread_key_create(&up.upt_save_key, NULL)) { uwsgi_error("pthread_key_create()"); exit(1); diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 6b2d75d59d..aca1f83b71 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -42,6 +42,10 @@ #define PYTHREE #endif +#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7) || PY_MAJOR_VERSION < 3 +#define UWSGI_SHOULD_CALL_PYEVAL_INITTHREADS +#endif + #if (PY_VERSION_HEX < 0x03090000) #ifndef Py_SET_SIZE #define Py_SET_SIZE(o, size) ((o)->ob_size = (size)) From 8c890c84604a0477b46a66eab8a620733f596cc8 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 22 Nov 2020 18:20:19 +0100 Subject: [PATCH 079/207] plugins/python: use Py_CompileString Instead of the deprecated PyParser_SimpleParseString, PyParser_SimpleParseFile and PyNode_Compile. While at it fixup a possible null pointer dereference when uwsgi_open_and_read returns an empty string. See https://bugs.python.org/issue40939 --- plugins/python/pyloader.c | 12 +----------- plugins/python/python_plugin.c | 36 ++++++++++++++-------------------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c index a63c375b58..d8ab6fe35a 100644 --- a/plugins/python/pyloader.c +++ b/plugins/python/pyloader.c @@ -757,25 +757,15 @@ PyObject *uwsgi_eval_loader(void *arg1) { PyObject *wsgi_eval_module, *wsgi_eval_callable = NULL; - struct _node *wsgi_eval_node = NULL; PyObject *wsgi_compiled_node; - wsgi_eval_node = PyParser_SimpleParseString(code, Py_file_input); - if (!wsgi_eval_node) { - PyErr_Print(); - uwsgi_log( "failed to parse code\n"); - exit(UWSGI_FAILED_APP_CODE); - } - - wsgi_compiled_node = (PyObject *) PyNode_Compile(wsgi_eval_node, "uwsgi_eval_config"); - + wsgi_compiled_node = Py_CompileString(code, "uwsgi_eval_config", Py_file_input); if (!wsgi_compiled_node) { PyErr_Print(); uwsgi_log( "failed to compile eval code\n"); exit(UWSGI_FAILED_APP_CODE); } - wsgi_eval_module = PyImport_ExecCodeModule("uwsgi_eval_config", wsgi_compiled_node); if (!wsgi_eval_module) { PyErr_Print(); diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 37d0b7bbf0..79f29d43ce 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -473,8 +473,7 @@ UWSGI_RELEASE_GIL PyObject *uwsgi_pyimport_by_filename(char *name, char *filename) { - FILE *pyfile; - struct _node *py_file_node = NULL; + char *pycontent; PyObject *py_compiled_node, *py_file_module; int is_a_package = 0; struct stat pystat; @@ -483,7 +482,7 @@ PyObject *uwsgi_pyimport_by_filename(char *name, char *filename) { if (!uwsgi_check_scheme(filename)) { - pyfile = fopen(filename, "r"); + FILE *pyfile = fopen(filename, "r"); if (!pyfile) { uwsgi_log("failed to open python file %s\n", filename); return NULL; @@ -507,37 +506,32 @@ PyObject *uwsgi_pyimport_by_filename(char *name, char *filename) { } } - py_file_node = PyParser_SimpleParseFile(pyfile, real_filename, Py_file_input); - if (!py_file_node) { - PyErr_Print(); - uwsgi_log("failed to parse file %s\n", real_filename); - if (is_a_package) + fclose(pyfile); + pycontent = uwsgi_simple_file_read(real_filename); + + if (!pycontent) { + if (is_a_package) { free(real_filename); - fclose(pyfile); + } + uwsgi_log("no data read from file %s\n", real_filename); return NULL; } - fclose(pyfile); } else { size_t pycontent_size = 0; - char *pycontent = uwsgi_open_and_read(filename, &pycontent_size, 1, NULL); + pycontent = uwsgi_open_and_read(filename, &pycontent_size, 1, NULL); - if (pycontent) { - py_file_node = PyParser_SimpleParseString(pycontent, Py_file_input); - if (!py_file_node) { - PyErr_Print(); - uwsgi_log("failed to parse url %s\n", real_filename); - return NULL; - } + if (!pycontent) { + uwsgi_log("no data read from url %s\n", real_filename); + return NULL; } } - py_compiled_node = (PyObject *) PyNode_Compile(py_file_node, real_filename); - + py_compiled_node = Py_CompileString(pycontent, real_filename, Py_file_input); if (!py_compiled_node) { PyErr_Print(); - uwsgi_log("failed to compile python file %s\n", real_filename); + uwsgi_log("failed to compile %s\n", real_filename); return NULL; } From 997c6df503986b2152255fd9ef1b550abef81003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20H?= Date: Fri, 4 Dec 2020 23:58:56 -0800 Subject: [PATCH 080/207] plugins/http: document http-timeout default of 60 seconds Refs #2264 --- plugins/http/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/http/http.c b/plugins/http/http.c index a2c9ce82f3..ab14d7df47 100644 --- a/plugins/http/http.c +++ b/plugins/http/http.c @@ -32,7 +32,7 @@ struct uwsgi_option http_options[] = { {"http-events", required_argument, 0, "set the number of concurrent http async events", uwsgi_opt_set_int, &uhttp.cr.nevents, 0}, {"http-subscription-server", required_argument, 0, "enable the subscription server", uwsgi_opt_corerouter_ss, &uhttp, 0}, {"http-subscription-fallback-key", required_argument, 0, "key to use for fallback http handler", uwsgi_opt_corerouter_fallback_key, &uhttp.cr, 0}, - {"http-timeout", required_argument, 0, "set internal http socket timeout", uwsgi_opt_set_int, &uhttp.cr.socket_timeout, 0}, + {"http-timeout", required_argument, 0, "set internal http socket timeout (default: 60 seconds)", uwsgi_opt_set_int, &uhttp.cr.socket_timeout, 0}, {"http-manage-expect", optional_argument, 0, "manage the Expect HTTP request header (optionally checking for Content-Length)", uwsgi_opt_set_64bit, &uhttp.manage_expect, 0}, {"http-keepalive", optional_argument, 0, "HTTP 1.1 keepalive support (non-pipelined) requests", uwsgi_opt_set_int, &uhttp.keepalive, 0}, {"http-auto-chunked", no_argument, 0, "automatically transform output to chunked encoding during HTTP 1.1 keepalive (if needed)", uwsgi_opt_true, &uhttp.auto_chunked, 0}, From 1a269ed249bf7fe258af5553973b907604f4a40c Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 6 Jan 2021 10:06:55 +0100 Subject: [PATCH 081/207] ci: stop building python2.6 plugin Because it's not installable anymore. And it's 2021. --- .github/workflows/test.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd7d36bff3..b628782615 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: - name: Install dependencies run: | sudo apt update -qq - sudo apt install --no-install-recommends -qqyf python{2.6,2.7,3.5,3.6,3.7,3.8}-dev \ + sudo apt install --no-install-recommends -qqyf python{2.7,3.5,3.6,3.7,3.8}-dev \ python3.8-distutils \ libpcre3-dev libjansson-dev libcap2-dev \ curl check @@ -28,10 +28,6 @@ jobs: run: make tests - name: Build uWSGI binary run: make - - name: Build python2.6 plugin - run: | - /usr/bin/python2.6 -V - /usr/bin/python2.6 uwsgiconfig.py --plugin plugins/python base python26 - name: Build python2.7 plugin run: | /usr/bin/python2.7 -V From 69d4c56adfbedbe38709dbcd0faede899792257b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 6 Jan 2021 10:08:29 +0100 Subject: [PATCH 082/207] Makefile: use python3 As it's a saner default in 2021. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6896b4245a..430c7ce807 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PYTHON := python +PYTHON := python3 all: $(PYTHON) uwsgiconfig.py --build $(PROFILE) From f1b51d92be4ed35a17f1749ce0d2c3b27e0baee2 Mon Sep 17 00:00:00 2001 From: Antonio Cuni Date: Fri, 22 Jan 2021 15:51:24 +0000 Subject: [PATCH 083/207] fix segfaults when using --wsgi-env-behavior=holy. PyTuple_SetItem does NOT increment the item, so we need to do it explicitly. Without this, then env gets a negative refcnt during the deallocation of async_args, with all kinds of funny behaviors. --- plugins/python/wsgi_subhandler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index 8f5019c462..32a3aee7eb 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -255,6 +255,7 @@ void *uwsgi_request_subhandler_wsgi(struct wsgi_request *wsgi_req, struct uwsgi_ // call if (PyTuple_GetItem(wsgi_req->async_args, 0) != wsgi_req->async_environ) { + Py_INCREF(wsgi_req->async_environ); if (PyTuple_SetItem(wsgi_req->async_args, 0, wsgi_req->async_environ)) { uwsgi_log_verbose("unable to set environ to the python application callable, consider using the holy env allocator\n"); return NULL; From 4f8d9c24c2074896ad391cb0595bc59f00d8fa06 Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Sun, 31 Jan 2021 19:26:14 +0000 Subject: [PATCH 084/207] core, proto: replace uwsgi.h system includes This replaces all `#include ` in core/ and proto/ with `#include "uwsgi.h"`. This is to make it easier to build uWSGI with build systems that are more opinonated about local-vs-system include directories (eg. Bazel), and is without detriment to uWSGI itself (at least as far as I can tell). --- core/async.c | 2 +- core/cache.c | 2 +- core/chunked.c | 2 +- core/config.c | 2 +- core/cookie.c | 2 +- core/cron.c | 2 +- core/daemons.c | 2 +- core/emperor.c | 2 +- core/errors.c | 2 +- core/exceptions.c | 2 +- core/fifo.c | 2 +- core/fork_server.c | 2 +- core/fsmon.c | 2 +- core/hash.c | 2 +- core/hooks.c | 2 +- core/ini.c | 2 +- core/logging.c | 4 ++-- core/master_checks.c | 2 +- core/master_events.c | 2 +- core/metrics.c | 2 +- core/mount.c | 2 +- core/notify.c | 2 +- core/plugins_builder.c | 2 +- core/progress.c | 2 +- core/querystring.c | 2 +- core/rb_timers.c | 2 +- core/reader.c | 2 +- core/routing.c | 2 +- core/rpc.c | 2 +- core/sharedarea.c | 2 +- core/snmp.c | 2 +- core/ssl.c | 2 +- core/strings.c | 2 +- core/timebomb.c | 2 +- core/transformations.c | 2 +- core/utils.c | 2 +- core/uwsgi.c | 2 +- core/webdav.c | 2 +- core/websockets.c | 2 +- core/zeus.c | 2 +- core/zlib.c | 2 +- proto/BUILD | 1 + proto/base.c | 2 +- proto/fastcgi.c | 2 +- proto/http.c | 2 +- proto/puwsgi.c | 2 +- proto/scgi.c | 2 +- proto/uwsgi.c | 2 +- 48 files changed, 49 insertions(+), 48 deletions(-) create mode 100644 proto/BUILD diff --git a/core/async.c b/core/async.c index f03aaf5978..db8949429f 100644 --- a/core/async.c +++ b/core/async.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/cache.c b/core/cache.c index 13a23dbee6..c3bf7d6e3c 100644 --- a/core/cache.c +++ b/core/cache.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; #define cache_item(x) (struct uwsgi_cache_item *) (((char *)uc->items) + ((sizeof(struct uwsgi_cache_item)+uc->keysize) * x)) diff --git a/core/chunked.c b/core/chunked.c index 3979c20039..fe693aad47 100644 --- a/core/chunked.c +++ b/core/chunked.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/config.c b/core/config.c index 7b05abc6f0..7153dfa164 100644 --- a/core/config.c +++ b/core/config.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/cookie.c b/core/cookie.c index 37350bf020..1664798545 100644 --- a/core/cookie.c +++ b/core/cookie.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/cron.c b/core/cron.c index 013ade6919..eac693a564 100644 --- a/core/cron.c +++ b/core/cron.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/daemons.c b/core/daemons.c index 0e7889f81f..70ff3d904f 100644 --- a/core/daemons.c +++ b/core/daemons.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/emperor.c b/core/emperor.c index eae7f901cd..cd99e75500 100644 --- a/core/emperor.c +++ b/core/emperor.c @@ -3,7 +3,7 @@ The uWSGI Emperor */ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; extern char **environ; diff --git a/core/errors.c b/core/errors.c index e4879854e3..deabd1f4ba 100644 --- a/core/errors.c +++ b/core/errors.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/exceptions.c b/core/exceptions.c index a939839de3..1d7196a88b 100644 --- a/core/exceptions.c +++ b/core/exceptions.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/fifo.c b/core/fifo.c index dae74194d1..45b051138c 100644 --- a/core/fifo.c +++ b/core/fifo.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/fork_server.c b/core/fork_server.c index fb75163c1d..fa2f27f3b6 100644 --- a/core/fork_server.c +++ b/core/fork_server.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/fsmon.c b/core/fsmon.c index cca53d9f70..3d5ee0957d 100644 --- a/core/fsmon.c +++ b/core/fsmon.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/hash.c b/core/hash.c index 86ddf83141..5940f661ee 100644 --- a/core/hash.c +++ b/core/hash.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/hooks.c b/core/hooks.c index 832d6d3193..9329d9ce26 100644 --- a/core/hooks.c +++ b/core/hooks.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/ini.c b/core/ini.c index 61a3c26ac1..97080bcd9b 100644 --- a/core/ini.c +++ b/core/ini.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/logging.c b/core/logging.c index 65b00c08a0..ac2f37a493 100644 --- a/core/logging.c +++ b/core/logging.c @@ -1,5 +1,5 @@ #ifndef __DragonFly__ -#include +#include "uwsgi.h" #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) #include @@ -15,7 +15,7 @@ #endif #ifdef __DragonFly__ -#include +#include "uwsgi.h" #endif extern struct uwsgi_server uwsgi; diff --git a/core/master_checks.c b/core/master_checks.c index f3d05764d0..3e6b76a061 100644 --- a/core/master_checks.c +++ b/core/master_checks.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/master_events.c b/core/master_events.c index 3466415f3a..a92117413f 100644 --- a/core/master_events.c +++ b/core/master_events.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/metrics.c b/core/metrics.c index c31b6d501d..9714fa658a 100644 --- a/core/metrics.c +++ b/core/metrics.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/mount.c b/core/mount.c index 23f5ea4158..ada7682a70 100644 --- a/core/mount.c +++ b/core/mount.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/notify.c b/core/notify.c index 7c08d68181..2e267dbe6c 100644 --- a/core/notify.c +++ b/core/notify.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/plugins_builder.c b/core/plugins_builder.c index f0fd38c909..967f4d2988 100644 --- a/core/plugins_builder.c +++ b/core/plugins_builder.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" #define UWSGI_BUILD_DIR ".uwsgi_plugins_builder" diff --git a/core/progress.c b/core/progress.c index 25bd8c5627..f46f45b620 100644 --- a/core/progress.c +++ b/core/progress.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/querystring.c b/core/querystring.c index e77120f068..6f299174fc 100644 --- a/core/querystring.c +++ b/core/querystring.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/rb_timers.c b/core/rb_timers.c index 1f2c1f7aa2..9c299ba58f 100644 --- a/core/rb_timers.c +++ b/core/rb_timers.c @@ -10,7 +10,7 @@ the "Introduction to Algorithms" by Cormen, Leiserson and Rivest. */ -#include +#include "uwsgi.h" #define uwsgi_rbt_red(node) ((node)->color = 1) #define uwsgi_rbt_black(node) ((node)->color = 0) diff --git a/core/reader.c b/core/reader.c index fcc1af5cec..7a6c4728ce 100644 --- a/core/reader.c +++ b/core/reader.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/routing.c b/core/routing.c index fd45e087cb..f3024fa02f 100644 --- a/core/routing.c +++ b/core/routing.c @@ -1,5 +1,5 @@ #ifdef UWSGI_ROUTING -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/rpc.c b/core/rpc.c index efb8c5badc..b6f0baf8b1 100644 --- a/core/rpc.c +++ b/core/rpc.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/sharedarea.c b/core/sharedarea.c index 1628572ff3..a4ca4b47b8 100644 --- a/core/sharedarea.c +++ b/core/sharedarea.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/snmp.c b/core/snmp.c index f0b42c46a9..028cf4a418 100644 --- a/core/snmp.c +++ b/core/snmp.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/ssl.c b/core/ssl.c index c6cea47bea..1fafd659e9 100644 --- a/core/ssl.c +++ b/core/ssl.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" #include #include #include diff --git a/core/strings.c b/core/strings.c index 7565879dbe..6ba04a73a3 100644 --- a/core/strings.c +++ b/core/strings.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" char *uwsgi_str_split_nget(char *str, size_t len, char what, size_t pos, size_t *rlen) { size_t i; diff --git a/core/timebomb.c b/core/timebomb.c index 0e60507660..d5e63304d5 100644 --- a/core/timebomb.c +++ b/core/timebomb.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/transformations.c b/core/transformations.c index c946c79244..2d3a97c32f 100644 --- a/core/transformations.c +++ b/core/transformations.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/utils.c b/core/utils.c index 1fecbfd675..d4eb98d814 100644 --- a/core/utils.c +++ b/core/utils.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/uwsgi.c b/core/uwsgi.c index 286f30d719..9396784f7d 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -21,7 +21,7 @@ */ -#include +#include "uwsgi.h" struct uwsgi_server uwsgi; pid_t masterpid; diff --git a/core/webdav.c b/core/webdav.c index 811c592654..a0bb1d07ed 100644 --- a/core/webdav.c +++ b/core/webdav.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/core/websockets.c b/core/websockets.c index d24f610df5..65ebd580d7 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/zeus.c b/core/zeus.c index aa20f81581..6e83f3cb51 100644 --- a/core/zeus.c +++ b/core/zeus.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" /* diff --git a/core/zlib.c b/core/zlib.c index 5857980ecd..2ea5d9db52 100644 --- a/core/zlib.c +++ b/core/zlib.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; diff --git a/proto/BUILD b/proto/BUILD new file mode 100644 index 0000000000..10a5c3ed32 --- /dev/null +++ b/proto/BUILD @@ -0,0 +1 @@ +exports_files(glob(["*.c"])) diff --git a/proto/base.c b/proto/base.c index 2325e7db3a..5bef156a7f 100644 --- a/proto/base.c +++ b/proto/base.c @@ -1,4 +1,4 @@ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/proto/fastcgi.c b/proto/fastcgi.c index d55971d8fc..6e8718015a 100644 --- a/proto/fastcgi.c +++ b/proto/fastcgi.c @@ -1,6 +1,6 @@ /* async fastcgi protocol parser */ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/proto/http.c b/proto/http.c index e70b77344e..53ce424378 100644 --- a/proto/http.c +++ b/proto/http.c @@ -1,6 +1,6 @@ /* async http protocol parser */ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/proto/puwsgi.c b/proto/puwsgi.c index ba08e93bb7..f7bde2c28c 100644 --- a/proto/puwsgi.c +++ b/proto/puwsgi.c @@ -1,6 +1,6 @@ /* async uwsgi protocol parser */ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/proto/scgi.c b/proto/scgi.c index 707fe3e5d0..09921528a6 100644 --- a/proto/scgi.c +++ b/proto/scgi.c @@ -1,6 +1,6 @@ /* async SCGI protocol parser */ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; diff --git a/proto/uwsgi.c b/proto/uwsgi.c index feaa47c85b..451a1b8c82 100644 --- a/proto/uwsgi.c +++ b/proto/uwsgi.c @@ -1,6 +1,6 @@ /* async uwsgi protocol parser */ -#include +#include "uwsgi.h" extern struct uwsgi_server uwsgi; From 1e1c11462f04a1b84c9c4654b3ba8c46a746ab4e Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Feb 2021 18:09:56 +0100 Subject: [PATCH 085/207] plugins/php: drop PHP < 7 code Not supported upstream since years. --- plugins/php/common.h | 5 ----- plugins/php/php_plugin.c | 28 ---------------------------- plugins/php/session.c | 21 --------------------- 3 files changed, 54 deletions(-) diff --git a/plugins/php/common.h b/plugins/php/common.h index 9bf1c0690b..061b0b597a 100644 --- a/plugins/php/common.h +++ b/plugins/php/common.h @@ -3,11 +3,6 @@ #include "php_main.h" #include "php_variables.h" -#if (PHP_MAJOR_VERSION < 7) -#include "ext/standard/php_smart_str.h" -#else -#define UWSGI_PHP7 -#endif #include "ext/standard/info.h" #include "ext/session/php_session.h" diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 16d495c004..16a0faa7d8 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -6,13 +6,8 @@ static sapi_module_struct uwsgi_sapi_module; static int uwsgi_php_init(void); -#ifdef UWSGI_PHP7 typedef size_t php_strlen_size; typedef zend_long php_long_size; -#else -typedef int php_strlen_size; -typedef uint64_t php_long_size; -#endif struct uwsgi_php { struct uwsgi_string_list *allowed_docroot; @@ -93,11 +88,7 @@ struct uwsgi_option uwsgi_php_options[] = { }; -#ifdef UWSGI_PHP7 static size_t sapi_uwsgi_ub_write(const char *str, size_t str_length TSRMLS_DC) -#else -static int sapi_uwsgi_ub_write(const char *str, uint str_length TSRMLS_DC) -#endif { struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context); @@ -143,11 +134,7 @@ static int sapi_uwsgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) return SAPI_HEADER_SENT_SUCCESSFULLY; } -#ifdef UWSGI_PHP7 static size_t sapi_uwsgi_read_post(char *buffer, size_t count_bytes TSRMLS_DC) -#else -static int sapi_uwsgi_read_post(char *buffer, uint count_bytes TSRMLS_DC) -#endif { uint read_bytes = 0; @@ -273,9 +260,6 @@ PHP_MINIT_FUNCTION(uwsgi_php_minit) { char *name = usl->value; char *strval = equal + 1; equal = NULL; -#ifndef UWSGI_PHP7 - name_len = name_len + 1; -#endif zend_register_string_constant(name, name_len, strval, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC); } usl = usl->next; @@ -284,11 +268,7 @@ PHP_MINIT_FUNCTION(uwsgi_php_minit) { } PHP_FUNCTION(uwsgi_version) { -#ifdef UWSGI_PHP7 RETURN_STRING(UWSGI_VERSION); -#else - RETURN_STRING(UWSGI_VERSION, 1); -#endif } PHP_FUNCTION(uwsgi_worker_id) { @@ -374,11 +354,7 @@ PHP_FUNCTION(uwsgi_cache_get) { if (value) { char *ret = estrndup(value, valsize); free(value); -#ifdef UWSGI_PHP7 RETURN_STRING(ret); -#else - RETURN_STRING(ret, 0); -#endif } RETURN_NULL(); } @@ -482,11 +458,7 @@ PHP_FUNCTION(uwsgi_rpc) { // here we do not free varargs for performance reasons char *ret = estrndup(response, size); free(response); -#ifdef UWSGI_PHP7 RETURN_STRING(ret); -#else - RETURN_STRING(ret, 0); -#endif } clear: diff --git a/plugins/php/session.c b/plugins/php/session.c index ddc375797f..cce0698559 100644 --- a/plugins/php/session.c +++ b/plugins/php/session.c @@ -12,37 +12,20 @@ PS_CLOSE_FUNC(uwsgi) { PS_READ_FUNC(uwsgi) { char *cache = PS_GET_MOD_DATA(); uint64_t valsize = 0; -#ifdef UWSGI_PHP7 char *value = uwsgi_cache_magic_get(key->val, key->len , &valsize, NULL, cache); -#else - char *value = uwsgi_cache_magic_get((char *)key, strlen((char *)key), &valsize, NULL, cache); -#endif if (!value) { *val = STR_EMPTY_ALLOC(); return SUCCESS; } -#ifdef UWSGI_PHP7 *val = zend_string_init(value, valsize, 0); -#else - char *new_val = emalloc(valsize); - memcpy(new_val, value, valsize); - free(value); - *val = new_val; - *vallen = valsize; -#endif return SUCCESS; } PS_WRITE_FUNC(uwsgi) { char *cache = PS_GET_MOD_DATA(); -#ifdef UWSGI_PHP7 if (val->len == 0) return SUCCESS; if (!uwsgi_cache_magic_set(key->val, key->len, val->val, val->len, 0, UWSGI_CACHE_FLAG_UPDATE, cache)) { -#else - if (vallen == 0) return SUCCESS; - if (!uwsgi_cache_magic_set((char *)key, strlen(key), (char *)val, vallen, 0, UWSGI_CACHE_FLAG_UPDATE, cache)) { -#endif return SUCCESS; } return FAILURE; @@ -50,14 +33,10 @@ PS_WRITE_FUNC(uwsgi) { PS_DESTROY_FUNC(uwsgi) { char *cache = PS_GET_MOD_DATA(); -#ifdef UWSGI_PHP7 if (!uwsgi_cache_magic_exists(key->val, key->len, cache)) return SUCCESS; if (!uwsgi_cache_magic_del(key->val, key->len, cache)) { -#else - if (!uwsgi_cache_magic_del((char *)key, strlen(key), cache)) { -#endif return SUCCESS; } return FAILURE; From 0f2ef524489575777184cf364b66243f00cdc37f Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Feb 2021 18:18:29 +0100 Subject: [PATCH 086/207] plugins/php: remove TSRMLS_* macros They are gone in PHP 8 and were inert since PHP 7. --- plugins/php/php_plugin.c | 70 +++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 16a0faa7d8..67812413f4 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -88,7 +88,7 @@ struct uwsgi_option uwsgi_php_options[] = { }; -static size_t sapi_uwsgi_ub_write(const char *str, size_t str_length TSRMLS_DC) +static size_t sapi_uwsgi_ub_write(const char *str, size_t str_length) { struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context); @@ -100,7 +100,7 @@ static size_t sapi_uwsgi_ub_write(const char *str, size_t str_length TSRMLS_DC) return str_length; } -static int sapi_uwsgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) +static int sapi_uwsgi_send_headers(sapi_headers_struct *sapi_headers) { sapi_header_struct *h; zend_llist_position pos; @@ -134,7 +134,7 @@ static int sapi_uwsgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) return SAPI_HEADER_SENT_SUCCESSFULLY; } -static size_t sapi_uwsgi_read_post(char *buffer, size_t count_bytes TSRMLS_DC) +static size_t sapi_uwsgi_read_post(char *buffer, size_t count_bytes) { uint read_bytes = 0; @@ -158,7 +158,7 @@ static size_t sapi_uwsgi_read_post(char *buffer, size_t count_bytes TSRMLS_DC) } -static char *sapi_uwsgi_read_cookies(TSRMLS_D) +static char *sapi_uwsgi_read_cookies() { uint16_t len = 0; struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context); @@ -171,55 +171,55 @@ static char *sapi_uwsgi_read_cookies(TSRMLS_D) return NULL; } -static void sapi_uwsgi_register_variables(zval *track_vars_array TSRMLS_DC) +static void sapi_uwsgi_register_variables(zval *track_vars_array) { int i; struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context); - php_import_environment_variables(track_vars_array TSRMLS_CC); + php_import_environment_variables(track_vars_array); if (uphp.server_software) { if (!uphp.server_software_len) uphp.server_software_len = strlen(uphp.server_software); - php_register_variable_safe("SERVER_SOFTWARE", uphp.server_software, uphp.server_software_len, track_vars_array TSRMLS_CC); + php_register_variable_safe("SERVER_SOFTWARE", uphp.server_software, uphp.server_software_len, track_vars_array); } else { - php_register_variable_safe("SERVER_SOFTWARE", "uWSGI", 5, track_vars_array TSRMLS_CC); + php_register_variable_safe("SERVER_SOFTWARE", "uWSGI", 5, track_vars_array); } for (i = 0; i < wsgi_req->var_cnt; i += 2) { php_register_variable_safe( estrndup(wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len), wsgi_req->hvec[i + 1].iov_base, wsgi_req->hvec[i + 1].iov_len, - track_vars_array TSRMLS_CC); + track_vars_array); } - php_register_variable_safe("PATH_INFO", wsgi_req->path_info, wsgi_req->path_info_len, track_vars_array TSRMLS_CC); + php_register_variable_safe("PATH_INFO", wsgi_req->path_info, wsgi_req->path_info_len, track_vars_array); if (wsgi_req->query_string_len > 0) { - php_register_variable_safe("QUERY_STRING", wsgi_req->query_string, wsgi_req->query_string_len, track_vars_array TSRMLS_CC); + php_register_variable_safe("QUERY_STRING", wsgi_req->query_string, wsgi_req->query_string_len, track_vars_array); } - php_register_variable_safe("SCRIPT_NAME", wsgi_req->script_name, wsgi_req->script_name_len, track_vars_array TSRMLS_CC); - php_register_variable_safe("SCRIPT_FILENAME", wsgi_req->file, wsgi_req->file_len, track_vars_array TSRMLS_CC); + php_register_variable_safe("SCRIPT_NAME", wsgi_req->script_name, wsgi_req->script_name_len, track_vars_array); + php_register_variable_safe("SCRIPT_FILENAME", wsgi_req->file, wsgi_req->file_len, track_vars_array); - php_register_variable_safe("DOCUMENT_ROOT", wsgi_req->document_root, wsgi_req->document_root_len, track_vars_array TSRMLS_CC); + php_register_variable_safe("DOCUMENT_ROOT", wsgi_req->document_root, wsgi_req->document_root_len, track_vars_array); if (wsgi_req->path_info_len) { char *path_translated = ecalloc(1, wsgi_req->file_len + wsgi_req->path_info_len + 1); memcpy(path_translated, wsgi_req->file, wsgi_req->file_len); memcpy(path_translated + wsgi_req->file_len, wsgi_req->path_info, wsgi_req->path_info_len); - php_register_variable_safe("PATH_TRANSLATED", path_translated, wsgi_req->file_len + wsgi_req->path_info_len , track_vars_array TSRMLS_CC); + php_register_variable_safe("PATH_TRANSLATED", path_translated, wsgi_req->file_len + wsgi_req->path_info_len , track_vars_array); } else { - php_register_variable_safe("PATH_TRANSLATED", "", 0, track_vars_array TSRMLS_CC); + php_register_variable_safe("PATH_TRANSLATED", "", 0, track_vars_array); } - php_register_variable_safe("PHP_SELF", wsgi_req->script_name, wsgi_req->script_name_len, track_vars_array TSRMLS_CC); + php_register_variable_safe("PHP_SELF", wsgi_req->script_name, wsgi_req->script_name_len, track_vars_array); struct uwsgi_string_list *usl = uphp.vars; while(usl) { char *equal = strchr(usl->value, '='); if (equal) { php_register_variable_safe( estrndup(usl->value, equal-usl->value), - equal+1, strlen(equal+1), track_vars_array TSRMLS_CC); + equal+1, strlen(equal+1), track_vars_array); } usl = usl->next; } @@ -260,7 +260,7 @@ PHP_MINIT_FUNCTION(uwsgi_php_minit) { char *name = usl->value; char *strval = equal + 1; equal = NULL; - zend_register_string_constant(name, name_len, strval, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC); + zend_register_string_constant(name, name_len, strval, CONST_CS | CONST_PERSISTENT, module_number); } usl = usl->next; } @@ -289,7 +289,7 @@ PHP_FUNCTION(uwsgi_cache_exists) { char *cache = NULL; php_strlen_size cachelen = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { RETURN_NULL(); } @@ -305,7 +305,7 @@ PHP_FUNCTION(uwsgi_cache_clear) { char *cache = NULL; php_strlen_size cachelen = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cache, &cachelen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &cache, &cachelen) == FAILURE) { RETURN_NULL(); } @@ -324,7 +324,7 @@ PHP_FUNCTION(uwsgi_cache_del) { char *cache = NULL; php_strlen_size cachelen = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { RETURN_NULL(); } @@ -346,7 +346,7 @@ PHP_FUNCTION(uwsgi_cache_get) { if (!uwsgi.caches) RETURN_NULL(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &key, &keylen, &cache, &cachelen) == FAILURE) { RETURN_NULL(); } @@ -371,7 +371,7 @@ PHP_FUNCTION(uwsgi_cache_set) { if (!uwsgi.caches) RETURN_NULL(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ls", &key, &keylen, &value, &vallen, &expires, &cache, &cachelen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ls", &key, &keylen, &value, &vallen, &expires, &cache, &cachelen) == FAILURE) { RETURN_NULL(); } @@ -394,7 +394,7 @@ PHP_FUNCTION(uwsgi_cache_update) { if (!uwsgi.caches) RETURN_NULL(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ls", &key, &keylen, &value, &vallen, &expires, &cache, &cachelen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ls", &key, &keylen, &value, &vallen, &expires, &cache, &cachelen) == FAILURE) { RETURN_NULL(); } @@ -419,7 +419,7 @@ PHP_FUNCTION(uwsgi_rpc) { uint16_t argvs[256]; uint64_t size = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &varargs, &num_args) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &varargs, &num_args) == FAILURE) { RETURN_NULL(); } @@ -473,7 +473,7 @@ PHP_FUNCTION(uwsgi_setprocname) { char *name; int name_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) { RETURN_NULL(); } @@ -487,7 +487,7 @@ PHP_FUNCTION(uwsgi_signal) { long long_signum; uint8_t signum = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &long_signum) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &long_signum) == FAILURE) { RETURN_NULL(); } @@ -624,7 +624,7 @@ static int php_uwsgi_startup(sapi_module_struct *sapi_module) #if ((PHP_MAJOR_VERSION >= 7) && (PHP_MINOR_VERSION >= 1)) static void sapi_uwsgi_log_message(char *message, int syslog_type_int) { #else -static void sapi_uwsgi_log_message(char *message TSRMLS_DC) { +static void sapi_uwsgi_log_message(char *message) { #endif uwsgi_log("%s\n", message); } @@ -824,10 +824,6 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { zend_file_handle file_handle; -#ifdef ZTS - TSRMLS_FETCH(); -#endif - SG(server_context) = (void *) wsgi_req; if (uwsgi_parse_vars(wsgi_req)) { @@ -1099,7 +1095,7 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { file_handle.type = ZEND_HANDLE_FILENAME; file_handle.filename = real_filename; - if (php_request_startup(TSRMLS_C) == FAILURE) { + if (php_request_startup() == FAILURE) { uwsgi_500(wsgi_req); return -1; } @@ -1107,13 +1103,13 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { struct uwsgi_string_list *usl=NULL; uwsgi_foreach(usl, uphp.exec_before) { - if (zend_eval_string_ex(usl->value, NULL, "uWSGI php exec before", 1 TSRMLS_CC) == FAILURE) goto end; + if (zend_eval_string_ex(usl->value, NULL, "uWSGI php exec before", 1) == FAILURE) goto end; } - php_execute_script(&file_handle TSRMLS_CC); + php_execute_script(&file_handle); uwsgi_foreach(usl, uphp.exec_after) { - if (zend_eval_string_ex(usl->value, NULL, "uWSGI php exec after", 1 TSRMLS_CC) == FAILURE) goto end; + if (zend_eval_string_ex(usl->value, NULL, "uWSGI php exec after", 1) == FAILURE) goto end; } end: From 459db6aaa54ac1ab3a0e89a59a55acb540399a97 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Feb 2021 18:25:26 +0100 Subject: [PATCH 087/207] plugins/php: fixup sapi_uwsgi_log_message signature for PHP 8 --- plugins/php/php_plugin.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 67812413f4..717d6317b3 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -621,7 +621,9 @@ static int php_uwsgi_startup(sapi_module_struct *sapi_module) } } -#if ((PHP_MAJOR_VERSION >= 7) && (PHP_MINOR_VERSION >= 1)) +#if (PHP_MAJOR_VERSION >= 8) +static void sapi_uwsgi_log_message(const char *message, int syslog_type_int) { +#elif ((PHP_MAJOR_VERSION == 7) && (PHP_MINOR_VERSION >= 1)) static void sapi_uwsgi_log_message(char *message, int syslog_type_int) { #else static void sapi_uwsgi_log_message(char *message) { From e95cad95a8f3141efdff425e3c8f05723170b64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20K=C3=A1rolyi?= Date: Thu, 15 Apr 2021 08:08:32 +0000 Subject: [PATCH 088/207] plugins/gevent: fix compilation with clang11 plugins/gevent/gevent.c[thread 11]: 392:21: error: cast to smaller integer type 'PyGILState_STATE' from 'void *' [-Werror,-Wvoid-pointer-to-enum-cast] PyGILState_Release((PyGILState_STATE) pthread_getspecific(up.upt_gil_key)); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fix #2238 --- plugins/gevent/gevent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gevent/gevent.c b/plugins/gevent/gevent.c index cc02e90b5a..b0d0c36160 100644 --- a/plugins/gevent/gevent.c +++ b/plugins/gevent/gevent.c @@ -389,7 +389,7 @@ static void gil_gevent_get() { } static void gil_gevent_release() { - PyGILState_Release((PyGILState_STATE) pthread_getspecific(up.upt_gil_key)); + PyGILState_Release((PyGILState_STATE)(long) pthread_getspecific(up.upt_gil_key)); } static void monkey_patch() { From a04820f092aafc8a8807769fe179757ee8337b5b Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 26 Apr 2021 22:36:23 +0200 Subject: [PATCH 089/207] Add trove classifier for Python 3.9 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 0792801ae5..9cf4ceb665 100644 --- a/setup.py +++ b/setup.py @@ -146,6 +146,7 @@ def get_extra_require(): 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ], install_requires=get_extra_require() ) From 4137b45636070d42eb28d6262b4cdbc7199c0f16 Mon Sep 17 00:00:00 2001 From: Delena Malan Date: Fri, 28 May 2021 20:30:37 +0200 Subject: [PATCH 090/207] Log SIGINT/SIGTERM triggered kill_them_all --- core/uwsgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/uwsgi.c b/core/uwsgi.c index 9396784f7d..a47fdde978 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1375,7 +1375,7 @@ void kill_them_all(int signum) { // unsubscribe if needed uwsgi_unsubscribe_all(); - uwsgi_log("SIGINT/SIGQUIT received...killing workers...\n"); + uwsgi_log("SIGINT/SIGTERM received...killing workers...\n"); int i; for (i = 1; i <= uwsgi.numproc; i++) { From 608a3f00f3f58bd4044cdc4e16cb99968a633a93 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 31 May 2021 11:02:26 +0200 Subject: [PATCH 091/207] cron: log signal dispatching from registered cron handler --- core/cron.c | 1 + 1 file changed, 1 insertion(+) diff --git a/core/cron.c b/core/cron.c index eac693a564..2c482aaea9 100644 --- a/core/cron.c +++ b/core/cron.c @@ -238,6 +238,7 @@ void uwsgi_manage_signal_cron(time_t now) { if (run_task == 1) { // date match, signal it ? if (now - ucron->last_job >= 60) { + uwsgi_log_verbose("[uwsgi-cron] routing signal %d\n", ucron->sig); uwsgi_route_signal(ucron->sig); ucron->last_job = now; } From dec530bc414ec238d8af8506c1e848e82631aa49 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 31 May 2021 18:02:55 +0200 Subject: [PATCH 092/207] core/cron: support 7 as weekday as an alias for sunday To match crontab behaviour. --- check/check_core.c | 32 ++++++++++++++++++++++++++++++++ core/master_utils.c | 3 ++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/check/check_core.c b/check/check_core.c index 35b88ce783..6aaee620dc 100644 --- a/check/check_core.c +++ b/check/check_core.c @@ -63,11 +63,43 @@ Suite *check_core_opt_parsing(void) return s; } +START_TEST(test_uwsgi_cron_task_needs_execution_handles_weekday_7_as_sunday) +{ + int result; + struct tm *t; + time_t now; + + now = time(NULL); + t = localtime(&now); + t->tm_wday= 0; + + result = uwsgi_cron_task_needs_execution(t, -1, -1, -1, -1, 0); + ck_assert(result == 1); + + result = uwsgi_cron_task_needs_execution(t, -1, -1, -1, -1, 7); + ck_assert(result == 1); + + result = uwsgi_cron_task_needs_execution(t, -1, -1, -1, -1, 1); + ck_assert(result == 0); +} +END_TEST + +Suite *check_core_cron(void) +{ + Suite *s = suite_create("uwsgi cron"); + TCase *tc = tcase_create("cron"); + + suite_add_tcase(s, tc); + tcase_add_test(tc, test_uwsgi_cron_task_needs_execution_handles_weekday_7_as_sunday); + return s; +} + int main(void) { int nf; SRunner *r = srunner_create(check_core_strings()); srunner_add_suite(r, check_core_opt_parsing()); + srunner_add_suite(r, check_core_cron()); srunner_run_all(r, CK_NORMAL); nf = srunner_ntests_failed(r); srunner_free(r); diff --git a/core/master_utils.c b/core/master_utils.c index 7737f1de1a..c3bfa6b519 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -1687,7 +1687,8 @@ int uwsgi_cron_task_needs_execution(struct tm *uwsgi_cron_delta, int minute, int uc_hour = hour; uc_day = day; uc_month = month; - uc_week = week; + // support 7 as alias for sunday (0) to match crontab behaviour + uc_week = week == 7 ? 0 : week; // negative values as interval -1 = * , -5 = */5 if (minute < 0) { From 18f891647e7e7a27e17ab2443134b4314d126e9b Mon Sep 17 00:00:00 2001 From: ilrico Date: Fri, 25 Jun 2021 19:18:47 +0200 Subject: [PATCH 093/207] take into account new naming for LIBPL since python 3.6 related to issues 2270 and 2293 --- plugins/python/uwsgiplugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/python/uwsgiplugin.py b/plugins/python/uwsgiplugin.py index 1b33972122..a2059566e0 100644 --- a/plugins/python/uwsgiplugin.py +++ b/plugins/python/uwsgiplugin.py @@ -52,6 +52,10 @@ def get_python_version(): # try 3.x style config dir if not os.path.exists(libdir): libdir = '%s/lib/python%s/config-%s' % (sys.prefix, version, get_python_version()) + # try >=3.6 style config dir with arch as suffix + if not os.path.exists(libdir): + multiarch = sysconfig.get_config_var('MULTIARCH') + libdir = '%s/lib/python%s/config-%s-%s' % (sys.prefix, version, get_python_version(), multiarch) # get cpu type uname = os.uname() From 64d0257869b0161038b177ed94b5d8faa9643ed5 Mon Sep 17 00:00:00 2001 From: Luciano Rocha Date: Wed, 30 Jun 2021 16:01:25 +0200 Subject: [PATCH 094/207] allow 'iteration' option to uwsgi::add_rb_timer The caller can now specify a custom iteration value when setting an rb_timer. The default is still zero if no third parameter is passed, i.e.: uwsgi::add_rb_timer($signal, $interval); Is the same as: uwsgi::add_rb_timer($signal, $interval, 0); --- plugins/psgi/uwsgi_plmodule.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/psgi/uwsgi_plmodule.c b/plugins/psgi/uwsgi_plmodule.c index 7298a3ee8f..4a6d998033 100644 --- a/plugins/psgi/uwsgi_plmodule.c +++ b/plugins/psgi/uwsgi_plmodule.c @@ -659,8 +659,13 @@ XS(XS_add_rb_timer) { uint8_t uwsgi_signal = SvIV(ST(0)); int seconds = SvIV(ST(1)); + int iterations = 0; - if (uwsgi_signal_add_rb_timer(uwsgi_signal, seconds, 0)) { + if (items > 2) { + iterations = SvIV(ST(2)); + } + + if (uwsgi_signal_add_rb_timer(uwsgi_signal, seconds, iterations)) { croak("unable to register rb timer"); XSRETURN_UNDEF; } From c8c4bd1b5439217f2cb2f146caf162de69638bc1 Mon Sep 17 00:00:00 2001 From: Cyrille Pontvieux Date: Thu, 1 Jul 2021 12:45:29 +0200 Subject: [PATCH 095/207] Allow to compile on Python versions with more that two digits (Python 3.10) --- plugins/python/pyloader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c index d8ab6fe35a..3a1465d673 100644 --- a/plugins/python/pyloader.c +++ b/plugins/python/pyloader.c @@ -22,7 +22,7 @@ PyMethodDef uwsgi_eventfd_write_method[] = { {"uwsgi_eventfd_write", py_eventfd_ void set_dyn_pyhome(char *home, uint16_t pyhome_len) { - char venv_version[15]; + char venv_version[30]; PyObject *site_module; PyObject *pysys_dict = get_uwsgi_pydict("sys"); @@ -45,8 +45,8 @@ void set_dyn_pyhome(char *home, uint16_t pyhome_len) { PyDict_SetItemString(pysys_dict, "prefix", venv_path); PyDict_SetItemString(pysys_dict, "exec_prefix", venv_path); - venv_version[14] = 0; - if (snprintf(venv_version, 15, "/lib/python%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION) == -1) { + bzero(venv_version, 30); + if (snprintf(venv_version, 30, "/lib/python%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION) == -1) { return; } From 40be4b50eb216e0479fa52d564e37f841fe0a9c8 Mon Sep 17 00:00:00 2001 From: James Brown Date: Mon, 4 Oct 2021 12:16:44 -0700 Subject: [PATCH 096/207] add additional http status codes HTTP 418: RFC 2324 HTTP 422: RFC 4918 HTTP 425: RFC 8470 HTTP 426: RFC 7231 HTTP 428: RFC 6585 HTTP 429: RFC 6585 HTTP 431: RFC 6585 HTTP 451: RFC 7725 --- core/init.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/init.c b/core/init.c index 0c3178a3d9..d0a5f532c6 100644 --- a/core/init.c +++ b/core/init.c @@ -46,6 +46,14 @@ struct http_status_codes hsc[] = { {"415", "Unsupported Media Type"}, {"416", "Requested Range Not Satisfiable"}, {"417", "Expectation Failed"}, + {"418", "I'm a teapot"}, + {"422", "Unprocessable Entity"}, + {"425", "Too Early"}, + {"426", "Upgrade Required"}, + {"428", "Precondition Required"}, + {"429", "Too Many Requests"}, + {"431", "Request Header Fields Too Large"}, + {"451", "Unavailable For Legal Reasons"}, {"501", "Not Implemented"}, {"502", "Bad Gateway"}, {"503", "Service Unavailable"}, From 339498ae061f1ccd69d96d4667754bc14305ffd4 Mon Sep 17 00:00:00 2001 From: James Brown Date: Mon, 4 Oct 2021 12:21:19 -0700 Subject: [PATCH 097/207] add 511, too --- core/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/core/init.c b/core/init.c index d0a5f532c6..2c744e00fd 100644 --- a/core/init.c +++ b/core/init.c @@ -60,6 +60,7 @@ struct http_status_codes hsc[] = { {"504", "Gateway Timeout"}, {"505", "HTTP Version Not Supported"}, {"509", "Bandwidth Limit Exceeded"}, + {"511", "Network Authentication Required"}, {"", NULL}, }; From de4213ba1864a632456c34ec5dc1619a79a75a2e Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 13 Oct 2021 19:29:26 +0200 Subject: [PATCH 098/207] core/metrics: fixup gcc misleading indentation warnings --- core/metrics.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/metrics.c b/core/metrics.c index 9714fa658a..733d42084f 100644 --- a/core/metrics.c +++ b/core/metrics.c @@ -703,16 +703,16 @@ int64_t uwsgi_metric_getn(char *name, size_t nlen, char *oid, size_t olen) { int uwsgi_metric_set_max(char *name, char *oid, int64_t value) { um_op; - if (value > *um->value) - *um->value = value; + if (value > *um->value) + *um->value = value; uwsgi_rwunlock(uwsgi.metrics_lock); return 0; } int uwsgi_metric_set_min(char *name, char *oid, int64_t value) { um_op; - if ((value > um->initial_value || 0) && value < *um->value) - *um->value = value; + if ((value > um->initial_value || 0) && value < *um->value) + *um->value = value; uwsgi_rwunlock(uwsgi.metrics_lock); return 0; } From 94b28b156c26d5b0b4ba93fedb057e9aebf59545 Mon Sep 17 00:00:00 2001 From: Thea Flowers Date: Tue, 2 Nov 2021 16:29:36 -0400 Subject: [PATCH 099/207] Add PY_SSIZE_T_CLEAN define for Python 3.10 support --- plugins/python/uwsgi_python.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index aca1f83b71..ec64ad80c7 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -1,4 +1,6 @@ #include +/* See https://docs.python.org/3.10/whatsnew/3.10.html#id2 */ +#define PY_SSIZE_T_CLEAN #include #include From e58837a86c390954ab6412d9e193b49ddcb5adfc Mon Sep 17 00:00:00 2001 From: Jonathan Rosser Date: Tue, 14 Dec 2021 17:31:44 +0000 Subject: [PATCH 100/207] Do not collide with the builtin compile function Rename the compile function in uwsgiconfig.py so that it does not collide with the builtin function of the same name, which is also used in the same file. The code is now using the builtin function as designed and should be clearer. --- uwsgiconfig.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 1712e54516..979db87461 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -256,7 +256,7 @@ def push_command(objfile, cmdline): compile_queue.put((objfile, cmdline)) -def compile(cflags, last_cflags_ts, objfile, srcfile): +def uwsgi_compile(cflags, last_cflags_ts, objfile, srcfile): source_stat = os.stat(srcfile) header_stat = os.stat('uwsgi.h') try: @@ -423,13 +423,13 @@ def build_uwsgi(uc, print_only=False, gcll=None): if objfile.endswith('.c') or objfile.endswith('.cc') or objfile.endswith('.m') or objfile.endswith('.go'): if objfile.endswith('.go'): cflags.append('-Wno-error') - compile(' '.join(cflags), last_cflags_ts, objfile + '.o', file) + uwsgi_compile(' '.join(cflags), last_cflags_ts, objfile + '.o', file) if objfile.endswith('.go'): cflags.pop() else: if objfile == 'core/dot_h': cflags.append('-g') - compile(' '.join(cflags), last_cflags_ts, objfile + '.o', file + '.c') + uwsgi_compile(' '.join(cflags), last_cflags_ts, objfile + '.o', file + '.c') if objfile == 'core/dot_h': cflags.pop() @@ -502,13 +502,13 @@ def build_uwsgi(uc, print_only=False, gcll=None): elif cfile.endswith('.o'): gcc_list.append('%s/%s' % (path, cfile)) elif not cfile.endswith('.c') and not cfile.endswith('.cc') and not cfile.endswith('.go') and not cfile.endswith('.m'): - compile(' '.join(uniq_warnings(p_cflags)), last_cflags_ts, + uwsgi_compile(' '.join(uniq_warnings(p_cflags)), last_cflags_ts, path + '/' + cfile + '.o', path + '/' + cfile + '.c') gcc_list.append('%s/%s' % (path, cfile)) else: if cfile.endswith('.go'): p_cflags.append('-Wno-error') - compile(' '.join(uniq_warnings(p_cflags)), last_cflags_ts, + uwsgi_compile(' '.join(uniq_warnings(p_cflags)), last_cflags_ts, path + '/' + cfile + '.o', path + '/' + cfile) gcc_list.append('%s/%s' % (path, cfile)) for bfile in up.get('BINARY_LIST', []): @@ -1396,7 +1396,7 @@ def get_remote_plugin(path): except NameError: def execfile(path, up): with open(path) as py: - code = __builtins__.compile(py.read(), path, 'exec') + code = compile(py.read(), path, 'exec') exec(code, up) From eafb025e63a9b5f00b37c83cd4c2c532bbf9254b Mon Sep 17 00:00:00 2001 From: Tahir Butt Date: Wed, 15 Dec 2021 17:09:39 -0500 Subject: [PATCH 101/207] fix: add tests for python worker deadlocks --- tests/deadlocks/main.py | 4 ++++ tests/deadlocks/master-nothreads.ini | 5 +++++ ...er-singleinterpreter-threads-10workers.ini | 6 ++++++ ...ster-singleinterpreter-threads-1worker.ini | 6 ++++++ tests/deadlocks/master-threads-10workers.ini | 5 +++++ tests/deadlocks/master-threads-1worker.ini | 5 +++++ .../deadlocks/nomaster-threads-10workers.ini | 5 +++++ tests/deadlocks/nomaster-threads-1worker.ini | 5 +++++ tests/deadlocks/sitecustomize.py | 14 +++++++++++++ tests/travis.sh | 21 +++++++++++++++++-- 10 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/deadlocks/main.py create mode 100644 tests/deadlocks/master-nothreads.ini create mode 100644 tests/deadlocks/master-singleinterpreter-threads-10workers.ini create mode 100644 tests/deadlocks/master-singleinterpreter-threads-1worker.ini create mode 100644 tests/deadlocks/master-threads-10workers.ini create mode 100644 tests/deadlocks/master-threads-1worker.ini create mode 100644 tests/deadlocks/nomaster-threads-10workers.ini create mode 100644 tests/deadlocks/nomaster-threads-1worker.ini create mode 100644 tests/deadlocks/sitecustomize.py diff --git a/tests/deadlocks/main.py b/tests/deadlocks/main.py new file mode 100644 index 0000000000..66e3b37ddf --- /dev/null +++ b/tests/deadlocks/main.py @@ -0,0 +1,4 @@ +def application(env, start_response): + start_response("200 OK", [("Content-Type", "text/plain")]) + message = "Hello World" + return [message.encode("utf-8")] diff --git a/tests/deadlocks/master-nothreads.ini b/tests/deadlocks/master-nothreads.ini new file mode 100644 index 0000000000..e922e5ef23 --- /dev/null +++ b/tests/deadlocks/master-nothreads.ini @@ -0,0 +1,5 @@ +[uwsgi] +show-config = true +master = true +enable-threads = false +workers = 1 diff --git a/tests/deadlocks/master-singleinterpreter-threads-10workers.ini b/tests/deadlocks/master-singleinterpreter-threads-10workers.ini new file mode 100644 index 0000000000..d97c74522a --- /dev/null +++ b/tests/deadlocks/master-singleinterpreter-threads-10workers.ini @@ -0,0 +1,6 @@ +[uwsgi] +show-config = true +master = true +enable-threads = true +workers = 10 +single-interpreter = true diff --git a/tests/deadlocks/master-singleinterpreter-threads-1worker.ini b/tests/deadlocks/master-singleinterpreter-threads-1worker.ini new file mode 100644 index 0000000000..a282ae4071 --- /dev/null +++ b/tests/deadlocks/master-singleinterpreter-threads-1worker.ini @@ -0,0 +1,6 @@ +[uwsgi] +show-config = true +master = true +enable-threads = true +workers = 1 +single-interpreter = true diff --git a/tests/deadlocks/master-threads-10workers.ini b/tests/deadlocks/master-threads-10workers.ini new file mode 100644 index 0000000000..27e8f08921 --- /dev/null +++ b/tests/deadlocks/master-threads-10workers.ini @@ -0,0 +1,5 @@ +[uwsgi] +show-config = true +master = true +enable-threads = true +workers = 10 diff --git a/tests/deadlocks/master-threads-1worker.ini b/tests/deadlocks/master-threads-1worker.ini new file mode 100644 index 0000000000..f4dd9605ce --- /dev/null +++ b/tests/deadlocks/master-threads-1worker.ini @@ -0,0 +1,5 @@ +[uwsgi] +show-config = true +master = true +enable-threads = true +workers = 1 diff --git a/tests/deadlocks/nomaster-threads-10workers.ini b/tests/deadlocks/nomaster-threads-10workers.ini new file mode 100644 index 0000000000..38cfe23b7a --- /dev/null +++ b/tests/deadlocks/nomaster-threads-10workers.ini @@ -0,0 +1,5 @@ +[uwsgi] +show-config = true +master = false +enable-threads = true +workers = 10 diff --git a/tests/deadlocks/nomaster-threads-1worker.ini b/tests/deadlocks/nomaster-threads-1worker.ini new file mode 100644 index 0000000000..eeb4e544c5 --- /dev/null +++ b/tests/deadlocks/nomaster-threads-1worker.ini @@ -0,0 +1,5 @@ +[uwsgi] +show-config = true +master = false +enable-threads = true +workers = 1 diff --git a/tests/deadlocks/sitecustomize.py b/tests/deadlocks/sitecustomize.py new file mode 100644 index 0000000000..0aff259f7b --- /dev/null +++ b/tests/deadlocks/sitecustomize.py @@ -0,0 +1,14 @@ +import threading +import time + + +def run(): + print("[DEADLOCKS] started run") + st = time.time() + while time.time() < st + 5: + pass + print("[DEADLOCKS] finished run") + +t = threading.Thread(target=run) +t.daemon = True +t.start() diff --git a/tests/travis.sh b/tests/travis.sh index a8b54fe1c3..c9db45df2e 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -69,6 +69,20 @@ test_python() { } +test_python_deadlocks() { + date > reload.txt + rm -f uwsgi.log + echo -e "${bldyel}================== TESTING DEADLOCKS $1 $2 =====================${txtrst}" + echo -e "${bldyel}>>> Starting python app${txtrst}" + echo -en "${bldred}" + # initialize with tests/deadlocks/sitecustomize.py + PYTHONPATH=tests/deadlocks ./uwsgi --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --wsgi-file tests/deadlocks/main.py --ini $2 --daemonize uwsgi.log + echo -en "${txtrst}" + http_test "http://localhost:8080/" + echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" +} + + test_rack() { date > reload.txt rm -f uwsgi.log @@ -90,8 +104,11 @@ while read PV ; do test_python $PV $WSGI_FILE done done < <(cat "$CI_CONFIG" | grep "plugins/python base" | sed s_".*plugins/python base "_""_g) - - +while read PV ; do + for INI_FILE in tests/deadlocks/*.ini ; do + test_python_deadlocks $PV $INI_FILE + done +done < <(cat "$CI_CONFIG" | grep "plugins/python base" | sed s_".*plugins/python base "_""_g) while read RV ; do for RACK in examples/config2.ru ; do test_rack $RV $RACK From 3b4dcdde2cf4d11125519f82bb65eb6c33f3bf43 Mon Sep 17 00:00:00 2001 From: Tahir Butt Date: Wed, 29 Dec 2021 00:22:07 -0500 Subject: [PATCH 102/207] fix(python-plugin): add uwsgi fork hooks to update internal interpreter state --- core/master_utils.c | 19 +++++ plugins/python/python_plugin.c | 79 +++++++++++++++++-- plugins/python/uwsgi_python.h | 3 + tests/deadlocks/master-nothreads.ini | 1 + ...er-singleinterpreter-threads-10workers.ini | 1 + ...ster-singleinterpreter-threads-1worker.ini | 1 + tests/deadlocks/master-threads-10workers.ini | 1 + tests/deadlocks/master-threads-1worker.ini | 1 + .../deadlocks/nomaster-threads-10workers.ini | 1 + tests/deadlocks/nomaster-threads-1worker.ini | 1 + uwsgi.h | 3 + 11 files changed, 106 insertions(+), 5 deletions(-) diff --git a/core/master_utils.c b/core/master_utils.c index c3bfa6b519..9a54b35e51 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -747,9 +747,22 @@ int uwsgi_respawn_worker(int wid) { pthread_mutex_lock(&uwsgi.threaded_logger_lock); } + + for (i = 0; i < 256; i++) { + if (uwsgi.p[i]->pre_uwsgi_fork) { + uwsgi.p[i]->pre_uwsgi_fork(); + } + } + pid_t pid = uwsgi_fork(uwsgi.workers[wid].name); if (pid == 0) { + for (i = 0; i < 256; i++) { + if (uwsgi.p[i]->post_uwsgi_fork) { + uwsgi.p[i]->post_uwsgi_fork(1); + } + } + signal(SIGWINCH, worker_wakeup); signal(SIGTSTP, worker_wakeup); uwsgi.mywid = wid; @@ -812,6 +825,12 @@ int uwsgi_respawn_worker(int wid) { uwsgi_error("fork()"); } else { + for (i = 0; i < 256; i++) { + if (uwsgi.p[i]->post_uwsgi_fork) { + uwsgi.p[i]->post_uwsgi_fork(0); + } + } + // the pid is set only in the master, as the worker should never use it uwsgi.workers[wid].pid = pid; diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 79f29d43ce..ecdf21926d 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -195,6 +195,7 @@ struct uwsgi_option uwsgi_python_options[] = { #endif {"py-call-osafterfork", no_argument, 0, "enable child processes running cpython to trap OS signals", uwsgi_opt_true, &up.call_osafterfork, 0}, + {"py-call-uwsgi-fork-hooks", no_argument, 0, "call pre and post hooks when uswgi forks to update the internal interpreter state of CPython", uwsgi_opt_true, &up.call_uwsgi_fork_hooks, 0}, {"early-python", no_argument, 0, "load the python VM as soon as possible (useful for the fork server)", uwsgi_early_python, NULL, UWSGI_OPT_IMMEDIATE}, {"early-pyimport", required_argument, 0, "import a python module in the early phase", uwsgi_early_python_import, NULL, UWSGI_OPT_IMMEDIATE}, @@ -428,12 +429,20 @@ void uwsgi_python_atexit() { void uwsgi_python_post_fork() { + // Need to acquire the gil when no master process is used as first worker + // will not have been forked like others + // Necessary if uwsgi fork hooks called to update interpreter state + if (up.call_uwsgi_fork_hooks && !uwsgi.master_process && uwsgi.mywid == 1) { + UWSGI_GET_GIL + } + if (uwsgi.i_am_a_spooler) { UWSGI_GET_GIL } // reset python signal flags so child processes can trap signals - if (up.call_osafterfork) { + // Necessary if uwsgi fork hooks not called to update interpreter state + if (!up.call_uwsgi_fork_hooks && up.call_osafterfork) { #ifdef HAS_NOT_PyOS_AfterFork_Child PyOS_AfterFork(); #else @@ -1129,6 +1138,13 @@ void uwsgi_python_preinit_apps() { up.loaders[LOADER_CALLABLE] = uwsgi_callable_loader; up.loaders[LOADER_STRING_CALLABLE] = uwsgi_string_callable_loader; + // GIL was released in previous initialization steps but init_pyargv expects + // the GIL to be acquired + // Necessary if uwsgi fork hooks called to update interpreter state + if (up.call_uwsgi_fork_hooks) { + UWSGI_GET_GIL + } + init_pyargv(); init_uwsgi_embedded_module(); @@ -1174,12 +1190,19 @@ void uwsgi_python_preinit_apps() { upli = upli->next; } + // Release the GIL before moving on forward with initialization + // Necessary if uwsgi fork hooks called to update interpreter state + if (up.call_uwsgi_fork_hooks) { + UWSGI_RELEASE_GIL + } + } void uwsgi_python_init_apps() { // lazy ? - if (uwsgi.mywid > 0) { + // Also necessary if uwsgi fork hooks called to update interpreter state + if (uwsgi.mywid > 0 || up.call_uwsgi_fork_hooks) { UWSGI_GET_GIL; } @@ -1291,7 +1314,8 @@ void uwsgi_python_init_apps() { } } // lazy ? - if (uwsgi.mywid > 0) { + // Also necessary if uwsgi fork hooks called to update interpreter state + if (uwsgi.mywid > 0 || up.call_uwsgi_fork_hooks) { UWSGI_RELEASE_GIL; } @@ -1304,6 +1328,10 @@ void uwsgi_python_master_fixup(int step) { if (!uwsgi.master_process) return; + // Skip master fixup if uwsgi fork hooks called to update interpreter state + if (up.call_uwsgi_fork_hooks) + return; + if (uwsgi.has_threads) { if (step == 0) { if (!master_fixed) { @@ -1320,6 +1348,40 @@ void uwsgi_python_master_fixup(int step) { } } +void uwsgi_python_pre_uwsgi_fork() { + if (!up.call_uwsgi_fork_hooks) + return; + + if (uwsgi.has_threads) { + // Acquire the gil and import lock before forking in order to avoid + // deadlocks in workers + UWSGI_GET_GIL + _PyImport_AcquireLock(); + } +} + + +void uwsgi_python_post_uwsgi_fork(int step) { + if (!up.call_uwsgi_fork_hooks) + return; + + if (uwsgi.has_threads) { + if (step == 0) { + // Release locks within master process + _PyImport_ReleaseLock(); + UWSGI_RELEASE_GIL + } + else { + // Ensure thread state and locks are cleaned up in child process +#ifdef HAS_NOT_PyOS_AfterFork_Child + PyOS_AfterFork(); +#else + PyOS_AfterFork_Child(); +#endif + } + } +} + void uwsgi_python_enable_threads() { #ifdef UWSGI_SHOULD_CALL_PYEVAL_INITTHREADS @@ -1349,7 +1411,11 @@ void uwsgi_python_enable_threads() { up.reset_ts = threaded_reset_ts; } - + // Release the newly created gil from call to PyEval_InitThreads above + // Necessary if uwsgi fork hooks called to update interpreter state + if (up.call_uwsgi_fork_hooks) { + UWSGI_RELEASE_GIL + } uwsgi_log("python threads support enabled\n"); @@ -2044,7 +2110,8 @@ static int uwsgi_python_worker() { return 0; UWSGI_GET_GIL; // ensure signals can be used again from python - if (!up.call_osafterfork) + // Necessary if fork hooks have been not used to update interpreter state + if (!up.call_osafterfork && !up.call_uwsgi_fork_hooks) #ifdef HAS_NOT_PyOS_AfterFork_Child PyOS_AfterFork(); #else @@ -2130,4 +2197,6 @@ struct uwsgi_plugin python_plugin = { .worker = uwsgi_python_worker, + .pre_uwsgi_fork = uwsgi_python_pre_uwsgi_fork, + .post_uwsgi_fork = uwsgi_python_post_uwsgi_fork, }; diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index aca1f83b71..80d68cc786 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -1,5 +1,6 @@ #include #include +#include #include @@ -238,6 +239,8 @@ struct uwsgi_python { int master_check_signals; char *executable; + + int call_uwsgi_fork_hooks; }; diff --git a/tests/deadlocks/master-nothreads.ini b/tests/deadlocks/master-nothreads.ini index e922e5ef23..15f79da446 100644 --- a/tests/deadlocks/master-nothreads.ini +++ b/tests/deadlocks/master-nothreads.ini @@ -3,3 +3,4 @@ show-config = true master = true enable-threads = false workers = 1 +py-call-uwsgi-fork-hooks = true diff --git a/tests/deadlocks/master-singleinterpreter-threads-10workers.ini b/tests/deadlocks/master-singleinterpreter-threads-10workers.ini index d97c74522a..47812651b9 100644 --- a/tests/deadlocks/master-singleinterpreter-threads-10workers.ini +++ b/tests/deadlocks/master-singleinterpreter-threads-10workers.ini @@ -4,3 +4,4 @@ master = true enable-threads = true workers = 10 single-interpreter = true +py-call-uwsgi-fork-hooks = true diff --git a/tests/deadlocks/master-singleinterpreter-threads-1worker.ini b/tests/deadlocks/master-singleinterpreter-threads-1worker.ini index a282ae4071..61a943b039 100644 --- a/tests/deadlocks/master-singleinterpreter-threads-1worker.ini +++ b/tests/deadlocks/master-singleinterpreter-threads-1worker.ini @@ -4,3 +4,4 @@ master = true enable-threads = true workers = 1 single-interpreter = true +py-call-uwsgi-fork-hooks = true diff --git a/tests/deadlocks/master-threads-10workers.ini b/tests/deadlocks/master-threads-10workers.ini index 27e8f08921..3dc7de510b 100644 --- a/tests/deadlocks/master-threads-10workers.ini +++ b/tests/deadlocks/master-threads-10workers.ini @@ -3,3 +3,4 @@ show-config = true master = true enable-threads = true workers = 10 +py-call-uwsgi-fork-hooks = true diff --git a/tests/deadlocks/master-threads-1worker.ini b/tests/deadlocks/master-threads-1worker.ini index f4dd9605ce..f9d2382c4f 100644 --- a/tests/deadlocks/master-threads-1worker.ini +++ b/tests/deadlocks/master-threads-1worker.ini @@ -3,3 +3,4 @@ show-config = true master = true enable-threads = true workers = 1 +py-call-uwsgi-fork-hooks = true diff --git a/tests/deadlocks/nomaster-threads-10workers.ini b/tests/deadlocks/nomaster-threads-10workers.ini index 38cfe23b7a..7f6ff91b8a 100644 --- a/tests/deadlocks/nomaster-threads-10workers.ini +++ b/tests/deadlocks/nomaster-threads-10workers.ini @@ -3,3 +3,4 @@ show-config = true master = false enable-threads = true workers = 10 +py-call-uwsgi-fork-hooks = true diff --git a/tests/deadlocks/nomaster-threads-1worker.ini b/tests/deadlocks/nomaster-threads-1worker.ini index eeb4e544c5..8252820817 100644 --- a/tests/deadlocks/nomaster-threads-1worker.ini +++ b/tests/deadlocks/nomaster-threads-1worker.ini @@ -3,3 +3,4 @@ show-config = true master = false enable-threads = true workers = 1 +py-call-uwsgi-fork-hooks = true diff --git a/uwsgi.h b/uwsgi.h index 2b32cad799..5fd6528fca 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -1095,6 +1095,9 @@ struct uwsgi_plugin { int (*worker)(void); void (*early_post_jail) (void); + + void (*pre_uwsgi_fork) (void); + void (*post_uwsgi_fork) (int); }; #ifdef UWSGI_PCRE From 90766833f2bcd2a83039ed4e18501bfb52162e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=98=D0=B0=D0=BD=20=D0=93=D0=B5=D0=BE?= =?UTF-8?q?=D1=80=D0=B3=D0=B8=D0=B5=D0=B2=D1=81=D0=BA=D0=B8?= Date: Wed, 29 Dec 2021 14:02:32 +0100 Subject: [PATCH 103/207] fix: missing arginfo when compiling against PHP 8 fixes https://github.com/unbit/uwsgi/issues/2356 --- plugins/php/php_plugin.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 717d6317b3..d336adddc4 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -497,21 +497,24 @@ PHP_FUNCTION(uwsgi_signal) { RETURN_NULL(); } +ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) +ZEND_END_ARG_INFO() + zend_function_entry uwsgi_php_functions[] = { - PHP_FE(uwsgi_version, NULL) - PHP_FE(uwsgi_setprocname, NULL) - PHP_FE(uwsgi_worker_id, NULL) - PHP_FE(uwsgi_masterpid, NULL) - PHP_FE(uwsgi_signal, NULL) - - PHP_FE(uwsgi_rpc, NULL) - - PHP_FE(uwsgi_cache_get, NULL) - PHP_FE(uwsgi_cache_set, NULL) - PHP_FE(uwsgi_cache_update, NULL) - PHP_FE(uwsgi_cache_del, NULL) - PHP_FE(uwsgi_cache_clear, NULL) - PHP_FE(uwsgi_cache_exists, NULL) + PHP_FE(uwsgi_version, arginfo_void) + PHP_FE(uwsgi_setprocname, arginfo_void) + PHP_FE(uwsgi_worker_id, arginfo_void) + PHP_FE(uwsgi_masterpid, arginfo_void) + PHP_FE(uwsgi_signal, arginfo_void) + + PHP_FE(uwsgi_rpc, arginfo_void) + + PHP_FE(uwsgi_cache_get, arginfo_void) + PHP_FE(uwsgi_cache_set, arginfo_void) + PHP_FE(uwsgi_cache_update, arginfo_void) + PHP_FE(uwsgi_cache_del, arginfo_void) + PHP_FE(uwsgi_cache_clear, arginfo_void) + PHP_FE(uwsgi_cache_exists, arginfo_void) { NULL, NULL, NULL}, }; From 8ca18da9a01eee19156243c5c0d28d2572698e4a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 30 Jan 2022 14:31:50 +0100 Subject: [PATCH 104/207] plugins/php: handle php8.1 zend_file_handle signature change filename is now a zend_string. Refs #2394 --- plugins/php/php_plugin.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index d336adddc4..8b5a241569 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -1096,14 +1096,19 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { SG(request_info).path_translated = wsgi_req->file; - memset(&file_handle, 0, sizeof(zend_file_handle)); - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = real_filename; +#if PHP_VERSION_ID >= 80100 + zend_string *handle_filename = zend_string_init(real_filename, real_filename_len, 0); +#else + const char *handle_filename = real_filename; +#endif + memset(&file_handle, 0, sizeof(zend_file_handle)); + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = handle_filename; - if (php_request_startup() == FAILURE) { + if (php_request_startup() == FAILURE) { uwsgi_500(wsgi_req); - return -1; - } + return -1; + } struct uwsgi_string_list *usl=NULL; From 0e6e7c600854889d8d45b35069ff3f5915a57781 Mon Sep 17 00:00:00 2001 From: Niels Doucet Date: Wed, 2 Feb 2022 12:58:56 +0100 Subject: [PATCH 105/207] Remove stray license headers that don't comply with the global GPL2 + linking exception licensing model adopted with version 2. Fixes #2401 --- apache2/mod_Ruwsgi.c | 24 ------------------------ apache2/mod_proxy_uwsgi.c | 16 +--------------- apache2/mod_uwsgi.c | 17 ----------------- core/uwsgi.c | 23 ----------------------- plugins/router_xmldir/router_xmldir.c | 20 -------------------- 5 files changed, 1 insertion(+), 99 deletions(-) diff --git a/apache2/mod_Ruwsgi.c b/apache2/mod_Ruwsgi.c index ecdd5d83cd..0bff85356a 100644 --- a/apache2/mod_Ruwsgi.c +++ b/apache2/mod_Ruwsgi.c @@ -1,27 +1,3 @@ -/* - - *** uWSGI/mod_Ruwsgi *** - - Copyright 2009-2010 Roger Florkowski - Copyright 2009-2010 Unbit S.a.s. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -*/ - #define MOD_UWSGI_VERSION "1.0" #include "ap_config.h" diff --git a/apache2/mod_proxy_uwsgi.c b/apache2/mod_proxy_uwsgi.c index 68fb8fb557..507262bed1 100644 --- a/apache2/mod_proxy_uwsgi.c +++ b/apache2/mod_proxy_uwsgi.c @@ -1,20 +1,6 @@ /* - -*** mod_proxy_uwsgi *** - -Copyright 2009-2017 Unbit S.a.s. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +*** mod_proxy_uwsgi *** To build: diff --git a/apache2/mod_uwsgi.c b/apache2/mod_uwsgi.c index ff5cad682e..b80b7744f0 100644 --- a/apache2/mod_uwsgi.c +++ b/apache2/mod_uwsgi.c @@ -2,23 +2,6 @@ *** uWSGI/mod_uwsgi *** -Copyright 2009-2014 Unbit S.a.s. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - To compile: (Linux) apxs2 -i -c mod_uwsgi.c diff --git a/core/uwsgi.c b/core/uwsgi.c index a47fdde978..d7bf318b39 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1,26 +1,3 @@ -/* - - *** uWSGI *** - - Copyright (C) 2009-2017 Unbit S.a.s. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -*/ - - #include "uwsgi.h" struct uwsgi_server uwsgi; diff --git a/plugins/router_xmldir/router_xmldir.c b/plugins/router_xmldir/router_xmldir.c index 525146beb2..c2cbff8fca 100644 --- a/plugins/router_xmldir/router_xmldir.c +++ b/plugins/router_xmldir/router_xmldir.c @@ -1,23 +1,3 @@ -/* - * Copyright (C) 2013 Unbit S.a.s. - * Copyright (C) 2013 Guido Berhoerster - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - #include #include #include From 7868cc86b807fad47fb5b8d4ac1bfd0c3da9e004 Mon Sep 17 00:00:00 2001 From: "Tahir H. Butt" Date: Tue, 22 Mar 2022 11:10:00 -0400 Subject: [PATCH 106/207] fix(tests/deadlock): sleep before checking uwsgi process --- tests/travis.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/travis.sh b/tests/travis.sh index c9db45df2e..ac5e8953cd 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -77,6 +77,7 @@ test_python_deadlocks() { echo -en "${bldred}" # initialize with tests/deadlocks/sitecustomize.py PYTHONPATH=tests/deadlocks ./uwsgi --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --wsgi-file tests/deadlocks/main.py --ini $2 --daemonize uwsgi.log + sleep 1 echo -en "${txtrst}" http_test "http://localhost:8080/" echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" From 5838086dd4490b8a55ff58fc0bf0f108caa4e079 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Sat, 2 Apr 2022 14:53:22 +0200 Subject: [PATCH 107/207] Update README --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index a5a521696f..0a0ae5fa8a 100644 --- a/README +++ b/README @@ -2,9 +2,9 @@ The uWSGI project For official documentation check: https://uwsgi-docs.readthedocs.io/en/latest/ -For commercial support check: http://unbit.com/ +Note: The project is in maintenance mode (only bugfixes and updates for new languages apis) -uWSGI development is sponsored by: +uWSGI development has been sponsored by: http://unbit.com https://www.pythonanywhere.com/ From 1746254fec076bd52e7682187041e82fafad5427 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Mon, 4 Apr 2022 09:55:48 +0200 Subject: [PATCH 108/207] plugins/python: Properly call `.close()` as mandated by WSGI specs. uWSGI did not call `.close()` on objects returned by `wsgi.file_wrapper`. This resulted on `.close()` beeing called when the gc managed to clean up the object (which apparently did not happen with refcounting due to cyclic references) resulting in hard to debug errors. --- plugins/python/wsgi_subhandler.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index 32a3aee7eb..faa3fd8521 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -365,23 +365,24 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { Py_DECREF((PyObject *) wsgi_req->async_sendfile); } - if (wsgi_req->async_placeholder) { - // CALL close() ALWAYS if we are working with an iterator !!! - if (PyObject_HasAttrString((PyObject *)wsgi_req->async_result, "close")) { - PyObject *close_method = PyObject_GetAttrString((PyObject *)wsgi_req->async_result, "close"); - PyObject *close_method_args = PyTuple_New(0); + if (wsgi_req->async_placeholder != NULL) { + Py_DECREF((PyObject *)wsgi_req->async_placeholder); + } + + // Call close() on the response, this is required by the WSGI spec + if (PyObject_HasAttrString((PyObject *)wsgi_req->async_result, "close")) { + PyObject *close_method = PyObject_GetAttrString((PyObject *)wsgi_req->async_result, "close"); + PyObject *close_method_args = PyTuple_New(0); #ifdef UWSGI_DEBUG - uwsgi_log("calling close() for %.*s %p %p\n", wsgi_req->uri_len, wsgi_req->uri, close_method, close_method_args); + uwsgi_log("calling close() for %.*s %p %p\n", wsgi_req->uri_len, wsgi_req->uri, close_method, close_method_args); #endif - PyObject *close_method_output = PyObject_CallObject(close_method, close_method_args); - if (PyErr_Occurred()) { - uwsgi_manage_exception(wsgi_req, 0); - } - Py_DECREF(close_method_args); - Py_XDECREF(close_method_output); - Py_DECREF(close_method); - } - Py_DECREF((PyObject *)wsgi_req->async_placeholder); + PyObject *close_method_output = PyObject_CallObject(close_method, close_method_args); + if (PyErr_Occurred()) { + uwsgi_manage_exception(wsgi_req, 0); + } + Py_DECREF(close_method_args); + Py_XDECREF(close_method_output); + Py_DECREF(close_method); } Py_DECREF((PyObject *)wsgi_req->async_result); From 81b25a0b8d7f3a414bc7ca67c73861821c9a39f0 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Mon, 4 Apr 2022 10:22:49 +0200 Subject: [PATCH 109/207] Added a sleep to give uWSGI time to start (?) --- tests/travis.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/travis.sh b/tests/travis.sh index ac5e8953cd..ec4c5c5d21 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -63,6 +63,7 @@ test_python() { echo -e "${bldyel}>>> Spawning uWSGI python app${txtrst}" echo -en "${bldred}" ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --wsgi-file $2 --daemonize uwsgi.log + sleep 1 echo -en "${txtrst}" http_test "http://localhost:8080/" echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" From 87ca2082630fa9cb9e9f06b6c8e8e6eabc1c19c9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 12 Jun 2022 16:33:14 +0300 Subject: [PATCH 110/207] Use threading.current_thread, has existed since Python 2.6 --- plugins/python/python_plugin.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index ecdf21926d..91e3537f93 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -1423,16 +1423,12 @@ void uwsgi_python_enable_threads() { } void uwsgi_python_set_thread_name(int core_id) { - // call threading.currentThread (taken from mod_wsgi, but removes DECREFs as thread in uWSGI are fixed) + // call threading.current_thread (taken from mod_wsgi, but removes DECREFs as thread in uWSGI are fixed) PyObject *threading_module = PyImport_ImportModule("threading"); if (threading_module) { PyObject *threading_module_dict = PyModule_GetDict(threading_module); if (threading_module_dict) { -#ifdef PYTHREE PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "current_thread"); -#else - PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread"); -#endif if (threading_current) { PyObject *current_thread = PyObject_CallObject(threading_current, (PyObject *)NULL); if (!current_thread) { @@ -1516,11 +1512,7 @@ PyObject *uwsgi_python_setup_thread(char *name, PyInterpreterState *interpreter) if (threading_module) { PyObject *threading_module_dict = PyModule_GetDict(threading_module); if (threading_module_dict) { -#ifdef PYTHREE PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "current_thread"); -#else - PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread"); -#endif if (threading_current) { PyObject *current_thread = PyObject_CallObject(threading_current, (PyObject *)NULL); if (!current_thread) { From 554799b69cdd1046df3d713005b3995d271c21c4 Mon Sep 17 00:00:00 2001 From: Peter Law Date: Thu, 23 Jun 2022 09:40:49 +0100 Subject: [PATCH 111/207] Replace all `os.system` usage with `subprocess.call` This aims to fix intermittent build failures on some platforms (most notably Debian Bullseye) which appear to be due to the use of `os.system` from background threads. The exact nature of the issue is unclear, however moving to `subprocess.call` appears to fix it. Fixes https://github.com/unbit/uwsgi/issues/2447. While there's more that could be done here to modernise these calls, this aims to be a minimal change which introduces subprocess without much intrusion. --- examples/mjpeg_stream.py | 5 ++-- plugins/gccgo/uwsgiplugin.py | 3 ++- plugins/jvm/uwsgiplugin.py | 5 ++-- plugins/mono/uwsgiplugin.py | 5 ++-- uwsgiconfig.py | 48 ++++++++++++++++++------------------ 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/examples/mjpeg_stream.py b/examples/mjpeg_stream.py index 9ed082c175..6b8f9d6b33 100644 --- a/examples/mjpeg_stream.py +++ b/examples/mjpeg_stream.py @@ -1,4 +1,5 @@ import os +import subprocess def application(env, start_response): @@ -16,8 +17,8 @@ def application(env, start_response): 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 --git a/plugins/gccgo/uwsgiplugin.py b/plugins/gccgo/uwsgiplugin.py index fee9364767..dbb2052c44 100644 --- a/plugins/gccgo/uwsgiplugin.py +++ b/plugins/gccgo/uwsgiplugin.py @@ -1,4 +1,5 @@ import os +import subprocess NAME = 'gccgo' @@ -10,6 +11,6 @@ def post_build(config): if os.path.exists('plugins/gccgo/uwsgi.go.o'): - if os.system("objcopy -j .go_export plugins/gccgo/uwsgi.go.o plugins/gccgo/uwsgi.gox") != 0: + if subprocess.call("objcopy -j .go_export plugins/gccgo/uwsgi.go.o plugins/gccgo/uwsgi.gox", shell=True) != 0: os._exit(1) print("*** uwsgi.gox available in %s/plugins/gccgo ***" % os.getcwd()) diff --git a/plugins/jvm/uwsgiplugin.py b/plugins/jvm/uwsgiplugin.py index bd03cea3d8..280ba7fadc 100644 --- a/plugins/jvm/uwsgiplugin.py +++ b/plugins/jvm/uwsgiplugin.py @@ -1,5 +1,6 @@ import os import shutil +import subprocess NAME = 'jvm' @@ -72,9 +73,9 @@ def post_build(config): - if os.system("javac %s/plugins/jvm/uwsgi.java" % os.getcwd()) != 0: + if subprocess.call("javac %s/plugins/jvm/uwsgi.java" % os.getcwd(), shell=True) != 0: os._exit(1) - if os.system("cd %s/plugins/jvm ; jar cvf uwsgi.jar *.class" % os.getcwd()) != 0: + if subprocess.call("cd %s/plugins/jvm ; jar cvf uwsgi.jar *.class" % os.getcwd(), shell=True) != 0: os._exit(1) print("*** uwsgi.jar available in %s/plugins/jvm/uwsgi.jar ***" % os.getcwd()) diff --git a/plugins/mono/uwsgiplugin.py b/plugins/mono/uwsgiplugin.py index 2232f88eb3..129619116a 100644 --- a/plugins/mono/uwsgiplugin.py +++ b/plugins/mono/uwsgiplugin.py @@ -1,4 +1,5 @@ import os +import subprocess NAME = 'mono' @@ -12,8 +13,8 @@ def post_build(config): - if os.system("sn -k plugins/mono/uwsgi.key") != 0: + if subprocess.call("sn -k plugins/mono/uwsgi.key", shell=True) != 0: os._exit(1) - if os.system("mcs /target:library /r:System.Configuration.dll /r:System.Web.dll /keyfile:plugins/mono/uwsgi.key plugins/mono/uwsgi.cs") != 0: + if subprocess.call("mcs /target:library /r:System.Configuration.dll /r:System.Web.dll /keyfile:plugins/mono/uwsgi.key plugins/mono/uwsgi.cs", shell=True) != 0: os._exit(1) print("*** uwsgi.dll available in %s/plugins/mono/uwsgi.dll ***" % os.getcwd()) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 979db87461..2f3d7342f6 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -118,7 +118,7 @@ def thread_compiler(num): print_lock.acquire() print_compilation_output("[thread %d][%s] %s" % (num, GCC, objfile), "[thread %d] %s" % (num, cmdline)) print_lock.release() - ret = os.system(cmdline) + ret = subprocess.call(cmdline, shell=True) if ret != 0: os._exit(1) elif cmdline: @@ -249,7 +249,7 @@ def push_print(msg): def push_command(objfile, cmdline): if not compile_queue: print_compilation_output("[%s] %s" % (GCC, objfile), cmdline) - ret = os.system(cmdline) + ret = subprocess.call(cmdline, shell=True) if ret != 0: sys.exit(1) else: @@ -515,12 +515,12 @@ def build_uwsgi(uc, print_only=False, gcll=None): try: binary_link_cmd = "ld -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1]) print(binary_link_cmd) - if os.system(binary_link_cmd) != 0: + if subprocess.call(binary_link_cmd, shell=True) != 0: raise Exception('unable to link binary file') for kind in ('start', 'end'): objcopy_cmd = "objcopy --redefine-sym _binary_%s_%s=%s_%s %s/%s.o" % (binarize('%s/%s' % (path, bfile[1])), kind, bfile[0], kind, path, bfile[1]) print(objcopy_cmd) - if os.system(objcopy_cmd) != 0: + if subprocess.call(objcopy_cmd, shell=True) != 0: raise Exception('unable to link binary file') gcc_list.append('%s/%s.o' % (path, bfile[1])) except Exception: @@ -583,7 +583,7 @@ def build_uwsgi(uc, print_only=False, gcll=None): ' '.join(uniq_warnings(libs)) ) print(ldline) - ret = os.system(ldline) + ret = subprocess.call(ldline, shell=True) if ret != 0: print("*** error linking uWSGI ***") sys.exit(1) @@ -1163,7 +1163,7 @@ def get_gcll(self): if self.embed_config: binary_link_cmd = "ld -r -b binary -o %s.o %s" % (binarize(self.embed_config), self.embed_config) print(binary_link_cmd) - os.system(binary_link_cmd) + subprocess.call(binary_link_cmd, shell=True) self.cflags.append("-DUWSGI_EMBED_CONFIG=_binary_%s_start" % binarize(self.embed_config)) self.cflags.append("-DUWSGI_EMBED_CONFIG_END=_binary_%s_end" % binarize(self.embed_config)) embed_files = os.environ.get('UWSGI_EMBED_FILES') @@ -1182,23 +1182,23 @@ def get_gcll(self): fname = "%s/%s" % (directory, f) binary_link_cmd = "ld -r -b binary -o %s.o %s" % (binarize(fname), fname) print(binary_link_cmd) - os.system(binary_link_cmd) + subprocess.call(binary_link_cmd, shell=True) if symbase: for kind in ('start', 'end'): objcopy_cmd = "objcopy --redefine-sym _binary_%s_%s=_binary_%s%s_%s build/%s.o" % (binarize(fname), kind, binarize(symbase), binarize(fname[len(ef):]), kind, binarize(fname)) print(objcopy_cmd) - os.system(objcopy_cmd) + 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) print(binary_link_cmd) - os.system(binary_link_cmd) + subprocess.call(binary_link_cmd, shell=True) binary_list.append(binarize(ef)) if symbase: for kind in ('start', 'end'): objcopy_cmd = "objcopy --redefine-sym _binary_%s_%s=_binary_%s_%s build/%s.o" % (binarize(ef), kind, binarize(symbase), kind, binarize(ef)) print(objcopy_cmd) - os.system(objcopy_cmd) + subprocess.call(objcopy_cmd, shell=True) self.cflags.append('-DUWSGI_VERSION="\\"' + uwsgi_version + '\\""') @@ -1383,10 +1383,10 @@ def get_remote_plugin(path): if git_dir.endswith('.git'): git_dir = git_dir[:-4] if not os.path.isdir(git_dir): - if os.system('git clone %s' % path) != 0: + if subprocess.call('git clone %s' % path, shell=True) != 0: sys.exit(1) else: - if os.system('cd %s ; git pull' % git_dir) != 0: + if subprocess.call('cd %s ; git pull' % git_dir, shell=True) != 0: sys.exit(1) return git_dir @@ -1496,7 +1496,7 @@ def build_plugin(path, uc, cflags, ldflags, libs, name=None): try: binary_link_cmd = "ld -r -b binary -o %s/%s.o %s/%s" % (path, bfile[1], path, bfile[1]) print(binary_link_cmd) - if os.system(binary_link_cmd) != 0: + if subprocess.call(binary_link_cmd, shell=True) != 0: raise Exception('unable to link binary file') for kind in ('start', 'end'): objcopy_cmd = "objcopy --redefine-sym _binary_%s_%s=%s_%s %s/%s.o" % ( @@ -1508,7 +1508,7 @@ def build_plugin(path, uc, cflags, ldflags, libs, name=None): bfile[1] ) print(objcopy_cmd) - if os.system(objcopy_cmd) != 0: + if subprocess.call(objcopy_cmd, shell=True) != 0: raise Exception('unable to link binary file') gcc_list.append('%s/%s.o' % (path, bfile[1])) except Exception: @@ -1567,7 +1567,7 @@ def build_plugin(path, uc, cflags, ldflags, libs, name=None): ) print_compilation_output("[%s] %s.so" % (GCC, plugin_dest), gccline) - ret = os.system(gccline) + ret = subprocess.call(gccline, shell=True) if ret != 0: print("*** unable to build %s plugin ***" % name) sys.exit(1) @@ -1580,7 +1580,7 @@ def build_plugin(path, uc, cflags, ldflags, libs, name=None): f.close() objline = "objcopy %s.so --add-section uwsgi=.uwsgi_plugin_section %s.so" % (plugin_dest, plugin_dest) print_compilation_output(None, objline) - os.system(objline) + subprocess.call(objline, shell=True) os.unlink('.uwsgi_plugin_section') except Exception: pass @@ -1692,15 +1692,15 @@ def vararg_callback(option, opt_str, value, parser): pass build_plugin(options.extra_plugin[0], None, cflags, ldflags, None, name) elif options.clean: - os.system("rm -f core/*.o") - os.system("rm -f proto/*.o") - os.system("rm -f lib/*.o") - os.system("rm -f plugins/*/*.o") - os.system("rm -f build/*.o") - os.system("rm -f core/dot_h.c") - os.system("rm -f core/config_py.c") + subprocess.call("rm -f core/*.o", shell=True) + subprocess.call("rm -f proto/*.o", shell=True) + subprocess.call("rm -f lib/*.o", shell=True) + subprocess.call("rm -f plugins/*/*.o", shell=True) + subprocess.call("rm -f build/*.o", shell=True) + subprocess.call("rm -f core/dot_h.c", shell=True) + subprocess.call("rm -f core/config_py.c", shell=True) elif options.check: - os.system("cppcheck --max-configs=1000 --enable=all -q core/ plugins/ proto/ lib/ apache2/") + subprocess.call("cppcheck --max-configs=1000 --enable=all -q core/ plugins/ proto/ lib/ apache2/", shell=True) else: parser.print_help() sys.exit(1) From 5287f48a999216883044f380c3fbb8da4365475f Mon Sep 17 00:00:00 2001 From: Peter Law Date: Thu, 23 Jun 2022 09:46:06 +0100 Subject: [PATCH 112/207] Modernise these simple git calls --- uwsgiconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 2f3d7342f6..4e02ad95d2 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1383,10 +1383,10 @@ def get_remote_plugin(path): if git_dir.endswith('.git'): git_dir = git_dir[:-4] if not os.path.isdir(git_dir): - if subprocess.call('git clone %s' % path, shell=True) != 0: + if subprocess.call(['git', 'clone', path]) != 0: sys.exit(1) else: - if subprocess.call('cd %s ; git pull' % git_dir, shell=True) != 0: + if subprocess.call(['git', 'pull'], cwd=git_dir) != 0: sys.exit(1) return git_dir From 2768aea6cb20d6e8d171618f2217e29f5ded4ab5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jun 2022 11:59:54 +0200 Subject: [PATCH 113/207] Add Python 3.11 support * Use PyFrame_GetCode(). * Add PyFrame_GetCode() for Python 3.8 and older. * Add UWSGI_PY311 macro: defined on Python 3.11 and newer. * struct uwsgi_python: "current_recursion_depth" becomes "current_recursion_remaining" and current_frame type becomes _PyCFrame** on Python 3.11. Related Python 3.11 changes: * https://docs.python.org/dev/whatsnew/3.11.html#id6 * The PyFrameObject structure became opaque. * PyThreadState.frame (PyFrameObject) became PyThreadState.cframe (_PyCFrame) in Python 3.11. * PyThreadState: recursion_depth was replaced with recursion_remaining + recursion_limit. --- plugins/python/profiler.c | 25 ++++++++++++++++++++----- plugins/python/python_plugin.c | 26 +++++++++++++++++++++++++- plugins/python/uwsgi_python.h | 12 ++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/plugins/python/profiler.c b/plugins/python/profiler.c index 92bc3da787..2a7e1cc231 100644 --- a/plugins/python/profiler.c +++ b/plugins/python/profiler.c @@ -13,6 +13,14 @@ int PyFrame_GetLineNumber(PyFrameObject *frame) { } #endif +#if PY_VERSION_HEX < 0x030900B1 +PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + Py_INCREF(frame->f_code); + return frame->f_code; +} +#endif + #ifdef PYTHREE #undef PyString_AsString static char *PyString_AsString(PyObject *o) { @@ -27,27 +35,32 @@ int uwsgi_python_profiler_call(PyObject *obj, PyFrameObject *frame, int what, Py static uint64_t last_ts = 0; uint64_t now = uwsgi_micros(); uint64_t delta = 0; + PyCodeObject *code; switch(what) { case PyTrace_CALL: if (last_ts == 0) delta = 0; else delta = now - last_ts; last_ts = now; + code = PyFrame_GetCode(frame); uwsgi_log("[uWSGI Python profiler %llu] CALL: %s (line %d) -> %s %d args, stacksize %d\n", (unsigned long long) delta, - PyString_AsString(frame->f_code->co_filename), + PyString_AsString(code->co_filename), PyFrame_GetLineNumber(frame), - PyString_AsString(frame->f_code->co_name), frame->f_code->co_argcount, frame->f_code->co_stacksize); + PyString_AsString(code->co_name), code->co_argcount, code->co_stacksize); + Py_DECREF(code); break; case PyTrace_C_CALL: if (last_ts == 0) delta = 0; else delta = now - last_ts; last_ts = now; + code = PyFrame_GetCode(frame); uwsgi_log("[uWSGI Python profiler %llu] C CALL: %s (line %d) -> %s %d args, stacksize %d\n", (unsigned long long) delta, - PyString_AsString(frame->f_code->co_filename), + PyString_AsString(code->co_filename), PyFrame_GetLineNumber(frame), - PyEval_GetFuncName(arg), frame->f_code->co_argcount, frame->f_code->co_stacksize); + PyEval_GetFuncName(arg), code->co_argcount, code->co_stacksize); + Py_DECREF(code); break; } @@ -68,7 +81,9 @@ int uwsgi_python_tracer(PyObject *obj, PyFrameObject *frame, int what, PyObject delta = now - last_ts; } last_ts = now; - uwsgi_log("[uWSGI Python profiler %llu] file %s line %d: %s argc:%d\n", (unsigned long long)delta, PyString_AsString(frame->f_code->co_filename), PyFrame_GetLineNumber(frame), PyString_AsString(frame->f_code->co_name), frame->f_code->co_argcount); + PyCodeObject *code = PyFrame_GetCode(frame); + uwsgi_log("[uWSGI Python profiler %llu] file %s line %d: %s argc:%d\n", (unsigned long long)delta, PyString_AsString(code->co_filename), PyFrame_GetLineNumber(frame), PyString_AsString(code->co_name), code->co_argcount); + Py_DECREF(code); } return 0; diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 79f29d43ce..6c917c662e 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -1185,8 +1185,12 @@ void uwsgi_python_init_apps() { // prepare for stack suspend/resume if (uwsgi.async > 0) { +#ifdef UWSGI_PY311 + up.current_recursion_remaining = uwsgi_malloc(sizeof(int)*uwsgi.async); +#else up.current_recursion_depth = uwsgi_malloc(sizeof(int)*uwsgi.async); - up.current_frame = uwsgi_malloc(sizeof(struct _frame)*uwsgi.async); +#endif + up.current_frame = uwsgi_malloc(sizeof(up.current_frame[0])*uwsgi.async); } struct uwsgi_string_list *upli = up.import_list; @@ -1581,12 +1585,22 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { PyGILState_Release(pgst); if (wsgi_req) { +#ifdef UWSGI_PY311 + up.current_recursion_remaining[wsgi_req->async_id] = tstate->recursion_remaining; + up.current_frame[wsgi_req->async_id] = tstate->cframe; +#else up.current_recursion_depth[wsgi_req->async_id] = tstate->recursion_depth; up.current_frame[wsgi_req->async_id] = tstate->frame; +#endif } else { +#ifdef UWSGI_PY311 + up.current_main_recursion_remaining = tstate->recursion_remaining; + up.current_main_frame = tstate->cframe; +#else up.current_main_recursion_depth = tstate->recursion_depth; up.current_main_frame = tstate->frame; +#endif } } @@ -1814,12 +1828,22 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { PyGILState_Release(pgst); if (wsgi_req) { +#ifdef UWSGI_PY311 + tstate->recursion_remaining = up.current_recursion_remaining[wsgi_req->async_id]; + tstate->cframe = up.current_frame[wsgi_req->async_id]; +#else tstate->recursion_depth = up.current_recursion_depth[wsgi_req->async_id]; tstate->frame = up.current_frame[wsgi_req->async_id]; +#endif } else { +#ifdef UWSGI_PY311 + tstate->recursion_remaining = up.current_main_recursion_remaining; + tstate->cframe = up.current_main_frame; +#else tstate->recursion_depth = up.current_main_recursion_depth; tstate->frame = up.current_main_frame; +#endif } } diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index ec64ad80c7..f91f7b43ab 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -16,6 +16,10 @@ #define UWSGI_PYTHON_OLD #endif +#if (PY_VERSION_HEX >= 0x030b0000) +# define UWSGI_PY311 +#endif + #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 7 #define HAS_NOT_PyMemoryView_FromBuffer #endif @@ -177,11 +181,19 @@ struct uwsgi_python { char *callable; +#ifdef UWSGI_PY311 + int *current_recursion_remaining; + _PyCFrame **current_frame; + + int current_main_recursion_remaining; + _PyCFrame *current_main_frame; +#else int *current_recursion_depth; struct _frame **current_frame; int current_main_recursion_depth; struct _frame *current_main_frame; +#endif void (*swap_ts)(struct wsgi_request *, struct uwsgi_app *); void (*reset_ts)(struct wsgi_request *, struct uwsgi_app *); From 7bddfa5d2d674b48b4140e5843ed0e14a62c1fb6 Mon Sep 17 00:00:00 2001 From: Shachar Itzhaky Date: Mon, 4 Jul 2022 17:37:43 +0300 Subject: [PATCH 114/207] Fixed compilation issue on macOS + Clang. Fixes #2446. --- core/mount.c | 4 ++++ core/utils.c | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/mount.c b/core/mount.c index ada7682a70..add9367db8 100644 --- a/core/mount.c +++ b/core/mount.c @@ -106,6 +106,7 @@ uint64_t uwsgi_mount_flag(char *mflag) { } int uwsgi_mount(char *fs, char *what, char *where, char *flags, char *data) { +#if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) #if defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) struct iovec iov[6]; #endif @@ -145,11 +146,13 @@ int uwsgi_mount(char *fs, char *what, char *where, char *flags, char *data) { iov[5].iov_len = strlen(what) + 1; return nmount(iov, 6, (int) mountflags); +#endif #endif return -1; } int uwsgi_umount(char *where, char *flags) { +#if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) unsigned long mountflags = 0; if (!flags) goto parsed; char *mflags = uwsgi_str(flags); @@ -198,6 +201,7 @@ int uwsgi_umount(char *where, char *flags) { return umount2(where, mountflags); #elif defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) return unmount(where, mountflags); +#endif #endif return -1; } diff --git a/core/utils.c b/core/utils.c index d4eb98d814..c198bb44a5 100644 --- a/core/utils.c +++ b/core/utils.c @@ -3721,6 +3721,7 @@ char *uwsgi_expand_path(char *dir, int dir_len, char *ptr) { void uwsgi_set_cpu_affinity() { +#if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) char buf[4096]; int ret; int pos = 0; @@ -3740,7 +3741,6 @@ void uwsgi_set_cpu_affinity() { #elif defined(__FreeBSD__) cpuset_t cpuset; #endif -#if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) CPU_ZERO(&cpuset); int i; for (i = 0; i < uwsgi.cpu_affinity; i++) { @@ -3755,7 +3755,6 @@ void uwsgi_set_cpu_affinity() { pos += ret; base_cpu++; } -#endif #if defined(__linux__) || defined(__GNU_kFreeBSD__) if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset)) { uwsgi_error("sched_setaffinity()"); @@ -3767,7 +3766,7 @@ void uwsgi_set_cpu_affinity() { #endif uwsgi_log("%s\n", buf); } - +#endif } #ifdef UWSGI_ELF From e0b7a37d86a32d58486cb987a73c363c63852e1b Mon Sep 17 00:00:00 2001 From: "gavin.jeong" Date: Mon, 11 Jul 2022 15:05:19 +0900 Subject: [PATCH 115/207] Fix segfault from GEVENT_SWITCH --- plugins/gevent/gevent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gevent/gevent.h b/plugins/gevent/gevent.h index c39034ec59..07f5cb1787 100644 --- a/plugins/gevent/gevent.h +++ b/plugins/gevent/gevent.h @@ -4,7 +4,7 @@ int uwsgi_gevent_wait_write_hook(int, int); int uwsgi_gevent_wait_read_hook(int, int); int uwsgi_gevent_wait_milliseconds_hook(int); -#define GEVENT_SWITCH PyObject *gswitch = python_call(ugevent.greenlet_switch, ugevent.greenlet_switch_args, 0, NULL); Py_DECREF(gswitch) +#define GEVENT_SWITCH PyObject *gswitch = python_call(ugevent.greenlet_switch, ugevent.greenlet_switch_args, 0, NULL); if (gswitch) { Py_DECREF(gswitch); } #define GET_CURRENT_GREENLET python_call(ugevent.get_current, ugevent.get_current_args, 0, NULL) #define free_req_queue uwsgi.async_queue_unused_ptr++; uwsgi.async_queue_unused[uwsgi.async_queue_unused_ptr] = wsgi_req #define stop_the_watchers if (timer) { ret = PyObject_CallMethod(timer, "stop", NULL);\ From 7aa2c45bd7a1499e33d4c09bdfe8c1a3b61a8317 Mon Sep 17 00:00:00 2001 From: Joe Date: Sat, 9 Jul 2022 03:52:27 +0200 Subject: [PATCH 116/207] PHP: Fix php-app for php81 --- plugins/php/php_plugin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 8b5a241569..af797645ae 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -880,6 +880,7 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { strncpy(real_filename, uphp.app, PATH_MAX); real_filename[PATH_MAX-1] = '\0'; + real_filename_len = strlen(real_filename); if (wsgi_req->path_info_len == 1 && wsgi_req->path_info[0] == '/') { goto appready; } From 3f511fbc3b43054e1fd48c46b2b9d91bd3210161 Mon Sep 17 00:00:00 2001 From: "joshua.biagio" Date: Mon, 18 Jul 2022 08:07:33 -0600 Subject: [PATCH 117/207] make dev version PEP-0440 compliant --- uwsgiconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 4e02ad95d2..8325684476 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -164,7 +164,7 @@ def uniq_warnings(elements): if uwsgi_version.endswith('-dev') and os.path.exists('%s/.git' % os.path.dirname(os.path.abspath(__file__))): try: - uwsgi_version += '-%s' % spcall('git rev-parse --short HEAD') + uwsgi_version += '+%s' % spcall('git rev-parse --short HEAD') except Exception: pass From afcc2d2f295de97db9b408879f2a27275125ca6f Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sat, 30 Jul 2022 19:23:04 +0200 Subject: [PATCH 118/207] ci: update python versions in smoke tests Drop python 3.5 and add 3.9 and 3.10 --- .github/workflows/test.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b628782615..7473d614af 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,8 +19,8 @@ jobs: - name: Install dependencies run: | sudo apt update -qq - sudo apt install --no-install-recommends -qqyf python{2.7,3.5,3.6,3.7,3.8}-dev \ - python3.8-distutils \ + sudo apt install --no-install-recommends -qqyf python{2.7,3.6,3.7,3.8,3.9,3.10}-dev \ + python3.8-distutils python3.9-distutils python3.10-distutils \ libpcre3-dev libjansson-dev libcap2-dev \ curl check - uses: actions/checkout@v2 @@ -32,10 +32,6 @@ jobs: run: | /usr/bin/python2.7 -V /usr/bin/python2.7 uwsgiconfig.py --plugin plugins/python base python27 - - name: Build python3.5 plugin - run: | - /usr/bin/python3.5 -V - /usr/bin/python3.5 uwsgiconfig.py --plugin plugins/python base python35 - name: Build python3.6 plugin run: | /usr/bin/python3.6 -V @@ -48,6 +44,14 @@ jobs: run: | /usr/bin/python3.8 -V /usr/bin/python3.8 uwsgiconfig.py --plugin plugins/python base python38 + - name: Build python3.9 plugin + run: | + /usr/bin/python3.9 -V + /usr/bin/python3.9 uwsgiconfig.py --plugin plugins/python base python39 + - name: Build python3.10 plugin + run: | + /usr/bin/python3.10 -V + /usr/bin/python3.10 uwsgiconfig.py --plugin plugins/python base python310 - name: Build rack plugin run: | ruby -v From 22fe6c86b04a501b7909848738c205b1206bc4f8 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 28 Aug 2022 10:49:21 +0200 Subject: [PATCH 119/207] uwsgiconfig: don't error on deprecated declarations Get us some more time to handle all the deprecations (pthread, openssl 3, python 3.11) to keep things compiling. --- uwsgiconfig.py | 1 + 1 file changed, 1 insertion(+) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 4e02ad95d2..3209afa8d7 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -685,6 +685,7 @@ def __init__(self, filename, mute=False): '-I.', '-Wall', '-Werror', + '-Wno-error=deprecated-declarations', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64' ] + os.environ.get("CFLAGS", "").split() + self.get('cflags', '').split() From b5c1878a687d6ba53c57869ad01b40cfb489e2cc Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 1 Aug 2022 16:30:59 +0200 Subject: [PATCH 120/207] ci: run smoke tests on python 3.11 --- .github/workflows/test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7473d614af..b61e43956f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,8 +19,8 @@ jobs: - name: Install dependencies run: | sudo apt update -qq - sudo apt install --no-install-recommends -qqyf python{2.7,3.6,3.7,3.8,3.9,3.10}-dev \ - python3.8-distutils python3.9-distutils python3.10-distutils \ + sudo apt install --no-install-recommends -qqyf python{2.7,3.6,3.7,3.8,3.9,3.10,3.11}-dev \ + python3.8-distutils python3.9-distutils python3.10-distutils python3.11-distutils \ libpcre3-dev libjansson-dev libcap2-dev \ curl check - uses: actions/checkout@v2 @@ -52,6 +52,10 @@ jobs: run: | /usr/bin/python3.10 -V /usr/bin/python3.10 uwsgiconfig.py --plugin plugins/python base python310 + - name: Build python3.11 plugin + run: | + /usr/bin/python3.11 -V + /usr/bin/python3.11 uwsgiconfig.py --plugin plugins/python base python311 - name: Build rack plugin run: | ruby -v From c6025c5ca7e3175bcfeff37f840c8f7578295e04 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 19 Sep 2022 12:15:57 +0200 Subject: [PATCH 121/207] Bump trove classifiers up to 3.11 --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 9cf4ceb665..a307bac518 100644 --- a/setup.py +++ b/setup.py @@ -147,6 +147,8 @@ def get_extra_require(): 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ], install_requires=get_extra_require() ) From 3e63655bd5507b330163acc8b4bbff3d4446e137 Mon Sep 17 00:00:00 2001 From: MRoci Date: Wed, 21 Sep 2022 15:06:04 +0200 Subject: [PATCH 122/207] ci: refactor tests to be run in parallel starting from `tests/travis.sh` subdivide test suites into: * `tests/gh-python.sh` * `tests/gh-deadlocks.sh` * `tests/gh-rack.sh` that share common variables and functions from `tests/gh-shared.sh` refactor github test workflow to use a multidimensional matrix in order to run each test suite (unittest, python, deadlocks) on each supported python version and rack tests on each supported rack version. closes https://github.com/unbit/uwsgi/issues/2479 leave `tests/travis.sh` because it can be useful as is somewhere else. --- .github/workflows/test.yml | 78 ++++++++++++++++----------- tests/gh-deadlocks.sh | 12 +++++ tests/gh-python.sh | 12 +++++ tests/gh-rack.sh | 12 +++++ tests/gh-shared.sh | 108 +++++++++++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 31 deletions(-) create mode 100755 tests/gh-deadlocks.sh create mode 100755 tests/gh-python.sh create mode 100755 tests/gh-rack.sh create mode 100755 tests/gh-shared.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b61e43956f..0d767be4ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,10 +7,13 @@ on: branches: [ master, uwsgi-2.0 ] jobs: - build: + python: runs-on: ubuntu-18.04 - + strategy: + matrix: + python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] + test-suite: [unittest, python, deadlocks] steps: - name: Add deadnakes ppa run: | @@ -19,47 +22,60 @@ jobs: - name: Install dependencies run: | sudo apt update -qq - sudo apt install --no-install-recommends -qqyf python{2.7,3.6,3.7,3.8,3.9,3.10,3.11}-dev \ - python3.8-distutils python3.9-distutils python3.10-distutils python3.11-distutils \ + sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-dev \ libpcre3-dev libjansson-dev libcap2-dev \ curl check + - name: Install distutils + if: contains(fromJson('["3.8","3.9","3.10","3.11"]'), matrix.python-version) + run: | + sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-distutils \ - uses: actions/checkout@v2 - name: Run unit tests + if: matrix.test-suite == 'unittest' run: make tests - name: Build uWSGI binary + if: matrix.test-suite != 'unittest' run: make - - name: Build python2.7 plugin - run: | - /usr/bin/python2.7 -V - /usr/bin/python2.7 uwsgiconfig.py --plugin plugins/python base python27 - - name: Build python3.6 plugin + - name: Build python${{ matrix.python-version }} plugin + if: matrix.test-suite != 'unittest' run: | - /usr/bin/python3.6 -V - /usr/bin/python3.6 uwsgiconfig.py --plugin plugins/python base python36 - - name: Build python3.7 plugin + PYTHON_VERSION=${{ matrix.python-version }} + PYTHON_VERSION=python${PYTHON_VERSION//.} + /usr/bin/python${{ matrix.python-version }} -V + /usr/bin/python${{ matrix.python-version }} uwsgiconfig.py --plugin plugins/python base $PYTHON_VERSION + - name: run smoke tests + if: matrix.test-suite != 'unittest' run: | - /usr/bin/python3.7 -V - /usr/bin/python3.7 uwsgiconfig.py --plugin plugins/python base python37 - - name: Build python3.8 plugin - run: | - /usr/bin/python3.8 -V - /usr/bin/python3.8 uwsgiconfig.py --plugin plugins/python base python38 - - name: Build python3.9 plugin - run: | - /usr/bin/python3.9 -V - /usr/bin/python3.9 uwsgiconfig.py --plugin plugins/python base python39 - - name: Build python3.10 plugin + PYTHON_VERSION=${{ matrix.python-version }} + PYTHON_VERSION=python${PYTHON_VERSION//.} + ./tests/gh-${{ matrix.test-suite }}.sh ${PYTHON_VERSION} + + rack: + + runs-on: ubuntu-18.04 + strategy: + matrix: + rack-version: ["251"] + steps: + - name: Add deadnakes ppa run: | - /usr/bin/python3.10 -V - /usr/bin/python3.10 uwsgiconfig.py --plugin plugins/python base python310 - - name: Build python3.11 plugin + sudo apt install -qqyf software-properties-common + sudo add-apt-repository ppa:deadsnakes/ppa -y + - name: Install dependencies run: | - /usr/bin/python3.11 -V - /usr/bin/python3.11 uwsgiconfig.py --plugin plugins/python base python311 + sudo apt update -qq + sudo apt install --no-install-recommends -qqyf python3-dev \ + libpcre3-dev libjansson-dev libcap2-dev \ + curl check + - uses: actions/checkout@v2 + - name: Run unit tests + run: make tests + - name: Build uWSGI binary + run: make - name: Build rack plugin run: | ruby -v - UWSGICONFIG_RUBYPATH=ruby /usr/bin/python uwsgiconfig.py --plugin plugins/rack base rack251 - - name: Run smoke tests + UWSGICONFIG_RUBYPATH=ruby /usr/bin/python uwsgiconfig.py --plugin plugins/rack base rack${{ matrix.rack-version }} + - name: run smoke tests run: | - ./tests/travis.sh .github/workflows/test.yml + ./tests/gh-rack.sh rack${{ matrix.rack-version}} diff --git a/tests/gh-deadlocks.sh b/tests/gh-deadlocks.sh new file mode 100755 index 0000000000..328f338c15 --- /dev/null +++ b/tests/gh-deadlocks.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -u + +PYTHON_VERSION="$1" +. "./tests/gh-shared.sh" + + +for INI_FILE in tests/deadlocks/*.ini ; do + test_python_deadlocks "${PYTHON_VERSION}" "$INI_FILE" +done + +results diff --git a/tests/gh-python.sh b/tests/gh-python.sh new file mode 100755 index 0000000000..903758b3ea --- /dev/null +++ b/tests/gh-python.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -u + +PYTHON_VERSION="$1" +. "./tests/gh-shared.sh" + + +for WSGI_FILE in tests/staticfile.py tests/testworkers.py tests/testrpc.py ; do + test_python "${PYTHON_VERSION}" "${WSGI_FILE}" +done + +results diff --git a/tests/gh-rack.sh b/tests/gh-rack.sh new file mode 100755 index 0000000000..1cdaa599ff --- /dev/null +++ b/tests/gh-rack.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -u + +RACK_VERSION="$1" +. "./tests/gh-shared.sh" + + +for RACK in examples/config2.ru ; do + test_rack "${RACK_VERSION}" "${RACK}" +done + +results diff --git a/tests/gh-shared.sh b/tests/gh-shared.sh new file mode 100755 index 0000000000..368afaaeea --- /dev/null +++ b/tests/gh-shared.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +txtund=$(tput sgr 0 1) # underline +txtbld=$(tput bold) # bold +bldred=${txtbld}$(tput setaf 1) # red +bldgre=${txtbld}$(tput setaf 2) # green +bldyel=${txtbld}$(tput setaf 3) # yellow +bldblu=${txtbld}$(tput setaf 4) # blue +txtcya=$(tput setaf 6) # cyan +bldwht=${txtbld}$(tput setaf 7) # white +txtrst=$(tput sgr0) # reset + + +ERROR=0 +SUCCESS=0 + + +die() { + date > reload.txt + sleep 3 + pidof uwsgi && killall uwsgi + sleep 1 + pidof uwsgi && killall -9 uwsgi + echo -e "$@" + if [ -e uwsgi.log ]; then + echo -e "${bldyel}>>> uwsgi.log:${txtrst}" + echo -e "${txtcya}" + cat uwsgi.log + echo -e "${txtrst}" + fi +} + + +http_test() { + URL=$1 + UPID=`pidof uwsgi` + if [ "$UPID" != "" ]; then + echo -e "${bldgre}>>> Spawned PID $UPID, running tests${txtrst}" + sleep 5 + curl -fI $URL + RET=$? + if [ $RET != 0 ]; then + die "${bldred}>>> Error during curl run${txtrst}" + ERROR=$((ERROR+1)) + else + SUCCESS=$((SUCCESS+1)) + fi + die "${bldyel}>>> SUCCESS: Done${txtrst}" + else + die "${bldred}>>> ERROR: uWSGI did not start${txtrst}" + ERROR=$((ERROR+1)) + fi +} + + +test_python() { + date > reload.txt + rm -f uwsgi.log + echo -e "${bldyel}================== TESTING $1 $2 =====================${txtrst}" + echo -e "${bldyel}>>> Spawning uWSGI python app${txtrst}" + echo -en "${bldred}" + ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --wsgi-file $2 --daemonize uwsgi.log + sleep 1 + echo -en "${txtrst}" + http_test "http://localhost:8080/" + echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" +} + + +test_python_deadlocks() { + date > reload.txt + rm -f uwsgi.log + echo -e "${bldyel}================== TESTING DEADLOCKS $1 $2 =====================${txtrst}" + echo -e "${bldyel}>>> Starting python app${txtrst}" + echo -en "${bldred}" + # initialize with tests/deadlocks/sitecustomize.py + PYTHONPATH=tests/deadlocks ./uwsgi --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --wsgi-file tests/deadlocks/main.py --ini $2 --daemonize uwsgi.log + sleep 1 + echo -en "${txtrst}" + http_test "http://localhost:8080/" + echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" +} + + +test_rack() { + date > reload.txt + rm -f uwsgi.log + # the code assumes that ruby environment is activated by `rvm use` + echo -e "${bldyel}================== TESTING $1 $2 =====================${txtrst}" + echo -e "${bldyel}>>> Installing sinatra gem using gem${txtrst}" + sudo gem install sinatra || die + echo -e "${bldyel}>>> Spawning uWSGI rack app${txtrst}" + echo -en "${bldred}" + ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --rack $2 --daemonize uwsgi.log + echo -en "${txtrst}" + http_test "http://localhost:8080/hi" + echo -e "${bldyel}===================== DONE $1 $2 =====================${txtrst}\n\n" +} + +results() { + echo "${bldgre}>>> $SUCCESS SUCCESSFUL TEST(S)${txtrst}" + if [ $ERROR -ge 1 ]; then + echo "${bldred}>>> $ERROR FAILED TEST(S)${txtrst}" + exit 1 + fi + + exit 0 +} From eb1d251751cf74ba7246fb39eb35f4070d3bc119 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 7 Oct 2022 06:24:23 +0200 Subject: [PATCH 123/207] Update README --- README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README b/README index 0a0ae5fa8a..15b0079af2 100644 --- a/README +++ b/README @@ -4,6 +4,9 @@ For official documentation check: https://uwsgi-docs.readthedocs.io/en/latest/ Note: The project is in maintenance mode (only bugfixes and updates for new languages apis) +Do not expect quick answers on github issues and/or pull requests (sorry for that) +A big thanks to all of the users and contributors since 2009. + uWSGI development has been sponsored by: http://unbit.com From 2fe305f88429b7e63aa69eaf34823f8c6f842ae3 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 7 Oct 2022 06:28:51 +0200 Subject: [PATCH 124/207] Update README --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 15b0079af2..03f39da510 100644 --- a/README +++ b/README @@ -9,7 +9,7 @@ A big thanks to all of the users and contributors since 2009. uWSGI development has been sponsored by: -http://unbit.com +http://unbit.it https://www.pythonanywhere.com/ https://lincolnloop.com/ https://yourlabs.io/oss From 9098b83abed0aaa6bce9d48652b002e115dc55c1 Mon Sep 17 00:00:00 2001 From: eleksir Date: Sat, 22 Oct 2022 21:46:45 +0300 Subject: [PATCH 125/207] Use parentheses in print() statement. In py3 print is not a keyword, it is function, so it must be called with parentheses. This fix compatible with py2 as it does not denies using print as print(). --- plugins/coroae/uwsgiplugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/coroae/uwsgiplugin.py b/plugins/coroae/uwsgiplugin.py index 83c2b25e7c..16da85bdb6 100644 --- a/plugins/coroae/uwsgiplugin.py +++ b/plugins/coroae/uwsgiplugin.py @@ -11,7 +11,7 @@ coroapi = p if not coroapi: - print "unable to find the Coro perl module !!!" + print("unable to find the Coro perl module !!!") sys.exit(1) CFLAGS = os.popen('perl -MExtUtils::Embed -e ccopts').read().rstrip().split() From 6aac1b87ba01e58e9f0a1423237749e9e061f3f2 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 24 Oct 2022 09:34:06 +0200 Subject: [PATCH 126/207] ci: force installing sinatra 2 Which still runs on ruby 2.5.0. Fix #2497 --- tests/gh-shared.sh | 2 +- tests/travis.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gh-shared.sh b/tests/gh-shared.sh index 368afaaeea..bad6dd043b 100755 --- a/tests/gh-shared.sh +++ b/tests/gh-shared.sh @@ -88,7 +88,7 @@ test_rack() { # the code assumes that ruby environment is activated by `rvm use` echo -e "${bldyel}================== TESTING $1 $2 =====================${txtrst}" echo -e "${bldyel}>>> Installing sinatra gem using gem${txtrst}" - sudo gem install sinatra || die + sudo gem install sinatra -v 2.2.2 || die echo -e "${bldyel}>>> Spawning uWSGI rack app${txtrst}" echo -en "${bldred}" ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --rack $2 --daemonize uwsgi.log diff --git a/tests/travis.sh b/tests/travis.sh index ec4c5c5d21..67ea2f5dbd 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -91,7 +91,7 @@ test_rack() { # the code assumes that ruby environment is activated by `rvm use` echo -e "${bldyel}================== TESTING $1 $2 =====================${txtrst}" echo -e "${bldyel}>>> Installing sinatra gem using gem${txtrst}" - sudo gem install sinatra || die + sudo gem install sinatra -v 2.2.2 || die echo -e "${bldyel}>>> Spawning uWSGI rack app${txtrst}" echo -en "${bldred}" ./uwsgi --master --plugin 0:$1 --http :8080 --exit-on-reload --touch-reload reload.txt --rack $2 --daemonize uwsgi.log From 835a395a5c1124fc931a261fd8c5c689d490b9c9 Mon Sep 17 00:00:00 2001 From: Nicolas Evrard Date: Thu, 17 Nov 2022 09:47:39 +0100 Subject: [PATCH 127/207] plugins/python: Use "backslashreplace" on stderr initialization Failing to use this value will result in enconding errors when logging unicode characters to stderr --- plugins/python/python_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index fbc85c2258..dcb5d348e0 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -591,7 +591,7 @@ void init_uwsgi_vars() { #ifdef HAS_NO_ERRORS_IN_PyFile_FromFd PyObject *new_stdprint = PyFile_FromFd(2, NULL, "w", _IOLBF, NULL, NULL, 0); #else - PyObject *new_stdprint = PyFile_FromFd(2, NULL, "w", _IOLBF, NULL, NULL, NULL, 0); + PyObject *new_stdprint = PyFile_FromFd(2, NULL, "w", _IOLBF, NULL, "backslashreplace", NULL, 0); #endif PyDict_SetItemString(pysys_dict, "stdout", new_stdprint); PyDict_SetItemString(pysys_dict, "__stdout__", new_stdprint); From 7823fd26c83204af67dea5caf3221b8e22c44242 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 24 Oct 2022 18:01:53 +0200 Subject: [PATCH 128/207] RELEASE: document the release process --- RELEASE.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..5d3b0106ca --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,27 @@ +## How to cut a new stable release + +Stable releases are tagged from the `uwsgi-2.0` branch. Changes to the `uwsgi-2.0` branch are +cherry-picked from the `master` branch. + +Before tagging a new release the release notes should be updated and a file named +`Changelog-2.0.XY.rst` where XY is the number of the release created in +the [uwsgi-docs repository](https://github.com/unbit/uwsgi-docs). The file should then be referenced +the newly created changelog file from the `index.rst`. + +In order to cut a new release you have to first bump the version and then tag with the same version +the last commit in git. All the commands are assumed to be run from the `uwsgi-2.0` branch. + +The the tag should be pushed to git and the source distribution created with the following commands. +Please remember to substitute XY with proper version. + +``` +git tag 2.0.XY +git push ---tags origin HEAD +git archive HEAD --prefix uwsgi-2.0.XY/ -o uwsgi-2.0.XY.tar.gz +``` + +Then the tarball must be uploaded to pypi with: + +``` +python3 -m twine upload uwsgi-2.0.XY.tar.gz +``` From 509a0fdd12870d0bf5b4c1ef7d1f7f1d0fd686b1 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 10 Jan 2023 10:01:05 +0000 Subject: [PATCH 129/207] fix build with PHP >= 8.2 from https://raw.githubusercontent.com/php/php-src/PHP-8.2/UPGRADING.INTERNALS: ======================== 5. SAPI changes ======================== * The signature of php_module_startup() has changed from int php_module_startup(sapi_module_struct *sf, zend_module_entry +*additional_modules, uint32_t num_additional_modules) to zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry +*additional_module) as only one additional module was ever provided. --- plugins/php/php_plugin.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index af797645ae..b3efa006ac 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -617,7 +617,11 @@ static void activate_user_config(const char *filename, const char *doc_root, siz static int php_uwsgi_startup(sapi_module_struct *sapi_module) { +#if ((PHP_MAJOR_VERSION >= 8) && (PHP_MINOR_VERSION >= 2)) + if (php_module_startup(&uwsgi_sapi_module, &uwsgi_module_entry)==FAILURE) { +#else if (php_module_startup(&uwsgi_sapi_module, &uwsgi_module_entry, 1)==FAILURE) { +#endif return FAILURE; } else { return SUCCESS; From 853a1436da2b7b03de58a165b3c46e873e31bc6a Mon Sep 17 00:00:00 2001 From: Filipe Niero Felisbino Date: Fri, 14 May 2021 19:08:55 -0700 Subject: [PATCH 130/207] core: add new flags for harakiri configuration: - harakiri-graceful-timeout additional timeout for the worker to attempt a graceful shutdown. The application can catch the termination signal and perform an "emergency shutdown" - harakiri-graceful-signal determines which signal should be used (default SIGTERM) - harakiri-graceful-queue-threshold only triggers a harakiri if/when the listen queue crosses a threshold. Harakiri continues to be checked until the conditions are met - Changes are backwards compatible when the flags are not present --- core/init.c | 2 ++ core/master_checks.c | 55 +++++++++++++++++++++++++++++++++----------- core/master_utils.c | 11 +++++++-- core/uwsgi.c | 3 +++ tests/harakiri.py | 24 +++++++++++++++++++ uwsgi.h | 3 +++ 6 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 tests/harakiri.py diff --git a/core/init.c b/core/init.c index 2c744e00fd..d8ef00daa0 100644 --- a/core/init.c +++ b/core/init.c @@ -205,6 +205,8 @@ void uwsgi_init_default() { uwsgi.notify_socket_fd = -1; uwsgi.mule_msg_recv_size = 65536; + + uwsgi.harakiri_graceful_signal = SIGTERM; } void uwsgi_setup_reload() { diff --git a/core/master_checks.c b/core/master_checks.c index 3e6b76a061..9fdffd4b12 100644 --- a/core/master_checks.c +++ b/core/master_checks.c @@ -180,28 +180,57 @@ void uwsgi_master_check_idle() { } +int uwsgi_master_check_harakiri(int w, int c, int harakiri) { + /** + * Triggers a harakiri when the following conditions are met: + * - harakiri timeout > current time + * - listen queue pressure (ie backlog > harakiri_queue_threshold) + * + * The first harakiri attempt on a worker will be graceful if harakiri_graceful_timeout > 0, + * then the worker has harakiri_graceful_timeout seconds to shutdown cleanly, otherwise + * a second harakiri will trigger a SIGKILL + * + */ +#ifdef __linux__ + int backlog = uwsgi.shared->backlog; +#else + int backlog = 0; +#endif + if (harakiri == 0 || harakiri > (time_t) uwsgi.current_time) { + return 0; + } + // no pending harakiri for the worker and no backlog pressure, safe to skip + if (uwsgi.workers[w].pending_harakiri == 0 && backlog < uwsgi.harakiri_queue_threshold) { + uwsgi_log_verbose("HARAKIRI: Skipping harakiri on worker %d. Listen queue is smaller than the threshold (%d < %d)\n", + w, backlog, uwsgi.harakiri_queue_threshold); + return 0; + } + + trigger_harakiri(w); + if (uwsgi.harakiri_graceful_timeout > 0) { + uwsgi.workers[w].cores[c].harakiri = harakiri + uwsgi.harakiri_graceful_timeout; + uwsgi_log_verbose("HARAKIRI: graceful termination attempt on worker %d with signal %d. Next harakiri: %d\n", + w, uwsgi.harakiri_graceful_signal, uwsgi.workers[w].cores[c].harakiri); + } + return 1; +} + int uwsgi_master_check_workers_deadline() { int i,j; int ret = 0; for (i = 1; i <= uwsgi.numproc; i++) { for(j=0;j 0) { - if (uwsgi.workers[i].cores[j].harakiri < (time_t) uwsgi.current_time) { - uwsgi_log_verbose("HARAKIRI triggered by worker %d core %d !!!\n", i, j); - trigger_harakiri(i); - ret = 1; - break; - } + if (uwsgi_master_check_harakiri(i, j, uwsgi.workers[i].cores[j].harakiri)) { + uwsgi_log_verbose("HARAKIRI triggered by worker %d core %d !!!\n", i, j); + ret = 1; + break; } /* then user-defined harakiri */ - if (uwsgi.workers[i].cores[j].user_harakiri > 0) { + if (uwsgi_master_check_harakiri(i, j, uwsgi.workers[i].cores[j].user_harakiri)) { uwsgi_log_verbose("HARAKIRI (user) triggered by worker %d core %d !!!\n", i, j); - if (uwsgi.workers[i].cores[j].user_harakiri < (time_t) uwsgi.current_time) { - trigger_harakiri(i); - ret = 1; - break; - } + ret = 1; + break; } } // then for evil memory checkers diff --git a/core/master_utils.c b/core/master_utils.c index 9a54b35e51..22977c07b7 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -1624,7 +1624,10 @@ void uwsgi_register_cheaper_algo(char *name, int (*func) (int)) { void trigger_harakiri(int i) { int j; - uwsgi_log_verbose("*** HARAKIRI ON WORKER %d (pid: %d, try: %d) ***\n", i, uwsgi.workers[i].pid, uwsgi.workers[i].pending_harakiri + 1); + uwsgi_log_verbose("*** HARAKIRI ON WORKER %d (pid: %d, try: %d, graceful: %s) ***\n", i, + uwsgi.workers[i].pid, + uwsgi.workers[i].pending_harakiri + 1, + uwsgi.workers[i].pending_harakiri > 0 ? "no": "yes"); if (uwsgi.harakiri_verbose) { #ifdef __linux__ int proc_file; @@ -1673,7 +1676,11 @@ void trigger_harakiri(int i) { } uwsgi_dump_worker(i, "HARAKIRI"); - kill(uwsgi.workers[i].pid, SIGKILL); + if (uwsgi.workers[i].pending_harakiri == 0 && uwsgi.harakiri_graceful_timeout > 0) { + kill(uwsgi.workers[i].pid, uwsgi.harakiri_graceful_signal); + } else { + kill(uwsgi.workers[i].pid, SIGKILL); + } if (!uwsgi.workers[i].pending_harakiri) uwsgi.workers[i].harakiri_count++; uwsgi.workers[i].pending_harakiri++; diff --git a/core/uwsgi.c b/core/uwsgi.c index d7bf318b39..16137f6ed1 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -60,6 +60,9 @@ static struct uwsgi_option uwsgi_base_options[] = { {"thunder-lock-watchdog", no_argument, 0, "watchdog for buggy pthread robust mutexes", uwsgi_opt_true, &uwsgi.use_thunder_lock_watchdog, 0}, {"harakiri", required_argument, 't', "set harakiri timeout", uwsgi_opt_set_int, &uwsgi.harakiri_options.workers, 0}, {"harakiri-verbose", no_argument, 0, "enable verbose mode for harakiri", uwsgi_opt_true, &uwsgi.harakiri_verbose, 0}, + {"harakiri-graceful-timeout", required_argument, 0, "interval between graceful harakiri attempt and a sigkill", uwsgi_opt_set_int, &uwsgi.harakiri_graceful_timeout, 0}, + {"harakiri-graceful-signal", required_argument, 0, "use this signal instead of sigterm for graceful harakiri attempts", uwsgi_opt_set_int, &uwsgi.harakiri_graceful_signal, 0}, + {"harakiri-queue-threshold", required_argument, 0, "only trigger harakiri if queue is greater than this threshold", uwsgi_opt_set_int, &uwsgi.harakiri_queue_threshold, 0}, {"harakiri-no-arh", no_argument, 0, "do not enable harakiri during after-request-hook", uwsgi_opt_true, &uwsgi.harakiri_no_arh, 0}, {"no-harakiri-arh", no_argument, 0, "do not enable harakiri during after-request-hook", uwsgi_opt_true, &uwsgi.harakiri_no_arh, 0}, {"no-harakiri-after-req-hook", no_argument, 0, "do not enable harakiri during after-request-hook", uwsgi_opt_true, &uwsgi.harakiri_no_arh, 0}, diff --git a/tests/harakiri.py b/tests/harakiri.py new file mode 100644 index 0000000000..81085a4e25 --- /dev/null +++ b/tests/harakiri.py @@ -0,0 +1,24 @@ +import time +import uwsgi +import signal +import sys +import atexit + +def sig_handler(n, fp): + print("[Python App] attempting graceful shutdown triggered by harakiri (signal %d)" % n) + exit(1) + +def application(e, s): + print("[Python App] sleeping") + time.sleep(3) + s('200 OK', [('Content-Type', 'text/html')]) + return [b"OK"] + + +def exit_handler(): + time.sleep(3) + # Should not reach this line (graceful harakiri deadline expired) + print("[Python App] exiting now") + +atexit.register(exit_handler) +signal.signal(signal.SIGSYS, sig_handler) diff --git a/uwsgi.h b/uwsgi.h index 5fd6528fca..1980bc2348 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2486,6 +2486,9 @@ struct uwsgi_server { int backtrace_depth; int harakiri_verbose; + int harakiri_graceful_timeout; + int harakiri_graceful_signal; + int harakiri_queue_threshold; int harakiri_no_arh; int magic_table_first_round; From 31eb2347244f6b9ed5b099ddb130f2133f37ad3b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 17 Mar 2023 16:46:22 +0100 Subject: [PATCH 131/207] tests: add uwsgi call in harakiri.py So that it's trivial to run. --- tests/harakiri.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/harakiri.py b/tests/harakiri.py index 81085a4e25..88c24afa4d 100644 --- a/tests/harakiri.py +++ b/tests/harakiri.py @@ -1,3 +1,4 @@ +# ./uwsgi --master --http :8080 --harakiri 1 --wsgi-file tests/harakiri.py --harakiri-graceful-timeout 1 --py-call-osafterfork --lazy-apps --enable-threads --threads 2 --harakiri-graceful-signal 31 import time import uwsgi import signal From a5df32bbf291f55173c45e851ff3af76d08791d0 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 6 Jun 2023 21:55:06 +0200 Subject: [PATCH 132/207] ci: bump compile test distro to ubuntu 20.04 Since 18.04 is gone. --- .github/workflows/compile-test.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/compile-test.yml b/.github/workflows/compile-test.yml index ebfdb68164..9d9c564fdc 100644 --- a/.github/workflows/compile-test.yml +++ b/.github/workflows/compile-test.yml @@ -9,16 +9,21 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: + - name: remove sury php ppa that does not ship libphpX.Y-embed + run: | + sudo add-apt-repository --remove ppa:ondrej/php + sudo apt remove php7.4-dev php7.4 php7.4-common - name: Install dependencies run: | sudo apt update -qq - sudo apt install --no-install-recommends -qqyf python3.6-dev \ + sudo apt install --no-install-recommends -qqyf python3.8-dev \ libxml2-dev libpcre3-dev libcap2-dev \ - php7.2-dev libphp7.2-embed libargon2-0-dev libsodium-dev \ - liblua5.1-0-dev ruby2.5-dev \ + libargon2-0-dev libsodium-dev \ + php7.4-dev libphp7.4-embed \ + liblua5.1-0-dev ruby2.7-dev \ libjansson-dev libldap2-dev libpq-dev \ libpam0g-dev libsqlite3-dev libyaml-dev \ libzmq3-dev libmatheval-dev libperl-dev \ @@ -27,12 +32,12 @@ jobs: libboost-thread-dev libboost-filesystem-dev \ libssl-dev libacl1-dev python-greenlet-dev \ libcurl4-openssl-dev \ - openjdk-8-jdk libgloox-dev gccgo \ + openjdk-11-jdk libgloox-dev gccgo \ cli-common-dev mono-devel mono-mcs uuid-dev \ curl check - uses: actions/checkout@v2 - name: Build kitchensink uWSGI binary - run: UWSGICONFIG_PHPPATH=php-config7.2 /usr/bin/python3 uwsgiconfig.py --build travis + run: UWSGICONFIG_PHPPATH=php-config7.4 /usr/bin/python3 uwsgiconfig.py --build travis - name: Build uWSGI binary run: | /usr/bin/python3 uwsgiconfig.py --build base From 267f2816e945595da97b4d4af3964f9cebf5d06f Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 6 Jun 2023 21:57:19 +0200 Subject: [PATCH 133/207] ci: bump test github workflow to ubuntu 20.04 Since ubuntu 18.04 is gone. While at it remove deadsnakes install on rake job. --- .github/workflows/test.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0d767be4ea..739bed40d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,16 +9,14 @@ on: jobs: python: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 strategy: matrix: python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] test-suite: [unittest, python, deadlocks] steps: - name: Add deadnakes ppa - run: | - sudo apt install -qqyf software-properties-common - sudo add-apt-repository ppa:deadsnakes/ppa -y + run: sudo add-apt-repository ppa:deadsnakes/ppa -y - name: Install dependencies run: | sudo apt update -qq @@ -26,7 +24,7 @@ jobs: libpcre3-dev libjansson-dev libcap2-dev \ curl check - name: Install distutils - if: contains(fromJson('["3.8","3.9","3.10","3.11"]'), matrix.python-version) + if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11"]'), matrix.python-version) run: | sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-distutils \ - uses: actions/checkout@v2 @@ -52,20 +50,16 @@ jobs: rack: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 strategy: matrix: - rack-version: ["251"] + rack-version: ["270"] steps: - - name: Add deadnakes ppa - run: | - sudo apt install -qqyf software-properties-common - sudo add-apt-repository ppa:deadsnakes/ppa -y - name: Install dependencies run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf python3-dev \ - libpcre3-dev libjansson-dev libcap2-dev \ + libpcre3-dev libjansson-dev libcap2-dev ruby2.7-dev \ curl check - uses: actions/checkout@v2 - name: Run unit tests From d440c741be547aaddda66917d553c6b2dba01507 Mon Sep 17 00:00:00 2001 From: Lalufu Date: Tue, 6 Jun 2023 15:59:47 +0200 Subject: [PATCH 134/207] plugins/{rack,ruby}: handle recent ruby versions The rack/ruby plugin code references rb_obj_taint(), which has been removed from Ruby 3.2. The function has been deprecated for a long time, and hasn't been doing anything useful since Ruby 2.7. Fix #2532 --- plugins/rack/rack_plugin.c | 4 ++++ plugins/rack/uwsgiplugin.py | 2 ++ plugins/ruby19/uwsgiplugin.py | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/rack/rack_plugin.c b/plugins/rack/rack_plugin.c index 1ddd2c892f..8bc6ce73bd 100644 --- a/plugins/rack/rack_plugin.c +++ b/plugins/rack/rack_plugin.c @@ -456,7 +456,11 @@ void uwsgi_ruby_gemset(char *gemset) { static void rack_hack_dollar_zero(VALUE name, ID id) { ur.dollar_zero = rb_obj_as_string(name); + // From ruby 2.7 onwards this is a noop, from ruby 3.2 onwards + // this function no longer exists +#if !defined(RUBY27) rb_obj_taint(ur.dollar_zero); +#endif } #ifndef RUBY19 diff --git a/plugins/rack/uwsgiplugin.py b/plugins/rack/uwsgiplugin.py index 8b13f74df9..9ac983114f 100644 --- a/plugins/rack/uwsgiplugin.py +++ b/plugins/rack/uwsgiplugin.py @@ -14,6 +14,8 @@ if (v[0] == '1' and v[1] == '9') or v[0] >= '2': CFLAGS = os.popen(RUBYPATH + " -e \"require 'rbconfig';print RbConfig::CONFIG['CFLAGS']\"").read().rstrip().split() CFLAGS.append('-DRUBY19') + if version >= '2.7': + CFLAGS.append('-DRUBY27') CFLAGS.append('-Wno-unused-parameter') rbconfig = 'RbConfig' else: diff --git a/plugins/ruby19/uwsgiplugin.py b/plugins/ruby19/uwsgiplugin.py index a3bffd4ce5..994db614bc 100644 --- a/plugins/ruby19/uwsgiplugin.py +++ b/plugins/ruby19/uwsgiplugin.py @@ -11,9 +11,11 @@ GCC_LIST = ['../rack/rack_plugin', '../rack/rack_api'] -if v[0] == '1' and v[1] == '9': +if (v[0] == '1' and v[1] == '9') or v[0] >= '2': CFLAGS = os.popen(RUBYPATH + " -e \"require 'rbconfig';print RbConfig::CONFIG['CFLAGS']\"").read().rstrip().split() CFLAGS.append('-DRUBY19') + if version >= '2.7': + CFLAGS.append('-DRUBY27') CFLAGS.append('-Wno-unused-parameter') rbconfig = 'RbConfig' else: From c70fe2815781cf7128cd9b6c9d8165d65caabbf1 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 15 Jun 2023 22:25:25 +0200 Subject: [PATCH 135/207] plugins/rack: fix compilation with ruby 3.1 --- plugins/rack/rack_api.c | 112 ++++++++++++++++++------------------- plugins/rack/rack_plugin.c | 16 +++--- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/plugins/rack/rack_api.c b/plugins/rack/rack_api.c index 6a1396e012..004fb17ada 100644 --- a/plugins/rack/rack_api.c +++ b/plugins/rack/rack_api.c @@ -7,13 +7,13 @@ extern struct uwsgi_plugin rack_plugin; #define uwsgi_rack_api(x, y, z) rb_define_module_function(rb_uwsgi_embedded, x, y, z) -static VALUE rack_uwsgi_metric_get(VALUE *class, VALUE key) { +static VALUE rack_uwsgi_metric_get(VALUE class, VALUE key) { Check_Type(key, T_STRING); int64_t value = uwsgi_metric_get(RSTRING_PTR(key), NULL); return LONG2NUM(value); } -static VALUE rack_uwsgi_metric_set(VALUE *class, VALUE key, VALUE val) { +static VALUE rack_uwsgi_metric_set(VALUE class, VALUE key, VALUE val) { Check_Type(key, T_STRING); Check_Type(val, T_FIXNUM); // should be T_BIGNUM... if (uwsgi_metric_set(RSTRING_PTR(key), NULL, NUM2LONG(val) )) { @@ -22,7 +22,7 @@ static VALUE rack_uwsgi_metric_set(VALUE *class, VALUE key, VALUE val) { return Qtrue; } -static VALUE rack_uwsgi_metric_inc(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_metric_inc(int argc, VALUE *argv, VALUE class) { int64_t value = 1; if (argc == 0) return Qnil; Check_Type(argv[0], T_STRING); @@ -38,7 +38,7 @@ static VALUE rack_uwsgi_metric_inc(int argc, VALUE *argv, VALUE *class) { return Qtrue; } -static VALUE rack_uwsgi_metric_dec(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_metric_dec(int argc, VALUE *argv, VALUE class) { int64_t value = 1; if (argc == 0) return Qnil; Check_Type(argv[0], T_STRING); @@ -54,7 +54,7 @@ static VALUE rack_uwsgi_metric_dec(int argc, VALUE *argv, VALUE *class) { return Qtrue; } -static VALUE rack_uwsgi_metric_mul(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_metric_mul(int argc, VALUE *argv, VALUE class) { int64_t value = 1; if (argc == 0) return Qnil; Check_Type(argv[0], T_STRING); @@ -70,7 +70,7 @@ static VALUE rack_uwsgi_metric_mul(int argc, VALUE *argv, VALUE *class) { return Qtrue; } -static VALUE rack_uwsgi_metric_div(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_metric_div(int argc, VALUE *argv, VALUE class) { int64_t value = 1; if (argc == 0) return Qnil; Check_Type(argv[0], T_STRING); @@ -89,7 +89,7 @@ static VALUE rack_uwsgi_metric_div(int argc, VALUE *argv, VALUE *class) { -static VALUE rack_uwsgi_warning(VALUE *class, VALUE rbmessage) { +static VALUE rack_uwsgi_warning(VALUE class, VALUE rbmessage) { Check_Type(rbmessage, T_STRING); char *message = RSTRING_PTR(rbmessage); @@ -108,7 +108,7 @@ static VALUE rack_uwsgi_warning(VALUE *class, VALUE rbmessage) { return Qnil; } -static VALUE rack_uwsgi_user_harakiri(VALUE *class, VALUE sec) { +static VALUE rack_uwsgi_user_harakiri(VALUE class, VALUE sec) { Check_Type(sec, T_FIXNUM); struct wsgi_request *wsgi_req = current_wsgi_req(); set_user_harakiri(wsgi_req, NUM2INT(sec)); @@ -116,7 +116,7 @@ static VALUE rack_uwsgi_user_harakiri(VALUE *class, VALUE sec) { } -static VALUE rack_uwsgi_log(VALUE *class, VALUE msg) { +static VALUE rack_uwsgi_log(VALUE class, VALUE msg) { Check_Type(msg, T_STRING); @@ -125,7 +125,7 @@ static VALUE rack_uwsgi_log(VALUE *class, VALUE msg) { return Qnil; } -static VALUE rack_uwsgi_i_am_the_spooler(VALUE *class) { +static VALUE rack_uwsgi_i_am_the_spooler(VALUE class) { if (uwsgi.i_am_a_spooler) { return Qtrue; } @@ -133,7 +133,7 @@ static VALUE rack_uwsgi_i_am_the_spooler(VALUE *class) { } #ifdef UWSGI_SSL -static VALUE rack_uwsgi_i_am_the_lord(VALUE *class, VALUE legion_name) { +static VALUE rack_uwsgi_i_am_the_lord(VALUE class, VALUE legion_name) { Check_Type(legion_name, T_STRING); if (uwsgi_legion_i_am_the_lord(RSTRING_PTR(legion_name))) { return Qtrue; @@ -142,14 +142,14 @@ static VALUE rack_uwsgi_i_am_the_lord(VALUE *class, VALUE legion_name) { } #endif -static VALUE rack_uwsgi_connection_fd(VALUE *class) { +static VALUE rack_uwsgi_connection_fd(VALUE class) { struct wsgi_request *wsgi_req = current_wsgi_req(); return INT2NUM(wsgi_req->fd); } -static VALUE rack_uwsgi_setprocname(VALUE *class, VALUE rbname) { +static VALUE rack_uwsgi_setprocname(VALUE class, VALUE rbname) { Check_Type(rbname, T_STRING); char *name = RSTRING_PTR(rbname); @@ -158,7 +158,7 @@ static VALUE rack_uwsgi_setprocname(VALUE *class, VALUE rbname) { return Qnil; } -static VALUE rack_uwsgi_mem(VALUE *class) { +static VALUE rack_uwsgi_mem(VALUE class) { uint64_t rss=0, vsz = 0; VALUE ml = rb_ary_new2(2); @@ -172,23 +172,23 @@ static VALUE rack_uwsgi_mem(VALUE *class) { } -static VALUE rack_uwsgi_request_id(VALUE *class) { +static VALUE rack_uwsgi_request_id(VALUE class) { return ULONG2NUM(uwsgi.workers[uwsgi.mywid].requests); } -static VALUE rack_uwsgi_worker_id(VALUE *class) { +static VALUE rack_uwsgi_worker_id(VALUE class) { return INT2NUM(uwsgi.mywid); } -static VALUE rack_uwsgi_mule_id(VALUE *class) { +static VALUE rack_uwsgi_mule_id(VALUE class) { return INT2NUM(uwsgi.muleid); } -static VALUE rack_uwsgi_logsize(VALUE *class) { +static VALUE rack_uwsgi_logsize(VALUE class) { return ULONG2NUM(uwsgi.shared->logsize); } -static VALUE rack_uwsgi_mule_msg(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_mule_msg(int argc, VALUE *argv, VALUE class) { int fd = -1; int mule_id = -1; @@ -267,7 +267,7 @@ static int uwsgi_ruby_hash_mule_callback(VALUE key, VALUE val, VALUE arg_array) return 0; } -static VALUE rack_uwsgi_mule_get_msg(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_mule_get_msg(int argc, VALUE *argv, VALUE class) { int manage_signals = 1; int manage_farms = 1; @@ -323,7 +323,7 @@ static VALUE rack_uwsgi_mule_get_msg(int argc, VALUE *argv, VALUE *class) { } -static VALUE rack_uwsgi_lock(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_lock(int argc, VALUE *argv, VALUE class) { int lock_num = 0; @@ -341,7 +341,7 @@ static VALUE rack_uwsgi_lock(int argc, VALUE *argv, VALUE *class) { return Qnil; } -static VALUE rack_uwsgi_unlock(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_unlock(int argc, VALUE *argv, VALUE class) { int lock_num = 0; @@ -363,7 +363,7 @@ static VALUE rack_uwsgi_unlock(int argc, VALUE *argv, VALUE *class) { -static VALUE rack_uwsgi_cache_set(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_set(int argc, VALUE *argv, VALUE class) { if (argc < 2) goto error; @@ -399,7 +399,7 @@ static VALUE rack_uwsgi_cache_set(int argc, VALUE *argv, VALUE *class) { } -static VALUE rack_uwsgi_cache_update(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_update(int argc, VALUE *argv, VALUE class) { if (argc < 2) goto error; @@ -436,7 +436,7 @@ static VALUE rack_uwsgi_cache_update(int argc, VALUE *argv, VALUE *class) { } -static VALUE rack_uwsgi_cache_set_exc(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_set_exc(int argc, VALUE *argv, VALUE class) { VALUE ret = rack_uwsgi_cache_set(argc, argv, class); if (ret == Qnil) { rb_raise(rb_eRuntimeError, "unable to set value in uWSGI cache"); @@ -444,7 +444,7 @@ static VALUE rack_uwsgi_cache_set_exc(int argc, VALUE *argv, VALUE *class) { return ret; } -static VALUE rack_uwsgi_cache_update_exc(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_update_exc(int argc, VALUE *argv, VALUE class) { VALUE ret = rack_uwsgi_cache_update(argc, argv, class); if (ret == Qnil) { rb_raise(rb_eRuntimeError, "unable to update value in uWSGI cache"); @@ -452,7 +452,7 @@ static VALUE rack_uwsgi_cache_update_exc(int argc, VALUE *argv, VALUE *class) { return ret; } -static VALUE rack_uwsgi_cache_del(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_del(int argc, VALUE *argv, VALUE class) { if (argc == 0) goto error; @@ -478,7 +478,7 @@ static VALUE rack_uwsgi_cache_del(int argc, VALUE *argv, VALUE *class) { return Qnil; } -static VALUE rack_uwsgi_cache_del_exc(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_del_exc(int argc, VALUE *argv, VALUE class) { VALUE ret = rack_uwsgi_cache_del(argc, argv, class); if (ret == Qnil) { rb_raise(rb_eRuntimeError, "unable to delete object from uWSGI cache"); @@ -488,7 +488,7 @@ static VALUE rack_uwsgi_cache_del_exc(int argc, VALUE *argv, VALUE *class) { -static VALUE rack_uwsgi_cache_exists(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_exists(int argc, VALUE *argv, VALUE class) { if (argc == 0) goto error; @@ -516,7 +516,7 @@ static VALUE rack_uwsgi_cache_exists(int argc, VALUE *argv, VALUE *class) { -static VALUE rack_uwsgi_cache_clear(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_clear(int argc, VALUE *argv, VALUE class) { char *cache = NULL; @@ -532,7 +532,7 @@ static VALUE rack_uwsgi_cache_clear(int argc, VALUE *argv, VALUE *class) { return Qnil; } -static VALUE rack_uwsgi_cache_clear_exc(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_clear_exc(int argc, VALUE *argv, VALUE class) { VALUE ret = rack_uwsgi_cache_clear(argc, argv, class); if (ret == Qnil) { rb_raise(rb_eRuntimeError, "unable to clear the uWSGI cache"); @@ -542,7 +542,7 @@ static VALUE rack_uwsgi_cache_clear_exc(int argc, VALUE *argv, VALUE *class) { -static VALUE rack_uwsgi_cache_get(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_get(int argc, VALUE *argv, VALUE class) { if (argc == 0) goto error; @@ -572,7 +572,7 @@ static VALUE rack_uwsgi_cache_get(int argc, VALUE *argv, VALUE *class) { } -static VALUE rack_uwsgi_cache_get_exc(int argc, VALUE *argv, VALUE *class) { +static VALUE rack_uwsgi_cache_get_exc(int argc, VALUE *argv, VALUE class) { VALUE ret = rack_uwsgi_cache_get(argc, argv, class); if (ret == Qnil) { rb_raise(rb_eRuntimeError, "unable to get value from uWSGI cache"); @@ -583,7 +583,7 @@ static VALUE rack_uwsgi_cache_get_exc(int argc, VALUE *argv, VALUE *class) { -static VALUE rack_uwsgi_add_cron(VALUE *class, VALUE rbsignum, VALUE rbmin, VALUE rbhour, VALUE rbday, VALUE rbmon, VALUE rbweek) { +static VALUE rack_uwsgi_add_cron(VALUE class, VALUE rbsignum, VALUE rbmin, VALUE rbhour, VALUE rbday, VALUE rbmon, VALUE rbweek) { Check_Type(rbsignum, T_FIXNUM); Check_Type(rbmin, T_FIXNUM); @@ -609,7 +609,7 @@ static VALUE rack_uwsgi_add_cron(VALUE *class, VALUE rbsignum, VALUE rbmin, VALU -static VALUE rack_uwsgi_add_timer(VALUE *class, VALUE rbsignum, VALUE secs) { +static VALUE rack_uwsgi_add_timer(VALUE class, VALUE rbsignum, VALUE secs) { Check_Type(rbsignum, T_FIXNUM); Check_Type(secs, T_FIXNUM); @@ -627,7 +627,7 @@ static VALUE rack_uwsgi_add_timer(VALUE *class, VALUE rbsignum, VALUE secs) { -static VALUE rack_uwsgi_add_rb_timer(VALUE *class, VALUE rbsignum, VALUE secs) { +static VALUE rack_uwsgi_add_rb_timer(VALUE class, VALUE rbsignum, VALUE secs) { Check_Type(rbsignum, T_FIXNUM); Check_Type(secs, T_FIXNUM); @@ -645,7 +645,7 @@ static VALUE rack_uwsgi_add_rb_timer(VALUE *class, VALUE rbsignum, VALUE secs) { } -static VALUE rack_uwsgi_alarm(VALUE *class, VALUE alarm, VALUE msg) { +static VALUE rack_uwsgi_alarm(VALUE class, VALUE alarm, VALUE msg) { Check_Type(alarm, T_STRING); Check_Type(msg, T_STRING); @@ -655,7 +655,7 @@ static VALUE rack_uwsgi_alarm(VALUE *class, VALUE alarm, VALUE msg) { return Qnil; } -static VALUE rack_uwsgi_add_file_monitor(VALUE *class, VALUE rbsignum, VALUE rbfilename) { +static VALUE rack_uwsgi_add_file_monitor(VALUE class, VALUE rbsignum, VALUE rbfilename) { Check_Type(rbsignum, T_FIXNUM); Check_Type(rbfilename, T_STRING); @@ -672,7 +672,7 @@ static VALUE rack_uwsgi_add_file_monitor(VALUE *class, VALUE rbsignum, VALUE rbf } -static VALUE uwsgi_ruby_wait_fd_read(VALUE *class, VALUE arg1, VALUE arg2) { +static VALUE uwsgi_ruby_wait_fd_read(VALUE class, VALUE arg1, VALUE arg2) { Check_Type(arg1, T_FIXNUM); Check_Type(arg2, T_FIXNUM); @@ -690,7 +690,7 @@ static VALUE uwsgi_ruby_wait_fd_read(VALUE *class, VALUE arg1, VALUE arg2) { return Qtrue; } -static VALUE uwsgi_ruby_wait_fd_write(VALUE *class, VALUE arg1, VALUE arg2) { +static VALUE uwsgi_ruby_wait_fd_write(VALUE class, VALUE arg1, VALUE arg2) { Check_Type(arg1, T_FIXNUM); Check_Type(arg2, T_FIXNUM); @@ -707,7 +707,7 @@ static VALUE uwsgi_ruby_wait_fd_write(VALUE *class, VALUE arg1, VALUE arg2) { return Qtrue; } -static VALUE uwsgi_ruby_async_connect(VALUE *class, VALUE arg) { +static VALUE uwsgi_ruby_async_connect(VALUE class, VALUE arg) { Check_Type(arg, T_STRING); @@ -717,7 +717,7 @@ static VALUE uwsgi_ruby_async_connect(VALUE *class, VALUE arg) { } -static VALUE uwsgi_ruby_async_sleep(VALUE *class, VALUE arg) { +static VALUE uwsgi_ruby_async_sleep(VALUE class, VALUE arg) { Check_Type(arg, T_FIXNUM); @@ -731,7 +731,7 @@ static VALUE uwsgi_ruby_async_sleep(VALUE *class, VALUE arg) { return Qtrue; } -static VALUE uwsgi_ruby_masterpid(VALUE *class) { +static VALUE uwsgi_ruby_masterpid(VALUE class) { if (uwsgi.master_process) { return INT2NUM(uwsgi.workers[0].pid); @@ -739,7 +739,7 @@ static VALUE uwsgi_ruby_masterpid(VALUE *class) { return INT2NUM(0); } -static VALUE uwsgi_ruby_suspend(VALUE *class) { +static VALUE uwsgi_ruby_suspend(VALUE class) { struct wsgi_request *wsgi_req = current_wsgi_req(); @@ -750,7 +750,7 @@ static VALUE uwsgi_ruby_suspend(VALUE *class) { } -static VALUE uwsgi_ruby_signal_wait(int argc, VALUE *argv, VALUE *class) { +static VALUE uwsgi_ruby_signal_wait(int argc, VALUE *argv, VALUE class) { struct wsgi_request *wsgi_req = current_wsgi_req(); int wait_for_specific_signal = 0; @@ -782,7 +782,7 @@ static VALUE uwsgi_ruby_signal_wait(int argc, VALUE *argv, VALUE *class) { return Qnil; } -static VALUE uwsgi_ruby_signal_received(VALUE *class) { +static VALUE uwsgi_ruby_signal_received(VALUE class) { struct wsgi_request *wsgi_req = current_wsgi_req(); @@ -790,7 +790,7 @@ static VALUE uwsgi_ruby_signal_received(VALUE *class) { } -static VALUE uwsgi_ruby_signal_registered(VALUE *class, VALUE signum) { +static VALUE uwsgi_ruby_signal_registered(VALUE class, VALUE signum) { Check_Type(signum, T_FIXNUM); @@ -803,7 +803,7 @@ static VALUE uwsgi_ruby_signal_registered(VALUE *class, VALUE signum) { return Qfalse; } -static VALUE uwsgi_ruby_do_rpc(int argc, VALUE *rpc_argv, VALUE *class) { +static VALUE uwsgi_ruby_do_rpc(int argc, VALUE *rpc_argv, VALUE class) { char *node = NULL, *func; uint64_t size = 0; @@ -854,7 +854,7 @@ static VALUE uwsgi_ruby_do_rpc(int argc, VALUE *rpc_argv, VALUE *class) { return Qnil; } -static VALUE uwsgi_ruby_register_rpc(int argc, VALUE *argv, VALUE *class) { +static VALUE uwsgi_ruby_register_rpc(int argc, VALUE *argv, VALUE class) { int rb_argc = 0; @@ -880,7 +880,7 @@ static VALUE uwsgi_ruby_register_rpc(int argc, VALUE *argv, VALUE *class) { return Qtrue; } -static VALUE uwsgi_ruby_register_signal(VALUE *class, VALUE signum, VALUE sigkind, VALUE rbhandler) { +static VALUE uwsgi_ruby_register_signal(VALUE class, VALUE signum, VALUE sigkind, VALUE rbhandler) { Check_Type(signum, T_FIXNUM); Check_Type(sigkind, T_STRING); @@ -900,7 +900,7 @@ static VALUE uwsgi_ruby_register_signal(VALUE *class, VALUE signum, VALUE sigkin } -static VALUE uwsgi_ruby_signal(int argc, VALUE *argv, VALUE *class) { +static VALUE uwsgi_ruby_signal(int argc, VALUE *argv, VALUE class) { if (argc < 1) { rb_raise(rb_eRuntimeError, "you have to specify a signum"); @@ -968,7 +968,7 @@ static int rack_uwsgi_build_spool(VALUE rbkey, VALUE rbval, VALUE argv) { } -static VALUE rack_uwsgi_send_spool(VALUE *class, VALUE args) { +static VALUE rack_uwsgi_send_spool(VALUE class, VALUE args) { char *body = NULL; size_t body_len= 0; @@ -1006,7 +1006,7 @@ static VALUE rack_uwsgi_send_spool(VALUE *class, VALUE args) { } -static VALUE uwsgi_ruby_websocket_handshake(int argc, VALUE *argv, VALUE *class) { +static VALUE uwsgi_ruby_websocket_handshake(int argc, VALUE *argv, VALUE class) { struct wsgi_request *wsgi_req = current_wsgi_req(); @@ -1035,7 +1035,7 @@ static VALUE uwsgi_ruby_websocket_handshake(int argc, VALUE *argv, VALUE *class) return Qnil; } -static VALUE uwsgi_ruby_websocket_send(VALUE *class, VALUE *msg) { +static VALUE uwsgi_ruby_websocket_send(VALUE class, VALUE msg) { Check_Type(msg, T_STRING); char *message = RSTRING_PTR(msg); size_t message_len = RSTRING_LEN(msg); @@ -1046,7 +1046,7 @@ static VALUE uwsgi_ruby_websocket_send(VALUE *class, VALUE *msg) { return Qnil; } -static VALUE uwsgi_ruby_websocket_recv(VALUE *class) { +static VALUE uwsgi_ruby_websocket_recv(VALUE class) { struct wsgi_request *wsgi_req = current_wsgi_req(); struct uwsgi_buffer *ub = uwsgi_websocket_recv(wsgi_req); @@ -1060,7 +1060,7 @@ static VALUE uwsgi_ruby_websocket_recv(VALUE *class) { } -static VALUE uwsgi_ruby_websocket_recv_nb(VALUE *class) { +static VALUE uwsgi_ruby_websocket_recv_nb(VALUE class) { struct wsgi_request *wsgi_req = current_wsgi_req(); struct uwsgi_buffer *ub = uwsgi_websocket_recv_nb(wsgi_req); diff --git a/plugins/rack/rack_plugin.c b/plugins/rack/rack_plugin.c index 8bc6ce73bd..1611deecde 100644 --- a/plugins/rack/rack_plugin.c +++ b/plugins/rack/rack_plugin.c @@ -208,7 +208,7 @@ VALUE rb_uwsgi_io_init(int argc, VALUE *argv, VALUE self) { return self; } -VALUE rb_uwsgi_io_gets(VALUE obj, VALUE args) { +VALUE rb_uwsgi_io_gets(VALUE obj) { struct wsgi_request *wsgi_req; Data_Get_Struct(obj, struct wsgi_request, wsgi_req); @@ -222,14 +222,14 @@ VALUE rb_uwsgi_io_gets(VALUE obj, VALUE args) { return Qnil; } -VALUE rb_uwsgi_io_each(VALUE obj, VALUE args) { +VALUE rb_uwsgi_io_each(VALUE obj) { if (!rb_block_given_p()) rb_raise(rb_eArgError, "Expected block on rack.input 'each' method"); // yield strings chunks for(;;) { - VALUE chunk = rb_uwsgi_io_gets(obj, Qnil); + VALUE chunk = rb_uwsgi_io_gets(obj); if (chunk == Qnil) { return Qnil; } @@ -273,7 +273,7 @@ VALUE rb_uwsgi_io_read(VALUE obj, VALUE args) { return Qnil; } -VALUE rb_uwsgi_io_rewind(VALUE obj, VALUE args) { +VALUE rb_uwsgi_io_rewind(VALUE obj) { struct wsgi_request *wsgi_req; Data_Get_Struct(obj, struct wsgi_request, wsgi_req); @@ -454,7 +454,7 @@ void uwsgi_ruby_gemset(char *gemset) { } -static void rack_hack_dollar_zero(VALUE name, ID id) { +static void rack_hack_dollar_zero(VALUE name, ID id, VALUE *_) { ur.dollar_zero = rb_obj_as_string(name); // From ruby 2.7 onwards this is a noop, from ruby 3.2 onwards // this function no longer exists @@ -702,7 +702,7 @@ VALUE call_dispatch(VALUE env) { } -static VALUE send_body(VALUE obj) { +static VALUE send_body(VALUE obj, VALUE data, int argc, const VALUE *argv, VALUE blockarg) { struct wsgi_request *wsgi_req = current_wsgi_req(); @@ -735,7 +735,7 @@ VALUE iterate_body(VALUE body) { #endif } -VALUE send_header(VALUE obj, VALUE headers) { +VALUE send_header(VALUE obj, VALUE headers, int argc, const VALUE *argv, VALUE blockarg) { struct wsgi_request *wsgi_req = current_wsgi_req(); @@ -796,7 +796,7 @@ VALUE send_header(VALUE obj, VALUE headers) { VALUE iterate_headers(VALUE headers) { #ifdef RUBY19 - return rb_block_call(headers, rb_intern("each"), 0, 0, send_header, headers ); + return rb_block_call(headers, rb_intern("each"), 0, 0, send_header, headers); #else return rb_iterate(rb_each, headers, send_header, headers); #endif From ce4fa51fa5d897424eff1d098da749ec64b9e1ea Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 15 Jun 2023 23:11:50 +0200 Subject: [PATCH 136/207] buildconf: drop v8 from travis profile It does not build with recent v8 so disable it. --- buildconf/travis.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildconf/travis.ini b/buildconf/travis.ini index 28653a5db6..34f5271636 100644 --- a/buildconf/travis.ini +++ b/buildconf/travis.ini @@ -1,3 +1,3 @@ [uwsgi] -main_plugin = psgi,rack,lua,python,gevent,php,cgi,pty,xslt,msgpack,geoip,v8,pam,ldap,mono,jvm,ring,jwsgi,servlet,pypy,airbrake,alarm_curl,alarm_xmpp,asyncio,cheaper_backlog2,clock_monotonic,clock_realtime,cplusplus,curl_cron,dumbloop,dummy,echo,emperor_amqp,emperor_pg,emperor_zeromq,example,exception_log,fiber,forkptyrouter,graylog2,legion_cache_fetch,libffi,logcrypto,logpipe,logzmq,matheval,notfound,rbthreads,router_access,router_radius,router_spnego,router_xmldir,sqlite3,ssi,stats_pusher_file,stats_pusher_statsd,tornado,transformation_toupper,tuntap,webdav,xattr,zabbix +main_plugin = psgi,rack,lua,python,gevent,php,cgi,pty,xslt,msgpack,geoip,pam,ldap,mono,jvm,ring,jwsgi,servlet,pypy,airbrake,alarm_curl,alarm_xmpp,asyncio,cheaper_backlog2,clock_monotonic,clock_realtime,cplusplus,curl_cron,dumbloop,dummy,echo,emperor_amqp,emperor_pg,emperor_zeromq,example,exception_log,fiber,forkptyrouter,graylog2,legion_cache_fetch,libffi,logcrypto,logpipe,logzmq,matheval,notfound,rbthreads,router_access,router_radius,router_spnego,router_xmldir,sqlite3,ssi,stats_pusher_file,stats_pusher_statsd,tornado,transformation_toupper,tuntap,webdav,xattr,zabbix inherit = base From c3081fbf5f922e83f9bed6f2ad07cf3ced0ac72f Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 16 Jun 2023 09:13:05 +0200 Subject: [PATCH 137/207] buildconf: drop alarm_xmpp from travis profile Remove plugin that does not build anymore. --- buildconf/travis.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildconf/travis.ini b/buildconf/travis.ini index 34f5271636..9212fd88c9 100644 --- a/buildconf/travis.ini +++ b/buildconf/travis.ini @@ -1,3 +1,3 @@ [uwsgi] -main_plugin = psgi,rack,lua,python,gevent,php,cgi,pty,xslt,msgpack,geoip,pam,ldap,mono,jvm,ring,jwsgi,servlet,pypy,airbrake,alarm_curl,alarm_xmpp,asyncio,cheaper_backlog2,clock_monotonic,clock_realtime,cplusplus,curl_cron,dumbloop,dummy,echo,emperor_amqp,emperor_pg,emperor_zeromq,example,exception_log,fiber,forkptyrouter,graylog2,legion_cache_fetch,libffi,logcrypto,logpipe,logzmq,matheval,notfound,rbthreads,router_access,router_radius,router_spnego,router_xmldir,sqlite3,ssi,stats_pusher_file,stats_pusher_statsd,tornado,transformation_toupper,tuntap,webdav,xattr,zabbix +main_plugin = psgi,rack,lua,python,gevent,php,cgi,pty,xslt,msgpack,geoip,pam,ldap,mono,jvm,ring,jwsgi,servlet,pypy,airbrake,alarm_curl,asyncio,cheaper_backlog2,clock_monotonic,clock_realtime,cplusplus,curl_cron,dumbloop,dummy,echo,emperor_amqp,emperor_pg,emperor_zeromq,example,exception_log,fiber,forkptyrouter,graylog2,legion_cache_fetch,libffi,logcrypto,logpipe,logzmq,matheval,notfound,rbthreads,router_access,router_radius,router_spnego,router_xmldir,sqlite3,ssi,stats_pusher_file,stats_pusher_statsd,tornado,transformation_toupper,tuntap,webdav,xattr,zabbix inherit = base From f30a3660bc54e6c4ebb995bf8d20a83ee51b85bd Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 16 Jun 2023 09:14:06 +0200 Subject: [PATCH 138/207] plugins/jvm: add more paths to build on debian with openjdk-11 out of the box --- plugins/jvm/uwsgiplugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/jvm/uwsgiplugin.py b/plugins/jvm/uwsgiplugin.py index 280ba7fadc..4a99386ab3 100644 --- a/plugins/jvm/uwsgiplugin.py +++ b/plugins/jvm/uwsgiplugin.py @@ -32,11 +32,14 @@ JVM_INCPATH = ['-I"/cygdrive/c/Program Files/Java/jdk1.7.0_17/include"', '-I"/cygdrive/c/Program Files/Java/jdk1.7.0_17/include/win32"'] JVM_LIBPATH = ['-L"/cygdrive/c/Program Files/Java/jdk1.7.0_17/jre/bin/server"'] else: - known_jvms = ('/usr/lib/jvm/java-7-openjdk', '/usr/local/openjdk7', '/usr/lib/jvm/java-6-openjdk', '/usr/local/openjdk', '/usr/java', '/usr/lib/jvm/java/', '/usr/lib/jvm/java-8-openjdk-%s' % arch) + known_jvms = ('/usr/lib/jvm/java-7-openjdk', '/usr/local/openjdk7', '/usr/lib/jvm/java-6-openjdk', '/usr/local/openjdk', '/usr/java', '/usr/lib/jvm/java/', '/usr/lib/jvm/java-8-openjdk-%s' % arch, '/usr/lib/jvm/java-11-openjdk-%s' % arch) for jvm in known_jvms: if os.path.exists(jvm + '/include'): JVM_INCPATH = ["-I%s/include/" % jvm, "-I%s/include/%s" % (jvm, operating_system)] - JVM_LIBPATH = ["-L%s/jre/lib/%s/server" % (jvm, arch)] + if os.path.exists("%s/jre"): + JVM_LIBPATH = ["-L%s/jre/lib/%s/server" % (jvm, arch)] + else: + JVM_LIBPATH = ["-L%s/lib/server" % (jvm,)] break if os.path.exists("%s-%s/include" % (jvm, arch)): jvm = "%s-%s" % (jvm, arch) From 70a79799423bf7b1f993955ed0f16bcb82eb5f59 Mon Sep 17 00:00:00 2001 From: Young Ziyi Date: Tue, 4 Jul 2023 10:28:29 +0800 Subject: [PATCH 139/207] fix typo --- uwsgi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uwsgi.h b/uwsgi.h index 1980bc2348..69f9fbf8ab 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2465,7 +2465,7 @@ struct uwsgi_server { int master_queue; int master_interval; - // mainly iseful for broodlord mode + // mainly useful for broodlord mode int vassal_sos_backlog; int no_defer_accept; From 62c35ef9c63b675c1e0c99aa80f4da2f3f1e687c Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 25 Jul 2023 07:49:03 +0200 Subject: [PATCH 140/207] fix use after free when DEBUG --- core/offload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/offload.c b/core/offload.c index 09f948bd07..71dbe9ec34 100644 --- a/core/offload.c +++ b/core/offload.c @@ -236,11 +236,11 @@ static void uwsgi_offload_close(struct uwsgi_thread *ut, struct uwsgi_offload_re close(uor->pipe[0]); } - free(uor); - #ifdef UWSGI_DEBUG uwsgi_log("[offload] destroyed session %p\n", uor); #endif + + free(uor); } static void uwsgi_offload_append(struct uwsgi_thread *ut, struct uwsgi_offload_request *uor) { From 58ee1df31fa9e9af106aaeabb82374c36b433822 Mon Sep 17 00:00:00 2001 From: Eric Covener Date: Tue, 25 Jul 2023 14:36:11 +0200 Subject: [PATCH 141/207] apache2/mod_proxy_uwsgi: stricter backend HTTP response parsing/validation HTTP Response Smuggling vulnerability in Apache HTTP Server via mod_proxy_uwsgi. Special characters in the origin response header can truncate/split the response forwarded to the client. Fix #2538 origin: https://github.com/apache/httpd/commit/d753ea76b5972a85349b68c31b59d04c60014f2d.patch bug-cve: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-27522 --- apache2/mod_proxy_uwsgi.c | 48 ++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/apache2/mod_proxy_uwsgi.c b/apache2/mod_proxy_uwsgi.c index 507262bed1..026e63e03e 100644 --- a/apache2/mod_proxy_uwsgi.c +++ b/apache2/mod_proxy_uwsgi.c @@ -301,18 +301,16 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec *backend, proxy_server_ apr_bucket_brigade *pass_bb = apr_brigade_create(r->pool, c->bucket_alloc); len = ap_getline(buffer, sizeof(buffer), rp, 1); - if (len <= 0) { - // oops + /* invalid or empty */ return HTTP_INTERNAL_SERVER_ERROR; } - backend->worker->s->read += len; - - if (len >= sizeof(buffer)-1) { - // oops + if ((apr_size_t)len >= sizeof(buffer)) { + /* too long */ return HTTP_INTERNAL_SERVER_ERROR; } + /* Position of http status code */ int status_start; if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { @@ -320,8 +318,8 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec *backend, proxy_server_ } else if (apr_date_checkmask(buffer, "HTTP/# ###*")) { status_start = 7; } else { - // oops - return HTTP_INTERNAL_SERVER_ERROR; + /* not HTTP */ + return HTTP_BAD_GATEWAY; } int status_end = status_start + 3; @@ -340,17 +338,41 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec *backend, proxy_server_ } r->status_line = apr_pstrdup(r->pool, &buffer[status_start]); - // start parsing headers; + /* parse headers */ while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) { + if ((apr_size_t)len >= sizeof(buffer)) { + /* too long */ + len = -1; + break; + } value = strchr(buffer, ':'); - // invalid header skip - if (!value) continue; - *value = '\0'; - ++value; + if (!value) { + /* invalid header */ + len = -1; + break; + } + *value++ = '\0'; + if (*ap_scan_http_token(buffer)) { + /* invalid name */ + len = -1; + break; + } while (apr_isspace(*value)) ++value; for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end) *end = '\0'; + if (*ap_scan_http_field_content(value)) { + /* invalid value */ + len = -1; + break; + } apr_table_add(r->headers_out, buffer, value); } + if (len < 0) { + /* Reset headers, but not to NULL because things below the chain expect + * this to be non NULL e.g. the ap_content_length_filter. + */ + r->headers_out = apr_table_make(r->pool, 1); + return HTTP_BAD_GATEWAY; + } if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { ap_set_content_type(r, apr_pstrdup(r->pool, buf)); From d94f6db506d8de2d21328845698359b8f6ad8135 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 25 Jul 2023 17:43:50 +0200 Subject: [PATCH 142/207] core/lock: silence glibc warnings against pthread robust mutex functions Since glibc 2.34 we are gettings warnings that pthread_mutexattr_setrobust_np and pthread_mutex_consistent_np are deprecated. Problem is that we are checking PTHREAD_MUTEX_ROBUST with the preprocessor but it doesn't work because it's an enum :) So in the end we are using the _np versions of the functions even if the standard ones are available. Since this stuff is implemented on linux libc since 2010-2011 and 2016 in freebsd assume it's here. --- core/lock.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/lock.c b/core/lock.c index 686e3b106a..eb0de35cfc 100644 --- a/core/lock.c +++ b/core/lock.c @@ -91,11 +91,6 @@ struct uwsgi_lock_item *uwsgi_lock_fast_init(char *id) { } #ifdef EOWNERDEAD -#ifndef PTHREAD_MUTEX_ROBUST -#define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP -#define pthread_mutexattr_setrobust pthread_mutexattr_setrobust_np -#define pthread_mutex_consistent pthread_mutex_consistent_np -#endif if (uwsgi_pthread_robust_mutexes_enabled) { int ret; if ((ret = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT)) != 0) { From 152123d1dbd3727793a9852149c5d255bdcf52ec Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 28 Jul 2023 09:40:59 +0200 Subject: [PATCH 143/207] RELEASE: fix typo in tags pushing --- RELEASE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 5d3b0106ca..0e112d57b4 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -16,7 +16,7 @@ Please remember to substitute XY with proper version. ``` git tag 2.0.XY -git push ---tags origin HEAD +git push --tags origin HEAD git archive HEAD --prefix uwsgi-2.0.XY/ -o uwsgi-2.0.XY.tar.gz ``` From 8924b79a9f1dc503431bad9048e964f5afb5d837 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 28 Jul 2023 22:15:17 +0200 Subject: [PATCH 144/207] plugins/jvm: fixup jvm library path detection Fix #2552 --- plugins/jvm/uwsgiplugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/jvm/uwsgiplugin.py b/plugins/jvm/uwsgiplugin.py index 4a99386ab3..f3c369abf6 100644 --- a/plugins/jvm/uwsgiplugin.py +++ b/plugins/jvm/uwsgiplugin.py @@ -36,7 +36,7 @@ for jvm in known_jvms: if os.path.exists(jvm + '/include'): JVM_INCPATH = ["-I%s/include/" % jvm, "-I%s/include/%s" % (jvm, operating_system)] - if os.path.exists("%s/jre"): + if os.path.exists("%s/jre" % jvm): JVM_LIBPATH = ["-L%s/jre/lib/%s/server" % (jvm, arch)] else: JVM_LIBPATH = ["-L%s/lib/server" % (jvm,)] From 21366abc98e8e15c4586d054a880587bee511d2f Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sat, 26 Aug 2023 17:20:45 +0200 Subject: [PATCH 145/207] RELEASE: add to update the Download uwsgi-docs page after release --- RELEASE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE.md b/RELEASE.md index 0e112d57b4..5ceb7a7e29 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -25,3 +25,6 @@ Then the tarball must be uploaded to pypi with: ``` python3 -m twine upload uwsgi-2.0.XY.tar.gz ``` + +Once the file is uploaded the `Download.rst` page of [uwsgi-docs repository](https://github.com/unbit/uwsgi-docs) +should be updated. From 064984116e86ac0a5d5d3805765395b661fc4455 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 4 Sep 2023 13:10:52 +0200 Subject: [PATCH 146/207] ini_entries is read-only PHP 8.3 --- plugins/php/php_plugin.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index b3efa006ac..d9b615bac9 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -27,6 +27,7 @@ struct uwsgi_php { char *fallback; char *fallback2; char *fallback_qs; + char *ini_entries; size_t ini_size; int dump_config; char *server_software; @@ -232,21 +233,22 @@ static sapi_module_struct uwsgi_sapi_module; void uwsgi_php_append_config(char *filename) { size_t file_size = 0; - char *file_content = uwsgi_open_and_read(filename, &file_size, 1, NULL); - uwsgi_sapi_module.ini_entries = realloc(uwsgi_sapi_module.ini_entries, uphp.ini_size + file_size); - memcpy(uwsgi_sapi_module.ini_entries + uphp.ini_size, file_content, file_size); + char *file_content = uwsgi_open_and_read(filename, &file_size, 1, NULL); + uphp.ini_entries = realloc(uphp.ini_entries, uphp.ini_size + file_size); + memcpy(uphp.ini_entries + uphp.ini_size, file_content, file_size); uphp.ini_size += file_size-1; free(file_content); + uwsgi_sapi_module.ini_entries = uphp.ini_entries; } void uwsgi_php_set(char *opt) { - uwsgi_sapi_module.ini_entries = realloc(uwsgi_sapi_module.ini_entries, uphp.ini_size + strlen(opt)+2); - memcpy(uwsgi_sapi_module.ini_entries + uphp.ini_size, opt, strlen(opt)); - + uphp.ini_entries = realloc(uphp.ini_entries, uphp.ini_size + strlen(opt)+2); + memcpy(uphp.ini_entries + uphp.ini_size, opt, strlen(opt)); uphp.ini_size += strlen(opt)+1; - uwsgi_sapi_module.ini_entries[uphp.ini_size-1] = '\n'; - uwsgi_sapi_module.ini_entries[uphp.ini_size] = 0; + uphp.ini_entries[uphp.ini_size-1] = '\n'; + uphp.ini_entries[uphp.ini_size] = 0; + uwsgi_sapi_module.ini_entries = uphp.ini_entries; } extern ps_module ps_mod_uwsgi; From 18816280a4a91ca39193bba73bc04b047645bbbd Mon Sep 17 00:00:00 2001 From: Ralf Ertzinger Date: Wed, 19 Jul 2023 19:42:31 +0200 Subject: [PATCH 147/207] This adds support for Python 3.12 The code compiles, and runs all my (admittedly pretty primitive) web applications. There may still be hidden issues in more complicated scenarios (like threading support). --- .github/workflows/test.yml | 4 +- plugins/python/python_plugin.c | 74 +++++++++++++++++++++++++--------- plugins/python/uwsgi_python.h | 14 ++++++- 3 files changed, 69 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 739bed40d4..009d48438e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] test-suite: [unittest, python, deadlocks] steps: - name: Add deadnakes ppa @@ -24,7 +24,7 @@ jobs: libpcre3-dev libjansson-dev libcap2-dev \ curl check - name: Install distutils - if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11"]'), matrix.python-version) + if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11","3.12"]'), matrix.python-version) run: | sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-distutils \ - uses: actions/checkout@v2 diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index dcb5d348e0..de92f34c2f 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -233,6 +233,21 @@ void uwsgi_python_pthread_child(void) { PyMethodDef uwsgi_spit_method[] = { {"uwsgi_spit", py_uwsgi_spit, METH_VARARGS, ""} }; PyMethodDef uwsgi_write_method[] = { {"uwsgi_write", py_uwsgi_write, METH_VARARGS, ""} }; +PyDoc_STRVAR(uwsgi_py_doc, "uWSGI api module."); + +#ifdef PYTHREE +static PyModuleDef uwsgi_module3 = { + PyModuleDef_HEAD_INIT, + "uwsgi", + uwsgi_py_doc, + -1, + NULL, +}; +PyObject *init_uwsgi3(void) { + return PyModule_Create(&uwsgi_module3); +} +#endif + int uwsgi_python_init() { char *pyversion = strchr(Py_GetVersion(), '\n'); @@ -298,6 +313,9 @@ int uwsgi_python_init() { wchar_t *pname = uwsgi_calloc(sizeof(wchar_t) * (strlen(program_name)+1)); mbstowcs(pname, program_name, strlen(program_name)+1); Py_SetProgramName(pname); +#ifdef UWSGI_PY312 + PyImport_AppendInittab("uwsgi", init_uwsgi3); +#endif #else Py_SetProgramName(program_name); #endif @@ -660,21 +678,6 @@ void init_uwsgi_vars() { -PyDoc_STRVAR(uwsgi_py_doc, "uWSGI api module."); - -#ifdef PYTHREE -static PyModuleDef uwsgi_module3 = { - PyModuleDef_HEAD_INIT, - "uwsgi", - uwsgi_py_doc, - -1, - NULL, -}; -PyObject *init_uwsgi3(void) { - return PyModule_Create(&uwsgi_module3); -} -#endif - void init_uwsgi_embedded_module() { PyObject *new_uwsgi_module, *zero; int i; @@ -695,7 +698,9 @@ void init_uwsgi_embedded_module() { #ifdef PYTHREE +#ifndef UWSGI_PY312 PyImport_AppendInittab("uwsgi", init_uwsgi3); +#endif new_uwsgi_module = PyImport_AddModule("uwsgi"); #else new_uwsgi_module = Py_InitModule3("uwsgi", NULL, uwsgi_py_doc); @@ -1208,7 +1213,10 @@ void uwsgi_python_init_apps() { // prepare for stack suspend/resume if (uwsgi.async > 0) { -#ifdef UWSGI_PY311 +#ifdef UWSGI_PY312 + up.current_c_recursion_remaining = uwsgi_malloc(sizeof(int)*uwsgi.async); + up.current_py_recursion_remaining = uwsgi_malloc(sizeof(int)*uwsgi.async); +#elif defined UWSGI_PY311 up.current_recursion_remaining = uwsgi_malloc(sizeof(int)*uwsgi.async); #else up.current_recursion_depth = uwsgi_malloc(sizeof(int)*uwsgi.async); @@ -1360,7 +1368,12 @@ void uwsgi_python_pre_uwsgi_fork() { // Acquire the gil and import lock before forking in order to avoid // deadlocks in workers UWSGI_GET_GIL +#if defined UWSGI_PY312 + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyImport_AcquireLock(interp); +#else _PyImport_AcquireLock(); +#endif } } @@ -1372,7 +1385,12 @@ void uwsgi_python_post_uwsgi_fork(int step) { if (uwsgi.has_threads) { if (step == 0) { // Release locks within master process +#if defined UWSGI_PY312 + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyImport_ReleaseLock(interp); +#else _PyImport_ReleaseLock(); +#endif UWSGI_RELEASE_GIL } else { @@ -1643,7 +1661,11 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { PyGILState_Release(pgst); if (wsgi_req) { -#ifdef UWSGI_PY311 +#ifdef UWSGI_PY312 + up.current_c_recursion_remaining[wsgi_req->async_id] = tstate->c_recursion_remaining; + up.current_py_recursion_remaining[wsgi_req->async_id] = tstate->py_recursion_remaining; + up.current_frame[wsgi_req->async_id] = tstate->cframe; +#elif defined UWSGI_PY311 up.current_recursion_remaining[wsgi_req->async_id] = tstate->recursion_remaining; up.current_frame[wsgi_req->async_id] = tstate->cframe; #else @@ -1652,7 +1674,11 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { #endif } else { -#ifdef UWSGI_PY311 +#ifdef UWSGI_PY312 + up.current_main_c_recursion_remaining = tstate->c_recursion_remaining; + up.current_main_py_recursion_remaining = tstate->py_recursion_remaining; + up.current_main_frame = tstate->cframe; +#elif defined UWSGI_PY311 up.current_main_recursion_remaining = tstate->recursion_remaining; up.current_main_frame = tstate->cframe; #else @@ -1886,7 +1912,11 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { PyGILState_Release(pgst); if (wsgi_req) { -#ifdef UWSGI_PY311 +#ifdef UWSGI_PY312 + tstate->c_recursion_remaining = up.current_c_recursion_remaining[wsgi_req->async_id]; + tstate->py_recursion_remaining = up.current_py_recursion_remaining[wsgi_req->async_id]; + tstate->cframe = up.current_frame[wsgi_req->async_id]; +#elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_recursion_remaining[wsgi_req->async_id]; tstate->cframe = up.current_frame[wsgi_req->async_id]; #else @@ -1895,7 +1925,11 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { #endif } else { -#ifdef UWSGI_PY311 +#ifdef UWSGI_PY312 + tstate->c_recursion_remaining = up.current_main_c_recursion_remaining; + tstate->py_recursion_remaining = up.current_main_py_recursion_remaining; + tstate->cframe = up.current_main_frame; +#elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_main_recursion_remaining; tstate->cframe = up.current_main_frame; #else diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 1e75fd641c..961a46c49f 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -21,6 +21,10 @@ # define UWSGI_PY311 #endif +#if (PY_VERSION_HEX >= 0x030c0000) +# define UWSGI_PY312 +#endif + #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 7 #define HAS_NOT_PyMemoryView_FromBuffer #endif @@ -182,7 +186,15 @@ struct uwsgi_python { char *callable; -#ifdef UWSGI_PY311 +#ifdef UWSGI_PY312 + int *current_c_recursion_remaining; + int *current_py_recursion_remaining; + _PyCFrame **current_frame; + + int current_main_c_recursion_remaining; + int current_main_py_recursion_remaining; + _PyCFrame *current_main_frame; +#elif defined UWSGI_PY311 int *current_recursion_remaining; _PyCFrame **current_frame; From 8f7178f1a825e646df564ed225ff3a18d28d479d Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 17 Sep 2023 16:23:17 +0200 Subject: [PATCH 148/207] ci: run compile test on ubuntu 22.04 too --- .github/workflows/compile-test.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/compile-test.yml b/.github/workflows/compile-test.yml index 9d9c564fdc..2a05e3495b 100644 --- a/.github/workflows/compile-test.yml +++ b/.github/workflows/compile-test.yml @@ -8,22 +8,31 @@ on: jobs: build: + strategy: + matrix: + include: + - os: ubuntu-20.04 + php: "php7.4" + php-config: "php-config7.4" + - os: ubuntu-22.04 + php: "php8.1" + php-config: "php-config8.1" - runs-on: ubuntu-20.04 + runs-on: ${{ matrix.os }} steps: - name: remove sury php ppa that does not ship libphpX.Y-embed run: | sudo add-apt-repository --remove ppa:ondrej/php - sudo apt remove php7.4-dev php7.4 php7.4-common + sudo apt remove ${{ matrix.php }}-dev ${{ matrix.php }} ${{ matrix.php }}-common - name: Install dependencies run: | sudo apt update -qq - sudo apt install --no-install-recommends -qqyf python3.8-dev \ + sudo apt install --no-install-recommends -qqyf python3-dev \ libxml2-dev libpcre3-dev libcap2-dev \ libargon2-0-dev libsodium-dev \ - php7.4-dev libphp7.4-embed \ - liblua5.1-0-dev ruby2.7-dev \ + ${{ matrix.php }}-dev lib${{ matrix.php }}-embed \ + liblua5.1-0-dev ruby-dev \ libjansson-dev libldap2-dev libpq-dev \ libpam0g-dev libsqlite3-dev libyaml-dev \ libzmq3-dev libmatheval-dev libperl-dev \ @@ -37,7 +46,7 @@ jobs: curl check - uses: actions/checkout@v2 - name: Build kitchensink uWSGI binary - run: UWSGICONFIG_PHPPATH=php-config7.4 /usr/bin/python3 uwsgiconfig.py --build travis + run: UWSGICONFIG_PHPPATH=${{ matrix.php-config }} /usr/bin/python3 uwsgiconfig.py --build travis - name: Build uWSGI binary run: | /usr/bin/python3 uwsgiconfig.py --build base From b030a71bb3f3d512a719ad71b5d5d73593e0b6f7 Mon Sep 17 00:00:00 2001 From: Steve Kowalik Date: Wed, 4 Oct 2023 12:36:02 +1100 Subject: [PATCH 149/207] Use sysconfig if distutils is not available distutils have been removed in Python 3.12. Co-authored-by: Steve Kowalik Co-authored-by: Terence D. Honles Co-authored-by: Riccardo Magliocchetti --- plugins/asyncio/uwsgiplugin.py | 18 +++++++++++++----- plugins/gevent/uwsgiplugin.py | 18 +++++++++++++----- plugins/greenlet/uwsgiplugin.py | 18 +++++++++++++----- plugins/python/uwsgiplugin.py | 20 +++++++++++++------- plugins/pyuwsgi/uwsgiplugin.py | 21 +++++++++++++++------ plugins/stackless/uwsgiplugin.py | 18 +++++++++++++----- plugins/tornado/uwsgiplugin.py | 18 +++++++++++++----- setup.cpyext.py | 3 +-- uwsgiconfig.py | 3 +-- 9 files changed, 95 insertions(+), 42 deletions(-) diff --git a/plugins/asyncio/uwsgiplugin.py b/plugins/asyncio/uwsgiplugin.py index 7cb17f54eb..56e56e8150 100644 --- a/plugins/asyncio/uwsgiplugin.py +++ b/plugins/asyncio/uwsgiplugin.py @@ -1,11 +1,19 @@ -from distutils import sysconfig +try: + from distutils import sysconfig + paths = [ + sysconfig.get_python_inc(), + sysconfig.get_python_inc(plat_specific=True), + ] +except ImportError: + import sysconfig + paths = [ + sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + ] NAME = 'asyncio' -CFLAGS = [ - '-I' + sysconfig.get_python_inc(), - '-I' + sysconfig.get_python_inc(plat_specific=True) -] +CFLAGS = ['-I' + path for path in paths] LDFLAGS = [] LIBS = [] GCC_LIST = ['asyncio'] diff --git a/plugins/gevent/uwsgiplugin.py b/plugins/gevent/uwsgiplugin.py index 4ff550a9f9..422a5ffc19 100644 --- a/plugins/gevent/uwsgiplugin.py +++ b/plugins/gevent/uwsgiplugin.py @@ -1,11 +1,19 @@ -from distutils import sysconfig +try: + from distutils import sysconfig + paths = [ + sysconfig.get_python_inc(), + sysconfig.get_python_inc(plat_specific=True), + ] +except ImportError: + import sysconfig + paths = [ + sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + ] NAME = 'gevent' -CFLAGS = [ - '-I' + sysconfig.get_python_inc(), - '-I' + sysconfig.get_python_inc(plat_specific=True) -] +CFLAGS = ['-I' + path for path in paths] LDFLAGS = [] LIBS = [] diff --git a/plugins/greenlet/uwsgiplugin.py b/plugins/greenlet/uwsgiplugin.py index 27b1da1235..c712b53699 100644 --- a/plugins/greenlet/uwsgiplugin.py +++ b/plugins/greenlet/uwsgiplugin.py @@ -1,11 +1,19 @@ -from distutils import sysconfig +try: + from distutils import sysconfig + paths = [ + sysconfig.get_python_inc(), + sysconfig.get_python_inc(plat_specific=True), + ] +except ImportError: + import sysconfig + paths = [ + sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + ] NAME = 'greenlet' -CFLAGS = [ - '-I' + sysconfig.get_python_inc(), - '-I' + sysconfig.get_python_inc(plat_specific=True) -] +CFLAGS = ['-I' + path for path in paths] LDFLAGS = [] LIBS = [] diff --git a/plugins/python/uwsgiplugin.py b/plugins/python/uwsgiplugin.py index e3765c997b..7de8457376 100644 --- a/plugins/python/uwsgiplugin.py +++ b/plugins/python/uwsgiplugin.py @@ -1,8 +1,17 @@ import os import sys - -from distutils import sysconfig - +try: + from distutils import sysconfig + paths = [ + sysconfig.get_python_inc(), + sysconfig.get_python_inc(plat_specific=True), + ] +except ImportError: + import sysconfig + paths = [ + sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + ] def get_python_version(): version = sysconfig.get_config_var('VERSION') @@ -30,10 +39,7 @@ def get_python_version(): 'raw' ] -CFLAGS = [ - '-I' + sysconfig.get_python_inc(), - '-I' + sysconfig.get_python_inc(plat_specific=True), -] +CFLAGS = ['-I' + path for path in paths] LDFLAGS = [] if 'UWSGI_PYTHON_NOLIB' not in os.environ: diff --git a/plugins/pyuwsgi/uwsgiplugin.py b/plugins/pyuwsgi/uwsgiplugin.py index 4b6bce042a..3bfd66eddf 100644 --- a/plugins/pyuwsgi/uwsgiplugin.py +++ b/plugins/pyuwsgi/uwsgiplugin.py @@ -1,14 +1,23 @@ -from distutils import sysconfig -import os, sys +import os +import sys +try: + from distutils import sysconfig + paths = [ + sysconfig.get_python_inc(), + sysconfig.get_python_inc(plat_specific=True), + ] +except ImportError: + import sysconfig + paths = [ + sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + ] os.environ['UWSGI_PYTHON_NOLIB'] = '1' NAME = 'pyuwsgi' -CFLAGS = [ - '-I' + sysconfig.get_python_inc(), - '-I' + sysconfig.get_python_inc(plat_specific=True), -] +CFLAGS = ['-I' + path for path in paths] LDFLAGS = [] LIBS = [] diff --git a/plugins/stackless/uwsgiplugin.py b/plugins/stackless/uwsgiplugin.py index d720e2b9bd..a304b97a52 100644 --- a/plugins/stackless/uwsgiplugin.py +++ b/plugins/stackless/uwsgiplugin.py @@ -1,11 +1,19 @@ -from distutils import sysconfig +try: + from distutils import sysconfig + paths = [ + sysconfig.get_python_inc(), + sysconfig.get_python_inc(plat_specific=True), + ] +except ImportError: + import sysconfig + paths = [ + sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + ] NAME = 'stackless' -CFLAGS = [ - '-I' + sysconfig.get_python_inc(), - '-I' + sysconfig.get_python_inc(plat_specific=True), -] +CFLAGS = ['-I' + path for path in paths] LDFLAGS = [] LIBS = [] diff --git a/plugins/tornado/uwsgiplugin.py b/plugins/tornado/uwsgiplugin.py index c0179b609b..f80b6efbf1 100644 --- a/plugins/tornado/uwsgiplugin.py +++ b/plugins/tornado/uwsgiplugin.py @@ -1,11 +1,19 @@ -from distutils import sysconfig +try: + from distutils import sysconfig + paths = [ + sysconfig.get_python_inc(), + sysconfig.get_python_inc(plat_specific=True), + ] +except ImportError: + import sysconfig + paths = [ + sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + ] NAME = 'tornado' -CFLAGS = [ - '-I' + sysconfig.get_python_inc(), - '-I' + sysconfig.get_python_inc(plat_specific=True), -] +CFLAGS = ['-I' + path for path in paths] LDFLAGS = [] LIBS = [] diff --git a/setup.cpyext.py b/setup.cpyext.py index c29b1d3a9a..9a238d7ab0 100644 --- a/setup.cpyext.py +++ b/setup.cpyext.py @@ -12,9 +12,8 @@ import shlex import uwsgiconfig -from setuptools import setup +from setuptools import setup, Extension from setuptools.command.build_ext import build_ext -from distutils.core import Extension class uWSGIBuildExt(build_ext): diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 344e0a941d..26a5297a53 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -12,6 +12,7 @@ import sys import subprocess +import sysconfig from threading import Thread, Lock from optparse import OptionParser @@ -20,8 +21,6 @@ except ImportError: from Queue import Queue -from distutils import sysconfig - try: import ConfigParser except ImportError: From 4d1651a833655f9696729b8f4e70bbbc08d48f5a Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 1 Nov 2023 14:23:55 +0100 Subject: [PATCH 150/207] Add python trove classifier for 3.12 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index a307bac518..1b1446244e 100644 --- a/setup.py +++ b/setup.py @@ -149,6 +149,7 @@ def get_extra_require(): 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], install_requires=get_extra_require() ) From a488187a2265809d276454a87e3f17fd2adc552e Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 7 Nov 2023 14:46:45 +0100 Subject: [PATCH 151/207] properly init cache for purge_lru Purging the least recently used cache item relies on a linked list maintained during cache get and set. While uwsgi is running, this works well. But if the uwsgi process is stopped, lru_head and lru_tail are not initialized when loading the existing cache file. As a consequence, least recently used items never get deleted a full cache fails to set any new keys until the cache file is deleted. This patch properly initializes those variables. --- core/cache.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/cache.c b/core/cache.c index c3bf7d6e3c..6e9b261493 100644 --- a/core/cache.c +++ b/core/cache.c @@ -737,6 +737,12 @@ void uwsgi_cache_fix(struct uwsgi_cache *uc) { if (uci->expires && (!next_scan || next_scan > uci->expires)) { next_scan = uci->expires; } + if (!uc->lru_head && !uci->lru_prev) { + uc->lru_head = i; + } + if (!uc->lru_tail && !uci->lru_next) { + uc->lru_tail = i; + } restored++; } else { From 14e53344d8e80ba697e9cf41628ffc13fcb953e0 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 13 Nov 2023 09:20:20 +0100 Subject: [PATCH 152/207] use libphp on PHP 8+, else libphp7 --- plugins/php/uwsgiplugin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/php/uwsgiplugin.py b/plugins/php/uwsgiplugin.py index 8657eb704d..8a3e71b5e7 100644 --- a/plugins/php/uwsgiplugin.py +++ b/plugins/php/uwsgiplugin.py @@ -21,7 +21,12 @@ LDFLAGS.append('-L%s' % ld_run_path) os.environ['LD_RUN_PATH'] = ld_run_path -LIBS = [os.popen(PHPPATH + ' --libs').read().rstrip(), '-lphp' + php_version] +# PHP8 and above does not add the version to the library +# name +if int(php_version) < 8: + LIBS = [os.popen(PHPPATH + ' --libs').read().rstrip(), '-lphp' + php_version] +else: + LIBS = [os.popen(PHPPATH + ' --libs').read().rstrip(), '-lphp'] phplibdir = os.environ.get('UWSGICONFIG_PHPLIBDIR') if phplibdir: From 7cfe9614be5ce57a6b2f38508bdeb96b5790df4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20K=C3=A1rolyi?= Date: Thu, 23 Nov 2023 15:45:40 +0100 Subject: [PATCH 153/207] Remove unused variables due to compilation failure --- core/ini.c | 3 --- core/master.c | 4 ---- core/yaml.c | 2 -- 3 files changed, 9 deletions(-) diff --git a/core/ini.c b/core/ini.c index 97080bcd9b..9ff18a47a3 100644 --- a/core/ini.c +++ b/core/ini.c @@ -87,8 +87,6 @@ void uwsgi_ini_config(char *file, char *magic_table[]) { char *key; char *val; - int lines = 1; - char *section_asked = "uwsgi"; char *colon; int got_section = 0; @@ -130,7 +128,6 @@ void uwsgi_ini_config(char *file, char *magic_table[]) { if (ini_line == NULL) { break; } - lines++; // skip empty line key = ini_lstrip(ini); diff --git a/core/master.c b/core/master.c index dccd590bd8..e58161687a 100644 --- a/core/master.c +++ b/core/master.c @@ -8,16 +8,12 @@ static void master_check_processes() { if (!uwsgi.die_on_no_workers) return; int alive_processes = 0; - int dead_processes = 0; int i; for (i = 1; i <= uwsgi.numproc; i++) { if (uwsgi.workers[i].cheaped == 0 && uwsgi.workers[i].pid > 0) { alive_processes++; } - else { - dead_processes++; - } } if (uwsgi.die_on_no_workers) { diff --git a/core/yaml.c b/core/yaml.c index edaad760ef..e859435660 100644 --- a/core/yaml.c +++ b/core/yaml.c @@ -211,7 +211,6 @@ void uwsgi_yaml_config(char *file, char *magic_table[]) { #else int depth; int current_depth = 0; - int lines = 1; char *yaml_line; char *section = ""; @@ -221,7 +220,6 @@ void uwsgi_yaml_config(char *file, char *magic_table[]) { if (yaml_line == NULL) { break; } - lines++; // skip empty line if (yaml[0] == 0) From 19620afcaa607fccd69c91e446856d18bb9c31b3 Mon Sep 17 00:00:00 2001 From: Shai Bentov Date: Thu, 7 Dec 2023 09:48:50 -0700 Subject: [PATCH 154/207] use right type for harakiri --- core/master_checks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/master_checks.c b/core/master_checks.c index 9fdffd4b12..c8dc66a7bd 100644 --- a/core/master_checks.c +++ b/core/master_checks.c @@ -180,7 +180,7 @@ void uwsgi_master_check_idle() { } -int uwsgi_master_check_harakiri(int w, int c, int harakiri) { +int uwsgi_master_check_harakiri(int w, int c, time_t harakiri) { /** * Triggers a harakiri when the following conditions are met: * - harakiri timeout > current time From 58c97429ecf7c7e08a859f9f398ba733c9dcd582 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 26 Dec 2023 11:45:53 +0100 Subject: [PATCH 155/207] core/master: fix socket queue stats for ipv6 It looks like the code works fine with ipv6 too so enable it. Fix #2577 --- core/master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/master.c b/core/master.c index e58161687a..44fbc0465d 100644 --- a/core/master.c +++ b/core/master.c @@ -276,7 +276,7 @@ static void master_check_listen_queue() { uint64_t backlog = 0; struct uwsgi_socket *uwsgi_sock = uwsgi.sockets; while(uwsgi_sock) { - if (uwsgi_sock->family == AF_INET) { + if (uwsgi_sock->family == AF_INET || uwsgi_sock->family == AF_INET6) { get_tcp_info(uwsgi_sock); } #ifdef __linux__ From 873727125564c9d984221cd24658764095486545 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 26 Dec 2023 12:13:09 +0100 Subject: [PATCH 156/207] core/logging: fixup -Wformat-signedness warnings --- core/logging.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/logging.c b/core/logging.c index ac2f37a493..e7ab9f9307 100644 --- a/core/logging.c +++ b/core/logging.c @@ -720,7 +720,7 @@ void uwsgi_logit_simple(struct wsgi_request *wsgi_req) { } if (uwsgi.logging_options.memory_report) { - rlen = snprintf(mempkt, 4096, "{address space usage: %lld bytes/%lluMB} {rss usage: %llu bytes/%lluMB} ", + rlen = snprintf(mempkt, 4096, "{address space usage: %llu bytes/%lluMB} {rss usage: %llu bytes/%lluMB} ", (unsigned long long) uwsgi.workers[uwsgi.mywid].vsz_size, (unsigned long long) uwsgi.workers[uwsgi.mywid].vsz_size / 1024 / 1024, (unsigned long long) uwsgi.workers[uwsgi.mywid].rss_size, @@ -772,7 +772,7 @@ void get_memusage(uint64_t * rss, uint64_t * vsz) { int i; procfile = fopen("/proc/self/stat", "r"); if (procfile) { - i = fscanf(procfile, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %llu %lld", (unsigned long long *) vsz, (unsigned long long *) rss); + i = fscanf(procfile, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %llu %llu", (unsigned long long *) vsz, (unsigned long long *) rss); if (i != 2) { uwsgi_log("warning: invalid record in /proc/self/stat\n"); } else { @@ -786,7 +786,7 @@ void get_memusage(uint64_t * rss, uint64_t * vsz) { int i; procfile = fopen("/proc/self/stat", "r"); if (procfile) { - i = fscanf(procfile, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %llu %lld", (unsigned long long *) vsz, (unsigned long long *) rss); + i = fscanf(procfile, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %llu %llu", (unsigned long long *) vsz, (unsigned long long *) rss); if (i != 2) { uwsgi_log("warning: invalid record in /proc/self/stat\n"); } From eb47ac2c9608e149d242aabf5b8bc812d787cade Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 26 Dec 2023 13:44:34 +0100 Subject: [PATCH 157/207] core/utils: use unsigned values in uwsgi_uuid --- core/utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/utils.c b/core/utils.c index c198bb44a5..420a343975 100644 --- a/core/utils.c +++ b/core/utils.c @@ -4097,7 +4097,7 @@ void uwsgi_uuid(char *buf) { uuid_generate(uuid_value); uuid_unparse(uuid_value, buf); #else - int i, r[11]; + unsigned int i, r[11]; if (!uwsgi_file_exists("/dev/urandom")) goto fallback; int fd = open("/dev/urandom", O_RDONLY); @@ -4113,7 +4113,7 @@ void uwsgi_uuid(char *buf) { goto done; fallback: for (i = 0; i < 11; i++) { - r[i] = rand(); + r[i] = (unsigned int) rand(); } done: snprintf(buf, 37, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10]); From a6f841f2a9d58c2b3a3928f3f03d343c1ae2e996 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 28 Dec 2023 11:53:45 +0100 Subject: [PATCH 158/207] uwsgiconfig: add -Wformat-signedness --- uwsgiconfig.py | 1 + 1 file changed, 1 insertion(+) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 26a5297a53..8fe31ccb15 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -684,6 +684,7 @@ def __init__(self, filename, mute=False): '-I.', '-Wall', '-Werror', + '-Wformat-signedness', '-Wno-error=deprecated-declarations', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64' From c4ecde031a4c957876320b1d1c1860360ab22323 Mon Sep 17 00:00:00 2001 From: Ben Kallus Date: Mon, 1 Jan 2024 13:00:39 -0500 Subject: [PATCH 159/207] Avoid strncpy from null in pyloader --- plugins/python/pyloader.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c index 3a1465d673..dc816fa102 100644 --- a/plugins/python/pyloader.c +++ b/plugins/python/pyloader.c @@ -97,7 +97,9 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre memset(wi, 0, sizeof(struct uwsgi_app)); wi->modifier1 = python_plugin.modifier1; wi->mountpoint_len = wsgi_req->appid_len < 0xff ? wsgi_req->appid_len : (0xff-1); - strncpy(wi->mountpoint, wsgi_req->appid, wi->mountpoint_len); + if (wi->mountpoint_len > 0) { + strncpy(wi->mountpoint, wsgi_req->appid, wi->mountpoint_len); + } // dynamic chdir ? if (wsgi_req->chdir_len > 0) { From 5c47c30326c0435b044c3634ec44b577cc14b5e0 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 8 Feb 2024 16:55:37 +0100 Subject: [PATCH 160/207] uwsgiconfig: make -Wformat-signedness conditional on gcc Since it's not available on clang. Fix #2602 --- uwsgiconfig.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 8fe31ccb15..ad1bef1d81 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -679,16 +679,18 @@ def __init__(self, filename, mute=False): if 'UWSGI_INCLUDES' in os.environ: self.include_path += os.environ['UWSGI_INCLUDES'].split(',') - self.cflags = [ + cflags = [ '-O2', '-I.', '-Wall', '-Werror', - '-Wformat-signedness', '-Wno-error=deprecated-declarations', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64' - ] + os.environ.get("CFLAGS", "").split() + self.get('cflags', '').split() + ] + if "gcc" in GCC: + cflags.append('-Wformat-signedness') + self.cflags = cflags + os.environ.get("CFLAGS", "").split() + self.get('cflags', '').split() python_venv_include = os.path.join(sys.prefix, 'include', 'site', 'python{0}.{1}'.format(*sys.version_info)) From c36c44a3ca80573e693a818ce6b479e8d2171c67 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 8 Feb 2024 17:02:02 +0100 Subject: [PATCH 161/207] ci: add clang to compile test matrix Add a default build of uwsgi with clang, cannot do the travis profile because at least perl headers don't compile. --- .github/workflows/compile-test.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/compile-test.yml b/.github/workflows/compile-test.yml index 2a05e3495b..d78edc9b92 100644 --- a/.github/workflows/compile-test.yml +++ b/.github/workflows/compile-test.yml @@ -14,9 +14,15 @@ jobs: - os: ubuntu-20.04 php: "php7.4" php-config: "php-config7.4" + cc: "gcc" - os: ubuntu-22.04 php: "php8.1" php-config: "php-config8.1" + cc: "gcc" + - os: ubuntu-22.04 + php: "php8.1" + php-config: "php-config8.1" + cc: "clang" runs-on: ${{ matrix.os }} @@ -43,16 +49,16 @@ jobs: libcurl4-openssl-dev \ openjdk-11-jdk libgloox-dev gccgo \ cli-common-dev mono-devel mono-mcs uuid-dev \ - curl check + curl check ${{ matrix.cc == 'clang' && 'clang' || '' }} - uses: actions/checkout@v2 - - name: Build kitchensink uWSGI binary - run: UWSGICONFIG_PHPPATH=${{ matrix.php-config }} /usr/bin/python3 uwsgiconfig.py --build travis + - name: Build kitchensink uWSGI binary with gcc or default with clang + run: CC=${{ matrix.cc }} UWSGICONFIG_PHPPATH=${{ matrix.php-config }} /usr/bin/python3 uwsgiconfig.py --build ${{ matrix.cc == 'gcc' && 'travis' || '' }} - name: Build uWSGI binary run: | - /usr/bin/python3 uwsgiconfig.py --build base + CC=${{ matrix.cc }} /usr/bin/python3 uwsgiconfig.py --build base - name: Build cgi plugin run: | - /usr/bin/python3 uwsgiconfig.py --plugin plugins/cgi base + CC=${{ matrix.cc }} /usr/bin/python3 uwsgiconfig.py --plugin plugins/cgi base - name: Build dummy plugin run: | - /usr/bin/python3 uwsgiconfig.py --plugin plugins/dummy base + CC=${{ matrix.cc }} /usr/bin/python3 uwsgiconfig.py --plugin plugins/dummy base From bb41750a4026602da84593f09773a522d4be94a0 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 8 Feb 2024 17:20:29 +0100 Subject: [PATCH 162/207] ci: use actions/checkout@v4 to avoid node versions warnings --- .github/workflows/compile-test.yml | 2 +- .github/workflows/test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compile-test.yml b/.github/workflows/compile-test.yml index d78edc9b92..d1ec99799e 100644 --- a/.github/workflows/compile-test.yml +++ b/.github/workflows/compile-test.yml @@ -50,7 +50,7 @@ jobs: openjdk-11-jdk libgloox-dev gccgo \ cli-common-dev mono-devel mono-mcs uuid-dev \ curl check ${{ matrix.cc == 'clang' && 'clang' || '' }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build kitchensink uWSGI binary with gcc or default with clang run: CC=${{ matrix.cc }} UWSGICONFIG_PHPPATH=${{ matrix.php-config }} /usr/bin/python3 uwsgiconfig.py --build ${{ matrix.cc == 'gcc' && 'travis' || '' }} - name: Build uWSGI binary diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 009d48438e..ba0cbbb49f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11","3.12"]'), matrix.python-version) run: | sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-distutils \ - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run unit tests if: matrix.test-suite == 'unittest' run: make tests @@ -61,7 +61,7 @@ jobs: sudo apt install --no-install-recommends -qqyf python3-dev \ libpcre3-dev libjansson-dev libcap2-dev ruby2.7-dev \ curl check - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run unit tests run: make tests - name: Build uWSGI binary From ec7afe03a87210d1e76b5c56101e94f9e4ba175a Mon Sep 17 00:00:00 2001 From: Ralf Ertzinger Date: Sat, 10 Feb 2024 18:01:49 +0100 Subject: [PATCH 163/207] Update glusterfs io callback function signature Starting with glusterfs 6.0, the IO callback function takes two additional parameters for stats structs. Ideally there'd be a way to detect which API version we're building against, but nothing convenient seems to exist. --- plugins/glusterfs/glusterfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/glusterfs/glusterfs.c b/plugins/glusterfs/glusterfs.c index 83428faf3f..c0063f4e58 100644 --- a/plugins/glusterfs/glusterfs.c +++ b/plugins/glusterfs/glusterfs.c @@ -46,7 +46,7 @@ struct uwsgi_glusterfs_async_io { ssize_t rlen; }; -static void uwsgi_glusterfs_read_async_cb(glfs_fd_t *fd, ssize_t rlen, void *data) { +static void uwsgi_glusterfs_read_async_cb(glfs_fd_t *fd, ssize_t rlen, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data) { struct uwsgi_glusterfs_async_io *aio = (struct uwsgi_glusterfs_async_io *) data; #ifdef UWSGI_DEBUG uwsgi_log("[glusterfs-cb] rlen = %d\n", rlen); From 4c7750bfd4bc5fd72203c41c940e48233f4caa83 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Wed, 14 Feb 2024 12:09:17 +0100 Subject: [PATCH 164/207] Fix default values doc for min-worker-lifetime & legion-skew-tolerance I reviewed all options that mention `default` in their help string and found these two discrepancies. --- core/uwsgi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/uwsgi.c b/core/uwsgi.c index 16137f6ed1..915528f7c7 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -278,7 +278,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"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 60)", uwsgi_opt_set_64bit, &uwsgi.min_worker_lifetime, 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}, @@ -649,7 +649,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"legion-freq", required_argument, 0, "set the frequency of legion packets", uwsgi_opt_set_int, &uwsgi.legion_freq, UWSGI_OPT_MASTER}, {"legion-tolerance", required_argument, 0, "set the tolerance of legion subsystem", uwsgi_opt_set_int, &uwsgi.legion_tolerance, UWSGI_OPT_MASTER}, {"legion-death-on-lord-error", required_argument, 0, "declare itself as a dead node for the specified amount of seconds if one of the lord hooks fails", uwsgi_opt_set_int, &uwsgi.legion_death_on_lord_error, UWSGI_OPT_MASTER}, - {"legion-skew-tolerance", required_argument, 0, "set the clock skew tolerance of legion subsystem (default 30 seconds)", uwsgi_opt_set_int, &uwsgi.legion_skew_tolerance, UWSGI_OPT_MASTER}, + {"legion-skew-tolerance", required_argument, 0, "set the clock skew tolerance of legion subsystem (default 60 seconds)", uwsgi_opt_set_int, &uwsgi.legion_skew_tolerance, UWSGI_OPT_MASTER}, {"legion-lord", required_argument, 0, "action to call on Lord election", uwsgi_opt_legion_hook, NULL, UWSGI_OPT_MASTER}, {"legion-unlord", required_argument, 0, "action to call on Lord dismiss", uwsgi_opt_legion_hook, NULL, UWSGI_OPT_MASTER}, {"legion-setup", required_argument, 0, "action to call on legion setup", uwsgi_opt_legion_hook, NULL, UWSGI_OPT_MASTER}, From d70c02bc78d7913a29ae158e589e1269e495af14 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sat, 24 Feb 2024 21:10:12 +0100 Subject: [PATCH 165/207] uwsgiconfig: don't add -Wformat-signedness with gcc < 5 Fix #2609 --- uwsgiconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index ad1bef1d81..e68ca4ac79 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -688,8 +688,6 @@ def __init__(self, filename, mute=False): '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64' ] - if "gcc" in GCC: - cflags.append('-Wformat-signedness') self.cflags = cflags + os.environ.get("CFLAGS", "").split() + self.get('cflags', '').split() python_venv_include = os.path.join(sys.prefix, 'include', 'site', @@ -770,6 +768,8 @@ def __init__(self, filename, mute=False): self.cflags += ['-Wextra', '-Wno-unused-parameter', '-Wno-missing-field-initializers'] if gcc_major == 4 and gcc_minor < 9: self.cflags.append('-Wno-format -Wno-format-security') + if "gcc" in GCC and gcc_major >= 5: + self.cflags.append('-Wformat-signedness') self.ldflags = os.environ.get("LDFLAGS", "").split() self.libs = ['-lpthread', '-lm', '-rdynamic'] From cb2e4ca9fbf0a8a73123887135a5687490875219 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 25 Jul 2023 16:17:52 +0200 Subject: [PATCH 166/207] core/regexp: add to pcre2 support Co-authored-by: loqs --- .github/workflows/compile-test.yml | 12 ++-- .github/workflows/test.yml | 2 +- check/Makefile | 2 +- core/alarm.c | 10 +-- core/config.c | 2 +- core/logging.c | 14 ++-- core/regexp.c | 102 ++++++++++++++++++++++++----- core/routing.c | 41 +++++++----- core/ssl.c | 8 +-- core/static.c | 20 +++--- core/utils.c | 13 ++-- core/uwsgi.c | 20 +++--- plugins/php/php_plugin.c | 12 ++-- uwsgi.h | 58 +++++++++------- uwsgiconfig.py | 45 +++++++------ 15 files changed, 227 insertions(+), 134 deletions(-) diff --git a/.github/workflows/compile-test.yml b/.github/workflows/compile-test.yml index d1ec99799e..6b56aea31d 100644 --- a/.github/workflows/compile-test.yml +++ b/.github/workflows/compile-test.yml @@ -10,18 +10,18 @@ jobs: build: strategy: matrix: + libpcre: ["libpcre3-dev", "libpcre2-dev"] + os: ["ubuntu-20.04", "ubuntu-22.04"] + cc: [gcc, clang] include: - os: ubuntu-20.04 php: "php7.4" php-config: "php-config7.4" - cc: "gcc" - - os: ubuntu-22.04 - php: "php8.1" - php-config: "php-config8.1" - cc: "gcc" - os: ubuntu-22.04 php: "php8.1" php-config: "php-config8.1" + exclude: + - os: ubuntu-20.04 cc: "clang" runs-on: ${{ matrix.os }} @@ -35,7 +35,7 @@ jobs: run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf python3-dev \ - libxml2-dev libpcre3-dev libcap2-dev \ + libxml2-dev ${{ matrix.libpcre }} libcap2-dev \ libargon2-0-dev libsodium-dev \ ${{ matrix.php }}-dev lib${{ matrix.php }}-embed \ liblua5.1-0-dev ruby-dev \ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ba0cbbb49f..6c396344d8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-dev \ - libpcre3-dev libjansson-dev libcap2-dev \ + libpcre2-dev libjansson-dev libcap2-dev \ curl check - name: Install distutils if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11","3.12"]'), matrix.python-version) diff --git a/check/Makefile b/check/Makefile index ac69b37891..a9a6555769 100644 --- a/check/Makefile +++ b/check/Makefile @@ -4,7 +4,7 @@ LDFLAGS = $(shell pkg-config --libs check) LDFLAGS += -ldl -lz LDFLAGS += $(shell xml2-config --libs) LDFLAGS += $(shell pkg-config --libs openssl) -LDFLAGS += $(shell pcre-config --libs) +LDFLAGS += $(shell pcre2-config --libs8) LDFLAGS += $(shell pkg-config --libs jansson) UNAME_S := $(shell uname -s) diff --git a/core/alarm.c b/core/alarm.c index 7691a7d0a4..bdd7b00ce4 100644 --- a/core/alarm.c +++ b/core/alarm.c @@ -160,7 +160,7 @@ static struct uwsgi_alarm_instance *uwsgi_alarm_get_instance(char *name) { } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) static int uwsgi_alarm_log_add(char *alarms, char *regexp, int negate) { struct uwsgi_alarm_log *old_ual = NULL, *ual = uwsgi.alarm_logs; @@ -170,7 +170,7 @@ static int uwsgi_alarm_log_add(char *alarms, char *regexp, int negate) { } ual = uwsgi_calloc(sizeof(struct uwsgi_alarm_log)); - if (uwsgi_regexp_build(regexp, &ual->pattern, &ual->pattern_extra)) { + if (uwsgi_regexp_build(regexp, &ual->pattern)) { free(ual); return -1; } @@ -331,7 +331,7 @@ void uwsgi_alarms_init() { usl = usl->next; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) // then map log-alarm usl = uwsgi.alarm_logs_list; while (usl) { @@ -377,14 +377,14 @@ void uwsgi_alarm_trigger_uai(struct uwsgi_alarm_instance *uai, char *msg, size_t } } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) // check if a log should raise an alarm void uwsgi_alarm_log_check(char *msg, size_t len) { if (!uwsgi_strncmp(msg, len, "[uwsgi-alarm", 12)) return; struct uwsgi_alarm_log *ual = uwsgi.alarm_logs; while (ual) { - if (uwsgi_regexp_match(ual->pattern, ual->pattern_extra, msg, len) >= 0) { + if (uwsgi_regexp_match(ual->pattern, msg, len) >= 0) { if (!ual->negate) { struct uwsgi_alarm_ll *uall = ual->alarms; while (uall) { diff --git a/core/config.c b/core/config.c index 7153dfa164..164bd84250 100644 --- a/core/config.c +++ b/core/config.c @@ -336,7 +336,7 @@ int uwsgi_logic_opt_if_not_hostname(char *key, char *value) { return 0; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) int uwsgi_logic_opt_if_hostname_match(char *key, char *value) { uwsgi.logic_opt_if_failed = 0; if (uwsgi_regexp_match_pattern(uwsgi.logic_opt_data, uwsgi.hostname)) { diff --git a/core/logging.c b/core/logging.c index e7ab9f9307..b174614c6b 100644 --- a/core/logging.c +++ b/core/logging.c @@ -414,7 +414,7 @@ void uwsgi_setup_log_master(void) { usl = usl->next; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) // set logger by its id struct uwsgi_regexp_list *url = uwsgi.log_route; while (url) { @@ -1443,11 +1443,11 @@ int uwsgi_master_log(void) { ssize_t rlen = read(uwsgi.shared->worker_log_pipe[0], uwsgi.log_master_buf, uwsgi.log_master_bufsize); if (rlen > 0) { -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) uwsgi_alarm_log_check(uwsgi.log_master_buf, rlen); struct uwsgi_regexp_list *url = uwsgi.log_drain_rules; while (url) { - if (uwsgi_regexp_match(url->pattern, url->pattern_extra, uwsgi.log_master_buf, rlen) >= 0) { + if (uwsgi_regexp_match(url->pattern, uwsgi.log_master_buf, rlen) >= 0) { return 0; } url = url->next; @@ -1456,7 +1456,7 @@ int uwsgi_master_log(void) { int show = 0; url = uwsgi.log_filter_rules; while (url) { - if (uwsgi_regexp_match(url->pattern, url->pattern_extra, uwsgi.log_master_buf, rlen) >= 0) { + if (uwsgi_regexp_match(url->pattern, uwsgi.log_master_buf, rlen) >= 0) { show = 1; break; } @@ -1469,7 +1469,7 @@ int uwsgi_master_log(void) { url = uwsgi.log_route; int finish = 0; while (url) { - if (uwsgi_regexp_match(url->pattern, url->pattern_extra, uwsgi.log_master_buf, rlen) >= 0) { + if (uwsgi_regexp_match(url->pattern, uwsgi.log_master_buf, rlen) >= 0) { struct uwsgi_logger *ul_route = (struct uwsgi_logger *) url->custom_ptr; if (ul_route) { uwsgi_log_func_do(uwsgi.requested_log_encoders, ul_route, uwsgi.log_master_buf, rlen); @@ -1509,11 +1509,11 @@ int uwsgi_master_req_log(void) { ssize_t rlen = read(uwsgi.shared->worker_req_log_pipe[0], uwsgi.log_master_buf, uwsgi.log_master_bufsize); if (rlen > 0) { -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *url = uwsgi.log_req_route; int finish = 0; while (url) { - if (uwsgi_regexp_match(url->pattern, url->pattern_extra, uwsgi.log_master_buf, rlen) >= 0) { + if (uwsgi_regexp_match(url->pattern, uwsgi.log_master_buf, rlen) >= 0) { struct uwsgi_logger *ul_route = (struct uwsgi_logger *) url->custom_ptr; if (ul_route) { uwsgi_log_func_do(uwsgi.requested_log_req_encoders, ul_route, uwsgi.log_master_buf, rlen); diff --git a/core/regexp.c b/core/regexp.c index a0569db7bd..74bb77751e 100644 --- a/core/regexp.c +++ b/core/regexp.c @@ -1,4 +1,4 @@ -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) #include "uwsgi.h" extern struct uwsgi_server uwsgi; @@ -10,51 +10,119 @@ void uwsgi_opt_pcre_jit(char *opt, char *value, void *foobar) { if (ret != 0 || has_jit != 1) return; uwsgi.pcre_jit = PCRE_STUDY_JIT_COMPILE; +#elif defined(PCRE2_CONFIG_JIT) + int has_jit = 0, ret; + ret = pcre2_config(PCRE2_CONFIG_JIT, &has_jit); + if (ret != 0) + return; + uwsgi.pcre_jit = has_jit; #endif } -int uwsgi_regexp_build(char *re, pcre ** pattern, pcre_extra ** pattern_extra) { +int uwsgi_regexp_build(char *re, uwsgi_pcre ** pattern) { + +#ifdef UWSGI_PCRE2 + int errnbr; + long unsigned int erroff; + *pattern = pcre2_compile((const unsigned char *) re, PCRE2_ZERO_TERMINATED, 0, &errnbr, &erroff, NULL); +#else const char *errstr; int erroff; - *pattern = pcre_compile((const char *) re, 0, &errstr, &erroff, NULL); - if (!*pattern) { + *pattern = uwsgi_malloc(sizeof(uwsgi_pcre)); + (*pattern)->p = pcre_compile((const char *) re, 0, &errstr, &erroff, NULL); +#endif +#ifdef UWSGI_PCRE2 + if (!(*pattern)) { + uwsgi_log("pcre error: code %d at offset %d\n", errnbr, erroff); +#else + if (!((*pattern)->p)) { uwsgi_log("pcre error: %s at offset %d\n", errstr, erroff); +#endif return -1; } +#ifdef UWSGI_PCRE2 + if (uwsgi.pcre_jit) { + errnbr = pcre2_jit_compile(*pattern, PCRE2_JIT_COMPLETE); + if (errnbr) { + pcre2_code_free(*pattern); + uwsgi_log("pcre JIT compile error code %d\n", errnbr); + return -1; + } +#else int opt = uwsgi.pcre_jit; - *pattern_extra = (pcre_extra *) pcre_study((const pcre *) *pattern, opt, &errstr); - if (*pattern_extra == NULL && errstr != NULL) { - pcre_free(*pattern); + (*pattern)->extra = (pcre_extra *) pcre_study((const pcre *) (*pattern)->p, opt, &errstr); + if ((*pattern)->extra == NULL && errstr != NULL) { + pcre_free((*pattern)->p); + free(*pattern); uwsgi_log("pcre (study) error: %s\n", errstr); return -1; +#endif } return 0; } -int uwsgi_regexp_match(pcre * pattern, pcre_extra * pattern_extra, char *subject, int length) { - - return pcre_exec((const pcre *) pattern, (const pcre_extra *) pattern_extra, subject, length, 0, 0, NULL, 0); +int uwsgi_regexp_match(uwsgi_pcre *pattern, const char *subject, int length) { +#ifdef UWSGI_PCRE2 + return pcre2_match(pattern, (const unsigned char *)subject, length, 0, 0, NULL, NULL); +#else + return pcre_exec((const pcre *) pattern->p, (const pcre_extra *) pattern->extra, subject, length, 0, 0, NULL, 0); +#endif } -int uwsgi_regexp_match_ovec(pcre * pattern, pcre_extra * pattern_extra, char *subject, int length, int *ovec, int n) { +int uwsgi_regexp_match_ovec(uwsgi_pcre *pattern, const char *subject, int length, int *ovec, int n) { +#ifdef UWSGI_PCRE2 + int rc; + int i; + pcre2_match_data *match_data; + size_t *pcre2_ovec; + + match_data = pcre2_match_data_create_from_pattern(pattern, NULL); + rc = pcre2_match(pattern, (const unsigned char *)subject, length, 0, 0, match_data, NULL); + + /* + * Quoting PCRE{,2} spec, "The first pair of integers, ovector[0] + * and ovector[1], identify the portion of the subject string matched + * by the entire pattern. The next pair is used for the first capturing + * subpattern, and so on." Therefore, the ovector size is the number of + * capturing subpatterns (INFO_CAPTURECOUNT), from uwsgi_regexp_ovector(), + * as matching pairs, plus room for the first pair. + */ if (n > 0) { - return pcre_exec((const pcre *) pattern, (const pcre_extra *) pattern_extra, subject, length, 0, 0, ovec, (n + 1) * 3); + // copy pcre2 output vector to uwsgi output vector + pcre2_ovec = pcre2_get_ovector_pointer(match_data); + for (i=0;i<(n+1)*2;i++) { + ovec[i] = pcre2_ovec[i]; + } +#else + if (n > 0) { + return pcre_exec((const pcre *) pattern->p, (const pcre_extra *) pattern->extra, subject, length, 0, 0, ovec, PCRE_OVECTOR_BYTESIZE(n)); +#endif } - return pcre_exec((const pcre *) pattern, (const pcre_extra *) pattern_extra, subject, length, 0, 0, NULL, 0); + +#ifdef UWSGI_PCRE2 + pcre2_match_data_free(match_data); + + return rc; +#else + return pcre_exec((const pcre *) pattern->p, (const pcre_extra *) pattern->extra, subject, length, 0, 0, NULL, 0); +#endif } -int uwsgi_regexp_ovector(pcre * pattern, pcre_extra * pattern_extra) { +int uwsgi_regexp_ovector(const uwsgi_pcre *pattern) { int n; - - if (pcre_fullinfo((const pcre *) pattern, (const pcre_extra *) pattern_extra, PCRE_INFO_CAPTURECOUNT, &n)) +#ifdef UWSGI_PCRE2 + if (pcre2_pattern_info(pattern, PCRE2_INFO_CAPTURECOUNT, &n)) +#else + if (pcre_fullinfo((const pcre *) pattern->p, (const pcre_extra *) pattern->extra, PCRE_INFO_CAPTURECOUNT, &n)) +#endif return 0; return n; @@ -66,7 +134,7 @@ char *uwsgi_regexp_apply_ovec(char *src, int src_n, char *dst, int dst_n, int *o int dollar = 0; size_t dollars = n; - + for(i=0;ipattern, routes->pattern_extra, subject, subject_len, routes->ovector[wsgi_req->async_id], routes->ovn[wsgi_req->async_id]); + n = uwsgi_regexp_match_ovec(routes->pattern, subject, subject_len, routes->ovector[wsgi_req->async_id], routes->ovn[wsgi_req->async_id]); } else { int ret = routes->if_func(wsgi_req, routes); @@ -506,15 +506,15 @@ void uwsgi_fixup_routes(struct uwsgi_route *ur) { // fill them if needed... (this is an optimization for route with a static subject) if (ur->subject && ur->subject_len) { - if (uwsgi_regexp_build(ur->orig_route, &ur->pattern, &ur->pattern_extra)) { + if (uwsgi_regexp_build(ur->orig_route, &ur->pattern)) { exit(1); } int i; for(i=0;iovn[i] = uwsgi_regexp_ovector(ur->pattern, ur->pattern_extra); + ur->ovn[i] = uwsgi_regexp_ovector(ur->pattern); if (ur->ovn[i] > 0) { - ur->ovector[i] = uwsgi_calloc(sizeof(int) * (3 * (ur->ovn[i] + 1))); + ur->ovector[i] = uwsgi_calloc(sizeof(int) * PCRE_OVECTOR_BYTESIZE(ur->ovn[i])); } } } @@ -1484,38 +1484,47 @@ static int uwsgi_route_condition_regexp(struct wsgi_request *wsgi_req, struct uw ur->condition_ub[wsgi_req->async_id] = uwsgi_routing_translate(wsgi_req, ur, NULL, 0, ur->subject_str, semicolon - ur->subject_str); if (!ur->condition_ub[wsgi_req->async_id]) return -1; - pcre *pattern; - pcre_extra *pattern_extra; + uwsgi_pcre *pattern; char *re = uwsgi_concat2n(semicolon+1, ur->subject_str_len - ((semicolon+1) - ur->subject_str), "", 0); - if (uwsgi_regexp_build(re, &pattern, &pattern_extra)) { + if (uwsgi_regexp_build(re, &pattern)) { free(re); return -1; } free(re); // a condition has no initialized vectors, let's create them - ur->ovn[wsgi_req->async_id] = uwsgi_regexp_ovector(pattern, pattern_extra); + ur->ovn[wsgi_req->async_id] = uwsgi_regexp_ovector(pattern); if (ur->ovn[wsgi_req->async_id] > 0) { ur->ovector[wsgi_req->async_id] = uwsgi_calloc(sizeof(int) * (3 * (ur->ovn[wsgi_req->async_id] + 1))); } - if (uwsgi_regexp_match_ovec(pattern, pattern_extra, ur->condition_ub[wsgi_req->async_id]->buf, ur->condition_ub[wsgi_req->async_id]->pos, ur->ovector[wsgi_req->async_id], ur->ovn[wsgi_req->async_id] ) >= 0) { - pcre_free(pattern); + if (uwsgi_regexp_match_ovec(pattern, ur->condition_ub[wsgi_req->async_id]->buf, ur->condition_ub[wsgi_req->async_id]->pos, ur->ovector[wsgi_req->async_id], ur->ovn[wsgi_req->async_id] ) >= 0) { +#ifdef UWSGI_PCRE2 + pcre2_code_free(pattern); +#else + pcre_free(pattern->p); #ifdef PCRE_STUDY_JIT_COMPILE - pcre_free_study(pattern_extra); + pcre_free_study(pattern->extra); #else - pcre_free(pattern_extra); + pcre_free(pattern->extra); +#endif + free(pattern); #endif return 1; } - pcre_free(pattern); +#ifdef UWSGI_PCRE2 + pcre2_code_free(pattern); +#else + pcre_free(pattern->p); #ifdef PCRE_STUDY_JIT_COMPILE - pcre_free_study(pattern_extra); + pcre_free_study(pattern->extra); #else - pcre_free(pattern_extra); + pcre_free(pattern->extra); #endif - return 0; + free(pattern); +#endif + return 0; } static int uwsgi_route_condition_empty(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { diff --git a/core/ssl.c b/core/ssl.c index 1fafd659e9..18b1103642 100644 --- a/core/ssl.c +++ b/core/ssl.c @@ -145,10 +145,10 @@ static int uwsgi_sni_cb(SSL *ssl, int *ad, void *arg) { if (uwsgi.subscription_dotsplit) goto end; -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *url = uwsgi.sni_regexp; while(url) { - if (uwsgi_regexp_match(url->pattern, url->pattern_extra, (char *)servername, servername_len) >= 0) { + if (uwsgi_regexp_match(url->pattern, (char *)servername, servername_len) >= 0) { SSL_set_SSL_CTX(ssl, url->custom_ptr); return SSL_TLSEXT_ERR_OK; } @@ -628,7 +628,7 @@ void uwsgi_opt_sni(char *opt, char *value, void *foobar) { return; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) if (!strcmp(opt, "sni-regexp")) { struct uwsgi_regexp_list *url = uwsgi_regexp_new_list(&uwsgi.sni_regexp, v); url->custom_ptr = ctx; @@ -637,7 +637,7 @@ void uwsgi_opt_sni(char *opt, char *value, void *foobar) { #endif struct uwsgi_string_list *usl = uwsgi_string_new_list(&uwsgi.sni, v); usl->custom_ptr = ctx; -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) } #endif diff --git a/core/static.c b/core/static.c index 27f225e4bf..9764db291c 100644 --- a/core/static.c +++ b/core/static.c @@ -35,11 +35,11 @@ int uwsgi_static_want_gzip(struct wsgi_request *wsgi_req, char *filename, size_t usl = usl->next; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) // check for regexp struct uwsgi_regexp_list *url = uwsgi.static_gzip; while(url) { - if (uwsgi_regexp_match(url->pattern, url->pattern_extra, filename, *filename_len) >= 0) { + if (uwsgi_regexp_match(url->pattern, filename, *filename_len) >= 0) { goto gzip; } url = url->next; @@ -220,7 +220,7 @@ int uwsgi_add_expires_type(struct wsgi_request *wsgi_req, char *mime_type, int m return 0; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) int uwsgi_add_expires(struct wsgi_request *wsgi_req, char *filename, int filename_len, struct stat *st) { struct uwsgi_dyn_dict *udd = uwsgi.static_expires; @@ -229,7 +229,7 @@ int uwsgi_add_expires(struct wsgi_request *wsgi_req, char *filename, int filenam char expires[31]; while (udd) { - if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, filename, filename_len) >= 0) { + if (uwsgi_regexp_match(udd->pattern, filename, filename_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = uwsgi_http_date(now + delta, expires); if (size > 0) { @@ -242,7 +242,7 @@ int uwsgi_add_expires(struct wsgi_request *wsgi_req, char *filename, int filenam udd = uwsgi.static_expires_mtime; while (udd) { - if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, filename, filename_len) >= 0) { + if (uwsgi_regexp_match(udd->pattern, filename, filename_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = uwsgi_http_date(st->st_mtime + delta, expires); if (size > 0) { @@ -264,7 +264,7 @@ int uwsgi_add_expires_path_info(struct wsgi_request *wsgi_req, struct stat *st) char expires[31]; while (udd) { - if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->path_info, wsgi_req->path_info_len) >= 0) { + if (uwsgi_regexp_match(udd->pattern, wsgi_req->path_info, wsgi_req->path_info_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = uwsgi_http_date(now + delta, expires); if (size > 0) { @@ -277,7 +277,7 @@ int uwsgi_add_expires_path_info(struct wsgi_request *wsgi_req, struct stat *st) udd = uwsgi.static_expires_path_info_mtime; while (udd) { - if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->path_info, wsgi_req->path_info_len) >= 0) { + if (uwsgi_regexp_match(udd->pattern, wsgi_req->path_info, wsgi_req->path_info_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = uwsgi_http_date(st->st_mtime + delta, expires); if (size > 0) { @@ -299,7 +299,7 @@ int uwsgi_add_expires_uri(struct wsgi_request *wsgi_req, struct stat *st) { char expires[31]; while (udd) { - if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) { + if (uwsgi_regexp_match(udd->pattern, wsgi_req->uri, wsgi_req->uri_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = uwsgi_http_date(now + delta, expires); if (size > 0) { @@ -312,7 +312,7 @@ int uwsgi_add_expires_uri(struct wsgi_request *wsgi_req, struct stat *st) { udd = uwsgi.static_expires_uri_mtime; while (udd) { - if (uwsgi_regexp_match(udd->pattern, udd->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) { + if (uwsgi_regexp_match(udd->pattern, wsgi_req->uri, wsgi_req->uri_len) >= 0) { int delta = uwsgi_str_num(udd->value, udd->vallen); int size = uwsgi_http_date(st->st_mtime + delta, expires); if (size > 0) { @@ -511,7 +511,7 @@ int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, si if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) uwsgi_add_expires(wsgi_req, real_filename, real_filename_len, st); uwsgi_add_expires_path_info(wsgi_req, st); uwsgi_add_expires_uri(wsgi_req, st); diff --git a/core/utils.c b/core/utils.c index 420a343975..44c7a52f37 100644 --- a/core/utils.c +++ b/core/utils.c @@ -2340,7 +2340,7 @@ struct uwsgi_string_list *uwsgi_string_new_list(struct uwsgi_string_list **list, return uwsgi_string; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *uwsgi_regexp_custom_new_list(struct uwsgi_regexp_list **list, char *value, char *custom) { struct uwsgi_regexp_list *url = *list, *old_url; @@ -2359,7 +2359,7 @@ struct uwsgi_regexp_list *uwsgi_regexp_custom_new_list(struct uwsgi_regexp_list old_url->next = url; } - if (uwsgi_regexp_build(value, &url->pattern, &url->pattern_extra)) { + if (uwsgi_regexp_build(value, &url->pattern)) { exit(1); } url->next = NULL; @@ -2372,14 +2372,13 @@ struct uwsgi_regexp_list *uwsgi_regexp_custom_new_list(struct uwsgi_regexp_list int uwsgi_regexp_match_pattern(char *pattern, char *str) { - pcre *regexp; - pcre_extra *regexp_extra; + uwsgi_pcre *regexp; - if (uwsgi_regexp_build(pattern, ®exp, ®exp_extra)) + if (uwsgi_regexp_build(pattern, ®exp)) return 1; - return !uwsgi_regexp_match(regexp, regexp_extra, str, strlen(str)); -} + return !uwsgi_regexp_match(regexp, str, strlen(str)); +} #endif diff --git a/core/uwsgi.c b/core/uwsgi.c index 915528f7c7..abcf467ec4 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -109,7 +109,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"if-hostname", required_argument, 0, "(opt logic) check for hostname", uwsgi_opt_logic, (void *) uwsgi_logic_opt_if_hostname, UWSGI_OPT_IMMEDIATE}, {"if-not-hostname", required_argument, 0, "(opt logic) check for hostname", uwsgi_opt_logic, (void *) uwsgi_logic_opt_if_not_hostname, UWSGI_OPT_IMMEDIATE}, -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) {"if-hostname-match", required_argument, 0, "(opt logic) try to match hostname against a regular expression", uwsgi_opt_logic, (void *) uwsgi_logic_opt_if_hostname_match, UWSGI_OPT_IMMEDIATE}, {"if-not-hostname-match", required_argument, 0, "(opt logic) try to match hostname against a regular expression", uwsgi_opt_logic, (void *) uwsgi_logic_opt_if_not_hostname_match, UWSGI_OPT_IMMEDIATE}, #endif @@ -565,7 +565,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"ksm", optional_argument, 0, "enable Linux KSM", uwsgi_opt_set_int, &uwsgi.linux_ksm, 0}, #endif #endif -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) {"pcre-jit", no_argument, 0, "enable pcre jit (if available)", uwsgi_opt_pcre_jit, NULL, UWSGI_OPT_IMMEDIATE}, #endif {"never-swap", no_argument, 0, "lock all memory pages avoiding swapping", uwsgi_opt_true, &uwsgi.never_swap, 0}, @@ -701,7 +701,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"ssl-enable-sslv3", no_argument, 0, "enable SSLv3 (insecure)", uwsgi_opt_true, &uwsgi.sslv3, 0}, {"ssl-enable-tlsv1", no_argument, 0, "enable TLSv1 (insecure)", uwsgi_opt_true, &uwsgi.tlsv1, 0}, {"ssl-option", required_argument, 0, "set a raw ssl option (numeric value)", uwsgi_opt_add_string_list, &uwsgi.ssl_options, 0}, -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) {"sni-regexp", required_argument, 0, "add an SNI-governed SSL context (the key is a regexp)", uwsgi_opt_sni, NULL, 0}, #endif {"ssl-tmp-dir", required_argument, 0, "store ssl-related temp files in the specified directory", uwsgi_opt_set_str, &uwsgi.ssl_tmp_dir, 0}, @@ -743,7 +743,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"worker-log-req-encoder", required_argument, 0, "add an item in the log req encoder chain", uwsgi_opt_add_string_list, &uwsgi.requested_log_req_encoders, 0}, -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) {"log-drain", required_argument, 0, "drain (do not show) log lines matching the specified regexp", uwsgi_opt_add_regexp_list, &uwsgi.log_drain_rules, UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, {"log-filter", required_argument, 0, "show only log lines matching the specified regexp", uwsgi_opt_add_regexp_list, &uwsgi.log_filter_rules, UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, {"log-route", required_argument, 0, "log to the specified named logger if regexp applied on logline matches", uwsgi_opt_add_regexp_custom_list, &uwsgi.log_route, UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, @@ -764,7 +764,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"alarm-lq", required_argument, 0, "raise the specified alarm when the socket backlog queue is full", uwsgi_opt_add_string_list, &uwsgi.alarm_backlog, UWSGI_OPT_MASTER}, {"alarm-listen-queue", required_argument, 0, "raise the specified alarm when the socket backlog queue is full", uwsgi_opt_add_string_list, &uwsgi.alarm_backlog, UWSGI_OPT_MASTER}, {"listen-queue-alarm", required_argument, 0, "raise the specified alarm when the socket backlog queue is full", uwsgi_opt_add_string_list, &uwsgi.alarm_backlog, UWSGI_OPT_MASTER}, -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) {"log-alarm", required_argument, 0, "raise the specified alarm when a log line matches the specified regexp, syntax: [,alarm...] ", uwsgi_opt_add_string_list, &uwsgi.alarm_logs_list, UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, {"alarm-log", required_argument, 0, "raise the specified alarm when a log line matches the specified regexp, syntax: [,alarm...] ", uwsgi_opt_add_string_list, &uwsgi.alarm_logs_list, UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, {"not-log-alarm", required_argument, 0, "skip the specified alarm when a log line matches the specified regexp, syntax: [,alarm...] ", uwsgi_opt_add_string_list_custom, &uwsgi.alarm_logs_list, UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, @@ -945,7 +945,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"static-expires-type", required_argument, 0, "set the Expires header based on content type", uwsgi_opt_add_dyn_dict, &uwsgi.static_expires_type, UWSGI_OPT_MIME}, {"static-expires-type-mtime", required_argument, 0, "set the Expires header based on content type and file mtime", uwsgi_opt_add_dyn_dict, &uwsgi.static_expires_type_mtime, UWSGI_OPT_MIME}, -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) {"static-expires", required_argument, 0, "set the Expires header based on filename regexp", uwsgi_opt_add_regexp_dyn_dict, &uwsgi.static_expires, UWSGI_OPT_MIME}, {"static-expires-mtime", required_argument, 0, "set the Expires header based on filename regexp and file mtime", uwsgi_opt_add_regexp_dyn_dict, &uwsgi.static_expires_mtime, UWSGI_OPT_MIME}, @@ -2500,7 +2500,7 @@ void uwsgi_setup(int argc, char *argv[], char *envp[]) { #endif uwsgi_log_initial("clock source: %s\n", uwsgi.clock->name); -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) if (uwsgi.pcre_jit) { uwsgi_log_initial("pcre jit enabled\n"); } @@ -4302,7 +4302,7 @@ void uwsgi_opt_add_string_list_custom(char *opt, char *value, void *list) { usl->custom = 1; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) void uwsgi_opt_add_regexp_list(char *opt, char *value, void *list) { struct uwsgi_regexp_list **ptr = (struct uwsgi_regexp_list **) list; uwsgi_regexp_new_list(ptr, value); @@ -4568,7 +4568,7 @@ void uwsgi_opt_add_dyn_dict(char *opt, char *value, void *dict) { } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) void uwsgi_opt_add_regexp_dyn_dict(char *opt, char *value, void *dict) { char *space = strchr(value, ' '); @@ -4583,7 +4583,7 @@ void uwsgi_opt_add_regexp_dyn_dict(char *opt, char *value, void *dict) { char *regexp = uwsgi_concat2n(value, space - value, "", 0); - if (uwsgi_regexp_build(regexp, &new_udd->pattern, &new_udd->pattern_extra)) { + if (uwsgi_regexp_build(regexp, &new_udd->pattern)) { exit(1); } diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index d9b615bac9..375fa6940a 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -16,7 +16,7 @@ struct uwsgi_php { struct uwsgi_string_list *index; struct uwsgi_string_list *set; struct uwsgi_string_list *append_config; -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *app_bypass; #endif struct uwsgi_string_list *vars; @@ -70,7 +70,7 @@ struct uwsgi_option uwsgi_php_options[] = { {"php-fallback", required_argument, 0, "run the specified php script when the requested one does not exist", uwsgi_opt_set_str, &uphp.fallback, 0}, {"php-fallback2", required_argument, 0, "run the specified php script relative to the document root when the requested one does not exist", uwsgi_opt_set_str, &uphp.fallback2, 0}, {"php-fallback-qs", required_argument, 0, "php-fallback with QUERY_STRING set", uwsgi_opt_set_str, &uphp.fallback_qs, 0}, -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) {"php-app-bypass", required_argument, 0, "if the regexp matches the uri the --php-app is bypassed", uwsgi_opt_add_regexp_list, &uphp.app_bypass, 0}, #endif {"php-var", required_argument, 0, "add/overwrite a CGI variable at each request", uwsgi_opt_add_string_list, &uphp.vars, 0}, @@ -874,10 +874,14 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { wsgi_req->document_root_len = strlen(wsgi_req->document_root); if (uphp.app) { -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *bypass = uphp.app_bypass; while (bypass) { +#ifdef UWSGI_PCRE2 + if (uwsgi_regexp_match(bypass->pattern, wsgi_req->uri, wsgi_req->uri_len) >= 0) { +#else if (uwsgi_regexp_match(bypass->pattern, bypass->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) { +#endif goto oldstyle; } bypass = bypass->next; @@ -914,7 +918,7 @@ int uwsgi_php_request(struct wsgi_request *wsgi_req) { goto secure2; } -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) oldstyle: #endif diff --git a/uwsgi.h b/uwsgi.h index 69f9fbf8ab..c512cc74ee 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -449,8 +449,26 @@ struct uwsgi_lock_ops { #define uwsgi_wait_read_req(x) uwsgi.wait_read_hook(x->fd, uwsgi.socket_timeout) ; x->switches++ #define uwsgi_wait_write_req(x) uwsgi.wait_write_hook(x->fd, uwsgi.socket_timeout) ; x->switches++ -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) +#ifdef UWSGI_PCRE2 + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include +#define PCRE_OVECTOR_BYTESIZE(n) (n+1)*2 + +typedef pcre2_code uwsgi_pcre; + +#else + #include +#define PCRE_OVECTOR_BYTESIZE(n) (n+1)*3 + +typedef struct { + pcre *p; + pcre_extra *extra; +} uwsgi_pcre; + +#endif #endif struct uwsgi_dyn_dict { @@ -466,9 +484,8 @@ struct uwsgi_dyn_dict { struct uwsgi_dyn_dict *prev; struct uwsgi_dyn_dict *next; -#ifdef UWSGI_PCRE - pcre *pattern; - pcre_extra *pattern_extra; +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) + uwsgi_pcre *pattern; #endif }; @@ -479,11 +496,10 @@ struct uwsgi_hook { struct uwsgi_hook *next; }; -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list { - pcre *pattern; - pcre_extra *pattern_extra; + uwsgi_pcre *pattern; uint64_t custom; char *custom_str; @@ -1100,11 +1116,11 @@ struct uwsgi_plugin { void (*post_uwsgi_fork) (int); }; -#ifdef UWSGI_PCRE -int uwsgi_regexp_build(char *, pcre **, pcre_extra **); -int uwsgi_regexp_match(pcre *, pcre_extra *, char *, int); -int uwsgi_regexp_match_ovec(pcre *, pcre_extra *, char *, int, int *, int); -int uwsgi_regexp_ovector(pcre *, pcre_extra *); +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) +int uwsgi_regexp_build(char *, uwsgi_pcre **); +int uwsgi_regexp_match(uwsgi_pcre *, const char *, int); +int uwsgi_regexp_match_ovec(uwsgi_pcre *, const char *, int, int *, int); +int uwsgi_regexp_ovector(const uwsgi_pcre *); char *uwsgi_regexp_apply_ovec(char *, int, char *, int, int *, int); int uwsgi_regexp_match_pattern(char *pattern, char *str); @@ -1195,8 +1211,7 @@ struct uwsgi_spooler { struct uwsgi_route { - pcre *pattern; - pcre_extra *pattern_extra; + uwsgi_pcre *pattern; char *orig_route; @@ -1305,15 +1320,14 @@ struct uwsgi_alarm_fd { struct uwsgi_alarm_fd *uwsgi_add_alarm_fd(int, char *, size_t, char *, size_t); -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_alarm_ll { struct uwsgi_alarm_instance *alarm; struct uwsgi_alarm_ll *next; }; struct uwsgi_alarm_log { - pcre *pattern; - pcre_extra *pattern_extra; + uwsgi_pcre *pattern; int negate; struct uwsgi_alarm_ll *alarms; struct uwsgi_alarm_log *next; @@ -2258,7 +2272,7 @@ struct uwsgi_server { struct uwsgi_string_list *requested_log_encoders; struct uwsgi_string_list *requested_log_req_encoders; -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) int pcre_jit; struct uwsgi_regexp_list *log_drain_rules; struct uwsgi_regexp_list *log_filter_rules; @@ -2340,7 +2354,7 @@ struct uwsgi_server { int static_gzip_all; struct uwsgi_string_list *static_gzip_dir; struct uwsgi_string_list *static_gzip_ext; -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *static_gzip; #endif @@ -2745,7 +2759,7 @@ struct uwsgi_server { int ssl_sessions_timeout; struct uwsgi_cache *ssl_sessions_cache; char *ssl_tmp_dir; -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *sni_regexp; #endif struct uwsgi_string_list *sni; @@ -3667,7 +3681,7 @@ void uwsgi_close_all_unshared_sockets(void); void uwsgi_shutdown_all_sockets(void); struct uwsgi_string_list *uwsgi_string_new_list(struct uwsgi_string_list **, char *); -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) struct uwsgi_regexp_list *uwsgi_regexp_custom_new_list(struct uwsgi_regexp_list **, char *, char *); #define uwsgi_regexp_new_list(x, y) uwsgi_regexp_custom_new_list(x, y, NULL); #endif @@ -3941,7 +3955,7 @@ void uwsgi_opt_add_addr_list(char *, char *, void *); void uwsgi_opt_add_string_list_custom(char *, char *, void *); void uwsgi_opt_add_dyn_dict(char *, char *, void *); void uwsgi_opt_binary_append_data(char *, char *, void *); -#ifdef UWSGI_PCRE +#if defined(UWSGI_PCRE) || defined(UWSGI_PCRE2) void uwsgi_opt_pcre_jit(char *, char *, void *); void uwsgi_opt_add_regexp_dyn_dict(char *, char *, void *); void uwsgi_opt_add_regexp_list(char *, char *, void *); diff --git a/uwsgiconfig.py b/uwsgiconfig.py index e68ca4ac79..f9bc612b79 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1099,30 +1099,29 @@ def get_gcll(self): has_pcre = False - # re-enable after pcre fix - if self.get('pcre'): - if self.get('pcre') == 'auto': - pcreconf = spcall('pcre-config --libs') - if pcreconf: - self.libs.append(pcreconf) - pcreconf = spcall("pcre-config --cflags") - self.cflags.append(pcreconf) - self.gcc_list.append('core/regexp') - self.cflags.append("-DUWSGI_PCRE") - has_pcre = True - + required_pcre = self.get('pcre') + if required_pcre: + pcre_libs = spcall('pcre2-config --libs8') + if pcre_libs: + pcre_cflags = spcall("pcre2-config --cflags") + pcre_define = "-DUWSGI_PCRE2" else: - pcreconf = spcall('pcre-config --libs') - if pcreconf is None: - print("*** libpcre headers unavailable. uWSGI build is interrupted. You have to install pcre development package or disable pcre") - sys.exit(1) - else: - self.libs.append(pcreconf) - pcreconf = spcall("pcre-config --cflags") - self.cflags.append(pcreconf) - self.gcc_list.append('core/regexp') - self.cflags.append("-DUWSGI_PCRE") - has_pcre = True + pcre_libs = spcall('pcre-config --libs') + pcre_cflags = spcall("pcre-config --cflags") + pcre_define = "-DUWSGI_PCRE" + else: + pcre_libs = None + + if required_pcre: + if required_pcre != 'auto' and pcre_libs is None: + print("*** libpcre headers unavailable. uWSGI build is interrupted. You have to install pcre development package or disable pcre") + sys.exit(1) + + self.libs.append(pcre_libs) + self.cflags.append(pcre_cflags) + self.gcc_list.append('core/regexp') + self.cflags.append(pcre_define) + has_pcre = True if has_pcre: report['pcre'] = True From b505adf5698f6947771100512fe527b72030071f Mon Sep 17 00:00:00 2001 From: Wynn Wilkes Date: Thu, 14 Mar 2024 08:47:03 -0600 Subject: [PATCH 167/207] Allow the valgrind generator script to run with a different python version - You can now pass a different path for a different python installation. --- valgrind/valgrind-generate-sups.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/valgrind/valgrind-generate-sups.sh b/valgrind/valgrind-generate-sups.sh index 37bfe3f883..3e28cb37bb 100755 --- a/valgrind/valgrind-generate-sups.sh +++ b/valgrind/valgrind-generate-sups.sh @@ -1,5 +1,7 @@ #!/bin/bash +pydir=${1:-/usr} + gensup() { for SUP in Cond Free Leak Overlap Addr1 Addr2 Addr4 Addr8 Addr16 Value1 Value2 Value4 Value8 Value16 ; do @@ -16,10 +18,10 @@ gensup() { while read SO ; do gensup libpython "$SO" -done < <(find /usr/lib*/ -type f -name libpython*) +done < <(find ${pydir}/lib*/ -type f -name libpython*) while read SO ; do gensup python "$SO" -done < <(find /usr/lib*/python*/ -type f -name \*.so) +done < <(find ${pydir}/lib*/python*/ -type f -name \*.so) From f321efc8b64fbd90c575bbfa5367f5ab599e08a3 Mon Sep 17 00:00:00 2001 From: Wynn Wilkes Date: Thu, 14 Mar 2024 08:48:19 -0600 Subject: [PATCH 168/207] Fix a potential error with not releasing the gil in uwsgi_python_rpc - If this call fails, we need to release the gil before returning as we've obtained it. --- plugins/python/python_plugin.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index de92f34c2f..6c5be6c7ff 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -1778,8 +1778,10 @@ uint64_t uwsgi_python_rpc(void *func, uint8_t argc, char **argv, uint16_t argvs[ PyObject *pyargs = PyTuple_New(argc); PyObject *ret; - if (!pyargs) + if (!pyargs) { + UWSGI_RELEASE_GIL; return 0; + } for (i = 0; i < argc; i++) { PyTuple_SetItem(pyargs, i, PyString_FromStringAndSize(argv[i], argvs[i])); From abfa00a43620a553a0b163b956641a0a0252e984 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Fri, 8 Mar 2024 15:28:35 +0000 Subject: [PATCH 169/207] core/uwsgi: graceful stop worker when max_requests/reload_on_* worker stops when reached max_requests or reload_on_*. https://github.com/unbit/uwsgi/blob/39f3ade88c88693f643e70ecf6c36f9b375f00a2/core/utils.c#L1216-L1251 `goodbye_cruel_world()` is not graceful. It caused `atexit` not called. If atexit stops daemon threads, worker won't stop until killed from master. Using a reproducer similar to tests/threads_atexit.py: *** uWSGI is running in multiple interpreter mode *** spawned uWSGI master process (pid: 93920) spawned uWSGI worker 1 (pid: 93921, cores: 80) ...The work of process 93921 is done (max requests reached (641 >= 20)). Seeya! worker 1 killed successfully (pid: 93921) Respawned uWSGI worker 1 (new pid: 94019) ...The work of process 94019 is done (max requests reached (721 >= 20)). Seeya! worker 1 killed successfully (pid: 94019) Respawned uWSGI worker 1 (new pid: 94099) ...The work of process 94099 is done (max requests reached (721 >= 20)). Seeya! worker 1 killed successfully (pid: 94099) Respawned uWSGI worker 1 (new pid: 94179) ...The work of process 94179 is done (max requests reached (721 >= 20)). Seeya! worker 1 killed successfully (pid: 94179) Respawned uWSGI worker 1 (new pid: 94260) ...The work of process 94260 is done (max requests reached (721 >= 20)). Seeya! worker 1 killed successfully (pid: 94260) Respawned uWSGI worker 1 (new pid: 94340) atexit is not called. *** uWSGI is running in multiple interpreter mode *** spawned uWSGI master process (pid: 94781) spawned uWSGI worker 1 (pid: 94782, cores: 80) ...The work of process 94782 is done (max requests reached (402 >= 20)). Seeya! on_exit: uwsgi.worker_id()=1 worker 1 killed successfully (pid: 94782) Respawned uWSGI worker 1 (new pid: 94880) ...The work of process 94880 is done (max requests reached (721 >= 20)). Seeya! on_exit: uwsgi.worker_id()=1 worker 1 killed successfully (pid: 94880) Respawned uWSGI worker 1 (new pid: 94960) ...The work of process 94960 is done (max requests reached (721 >= 20)). Seeya! on_exit: uwsgi.worker_id()=1 worker 1 killed successfully (pid: 94960) Respawned uWSGI worker 1 (new pid: 95040) ...The work of process 95040 is done (max requests reached (721 >= 20)). Seeya! on_exit: uwsgi.worker_id()=1 worker 1 killed successfully (pid: 95040) Respawned uWSGI worker 1 (new pid: 95120) ...The work of process 95120 is done (max requests reached (721 >= 20)). Seeya! on_exit: uwsgi.worker_id()=1 worker 1 killed successfully (pid: 95120) Respawned uWSGI worker 1 (new pid: 95200) atexit is called Related issue: https://github.com/open-telemetry/opentelemetry-python/issues/3640 --- core/loop.c | 3 +++ core/uwsgi.c | 21 +++++++++++++------- tests/threads_atexit.py | 44 +++++++++++++++++++++++++++++++++++++++++ tests/threads_heavy.py | 29 +++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 tests/threads_atexit.py create mode 100644 tests/threads_heavy.py diff --git a/core/loop.c b/core/loop.c index 2c856826e2..cd3cf61263 100644 --- a/core/loop.c +++ b/core/loop.c @@ -60,6 +60,9 @@ void *uwsgi_get_loop(char *name) { void simple_loop() { uwsgi_loop_cores_run(simple_loop_run); + // Other threads may still run. Make sure they will stop. + uwsgi.workers[uwsgi.mywid].manage_next_request = 0; + if (uwsgi.workers[uwsgi.mywid].shutdown_sockets) uwsgi_shutdown_all_sockets(); } diff --git a/core/uwsgi.c b/core/uwsgi.c index abcf467ec4..dca9c6896a 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1255,6 +1255,10 @@ void wait_for_threads() { if (ret) { uwsgi_log("pthread_join() = %d\n", ret); } + else { + // uwsgi_worker_is_busy() should not consider this thread as busy. + uwsgi.workers[uwsgi.mywid].cores[i].in_request = 0; + } } } @@ -1319,14 +1323,12 @@ void end_me(int signum) { } static void simple_goodbye_cruel_world(const char *reason) { - - if (uwsgi.threads > 1 && !uwsgi_instance_is_dying) { - wait_for_threads(); - } - + int prev = uwsgi.workers[uwsgi.mywid].manage_next_request; uwsgi.workers[uwsgi.mywid].manage_next_request = 0; - uwsgi_log("...The work of process %d is done (%s). Seeya!\n", getpid(), (reason != NULL ? reason : "no reason given")); - exit(0); + if (prev) { + // Avoid showing same message from all threads. + uwsgi_log("...The work of process %d is done (%s). Seeya!\n", getpid(), (reason != NULL ? reason : "no reason given")); + } } void goodbye_cruel_world(const char *reason_fmt, ...) { @@ -3721,6 +3723,11 @@ void uwsgi_ignition() { } } + // main thread waits other threads. + if (uwsgi.threads > 1) { + wait_for_threads(); + } + // end of the process... end_me(0); } diff --git a/tests/threads_atexit.py b/tests/threads_atexit.py new file mode 100644 index 0000000000..efb216040a --- /dev/null +++ b/tests/threads_atexit.py @@ -0,0 +1,44 @@ +# https://github.com/unbit/uwsgi/pull/2615 +# atexit should be called when reached max-requests. +# +# Start this app: +# +# $ ./uwsgi --http-socket :8000 --master -L --wsgi-file=tests/threads_atexit.py \ +# --workers 1 --threads 32 --max-requests 40 --min-worker-lifetime 6 --lazy-apps +# +# Access to this app with hey[1]: +# +# # Do http access for 5 minutes with 32 concurrency +# $ ./hey -c 32 -z 5m 'http://127.0.0.1:8000/' +# +# Search how many stamp files: +# +# $ ls uwsgi_worker*.txt | wc -l +# 39 # should be 0 +# +# [1] https://github.com/rakyll/hey + +import atexit +import os +import sys +import time + + +pid = os.getpid() +stamp_file = f"./uwsgi_worker{pid}.txt" + + +with open(stamp_file, "w") as f: + print(time.time(), file=f) + + +@atexit.register +def on_finish_worker(): + print(f"removing {stamp_file}", file=sys.stderr) + os.remove(stamp_file) + + +def application(env, start_response): + time.sleep(1) + start_response('200 OK', [('Content-Type', 'text/html')]) + return [b"Hello World"] diff --git a/tests/threads_heavy.py b/tests/threads_heavy.py new file mode 100644 index 0000000000..632cc68e76 --- /dev/null +++ b/tests/threads_heavy.py @@ -0,0 +1,29 @@ +# https://github.com/unbit/uwsgi/pull/2615 +# CPU heavy application in multi threaded uWSGI doesn't shutdown gracefully. +# +# $ ./uwsgi \ +# --wsgi-file=threads_heavy.py --master --http-socket=:8000 \ +# --workers=4 --threads=8 --max-requests=20 --min-worker-lifetime=6 -L \ +# --worker-reload-mercy=20 2>&1 | tee uwsgi.log +# +# $ hey -c 16 -z 3m 'http://127.0.0.1:8000/' +# +# $ grep MERCY uwsgi.log +# Tue Mar 19 14:01:59 2024 - worker 1 (pid: 62113) is taking too much time to die...NO MERCY !!! +# Tue Mar 19 14:02:23 2024 - worker 2 (pid: 62218) is taking too much time to die...NO MERCY !!! +# ... +# +# This was caused by pthread_cancel() is called from non-main thread. + +def fibonacci(n): + if n <= 1: + return n + return fibonacci(n - 1) + fibonacci(n - 2) + + +def application(env, start_response): + start_response('200 OK', [('Content-Type', 'text/html')]) + n = 24 + r = fibonacci(n) + s = f"F({n}) = {r}" + return [s.encode()] From 7610f523b4f4dde65e30d246d2a37cc7709d755b Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 19 Mar 2024 14:39:14 +0000 Subject: [PATCH 170/207] core/uwsgi: stop using pthread_cancel() When working to reproduce #2615 I saw many strange "defunct" (zombie) workers. The master called waitpid(-1, ...) but it return 0 even there are some zombies. Finally, master sends KILL signal (MERCY) and worker is restarted. I believe this strange zombie was born from pthread_cancel. Subthreads calls pthread_cancel() for main thread and it cause strange process. pthread_cancel() is very hard to use and debug. I can not even attach the strange zombie with gdb --pid. I think it is not maintainable. In the end we can remove six_feet_under_lock and make wait_for_threads() static. --- core/loop.c | 3 --- core/utils.c | 21 --------------------- core/uwsgi.c | 46 ++++++---------------------------------------- uwsgi.h | 1 - 4 files changed, 6 insertions(+), 65 deletions(-) diff --git a/core/loop.c b/core/loop.c index cd3cf61263..8c64279bc1 100644 --- a/core/loop.c +++ b/core/loop.c @@ -81,9 +81,6 @@ void uwsgi_setup_thread_req(long core_id, struct wsgi_request *wsgi_req) { int i; sigset_t smask; - - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &i); - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &i); pthread_setspecific(uwsgi.tur_key, (void *) wsgi_req); if (core_id > 0) { diff --git a/core/utils.c b/core/utils.c index 44c7a52f37..c944f64781 100644 --- a/core/utils.c +++ b/core/utils.c @@ -1034,12 +1034,6 @@ void uwsgi_destroy_request(struct wsgi_request *wsgi_req) { close_and_free_request(wsgi_req); - int foo; - if (uwsgi.threads > 1) { - // now the thread can die... - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &foo); - } - // reset for avoiding following requests to fail on non-uwsgi protocols // thanks Marko Tiikkaja for catching it wsgi_req->uh->_pktsize = 0; @@ -1131,11 +1125,6 @@ void uwsgi_close_request(struct wsgi_request *wsgi_req) { func(wsgi_req); } - if (uwsgi.threads > 1) { - // now the thread can die... - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &tmp_id); - } - // leave harakiri mode if (uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].harakiri > 0) { set_harakiri(wsgi_req, 0); @@ -1583,18 +1572,12 @@ int wsgi_req_accept(int queue, struct wsgi_request *wsgi_req) { } } - // kill the thread after the request completion - if (uwsgi.threads > 1) - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ret); - if (uwsgi.signal_socket > -1 && (interesting_fd == uwsgi.signal_socket || interesting_fd == uwsgi.my_signal_socket)) { thunder_unlock; uwsgi_receive_signal(wsgi_req, interesting_fd, "worker", uwsgi.mywid); - if (uwsgi.threads > 1) - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ret); return -1; } @@ -1605,8 +1588,6 @@ int wsgi_req_accept(int queue, struct wsgi_request *wsgi_req) { wsgi_req->fd = wsgi_req->socket->proto_accept(wsgi_req, interesting_fd); thunder_unlock; if (wsgi_req->fd < 0) { - if (uwsgi.threads > 1) - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ret); return -1; } @@ -1621,8 +1602,6 @@ int wsgi_req_accept(int queue, struct wsgi_request *wsgi_req) { } thunder_unlock; - if (uwsgi.threads > 1) - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ret); return -1; } diff --git a/core/uwsgi.c b/core/uwsgi.c index dca9c6896a..1bf257f4ef 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1226,30 +1226,17 @@ void warn_pipe() { } } -// in threading mode we need to use the cancel pthread subsystem -void wait_for_threads() { +// This function is called from signal handler or main thread to wait worker threads. +// `uwsgi.workers[uwsgi.mywid].manage_next_request` should be set to 0 to stop worker threads. +static void wait_for_threads() { int i, ret; - // on some platform thread cancellation is REALLY flaky + // This option was added because we used pthread_cancel(). + // thread cancellation is REALLY flaky if (uwsgi.no_threads_wait) return; - int sudden_death = 0; - - pthread_mutex_lock(&uwsgi.six_feet_under_lock); - for (i = 1; i < uwsgi.threads; i++) { - if (!pthread_equal(uwsgi.workers[uwsgi.mywid].cores[i].thread_id, pthread_self())) { - if (pthread_cancel(uwsgi.workers[uwsgi.mywid].cores[i].thread_id)) { - uwsgi_error("pthread_cancel()\n"); - sudden_death = 1; - } - } - } - - if (sudden_death) - goto end; - // wait for thread termination - for (i = 1; i < uwsgi.threads; i++) { + for (i = 0; i < uwsgi.threads; i++) { if (!pthread_equal(uwsgi.workers[uwsgi.mywid].cores[i].thread_id, pthread_self())) { ret = pthread_join(uwsgi.workers[uwsgi.mywid].cores[i].thread_id, NULL); if (ret) { @@ -1261,26 +1248,6 @@ void wait_for_threads() { } } } - - // cancel initial thread last since after pthread_cancel() and - // pthread_join() is called on it, the whole process will appear to be - // a zombie. although it won't eliminate process zombie time, but it - // should minimize it. - if (!pthread_equal(uwsgi.workers[uwsgi.mywid].cores[0].thread_id, pthread_self())) { - if (pthread_cancel(uwsgi.workers[uwsgi.mywid].cores[0].thread_id)) { - uwsgi_error("pthread_cancel() on initial thread\n"); - goto end; - } - - ret = pthread_join(uwsgi.workers[uwsgi.mywid].cores[0].thread_id, NULL); - if (ret) { - uwsgi_log("pthread_join() = %d on initial thread\n", ret); - } - } - -end: - - pthread_mutex_unlock(&uwsgi.six_feet_under_lock); } @@ -3641,7 +3608,6 @@ void uwsgi_worker_run() { if (uwsgi.cores > 1) { uwsgi.workers[uwsgi.mywid].cores[0].thread_id = pthread_self(); - pthread_mutex_init(&uwsgi.six_feet_under_lock, NULL); } uwsgi_ignition(); diff --git a/uwsgi.h b/uwsgi.h index c512cc74ee..39c3282db1 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2526,7 +2526,6 @@ struct uwsgi_server { // avoid thundering herd in threaded modes pthread_mutex_t thunder_mutex; - pthread_mutex_t six_feet_under_lock; pthread_mutex_t lock_static; int use_thunder_lock; From 7e5b4e65805afb69944041fd00b3d7a667a2b013 Mon Sep 17 00:00:00 2001 From: iKlotho Date: Mon, 15 Apr 2024 19:08:32 +0300 Subject: [PATCH 171/207] Check if the pcre_libs exists --- uwsgiconfig.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index f9bc612b79..032bd884f6 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1117,11 +1117,12 @@ def get_gcll(self): print("*** libpcre headers unavailable. uWSGI build is interrupted. You have to install pcre development package or disable pcre") sys.exit(1) - self.libs.append(pcre_libs) - self.cflags.append(pcre_cflags) - self.gcc_list.append('core/regexp') - self.cflags.append(pcre_define) - has_pcre = True + if pcre_libs: + self.libs.append(pcre_libs) + self.cflags.append(pcre_cflags) + self.gcc_list.append('core/regexp') + self.cflags.append(pcre_define) + has_pcre = True if has_pcre: report['pcre'] = True From bb4ab2924a59762c58e9c89a85f0b206f4d06188 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Mon, 29 Apr 2024 22:53:52 +0200 Subject: [PATCH 172/207] fix uwsgi_regexp_match() with pcre2 (Fix #2634) pcre2_match() with no match_data structure does not work --- core/regexp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/regexp.c b/core/regexp.c index 74bb77751e..3c3e154296 100644 --- a/core/regexp.c +++ b/core/regexp.c @@ -69,7 +69,7 @@ int uwsgi_regexp_build(char *re, uwsgi_pcre ** pattern) { int uwsgi_regexp_match(uwsgi_pcre *pattern, const char *subject, int length) { #ifdef UWSGI_PCRE2 - return pcre2_match(pattern, (const unsigned char *)subject, length, 0, 0, NULL, NULL); + return uwsgi_regexp_match_ovec(pattern, subject, length, NULL, 0); #else return pcre_exec((const pcre *) pattern->p, (const pcre_extra *) pattern->extra, subject, length, 0, 0, NULL, 0); #endif From f11e103e707023896047683f1b214e87f374091a Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Mon, 29 Apr 2024 23:08:01 +0200 Subject: [PATCH 173/207] add unittest for uwsgi_regexp_match() --- Makefile | 3 +- {check => unittest}/Makefile | 5 +- {check => unittest}/check_core.c | 0 unittest/check_regexp.c | 86 ++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 3 deletions(-) rename {check => unittest}/Makefile (84%) rename {check => unittest}/check_core.c (100%) create mode 100644 unittest/check_regexp.c diff --git a/Makefile b/Makefile index 430c7ce807..b576335a31 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ all: clean: $(PYTHON) uwsgiconfig.py --clean + cd unittest && make clean check: $(PYTHON) uwsgiconfig.py --check @@ -14,7 +15,7 @@ plugin.%: tests: $(PYTHON) uwsgiconfig.py --build unittest - cd check && make && make test + cd unittest && make && make test %: $(PYTHON) uwsgiconfig.py --build $@ diff --git a/check/Makefile b/unittest/Makefile similarity index 84% rename from check/Makefile rename to unittest/Makefile index a9a6555769..f0c22acefa 100644 --- a/check/Makefile +++ b/unittest/Makefile @@ -1,5 +1,6 @@ CFLAGS = $(shell pkg-config --cflags check) +CFLAGS += -DUWSGI_PCRE2 LDFLAGS = $(shell pkg-config --libs check) LDFLAGS += -ldl -lz LDFLAGS += $(shell xml2-config --libs) @@ -13,11 +14,11 @@ ifeq ($(UNAME_S),Linux) endif -objects = check_core +objects = check_core check_regexp all: $(objects) -$(objects): %: %.c +$(objects): %: %.c ../libuwsgi.a $(CC) $(CFLAGS) -o $@ $< ../libuwsgi.a $(LDFLAGS) test: diff --git a/check/check_core.c b/unittest/check_core.c similarity index 100% rename from check/check_core.c rename to unittest/check_core.c diff --git a/unittest/check_regexp.c b/unittest/check_regexp.c new file mode 100644 index 0000000000..b2025bfb2f --- /dev/null +++ b/unittest/check_regexp.c @@ -0,0 +1,86 @@ +#include +#include "../uwsgi.h" + + +START_TEST(test_uwsgi_regexp_match) +{ + int result; + uwsgi_pcre *pattern_all; + uwsgi_pcre *pattern; + + result = uwsgi_regexp_build(".*", &pattern_all); + ck_assert(result == 0); + + result = uwsgi_regexp_match(pattern_all, "/fooba", 6); + ck_assert(result >= 0); + + result = uwsgi_regexp_build("/foobar/.*", &pattern); + ck_assert(result == 0); + + result = uwsgi_regexp_match(pattern, "/fooba", 6); + ck_assert(result < 0); + + result = uwsgi_regexp_match(pattern, "/foobar/baz", 11); + ck_assert(result >= 0); + + pcre2_code_free(pattern_all); + pcre2_code_free(pattern); +} +END_TEST + +START_TEST(test_uwsgi_regexp_match_ovec) +{ + int result; + uwsgi_pcre *pattern; + int *ovec = calloc((2+1)*2, sizeof(int)); + char buf[20], sub[20]; + + result = uwsgi_regexp_build("^/foo/(.*)\\.jpg\\?([0-9]{2})", &pattern); + ck_assert(result == 0); + result = uwsgi_regexp_ovector(pattern); + ck_assert(result == 2); + + result = uwsgi_regexp_match_ovec(pattern, "/fooba", 6, ovec, 2); + ck_assert(result < 0); + + strcpy(buf, "/foo/bar.jpg?422"); + result = uwsgi_regexp_match_ovec(pattern, buf, strlen(buf), ovec, 2); + ck_assert(result >= 0); + strncpy(sub, buf+ovec[0], ovec[1]-ovec[0]); + sub[ovec[1]-ovec[0]] = '\0'; + ck_assert_str_eq(sub, "/foo/bar.jpg?42"); + strncpy(sub, buf+ovec[2], ovec[3]-ovec[2]); + sub[ovec[3]-ovec[2]] = '\0'; + ck_assert_str_eq(sub, "bar"); + strncpy(sub, buf+ovec[4], ovec[5]-ovec[4]); + sub[ovec[5]-ovec[4]] = '\0'; + ck_assert_str_eq(sub, "42"); + + strcpy(sub, uwsgi_regexp_apply_ovec(buf, strlen(buf), "key=$1.$2.jpg", 13, ovec, 2)); + ck_assert_str_eq(sub, "key=bar.42.jpg"); + + pcre2_code_free(pattern); + free(ovec); +} +END_TEST + +Suite *check_regexp(void) +{ + Suite *s = suite_create("uwsgi regexp"); + TCase *tc = tcase_create("regexp"); + + suite_add_tcase(s, tc); + tcase_add_test(tc, test_uwsgi_regexp_match); + tcase_add_test(tc, test_uwsgi_regexp_match_ovec); + return s; +} + +int main(void) +{ + int nf; + SRunner *r = srunner_create(check_regexp()); + srunner_run_all(r, CK_NORMAL); + nf = srunner_ntests_failed(r); + srunner_free(r); + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} From 5c6a3dcb556f175df796b26c6091fadfc2f2fdeb Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Mon, 29 Apr 2024 23:09:39 +0200 Subject: [PATCH 174/207] make unittest a specific test job also rename unittests as such, making the distinction with tests and checks. --- .github/workflows/test.yml | 32 ++++++++++++++++++-------------- Makefile | 4 ++-- unittest/Makefile | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6c396344d8..19f06baa0a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,13 +7,26 @@ on: branches: [ master, uwsgi-2.0 ] jobs: - python: + unittest: + runs-on: ubuntu-20.04 + steps: + - name: Install dependencies + run: | + sudo apt update -qq + sudo apt install --no-install-recommends -qqyf \ + libpcre2-dev libjansson-dev libcap2-dev \ + check + - uses: actions/checkout@v4 + - name: Run unit tests + run: make unittests + + python: runs-on: ubuntu-20.04 strategy: matrix: python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] - test-suite: [unittest, python, deadlocks] + test-suite: [python, deadlocks] steps: - name: Add deadnakes ppa run: sudo add-apt-repository ppa:deadsnakes/ppa -y @@ -22,34 +35,27 @@ jobs: sudo apt update -qq sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-dev \ libpcre2-dev libjansson-dev libcap2-dev \ - curl check + curl - name: Install distutils if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11","3.12"]'), matrix.python-version) run: | sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-distutils \ - uses: actions/checkout@v4 - - name: Run unit tests - if: matrix.test-suite == 'unittest' - run: make tests - name: Build uWSGI binary - if: matrix.test-suite != 'unittest' run: make - name: Build python${{ matrix.python-version }} plugin - if: matrix.test-suite != 'unittest' run: | PYTHON_VERSION=${{ matrix.python-version }} PYTHON_VERSION=python${PYTHON_VERSION//.} /usr/bin/python${{ matrix.python-version }} -V /usr/bin/python${{ matrix.python-version }} uwsgiconfig.py --plugin plugins/python base $PYTHON_VERSION - name: run smoke tests - if: matrix.test-suite != 'unittest' run: | PYTHON_VERSION=${{ matrix.python-version }} PYTHON_VERSION=python${PYTHON_VERSION//.} ./tests/gh-${{ matrix.test-suite }}.sh ${PYTHON_VERSION} rack: - runs-on: ubuntu-20.04 strategy: matrix: @@ -59,11 +65,9 @@ jobs: run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf python3-dev \ - libpcre3-dev libjansson-dev libcap2-dev ruby2.7-dev \ - curl check + libpcre2-dev libjansson-dev libcap2-dev ruby2.7-dev \ + curl - uses: actions/checkout@v4 - - name: Run unit tests - run: make tests - name: Build uWSGI binary run: make - name: Build rack plugin diff --git a/Makefile b/Makefile index b576335a31..2a4f0d6782 100644 --- a/Makefile +++ b/Makefile @@ -13,9 +13,9 @@ check: plugin.%: $(PYTHON) uwsgiconfig.py --plugin plugins/$* $(PROFILE) -tests: +unittests: $(PYTHON) uwsgiconfig.py --build unittest - cd unittest && make && make test + cd unittest && make test %: $(PYTHON) uwsgiconfig.py --build $@ diff --git a/unittest/Makefile b/unittest/Makefile index f0c22acefa..c28b8d70c0 100644 --- a/unittest/Makefile +++ b/unittest/Makefile @@ -21,7 +21,7 @@ all: $(objects) $(objects): %: %.c ../libuwsgi.a $(CC) $(CFLAGS) -o $@ $< ../libuwsgi.a $(LDFLAGS) -test: +test: all @for file in $(objects); do ./$$file; done clean: From e47b900187ed8e3aac329ebb4485a85d2e8b4da1 Mon Sep 17 00:00:00 2001 From: "Hanan .T" Date: Sat, 11 May 2024 18:05:31 +0300 Subject: [PATCH 175/207] Remove old code that causes race-condition over termination of uWSGI process when using need-app and lazy-apps flag. --- core/uwsgi.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/uwsgi.c b/core/uwsgi.c index 1bf257f4ef..9bcfe15abc 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -3937,13 +3937,6 @@ void uwsgi_init_all_apps() { if (uwsgi.need_app) { if (!uwsgi.lazy) uwsgi_log("*** no app loaded. GAME OVER ***\n"); - if (uwsgi.lazy_apps) { - if (uwsgi.master_process) { - if (kill(uwsgi.workers[0].pid, SIGINT)) { - uwsgi_error("kill()"); - } - } - } exit(UWSGI_FAILED_APP_CODE); } else { From 2abdd3df894d41edc512500bfc5b77650fee7d13 Mon Sep 17 00:00:00 2001 From: Eric Covener Date: Sat, 11 May 2024 21:33:09 +0200 Subject: [PATCH 176/207] apache2: let httpd handle CL/TE for non-http handlers Fix #2635 origin: https://github.com/apache/httpd/commit/a29723ce1af75eed0813c3717d3f6dee9b405ca8.patch bug-cve: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-24795 --- apache2/mod_proxy_uwsgi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apache2/mod_proxy_uwsgi.c b/apache2/mod_proxy_uwsgi.c index 026e63e03e..c28714ceb2 100644 --- a/apache2/mod_proxy_uwsgi.c +++ b/apache2/mod_proxy_uwsgi.c @@ -374,6 +374,12 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec *backend, proxy_server_ return HTTP_BAD_GATEWAY; } + /* T-E wins over C-L */ + if (apr_table_get(r->headers_out, "Transfer-Encoding")) { + apr_table_unset(r->headers_out, "Content-Length"); + backend->close = 1; + } + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { ap_set_content_type(r, apr_pstrdup(r->pool, buf)); } From 1a71973117bd6490d2d48e7c2fcd34cf9aa3f7fb Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 30 Apr 2024 11:23:42 +0200 Subject: [PATCH 177/207] add integration test for static-expires-uri --- .github/workflows/test.yml | 12 ++++++ Makefile | 3 ++ t/runner | 82 ++++++++++++++++++++++++++++++++++++++ t/static/config.ini | 4 ++ 4 files changed, 101 insertions(+) create mode 100755 t/runner create mode 100644 t/static/config.ini diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19f06baa0a..88df146b6c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,18 @@ jobs: - name: Run unit tests run: make unittests + test: + runs-on: ubuntu-20.04 + steps: + - name: Install dependencies + run: | + sudo apt update -qq + sudo apt install --no-install-recommends -qqyf \ + libpcre2-dev libjansson-dev libcap2-dev + - uses: actions/checkout@v4 + - name: Run integration tests + run: make all tests + python: runs-on: ubuntu-20.04 strategy: diff --git a/Makefile b/Makefile index 2a4f0d6782..fd2e3301c6 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,9 @@ unittests: $(PYTHON) uwsgiconfig.py --build unittest cd unittest && make test +tests: + $(PYTHON) t/runner + %: $(PYTHON) uwsgiconfig.py --build $@ diff --git a/t/runner b/t/runner new file mode 100755 index 0000000000..b9d9ce5294 --- /dev/null +++ b/t/runner @@ -0,0 +1,82 @@ +#!/usr/bin/python3 + + +import os +import requests +import signal +import socket +import subprocess +import time +import unittest + + +TESTS_DIR = os.path.dirname(__file__) +UWSGI_BINARY = os.getenv("UWSGI_BINARY", os.path.join(TESTS_DIR, "..", "uwsgi")) +UWSGI_ADDR = "127.0.0.1" +UWSGI_PORT = 8000 +UWSGI_HTTP = f"{UWSGI_ADDR}:{UWSGI_PORT}" + + +class BaseTest: + """ + Container class to avoid base test being run + """ + + class UwsgiServerTest(unittest.TestCase): + """ + Test case with a server instance available on a socket for requests + """ + + @classmethod + def uwsgi_ready(cls): + try: + s = socket.socket() + s.connect( + ( + UWSGI_ADDR, + UWSGI_PORT, + ) + ) + except socket.error: + return False + else: + return True + finally: + s.close() + + @classmethod + def setUpClass(cls): + # launch server + cls.testserver = subprocess.Popen( + [UWSGI_BINARY, "--http-socket", UWSGI_HTTP] + cls.ARGS + ) + + # ensure server is ready + retries = 10 + while not cls.uwsgi_ready() and retries > 0: + time.sleep(0.1) + retries = retries - 1 + if retries == 0: + raise RuntimeError("uwsgi test server is not available") + + @classmethod + def tearDownClass(cls): + cls.testserver.send_signal(signal.SIGTERM) + cls.testserver.wait() + + +class StaticTest(BaseTest.UwsgiServerTest): + + ARGS = [ + "--plugin", + "python3", # provide a request plugin if no embedded request plugin + os.path.join(TESTS_DIR, "static", "config.ini"), + ] + + def test_static_expires(self): + with requests.get(f"http://{UWSGI_HTTP}/foobar/config.ini") as r: + self.assertTrue("Expires" in r.headers) + + +if __name__ == "__main__": + unittest.main() diff --git a/t/static/config.ini b/t/static/config.ini new file mode 100644 index 0000000000..e0e23c55ed --- /dev/null +++ b/t/static/config.ini @@ -0,0 +1,4 @@ +[uwsgi] +need-app = False +static-map = /foobar=t/static +static-expires-uri = ^/foobar/ 315360000 From 93d07ec38b319c2fba7c71d3fd0d5acc2882d65a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 14 May 2024 21:08:14 -0700 Subject: [PATCH 178/207] fix 32-bit compilation with GCC14 Wrong pointer type is used. --- core/regexp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/regexp.c b/core/regexp.c index 74bb77751e..2b59f16fb7 100644 --- a/core/regexp.c +++ b/core/regexp.c @@ -23,7 +23,7 @@ int uwsgi_regexp_build(char *re, uwsgi_pcre ** pattern) { #ifdef UWSGI_PCRE2 int errnbr; - long unsigned int erroff; + size_t erroff; *pattern = pcre2_compile((const unsigned char *) re, PCRE2_ZERO_TERMINATED, 0, &errnbr, &erroff, NULL); #else From aa454f38717fdd9233e9deff7b855b75829df8ed Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 27 May 2024 21:08:30 +0200 Subject: [PATCH 179/207] uwsgiconfig: get compiler version with -dumpfullversion Fix #2644 --- uwsgiconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 032bd884f6..e390695991 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -712,13 +712,13 @@ def __init__(self, filename, mute=False): if uwsgi_os == 'GNU': self.cflags.append('-D__HURD__') - gcc_version = spcall("%s -dumpversion" % GCC) + gcc_version = spcall("%s -dumpfullversion -dumpversion" % GCC) if not gcc_version and GCC.startswith('gcc'): if uwsgi_os == 'Darwin': GCC = 'llvm-' + GCC else: GCC = 'gcc' - gcc_version = spcall("%s -dumpversion" % GCC) + gcc_version = spcall("%s -dumpfullversion -dumpversion" % GCC) try: add_it = False From 7917896aab295b2cb670e1e9b468eb109d55752b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sat, 1 Jun 2024 21:07:17 +0200 Subject: [PATCH 180/207] RELEASE.md: improve uwsgi-docs instructions --- RELEASE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 5ceb7a7e29..10b7729d5d 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -5,8 +5,9 @@ cherry-picked from the `master` branch. Before tagging a new release the release notes should be updated and a file named `Changelog-2.0.XY.rst` where XY is the number of the release created in -the [uwsgi-docs repository](https://github.com/unbit/uwsgi-docs). The file should then be referenced -the newly created changelog file from the `index.rst`. +the [uwsgi-docs repository](https://github.com/unbit/uwsgi-docs). The newly created changelog file +should then be referenced from the `index.rst`. The release should also be updated in the `Download.rst` +page. In order to cut a new release you have to first bump the version and then tag with the same version the last commit in git. All the commands are assumed to be run from the `uwsgi-2.0` branch. From 71a492f50f1479b4aeaaeef96993feb2869ba110 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Mon, 8 Jul 2024 08:11:52 +0000 Subject: [PATCH 181/207] gracefully_kill() uses pipe to stop worker loop --- core/loop.c | 1 + core/uwsgi.c | 30 +++++++++++++++++++++++------- uwsgi.h | 2 ++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/core/loop.c b/core/loop.c index 8c64279bc1..c6824edf21 100644 --- a/core/loop.c +++ b/core/loop.c @@ -127,6 +127,7 @@ void *simple_loop_run(void *arg1) { int main_queue = event_queue_init(); uwsgi_add_sockets_to_queue(main_queue, core_id); + event_queue_add_fd_read(main_queue, uwsgi.loop_stop_pipe[0]); if (uwsgi.signal_socket > -1) { event_queue_add_fd_read(main_queue, uwsgi.signal_socket); diff --git a/core/uwsgi.c b/core/uwsgi.c index 9bcfe15abc..3a6d9da761 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1255,16 +1255,17 @@ void gracefully_kill(int signum) { uwsgi_log("Gracefully killing worker %d (pid: %d)...\n", uwsgi.mywid, uwsgi.mypid); uwsgi.workers[uwsgi.mywid].manage_next_request = 0; + if (uwsgi.threads > 1) { - struct wsgi_request *wsgi_req = current_wsgi_req(); - wait_for_threads(); - if (!uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request) { - if (uwsgi.workers[uwsgi.mywid].shutdown_sockets) - uwsgi_shutdown_all_sockets(); - exit(UWSGI_RELOAD_CODE); + // Stop event_queue_wait() in other threads. + // We use loop_stop_pipe only in threaded workers to avoid + // unintensional behavior changes in single threaded workers. + int fd; + if ((fd = uwsgi.loop_stop_pipe[1]) > 0) { + close(fd); + uwsgi.loop_stop_pipe[1] = 0; } return; - // never here } // still not found a way to gracefully reload in async mode @@ -1296,6 +1297,17 @@ static void simple_goodbye_cruel_world(const char *reason) { // Avoid showing same message from all threads. uwsgi_log("...The work of process %d is done (%s). Seeya!\n", getpid(), (reason != NULL ? reason : "no reason given")); } + + if (uwsgi.threads > 1) { + // Stop event_queue_wait() in other threads. + // We use loop_stop_pipe only in threaded workers to avoid + // unintensional behavior changes in single threaded workers. + int fd; + if ((fd = uwsgi.loop_stop_pipe[1]) > 0) { + close(fd); + uwsgi.loop_stop_pipe[1] = 0; + } + } } void goodbye_cruel_world(const char *reason_fmt, ...) { @@ -3641,6 +3653,10 @@ void uwsgi_ignition() { exit(1); } } + if (pipe(&uwsgi.loop_stop_pipe[0])) { + uwsgi_error("pipe()") + exit(1); + } // mark the worker as "accepting" (this is a mark used by chain reloading) uwsgi.workers[uwsgi.mywid].accepting = 1; diff --git a/uwsgi.h b/uwsgi.h index 39c3282db1..748bc856a7 100755 --- a/uwsgi.h +++ b/uwsgi.h @@ -2953,6 +2953,8 @@ struct uwsgi_server { struct uwsgi_buffer *websockets_continuation_buffer; uint64_t max_worker_lifetime_delta; + // This pipe is used to stop event_queue_wait() in threaded workers. + int loop_stop_pipe[2]; }; struct uwsgi_rpc { From e5618dce4caa5c658c76d0d03b0178474a28d356 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 15:49:53 +0200 Subject: [PATCH 182/207] plugins/python: use PyOS_*Fork stable API functions on 3.7+ To avoid calling functions made private from 3.13+. And probably fixing issues with C extensions. --- plugins/python/python_plugin.c | 20 +++++++++----------- plugins/python/uwsgi_python.h | 8 ++------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 6c5be6c7ff..066bde668e 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -461,7 +461,7 @@ void uwsgi_python_post_fork() { // reset python signal flags so child processes can trap signals // Necessary if uwsgi fork hooks not called to update interpreter state if (!up.call_uwsgi_fork_hooks && up.call_osafterfork) { -#ifdef HAS_NOT_PyOS_AfterFork_Child +#ifdef HAS_NOT_PYOS_FORK_STABLE_API PyOS_AfterFork(); #else PyOS_AfterFork_Child(); @@ -1368,11 +1368,10 @@ void uwsgi_python_pre_uwsgi_fork() { // Acquire the gil and import lock before forking in order to avoid // deadlocks in workers UWSGI_GET_GIL -#if defined UWSGI_PY312 - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyImport_AcquireLock(interp); -#else +#ifdef HAS_NOT_PYOS_FORK_STABLE_API _PyImport_AcquireLock(); +#else + PyOS_BeforeFork(); #endif } } @@ -1385,17 +1384,16 @@ void uwsgi_python_post_uwsgi_fork(int step) { if (uwsgi.has_threads) { if (step == 0) { // Release locks within master process -#if defined UWSGI_PY312 - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyImport_ReleaseLock(interp); -#else +#ifdef HAS_NOT_PYOS_FORK_STABLE_API _PyImport_ReleaseLock(); +#else + PyOS_AfterFork_Parent(); #endif UWSGI_RELEASE_GIL } else { // Ensure thread state and locks are cleaned up in child process -#ifdef HAS_NOT_PyOS_AfterFork_Child +#ifdef HAS_NOT_PYOS_FORK_STABLE_API PyOS_AfterFork(); #else PyOS_AfterFork_Child(); @@ -2164,7 +2162,7 @@ static int uwsgi_python_worker() { // ensure signals can be used again from python // Necessary if fork hooks have been not used to update interpreter state if (!up.call_osafterfork && !up.call_uwsgi_fork_hooks) -#ifdef HAS_NOT_PyOS_AfterFork_Child +#ifdef HAS_NOT_PYOS_FORK_STABLE_API PyOS_AfterFork(); #else PyOS_AfterFork_Child(); diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 961a46c49f..538f3315bc 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -41,12 +41,8 @@ #define HAS_NO_ERRORS_IN_PyFile_FromFd #endif -#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7 -#define HAS_NOT_PyOS_AfterFork_Child -#endif - -#if PY_MAJOR_VERSION < 3 -#define HAS_NOT_PyOS_AfterFork_Child +#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 7) || PY_MAJOR_VERSION < 3 +#define HAS_NOT_PYOS_FORK_STABLE_API #endif #if PY_MAJOR_VERSION > 2 From 896ee575d0743e78237c7220007f4bbeaa8cedf8 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 16:00:35 +0200 Subject: [PATCH 183/207] core/uwsgi: set enable threads by default As it should have been since years :_) --- core/init.c | 2 +- core/uwsgi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/init.c b/core/init.c index d8ef00daa0..62dfb0a8c1 100644 --- a/core/init.c +++ b/core/init.c @@ -468,8 +468,8 @@ void sanitize_args() { uwsgi.cores = uwsgi.async; } + uwsgi.has_threads = 1; if (uwsgi.threads > 1) { - uwsgi.has_threads = 1; uwsgi.cores = uwsgi.threads; } diff --git a/core/uwsgi.c b/core/uwsgi.c index 9bcfe15abc..befaf9621b 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -178,7 +178,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"freebind", no_argument, 0, "put socket in freebind mode", uwsgi_opt_true, &uwsgi.freebind, 0}, #endif {"map-socket", required_argument, 0, "map sockets to specific workers", uwsgi_opt_add_string_list, &uwsgi.map_socket, 0}, - {"enable-threads", no_argument, 'T', "enable threads", uwsgi_opt_true, &uwsgi.has_threads, 0}, + {"enable-threads", no_argument, 'T', "enable threads (stub option this is true by default)", uwsgi_opt_true, &uwsgi.has_threads, 0}, {"no-threads-wait", no_argument, 0, "do not wait for threads cancellation on quit/reload", uwsgi_opt_true, &uwsgi.no_threads_wait, 0}, {"auto-procname", no_argument, 0, "automatically set processes name to something meaningful", uwsgi_opt_true, &uwsgi.auto_procname, 0}, From 699dc20f8204ee18812951600b0221156d217530 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 16:32:31 +0200 Subject: [PATCH 184/207] plugins/python: handle cframe removal from CPython thread state Use current_frame instead --- plugins/python/python_plugin.c | 16 ++++++++++++++++ plugins/python/uwsgi_python.h | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 066bde668e..a3969d571c 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -1662,7 +1662,11 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 up.current_c_recursion_remaining[wsgi_req->async_id] = tstate->c_recursion_remaining; up.current_py_recursion_remaining[wsgi_req->async_id] = tstate->py_recursion_remaining; +#ifdef UWSGI_PY313 + up.current_frame[wsgi_req->async_id] = tstate->current_frame; +#else up.current_frame[wsgi_req->async_id] = tstate->cframe; +#endif #elif defined UWSGI_PY311 up.current_recursion_remaining[wsgi_req->async_id] = tstate->recursion_remaining; up.current_frame[wsgi_req->async_id] = tstate->cframe; @@ -1675,7 +1679,11 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 up.current_main_c_recursion_remaining = tstate->c_recursion_remaining; up.current_main_py_recursion_remaining = tstate->py_recursion_remaining; +#ifdef UWSGI_PY313 + up.current_main_frame = tstate->current_frame; +#else up.current_main_frame = tstate->cframe; +#endif #elif defined UWSGI_PY311 up.current_main_recursion_remaining = tstate->recursion_remaining; up.current_main_frame = tstate->cframe; @@ -1915,7 +1923,11 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 tstate->c_recursion_remaining = up.current_c_recursion_remaining[wsgi_req->async_id]; tstate->py_recursion_remaining = up.current_py_recursion_remaining[wsgi_req->async_id]; +#ifdef UWSGI_PY313 + tstate->current_frame = up.current_frame[wsgi_req->async_id]; +#else tstate->cframe = up.current_frame[wsgi_req->async_id]; +#endif #elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_recursion_remaining[wsgi_req->async_id]; tstate->cframe = up.current_frame[wsgi_req->async_id]; @@ -1928,7 +1940,11 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { #ifdef UWSGI_PY312 tstate->c_recursion_remaining = up.current_main_c_recursion_remaining; tstate->py_recursion_remaining = up.current_main_py_recursion_remaining; +#ifdef UWSGI_PY313 + tstate->current_frame = up.current_main_frame; +#else tstate->cframe = up.current_main_frame; +#endif #elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_main_recursion_remaining; tstate->cframe = up.current_main_frame; diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 538f3315bc..de83ac4540 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -25,6 +25,10 @@ # define UWSGI_PY312 #endif +#if (PY_VERSION_HEX >= 0x030d0000) +# define UWSGI_PY313 +#endif + #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 7 #define HAS_NOT_PyMemoryView_FromBuffer #endif @@ -185,11 +189,19 @@ struct uwsgi_python { #ifdef UWSGI_PY312 int *current_c_recursion_remaining; int *current_py_recursion_remaining; +#ifdef UWSGI_PY313 + struct _PyInterpreterFrame **current_frame; +#else _PyCFrame **current_frame; +#endif int current_main_c_recursion_remaining; int current_main_py_recursion_remaining; +#ifdef UWSGI_PY313 + struct _PyInterpreterFrame *current_main_frame; +#else _PyCFrame *current_main_frame; +#endif #elif defined UWSGI_PY311 int *current_recursion_remaining; _PyCFrame **current_frame; From d6211dc1d3b00ec3a93d317644a9982d77a75b58 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 21 Jul 2024 16:13:10 +0200 Subject: [PATCH 185/207] ci: test on Python 3.13 That is still in beta but removed private C APIs we were using. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88df146b6c..2d4731914b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] test-suite: [python, deadlocks] steps: - name: Add deadnakes ppa From 8c408eb4fe42eb3dba96e48805ba55833edf2fca Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Thu, 29 Aug 2024 08:45:24 +0200 Subject: [PATCH 186/207] port pypy plugin to python3 --- plugins/pypy/pypy_setup.py | 78 +++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/plugins/pypy/pypy_setup.py b/plugins/pypy/pypy_setup.py index ecfe14f650..ffb447fa69 100644 --- a/plugins/pypy/pypy_setup.py +++ b/plugins/pypy/pypy_setup.py @@ -48,13 +48,14 @@ uwsgi_defines = [] uwsgi_cflags = ffi.string(lib0.uwsgi_get_cflags()).split() for cflag in uwsgi_cflags: - if cflag.startswith('-D'): + if cflag.startswith(b'-D'): line = cflag[2:] - if '=' in line: - (key, value) = line.split('=', 1) + if b'=' in line: + (key, value) = line.decode().split('=', 1) uwsgi_cdef.append('#define %s ...' % key) uwsgi_defines.append('#define %s %s' % (key, value.replace('\\"', '"').replace('""', '"'))) else: + line = line.decode() uwsgi_cdef.append('#define %s ...' % line) uwsgi_defines.append('#define %s 1' % line) uwsgi_dot_h = ffi.string(lib0.uwsgi_get_dot_h()) @@ -166,22 +167,22 @@ struct uwsgi_plugin *p[]; ...; }; -struct uwsgi_server uwsgi; +extern struct uwsgi_server uwsgi; -struct uwsgi_plugin pypy_plugin; +extern struct uwsgi_plugin pypy3_plugin; -const char *uwsgi_pypy_version; +extern const char *uwsgi_pypy_version; char *uwsgi_binary_path(); void *uwsgi_malloc(size_t); struct uwsgi_logvar { - char key[256]; - uint8_t keylen; - char val[256]; - uint8_t vallen; - struct uwsgi_logvar *next; + char key[256]; + uint8_t keylen; + char val[256]; + uint8_t vallen; + struct uwsgi_logvar *next; }; struct uwsgi_logvar *uwsgi_logvar_get(struct wsgi_request *, char *, uint8_t); @@ -248,7 +249,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); @@ -269,9 +270,9 @@ %s extern struct uwsgi_server uwsgi; -extern struct uwsgi_plugin pypy_plugin; +extern struct uwsgi_plugin pypy3_plugin; %s -''' % ('\n'.join(uwsgi_defines), uwsgi_dot_h, hooks) +''' % ('\n'.join(uwsgi_defines), uwsgi_dot_h.decode(), hooks) ffi.cdef(cdefines) lib = ffi.verify(cverify) @@ -286,7 +287,7 @@ # fix argv if needed if len(sys.argv) == 0: - sys.argv.insert(0, ffi.string(lib.uwsgi_binary_path())) + sys.argv.insert(0, ffi.string(lib.uwsgi_binary_path()).decode()) @ffi.callback("void(char *)") @@ -307,12 +308,12 @@ def uwsgi_pypy_loader(module): global wsgi_application m = ffi.string(module) c = 'application' - if ':' in m: - m, c = m.split(':') - if '.' in m: - mod = __import__(m, None, None, '*') + if b':' in m: + m, c = m.split(b':') + if b'.' in m: + mod = __import__(m.decode(), None, None, '*') else: - mod = __import__(m) + mod = __import__(m.decode()) wsgi_application = getattr(mod, c) @@ -324,7 +325,7 @@ def uwsgi_pypy_file_loader(filename): global wsgi_application w = ffi.string(filename) c = 'application' - mod = imp.load_source('uwsgi_file_wsgi', w) + mod = imp.load_source('uwsgi_file_wsgi', w.decode()) wsgi_application = getattr(mod, c) @@ -335,15 +336,15 @@ def uwsgi_pypy_paste_loader(config): """ global wsgi_application c = ffi.string(config) - if c.startswith('config:'): + if c.startswith(b'config:'): c = c[7:] - if c[0] != '/': - c = os.getcwd() + '/' + c + if c[0] != b'/'[0]: + c = os.getcwd() + '/' + c.decode() try: - from logging.config import fileConfig + from paste.script.util.logging_config import fileConfig fileConfig(c) except ImportError: - print("PyPy WARNING: unable to load logging.config") + print("PyPy WARNING: unable to load paste.script.util.logging_config") from paste.deploy import loadapp wsgi_application = loadapp('config:%s' % c) @@ -364,8 +365,8 @@ def uwsgi_pypy_pythonpath(item): add an item to the pythonpath """ path = ffi.string(item) - sys.path.append(path) - print("added %s to pythonpath" % path) + sys.path.append(path.decode()) + print("added %s to pythonpath" % path.decode()) class WSGIfilewrapper(object): @@ -470,15 +471,17 @@ def writer(data): def start_response(status, headers, exc_info=None): if exc_info: traceback.print_exception(*exc_info) + status = status.encode() lib.uwsgi_response_prepare_headers(wsgi_req, ffi.new("char[]", status), len(status)) for hh in headers: + hh = (hh[0].encode(), hh[1].encode()) lib.uwsgi_response_add_header(wsgi_req, ffi.new("char[]", hh[0]), len(hh[0]), ffi.new("char[]", hh[1]), len(hh[1])) return writer environ = {} iov = wsgi_req.hvec for i in range(0, wsgi_req.var_cnt, 2): - environ[ffi.string(ffi.cast("char*", iov[i].iov_base), iov[i].iov_len)] = ffi.string(ffi.cast("char*", iov[i+1].iov_base), iov[i+1].iov_len) + environ[ffi.string(ffi.cast("char*", iov[i].iov_base), iov[i].iov_len).decode()] = ffi.string(ffi.cast("char*", iov[i+1].iov_base), iov[i+1].iov_len).decode() environ['wsgi.version'] = (1, 0) scheme = 'http' @@ -598,8 +601,8 @@ def uwsgi_pypy_rpc(node, func, *args): def uwsgi_pypy_call(func, *args): node = None - if '@' in func: - (func, node) = func.split('@') + if b'@' in func: + (func, node) = func.split(b'@') return uwsgi_pypy_rpc(node, func, *args) uwsgi.call = uwsgi_pypy_call @@ -975,13 +978,10 @@ def uwsgi_pypy_chunked_read_nb(): 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): @@ -1067,7 +1067,7 @@ def uwsgi_pypy_continulet_switch(wsgi_req): def uwsgi_pypy_setup_continulets(): - if lib.uwsgi.async < 1: + if lib.uwsgi["async"] < 1: raise Exception("pypy continulets require async mode !!!") lib.uwsgi.schedule_to_main = uwsgi_pypy_continulet_switch lib.uwsgi.schedule_to_req = uwsgi_pypy_continulet_schedule From c1fdce147d24378c085b733dbfe26a6c0f5edc05 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 3 Sep 2024 08:34:18 +0200 Subject: [PATCH 187/207] PHP: Add support for uwsgi.disconnect() function This allows to run some code after finishing request. As described in https://symfony.com/doc/current/components/http_kernel.html#8-the-kernel-terminate-event --- plugins/php/common.h | 2 ++ plugins/php/php_plugin.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/plugins/php/common.h b/plugins/php/common.h index 061b0b597a..c5bb98dd29 100644 --- a/plugins/php/common.h +++ b/plugins/php/common.h @@ -7,5 +7,7 @@ #include "ext/session/php_session.h" +#include "ext/standard/head.h" + #include diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index 375fa6940a..a888e524bd 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -499,6 +499,21 @@ PHP_FUNCTION(uwsgi_signal) { RETURN_NULL(); } +PHP_FUNCTION(uwsgi_disconnect) { + + struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context); + + php_output_end_all(); + php_header(); + + uwsgi_disconnect(wsgi_req); + + php_output_set_status(PHP_OUTPUT_DISABLED); + + RETURN_NULL(); +} + + ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) ZEND_END_ARG_INFO() @@ -517,6 +532,7 @@ zend_function_entry uwsgi_php_functions[] = { PHP_FE(uwsgi_cache_del, arginfo_void) PHP_FE(uwsgi_cache_clear, arginfo_void) PHP_FE(uwsgi_cache_exists, arginfo_void) + PHP_FE(uwsgi_disconnect, arginfo_void) { NULL, NULL, NULL}, }; From 25dbb4b75579d88c5d8a4f325345f0341c79aaf9 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 19 Aug 2024 15:50:31 -0400 Subject: [PATCH 188/207] pyuwsgi: avoid interleaving pywsgi threadstate In all versions of pyuwsgi at the moment the first fork has a NULL threadstate due to uwsgi_python_master_fixup which calls UWSGI_RELEASE_GIL (expanded to PyEval_SaveThread -- which drops the GIL and sets threadstate to NULL). This is called during uwsgi_setup. After uwsgi_setup was returning, PyThreadState_Swap was restoring the pyuwsgi threadstate (in both the original and worker processes) Future forks would have the pyuwsgi threadstate active (from the restoration at PyThreadState_Swap) in python versions < 3.12 this wasn't an issue. In 3.12+ the PyEval_RestoreThread would attempt to take_gil and then block forever on the GIL mutex (despite it actually holding it? due to the fork state from the parent process). Bisecting cpython showed that python/cpython@92d8bff slightly changed behaviour of PyThreadState_Swap (it now additionally manages GIL state: unlocking the previous threadstate and locking the new threadstate). Putting a log line in the PyThreadState_Swap showed a suspicious swapping from oldts=123123 to newts=123123 (swapping from its own threadstate to itself?); this is because after forking control would be given back to the original threadstate (which mostly worked but was in UB territory given the GIL state). In 3.11 the threadstate that was restored after the PyThreadState_Swap did not have the GIL locked (technically this could have allowed a data race if threads existed before starting uwsgi via pyuwsgi). In 3.12 since PyThreadState_Swap was changed to release the old threadstate's GIL and acquire the GIL in the new threadstate this meant that the saved threadstate had ->locked = 1 (which is sort of an invalid state?). As far as I can tell there aren't any public apis to undo this and "restore" the 3.11 behaviour precisely. Then later it would try and lock (despite already being -> locked = 1) and deadlock against itself this is actually called out on the docs: If the lock has been created, the current thread must not have acquired it, otherwise deadlock ensues. To fix this once we call uwsgi_setup we never give control back to the original pyuwsgi threadstate avoiding the Swap dance entirely. --- plugins/pyuwsgi/pyuwsgi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/pyuwsgi/pyuwsgi.c b/plugins/pyuwsgi/pyuwsgi.c index 7079d1e24b..14135a6117 100644 --- a/plugins/pyuwsgi/pyuwsgi.c +++ b/plugins/pyuwsgi/pyuwsgi.c @@ -115,12 +115,6 @@ pyuwsgi_setup(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - //TODO: ...??? - // actually do the thing! - PyThreadState *_tstate = PyThreadState_Get(); - uwsgi_setup(orig_argc, orig_argv, environ); - PyThreadState_Swap(_tstate); - Py_INCREF(self); return self; } @@ -133,6 +127,7 @@ pyuwsgi_init(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } + uwsgi_setup(orig_argc, orig_argv, environ); int rc = uwsgi_run(); // never(?) here @@ -149,6 +144,7 @@ pyuwsgi_run(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } + uwsgi_setup(orig_argc, orig_argv, environ); int rc = uwsgi_run(); // never(?) here From 337ba4cb6b2c3a6aabdf629083d883b35b8c4651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=B5=A9=E5=BD=AC?= Date: Mon, 22 Jul 2024 17:25:37 +0800 Subject: [PATCH 189/207] Fix gracefully_kill_them_all with running requests With the following: ``` wsgi = app:app http = :8000 master = true processes = 2 harakiri = 15 harakiri-verbose = true harakiri-graceful-timeout = 15 harakiri-graceful-signal = 15 max-requests = 100000 memory-report = true enable-threads = true threads = 4 enable-thread = true showconfig = true listen = 1024 post-buffering = 8192 buffer-size = 32768 lazy = true http-keepalive = 1 add-header = Connection: Keep-Alive http-timeout = 70 socket-timeout = 75 hook-master-start = unix_signal:15 gracefully_kill_them_all vacuum = true hook-master-start = unix_signal:15 gracefully_kill_them_all ``` kill -s 15 master-pid while request does not complete. fllowing is uwsgi log: ``` running "unix_signal:15 gracefully_kill_them_all" (master-start)... WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x55de52cb68f0 pid: 143521 (default app) WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x55de52cb68f0 pid: 143520 (default app) graceful shutdown triggered... Gracefully killing worker 1 (pid: 143520)... gateway "uWSGI http 1" has been buried (pid: 143522) Gracefully killing worker 2 (pid: 143521)... worker 1 buried after 1 seconds {address space usage: 277147648 bytes/264MB} {rss usage: 34459648 bytes/32MB} [pid: 143521|app: 0|req: 1/1] 127.0.0.1 () {28 vars in 291 bytes } [Mon Jul 22 09:13:34 2024] GET / => generated 11 bytes in 6036 msecs (HTTP/1.1 200) 3 headers in 103 bytes (1 switches on core 0) worker 2 buried after 4 seconds goodbye to uWSGI. ``` The gateway process(pid=143522) is closed prematurely, causing the client to be unable to correctly receive the request result. I think you should wait for the worker process to shut down before shutting down the gateway process Fix #2656 --- core/master_utils.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/master_utils.c b/core/master_utils.c index 22977c07b7..d67be63945 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -54,6 +54,12 @@ void uwsgi_destroy_processes() { uwsgi_detach_daemons(); + for (i = 1; i <= uwsgi.numproc; i++) { + if (uwsgi.workers[i].pid > 0) { + waitpid(uwsgi.workers[i].pid, &waitpid_status, 0); + } + } + for (i = 0; i < ushared->gateways_cnt; i++) { if (ushared->gateways[i].pid > 0) { kill(ushared->gateways[i].pid, SIGKILL); From fe44f47aa550851235921f1caa383f9c39d7eb8c Mon Sep 17 00:00:00 2001 From: John Garland Date: Wed, 3 Feb 2016 14:18:17 +1100 Subject: [PATCH 190/207] Fix --catch-exceptions causing a segfault in Python 3.5. This bug arose due to the fact that the traceback module was overhauled in python 3.5: https://hg.python.org/cpython/rev/73afda5a4e4c The crash was due to PyTuple_GetItem returning NULL as a traceback is no longer a list of tuples but rather a list of objects (which support indexing). --- plugins/python/pyutils.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/python/pyutils.c b/plugins/python/pyutils.c index 9cb111d0bc..4adbf763b0 100644 --- a/plugins/python/pyutils.c +++ b/plugins/python/pyutils.c @@ -83,12 +83,12 @@ struct uwsgi_buffer *uwsgi_python_backtrace(struct wsgi_request *wsgi_req) { ub = uwsgi_buffer_new(4096); Py_ssize_t i; // we have to build a uwsgi array with 5 items (4 are taken from the python tb) - for(i=0;i< PyList_Size(result);i++) { - PyObject *t = PyList_GetItem(result, i); - PyObject *tb_filename = PyTuple_GetItem(t, 0); - PyObject *tb_lineno = PyTuple_GetItem(t, 1); - PyObject *tb_function = PyTuple_GetItem(t, 2); - PyObject *tb_text = PyTuple_GetItem(t, 3); + for(i=0;i< PySequence_Size(result);i++) { + PyObject *t = PySequence_GetItem(result, i); + PyObject *tb_filename = PySequence_GetItem(t, 0); + PyObject *tb_lineno = PySequence_GetItem(t, 1); + PyObject *tb_function = PySequence_GetItem(t, 2); + PyObject *tb_text = PySequence_GetItem(t, 3); int64_t line_no = PyInt_AsLong(tb_lineno); #ifdef PYTHREE From 420149c99bdc6580e17b5d65d4c3644d818eb6fb Mon Sep 17 00:00:00 2001 From: Ralf Ertzinger Date: Sun, 8 Sep 2024 20:34:01 +0200 Subject: [PATCH 191/207] Minor rework of Python 3.13 support As mentioned in https://github.com/unbit/uwsgi/pull/2655, this changes the way support for python 3.13 is handled. Instead of handling python 3.13 as a minor change from 3.12, and handling support for it under the 3.12 `#ifdef` blocks, this breaks out 3.13 into its own block, apart from 3.12. This makes the code a bit more verbose, but makes it easier to see what the structures look like for different python versions. --- plugins/python/python_plugin.c | 32 ++++++++++++++++---------------- plugins/python/uwsgi_python.h | 16 ++++++++-------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index a3969d571c..289607b868 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -1659,14 +1659,14 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { PyGILState_Release(pgst); if (wsgi_req) { -#ifdef UWSGI_PY312 +#ifdef UWSGI_PY313 up.current_c_recursion_remaining[wsgi_req->async_id] = tstate->c_recursion_remaining; up.current_py_recursion_remaining[wsgi_req->async_id] = tstate->py_recursion_remaining; -#ifdef UWSGI_PY313 up.current_frame[wsgi_req->async_id] = tstate->current_frame; -#else +#elif defined UWSGI_PY312 + up.current_c_recursion_remaining[wsgi_req->async_id] = tstate->c_recursion_remaining; + up.current_py_recursion_remaining[wsgi_req->async_id] = tstate->py_recursion_remaining; up.current_frame[wsgi_req->async_id] = tstate->cframe; -#endif #elif defined UWSGI_PY311 up.current_recursion_remaining[wsgi_req->async_id] = tstate->recursion_remaining; up.current_frame[wsgi_req->async_id] = tstate->cframe; @@ -1676,14 +1676,14 @@ void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { #endif } else { -#ifdef UWSGI_PY312 +#ifdef UWSGI_PY313 up.current_main_c_recursion_remaining = tstate->c_recursion_remaining; up.current_main_py_recursion_remaining = tstate->py_recursion_remaining; -#ifdef UWSGI_PY313 up.current_main_frame = tstate->current_frame; -#else +#elif defined UWSGI_PY312 + up.current_main_c_recursion_remaining = tstate->c_recursion_remaining; + up.current_main_py_recursion_remaining = tstate->py_recursion_remaining; up.current_main_frame = tstate->cframe; -#endif #elif defined UWSGI_PY311 up.current_main_recursion_remaining = tstate->recursion_remaining; up.current_main_frame = tstate->cframe; @@ -1920,14 +1920,14 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { PyGILState_Release(pgst); if (wsgi_req) { -#ifdef UWSGI_PY312 +#ifdef UWSGI_PY313 tstate->c_recursion_remaining = up.current_c_recursion_remaining[wsgi_req->async_id]; tstate->py_recursion_remaining = up.current_py_recursion_remaining[wsgi_req->async_id]; -#ifdef UWSGI_PY313 tstate->current_frame = up.current_frame[wsgi_req->async_id]; -#else +#elif defined UWSGI_PY312 + tstate->c_recursion_remaining = up.current_c_recursion_remaining[wsgi_req->async_id]; + tstate->py_recursion_remaining = up.current_py_recursion_remaining[wsgi_req->async_id]; tstate->cframe = up.current_frame[wsgi_req->async_id]; -#endif #elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_recursion_remaining[wsgi_req->async_id]; tstate->cframe = up.current_frame[wsgi_req->async_id]; @@ -1937,14 +1937,14 @@ void uwsgi_python_resume(struct wsgi_request *wsgi_req) { #endif } else { -#ifdef UWSGI_PY312 +#ifdef UWSGI_PY313 tstate->c_recursion_remaining = up.current_main_c_recursion_remaining; tstate->py_recursion_remaining = up.current_main_py_recursion_remaining; -#ifdef UWSGI_PY313 tstate->current_frame = up.current_main_frame; -#else +#elif defined UWSGI_PY312 + tstate->c_recursion_remaining = up.current_main_c_recursion_remaining; + tstate->py_recursion_remaining = up.current_main_py_recursion_remaining; tstate->cframe = up.current_main_frame; -#endif #elif defined UWSGI_PY311 tstate->recursion_remaining = up.current_main_recursion_remaining; tstate->cframe = up.current_main_frame; diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index de83ac4540..080824c301 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -186,22 +186,22 @@ struct uwsgi_python { char *callable; -#ifdef UWSGI_PY312 +#ifdef UWSGI_PY313 int *current_c_recursion_remaining; int *current_py_recursion_remaining; -#ifdef UWSGI_PY313 struct _PyInterpreterFrame **current_frame; -#else - _PyCFrame **current_frame; -#endif int current_main_c_recursion_remaining; int current_main_py_recursion_remaining; -#ifdef UWSGI_PY313 struct _PyInterpreterFrame *current_main_frame; -#else +#elif defined UWSGI_PY312 + int *current_c_recursion_remaining; + int *current_py_recursion_remaining; + _PyCFrame **current_frame; + + int current_main_c_recursion_remaining; + int current_main_py_recursion_remaining; _PyCFrame *current_main_frame; -#endif #elif defined UWSGI_PY311 int *current_recursion_remaining; _PyCFrame **current_frame; From d8c43c237759143036de17e9d6ef8cde3abb9b8b Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Mon, 9 Sep 2024 10:30:28 +0200 Subject: [PATCH 192/207] simplify pypy plugin port to python3 - bring back changes removed by old patch - less .encode() - plugin is named pypy, not pypy3 --- plugins/pypy/pypy_setup.py | 54 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/plugins/pypy/pypy_setup.py b/plugins/pypy/pypy_setup.py index ffb447fa69..148533d6bd 100644 --- a/plugins/pypy/pypy_setup.py +++ b/plugins/pypy/pypy_setup.py @@ -49,13 +49,12 @@ uwsgi_cflags = ffi.string(lib0.uwsgi_get_cflags()).split() for cflag in uwsgi_cflags: if cflag.startswith(b'-D'): - line = cflag[2:] - if b'=' in line: - (key, value) = line.decode().split('=', 1) + line = cflag[2:].decode() + if '=' in line: + (key, value) = line.split('=', 1) uwsgi_cdef.append('#define %s ...' % key) uwsgi_defines.append('#define %s %s' % (key, value.replace('\\"', '"').replace('""', '"'))) else: - line = line.decode() uwsgi_cdef.append('#define %s ...' % line) uwsgi_defines.append('#define %s 1' % line) uwsgi_dot_h = ffi.string(lib0.uwsgi_get_dot_h()) @@ -169,7 +168,7 @@ }; extern struct uwsgi_server uwsgi; -extern struct uwsgi_plugin pypy3_plugin; +extern struct uwsgi_plugin pypy_plugin; extern const char *uwsgi_pypy_version; @@ -249,7 +248,7 @@ int uwsgi_ready_fd(struct wsgi_request *); -void set_user_harakiri(int); +void set_user_harakiri(struct wsgi_request *, int); int uwsgi_metric_set(char *, char *, int64_t); int uwsgi_metric_inc(char *, char *, int64_t); @@ -270,7 +269,7 @@ %s extern struct uwsgi_server uwsgi; -extern struct uwsgi_plugin pypy3_plugin; +extern struct uwsgi_plugin pypy_plugin; %s ''' % ('\n'.join(uwsgi_defines), uwsgi_dot_h.decode(), hooks) @@ -306,14 +305,14 @@ def uwsgi_pypy_loader(module): load a wsgi module """ global wsgi_application - m = ffi.string(module) + m = ffi.string(module).decode() c = 'application' - if b':' in m: - m, c = m.split(b':') - if b'.' in m: - mod = __import__(m.decode(), None, None, '*') + if ':' in m: + m, c = m.split(':') + if '.' in m: + mod = __import__(m, None, None, '*') else: - mod = __import__(m.decode()) + mod = __import__(m) wsgi_application = getattr(mod, c) @@ -335,16 +334,16 @@ def uwsgi_pypy_paste_loader(config): load a .ini paste app """ global wsgi_application - c = ffi.string(config) - if c.startswith(b'config:'): + c = ffi.string(config).decode() + if c.startswith('config:'): c = c[7:] - if c[0] != b'/'[0]: - c = os.getcwd() + '/' + c.decode() + if c[0] != '/': + c = os.getcwd() + '/' + c try: - from paste.script.util.logging_config import fileConfig + from logging.config import fileConfig fileConfig(c) except ImportError: - print("PyPy WARNING: unable to load paste.script.util.logging_config") + print("PyPy WARNING: unable to load logging.config") from paste.deploy import loadapp wsgi_application = loadapp('config:%s' % c) @@ -364,9 +363,9 @@ def uwsgi_pypy_pythonpath(item): """ add an item to the pythonpath """ - path = ffi.string(item) - sys.path.append(path.decode()) - print("added %s to pythonpath" % path.decode()) + path = ffi.string(item).decode() + sys.path.append(path) + print("added %s to pythonpath" % path) class WSGIfilewrapper(object): @@ -978,10 +977,13 @@ def uwsgi_pypy_chunked_read_nb(): uwsgi.chunked_read_nb = uwsgi_pypy_chunked_read_nb -""" -uwsgi.set_user_harakiri(sec) -""" -uwsgi.set_user_harakiri = lambda x: lib.set_user_harakiri(x) +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 def uwsgi_pypy_get_logvar(key): From 9e9982dc5983c5504499ca9290ff4af20f7f3fb1 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 28 May 2024 17:48:43 +0200 Subject: [PATCH 193/207] tests: allow log output only if test fails --- t/runner | 113 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 53 deletions(-) diff --git a/t/runner b/t/runner index b9d9ce5294..8dc0dc4078 100755 --- a/t/runner +++ b/t/runner @@ -6,6 +6,7 @@ import requests import signal import socket import subprocess +import sys import time import unittest @@ -17,63 +18,69 @@ UWSGI_PORT = 8000 UWSGI_HTTP = f"{UWSGI_ADDR}:{UWSGI_PORT}" -class BaseTest: - """ - Container class to avoid base test being run - """ - - class UwsgiServerTest(unittest.TestCase): - """ - Test case with a server instance available on a socket for requests - """ - - @classmethod - def uwsgi_ready(cls): - try: - s = socket.socket() - s.connect( - ( - UWSGI_ADDR, - UWSGI_PORT, - ) - ) - except socket.error: - return False - else: - return True - finally: - s.close() - - @classmethod - def setUpClass(cls): - # launch server - cls.testserver = subprocess.Popen( - [UWSGI_BINARY, "--http-socket", UWSGI_HTTP] + cls.ARGS - ) - - # ensure server is ready - retries = 10 - while not cls.uwsgi_ready() and retries > 0: - time.sleep(0.1) - retries = retries - 1 - if retries == 0: - raise RuntimeError("uwsgi test server is not available") - - @classmethod - def tearDownClass(cls): - cls.testserver.send_signal(signal.SIGTERM) - cls.testserver.wait() +class UwsgiTest(unittest.TestCase): + def start_server(self, args): + self.testserver = subprocess.Popen( + [UWSGI_BINARY] + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) -class StaticTest(BaseTest.UwsgiServerTest): - - ARGS = [ - "--plugin", - "python3", # provide a request plugin if no embedded request plugin - os.path.join(TESTS_DIR, "static", "config.ini"), - ] + def uwsgi_ready(self): + try: + s = socket.socket() + s.connect( + ( + UWSGI_ADDR, + UWSGI_PORT, + ) + ) + except socket.error: + return False + else: + return True + finally: + s.close() + + def start_listen_server(self, args): + self.start_server(["--http-socket", UWSGI_HTTP] + args) + + # ensure server is ready + retries = 10 + while not self.uwsgi_ready() and retries > 0: + time.sleep(0.1) + retries = retries - 1 + if retries == 0: + raise RuntimeError("uwsgi test server is not available") + + def tearDown(self): + if hasattr(self._outcome, "errors"): + # Python 3.4 - 3.10 (These two methods have no side effects) + result = self.defaultTestResult() + self._feedErrorsToResult(result, self._outcome.errors) + else: + # Python 3.11+ + result = self._outcome.result + ok = not (result.errors + result.failures) + + self.testserver.send_signal(signal.SIGTERM) + if not ok: + print(self.testserver.stdout.read(), file=sys.stderr) + + self.testserver.wait() + self.testserver.stdout.close() def test_static_expires(self): + self.start_listen_server( + [ + "--plugin", + "python3", # provide a request plugin if no embedded request plugin + os.path.join(TESTS_DIR, "static", "config.ini"), + ] + ) + with requests.get(f"http://{UWSGI_HTTP}/foobar/config.ini") as r: self.assertTrue("Expires" in r.headers) From 1a3c13c4197560cf7f00cd4024c8009296398ba0 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 28 May 2024 17:49:44 +0200 Subject: [PATCH 194/207] tests: use UWSGI_PLUGINS env var to define plugins tests --- t/runner | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/t/runner b/t/runner index 8dc0dc4078..b0431b7b06 100755 --- a/t/runner +++ b/t/runner @@ -13,11 +13,21 @@ import unittest TESTS_DIR = os.path.dirname(__file__) UWSGI_BINARY = os.getenv("UWSGI_BINARY", os.path.join(TESTS_DIR, "..", "uwsgi")) +UWSGI_PLUGINS = os.getenv("UWSGI_PLUGINS", "all").split(" ") UWSGI_ADDR = "127.0.0.1" UWSGI_PORT = 8000 UWSGI_HTTP = f"{UWSGI_ADDR}:{UWSGI_PORT}" +def plugins_available(plugins): + available = False + if "all" in UWSGI_PLUGINS: + available = True + else: + available = all([plugin in UWSGI_PLUGINS for plugin in plugins]) + return available, f"{plugins} plugins not available but required for this test case" + + class UwsgiTest(unittest.TestCase): def start_server(self, args): @@ -72,6 +82,7 @@ class UwsgiTest(unittest.TestCase): self.testserver.wait() self.testserver.stdout.close() + @unittest.skipUnless(*plugins_available(["python3"])) def test_static_expires(self): self.start_listen_server( [ From 621d00619ebdd8d2e73c99c050f890c4b8bb7a19 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Mon, 9 Sep 2024 12:02:58 +0200 Subject: [PATCH 195/207] integration tests: add basic test for php, python and pypy plugins --- .github/workflows/test.yml | 8 ++++-- buildconf/integration-tests.ini | 4 +++ t/php/config.ini | 3 +++ t/pypy/config.ini | 7 +++++ t/python/helloapp.py | 3 +++ t/runner | 48 +++++++++++++++++++++++++++++++-- 6 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 buildconf/integration-tests.ini create mode 100644 t/pypy/config.ini create mode 100644 t/python/helloapp.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2d4731914b..20541b77c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,14 +22,18 @@ jobs: run: make unittests test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Install dependencies run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf \ - libpcre2-dev libjansson-dev libcap2-dev + libpcre2-dev libjansson-dev libcap2-dev \ + php-dev libphp-embed libargon2-dev libsodium-dev \ + pypy3 - uses: actions/checkout@v4 + - name: Set env + run: echo "PROFILE=integration-tests" >> $GITHUB_ENV - name: Run integration tests run: make all tests diff --git a/buildconf/integration-tests.ini b/buildconf/integration-tests.ini new file mode 100644 index 0000000000..55f4188ca7 --- /dev/null +++ b/buildconf/integration-tests.ini @@ -0,0 +1,4 @@ +[uwsgi] +inherit = base +main_plugin = +plugins = python,php,pypy diff --git a/t/php/config.ini b/t/php/config.ini index 35a6dee9f6..a94e3abd5e 100644 --- a/t/php/config.ini +++ b/t/php/config.ini @@ -1,6 +1,9 @@ [uwsgi] http-socket = :8080 http-socket-modifier1 = 14 +# required for php +need-app = false +plugins = php cache2 = name=session,items=1000,store=/tmp/uwsgi-session-cache,bitmap=1 diff --git a/t/pypy/config.ini b/t/pypy/config.ini new file mode 100644 index 0000000000..8643589c46 --- /dev/null +++ b/t/pypy/config.ini @@ -0,0 +1,7 @@ +[uwsgi] +plugin = pypy +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 diff --git a/t/python/helloapp.py b/t/python/helloapp.py new file mode 100644 index 0000000000..49f1907cc3 --- /dev/null +++ b/t/python/helloapp.py @@ -0,0 +1,3 @@ +def application(env, start_response): + start_response("200 OK", [("Content-Type", "text/html")]) + return [b"Hello World"] diff --git a/t/runner b/t/runner index b0431b7b06..42d6393343 100755 --- a/t/runner +++ b/t/runner @@ -1,4 +1,12 @@ #!/usr/bin/python3 +# +# This test suite runner runs some integration tests for uwsgi, that is +# each test launches a test server with a specific configuration and +# verifies (usually using a HTTP request) that this test server behaves as +# expected. +# +# buildconf/integration-tests.ini holds the build configuration for this +# to run fine. import os @@ -82,12 +90,12 @@ class UwsgiTest(unittest.TestCase): self.testserver.wait() self.testserver.stdout.close() - @unittest.skipUnless(*plugins_available(["python3"])) + @unittest.skipUnless(*plugins_available(["python"])) def test_static_expires(self): self.start_listen_server( [ "--plugin", - "python3", # provide a request plugin if no embedded request plugin + "python", # provide a request plugin os.path.join(TESTS_DIR, "static", "config.ini"), ] ) @@ -95,6 +103,42 @@ class UwsgiTest(unittest.TestCase): with requests.get(f"http://{UWSGI_HTTP}/foobar/config.ini") as r: self.assertTrue("Expires" in r.headers) + @unittest.skipUnless(*plugins_available(["python"])) + def test_python3_helloworld(self): + self.start_listen_server( + [ + "--plugin", + "python", + "--wsgi-file", + os.path.join(TESTS_DIR, "python", "helloapp.py"), + ] + ) + + with requests.get(f"http://{UWSGI_HTTP}/") as r: + self.assertEqual(r.text, "Hello World") + + @unittest.skipUnless(*plugins_available(["pypy"])) + def test_pypy3_helloworld(self): + self.start_listen_server( + [ + os.path.join(TESTS_DIR, "pypy", "config.ini"), + ] + ) + + with requests.get(f"http://{UWSGI_HTTP}/") as r: + self.assertEqual(r.text, "Hello World") + + @unittest.skipUnless(*plugins_available(["php"])) + def test_php_session(self): + self.start_listen_server( + [ + os.path.join(TESTS_DIR, "php", "config.ini"), + ] + ) + + with requests.get(f"http://{UWSGI_HTTP}/test.php") as r: + self.assertEqual(r.text, "PASS\n") + if __name__ == "__main__": unittest.main() From e77e41b1e6c16db9997482da47ac83e9da7d7440 Mon Sep 17 00:00:00 2001 From: Lalufu Date: Sat, 26 Oct 2024 11:13:54 +0200 Subject: [PATCH 196/207] Fix reload-on-touch/change (#2689) * Fix reload-on-touch/change As part of a fix for #2656, commit 8f1d0e5 introduced a measure to wait for processes to finish work while reloading. The code to do this affects other logic flows was well, breaking the ability to reload uwsgi when a config file changes. This moves the code around a bit so #2656 stays fixed, but reload-on-touch/change works again. Fixes #2681. * Apply suggestions from code review --------- Co-authored-by: Riccardo Magliocchetti --- core/master_utils.c | 6 ------ core/uwsgi.c | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/master_utils.c b/core/master_utils.c index d67be63945..22977c07b7 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -54,12 +54,6 @@ void uwsgi_destroy_processes() { uwsgi_detach_daemons(); - for (i = 1; i <= uwsgi.numproc; i++) { - if (uwsgi.workers[i].pid > 0) { - waitpid(uwsgi.workers[i].pid, &waitpid_status, 0); - } - } - for (i = 0; i < ushared->gateways_cnt; i++) { if (ushared->gateways[i].pid > 0) { kill(ushared->gateways[i].pid, SIGKILL); diff --git a/core/uwsgi.c b/core/uwsgi.c index 485fe4986e..36004dce3a 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -1356,6 +1356,8 @@ void kill_them_all(int signum) { // gracefully destroy void gracefully_kill_them_all(int signum) { + int waitpid_status; + if (uwsgi_instance_is_dying) return; uwsgi.status.gracefully_destroying = 1; @@ -1377,6 +1379,12 @@ void gracefully_kill_them_all(int signum) { } } + for (i = 1; i <= uwsgi.numproc; i++) { + if (uwsgi.workers[i].pid > 0) { + waitpid(uwsgi.workers[i].pid, &waitpid_status, 0); + } + } + uwsgi_destroy_processes(); } From 9c3be6bbd4049c09b4f12f41c42db451825eaa2a Mon Sep 17 00:00:00 2001 From: niol Date: Sat, 26 Oct 2024 11:35:29 +0200 Subject: [PATCH 197/207] Integration tests : more basic tests (#2684) * integration-tests: better handling of failures * integration-tests: use notfound as basic request plugin * allow --need-app=0 on command line * integration tests: rename plugins to test envvar to avoid conflict with uwsgi * integration tests: add basic test for jvm jwsgi * integration tests: add basic test for psgi * integration tests: add basic test for cgi * integration tests: add basic test for Ruby rack * integration tests: fix failure when run from another dir * fix test_classic_mountpoints with py3 (Fixes #2554) * integration tests: include manage_script_name tests * integration tests: factor test GET body code --- .github/workflows/test.yml | 3 +- buildconf/integration-tests.ini | 2 +- core/uwsgi.c | 2 +- t/cgi/hello.cgi | 6 + t/php/config.ini | 2 +- .../manage_script_name_test.ini | 4 - .../test_manage_script_name.py | 59 -------- t/python/manage_script_name/useless_app.py | 2 +- t/rack/app.ru | 9 ++ t/runner | 142 ++++++++++++++++-- t/static/config.ini | 2 +- 11 files changed, 150 insertions(+), 83 deletions(-) create mode 100755 t/cgi/hello.cgi delete mode 100644 t/python/manage_script_name/test_manage_script_name.py create mode 100644 t/rack/app.ru diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 20541b77c0..83e476188e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,7 +30,8 @@ jobs: sudo apt install --no-install-recommends -qqyf \ libpcre2-dev libjansson-dev libcap2-dev \ php-dev libphp-embed libargon2-dev libsodium-dev \ - pypy3 + pypy3 default-jdk-headless libperl-dev \ + ruby-dev ruby-rack - uses: actions/checkout@v4 - name: Set env run: echo "PROFILE=integration-tests" >> $GITHUB_ENV diff --git a/buildconf/integration-tests.ini b/buildconf/integration-tests.ini index 55f4188ca7..cf92e64a02 100644 --- a/buildconf/integration-tests.ini +++ b/buildconf/integration-tests.ini @@ -1,4 +1,4 @@ [uwsgi] inherit = base main_plugin = -plugins = python,php,pypy +plugins = notfound,python,php,pypy,jvm,jwsgi,psgi,cgi,rack diff --git a/core/uwsgi.c b/core/uwsgi.c index 36004dce3a..bbdb37e3c0 100755 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -189,7 +189,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"procname-master", required_argument, 0, "set master process name", uwsgi_opt_set_str, &uwsgi.procname_master, UWSGI_OPT_PROCNAME}, {"single-interpreter", no_argument, 'i', "do not use multiple interpreters (where available)", uwsgi_opt_true, &uwsgi.single_interpreter, 0}, - {"need-app", no_argument, 0, "exit if no app can be loaded", uwsgi_opt_true, &uwsgi.need_app, 0}, + {"need-app", optional_argument, 0, "exit if no app can be loaded", uwsgi_opt_true, &uwsgi.need_app, 0}, {"dynamic-apps", no_argument, 0, "allows apps to be dynamically loaded via uwsgi protocol", uwsgi_opt_true, &uwsgi.dynamic_apps, 0}, {"master", no_argument, 'M', "enable master process", uwsgi_opt_true, &uwsgi.master_process, 0}, {"honour-stdin", no_argument, 0, "do not remap stdin to /dev/null", uwsgi_opt_true, &uwsgi.honour_stdin, 0}, diff --git a/t/cgi/hello.cgi b/t/cgi/hello.cgi new file mode 100755 index 0000000000..b3e56cfcc2 --- /dev/null +++ b/t/cgi/hello.cgi @@ -0,0 +1,6 @@ +#!/bin/sh + +echo "Content-Type: text/plain" +echo +echo "Hello world!" +echo "PATH_INFO=${PATH_INFO}" diff --git a/t/php/config.ini b/t/php/config.ini index a94e3abd5e..b1bea5d48f 100644 --- a/t/php/config.ini +++ b/t/php/config.ini @@ -10,4 +10,4 @@ cache2 = name=session,items=1000,store=/tmp/uwsgi-session-cache,bitmap=1 php-set = session.save_handler=uwsgi php-set = session.save_path=session -php-docroot = t/php/ +php-docroot = %d diff --git a/t/python/manage_script_name/manage_script_name_test.ini b/t/python/manage_script_name/manage_script_name_test.ini index f2287768f5..3563486fce 100644 --- a/t/python/manage_script_name/manage_script_name_test.ini +++ b/t/python/manage_script_name/manage_script_name_test.ini @@ -1,8 +1,4 @@ [uwsgi] -http-socket = :8080 - -master = 1 - ; Three apps on three mountpoints wsgi-file = %d/useless_app.py diff --git a/t/python/manage_script_name/test_manage_script_name.py b/t/python/manage_script_name/test_manage_script_name.py deleted file mode 100644 index 0908b0fc3c..0000000000 --- a/t/python/manage_script_name/test_manage_script_name.py +++ /dev/null @@ -1,59 +0,0 @@ -#! /usr/bin/env python3 -# coding = utf-8 -# author = Adriano Di Luzio - -# I require requests! - -""" -First run: - $ ./uwsgi t/python/manage_script_name/manage_script_name_test.ini - -Then run me! -""" - -import unittest -import requests - -HOST = "http://127.0.0.1:8080" - - -class ManageScriptNameTest(unittest.TestCase): - - def test_classic_mountpoints(self): - mps = { - "/foo", - "/foobis/", - "/footris/" - } - - for mp in mps: - # Requests to /foo should kick-in the managed script name. - r = requests.get(HOST + mp) - self.assertEqual(r.text, mp) - - ends = mp.endswith("/") - - # And equally requests to /foo/ - r = requests.get( - HOST + mp + "/") if not ends else requests.get(HOST + mp[:-1]) - self.assertEqual(r.text, mp) - - # Or correct requests (/foo/resource) - r = requests.get( - HOST + mp + "/" + "resource") if not ends else requests.get(HOST + mp + "resource") - self.assertEqual(r.text, mp) - - def test_intriguing_mountpoints(self): - mps = { - "/fooanything", - "/foobisis/", - "/foofighters", - } - - for mp in mps: - r = requests.get(HOST + mp) - self.assertEqual(r.text, "") - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/t/python/manage_script_name/useless_app.py b/t/python/manage_script_name/useless_app.py index b19c08104c..115f4dbc9e 100644 --- a/t/python/manage_script_name/useless_app.py +++ b/t/python/manage_script_name/useless_app.py @@ -1,3 +1,3 @@ def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) - return env['SCRIPT_NAME'] + return [env['SCRIPT_NAME'].encode('ascii'), ] diff --git a/t/rack/app.ru b/t/rack/app.ru new file mode 100644 index 0000000000..6a4323e186 --- /dev/null +++ b/t/rack/app.ru @@ -0,0 +1,9 @@ +class App + + def call(environ) + [200, {'Content-Type' => 'text/html'}, ['Hello']] + end + +end + +run App.new diff --git a/t/runner b/t/runner index 42d6393343..b59966b5d4 100755 --- a/t/runner +++ b/t/runner @@ -21,7 +21,7 @@ import unittest TESTS_DIR = os.path.dirname(__file__) UWSGI_BINARY = os.getenv("UWSGI_BINARY", os.path.join(TESTS_DIR, "..", "uwsgi")) -UWSGI_PLUGINS = os.getenv("UWSGI_PLUGINS", "all").split(" ") +UWSGI_PLUGINS = os.getenv("UWSGI_PLUGINS_TEST", "all").split(" ") UWSGI_ADDR = "127.0.0.1" UWSGI_PORT = 8000 UWSGI_HTTP = f"{UWSGI_ADDR}:{UWSGI_PORT}" @@ -83,19 +83,23 @@ class UwsgiTest(unittest.TestCase): result = self._outcome.result ok = not (result.errors + result.failures) - self.testserver.send_signal(signal.SIGTERM) - if not ok: - print(self.testserver.stdout.read(), file=sys.stderr) + if hasattr(self, "testserver"): + self.testserver.send_signal(signal.SIGTERM) + if not ok: + print(self.testserver.stdout.read(), file=sys.stderr) - self.testserver.wait() - self.testserver.stdout.close() + self.testserver.wait() + self.testserver.stdout.close() + + def assert_GET_body(self, url_path, body_expected): + with requests.get(f"http://{UWSGI_HTTP}{url_path}") as r: + self.assertEqual(r.text, body_expected) - @unittest.skipUnless(*plugins_available(["python"])) def test_static_expires(self): self.start_listen_server( [ "--plugin", - "python", # provide a request plugin + "notfound", os.path.join(TESTS_DIR, "static", "config.ini"), ] ) @@ -103,6 +107,44 @@ class UwsgiTest(unittest.TestCase): with requests.get(f"http://{UWSGI_HTTP}/foobar/config.ini") as r: self.assertTrue("Expires" in r.headers) + @unittest.skipUnless(*plugins_available(["python"])) + def test_mountpoints(self): + self.start_listen_server( + [ + "--plugin", + "python", + os.path.join( + TESTS_DIR, + "python", + "manage_script_name", + "manage_script_name_test.ini", + ), + ] + ) + + mps = {"/foo", "/foobis/", "/footris/"} + + for mp in mps: + # Requests to /foo should kick-in the managed script name. + self.assert_GET_body(mp, mp) + + ends = mp.endswith("/") + + # And equally requests to /foo/ + self.assert_GET_body(f"{mp}/" if not ends else f"{mp}"[:-1], mp) + + # Or correct requests (/foo/resource) + self.assert_GET_body(f"{mp}/resource" if not ends else f"{mp}resource", mp) + + mps = { + "/fooanything", + "/foobisis/", + "/foofighters", + } + + for mp in mps: + self.assert_GET_body(mp, "") + @unittest.skipUnless(*plugins_available(["python"])) def test_python3_helloworld(self): self.start_listen_server( @@ -114,8 +156,7 @@ class UwsgiTest(unittest.TestCase): ] ) - with requests.get(f"http://{UWSGI_HTTP}/") as r: - self.assertEqual(r.text, "Hello World") + self.assert_GET_body("/", "Hello World") @unittest.skipUnless(*plugins_available(["pypy"])) def test_pypy3_helloworld(self): @@ -125,8 +166,7 @@ class UwsgiTest(unittest.TestCase): ] ) - with requests.get(f"http://{UWSGI_HTTP}/") as r: - self.assertEqual(r.text, "Hello World") + self.assert_GET_body("/", "Hello World") @unittest.skipUnless(*plugins_available(["php"])) def test_php_session(self): @@ -136,8 +176,82 @@ class UwsgiTest(unittest.TestCase): ] ) - with requests.get(f"http://{UWSGI_HTTP}/test.php") as r: - self.assertEqual(r.text, "PASS\n") + self.assert_GET_body("/test.php", "PASS\n") + + @unittest.skipUnless(*plugins_available(["jvm"])) + def test_jvm_hellworld(self): + classpath = ":".join( + [ + "/usr/share/java/uwsgi.jar", + os.path.join(TESTS_DIR, "java"), + os.path.join(TESTS_DIR, "..", "plugins", "jvm"), + ] + ) + + subprocess.call( + [ + "javac", + "-classpath", + classpath, + os.path.join(TESTS_DIR, "java", "rpc.java"), + ] + ) + + self.start_listen_server( + [ + "--need-app=0", + "--plugins", + "0:jvm,jwsgi", + "--jvm-classpath", + classpath, + "--jwsgi", + "rpc:application", + ] + ) + + self.assert_GET_body("/", "

null

") + + @unittest.skipUnless(*plugins_available(["psgi"])) + def test_psgi_helloworld(self): + self.start_listen_server( + [ + "--plugins", + "psgi", + "--psgi", + os.path.join(TESTS_DIR, "perl", "test_hello.psgi"), + ] + ) + + self.assert_GET_body("/", "Hello, world!") + + @unittest.skipUnless(*plugins_available(["cgi"])) + def test_cgi_helloworld(self): + self.start_listen_server( + [ + "--need-app=0", + "--plugins", + "0:cgi", + "--cgi", + os.path.join(TESTS_DIR, "cgi", "hello.cgi"), + ] + ) + + self.assert_GET_body( + "/foobar/say_hello", "Hello world!\nPATH_INFO=/foobar/say_hello\n" + ) + + @unittest.skipUnless(*plugins_available(["rack"])) + def test_rack_helloworld(self): + self.start_listen_server( + [ + "--plugins", + "0:rack", + "--rack", + os.path.join(TESTS_DIR, "rack", "app.ru"), + ] + ) + + self.assert_GET_body("/", "Hello") if __name__ == "__main__": diff --git a/t/static/config.ini b/t/static/config.ini index e0e23c55ed..b6bedc0ed5 100644 --- a/t/static/config.ini +++ b/t/static/config.ini @@ -1,4 +1,4 @@ [uwsgi] need-app = False -static-map = /foobar=t/static +static-map = /foobar=%d static-expires-uri = ^/foobar/ 315360000 From 89cb161cda959697a4afe013f348b06646b960aa Mon Sep 17 00:00:00 2001 From: Yong-Siang Shih Date: Sat, 26 Oct 2024 05:39:02 -0400 Subject: [PATCH 198/207] Fix pip install static library not found errors for conda (#2683) Under certain circumstances Py_ENABLE_SHARED would be False but the static library cannot be found. For example, if you use conda environments or if you use specific versions of MacOS #2109, #2270. pip install would fail in such a situation. In this PR, we adjust the configurations so that the installation script would attempt to use shared library instead of the static one if no static library is found. This fixes pip install for Python 3.10.15 | packaged by conda-forge. It's not clear if this would fix the MacOS problems mentioned in the above issues though, as I am unable to verify those problems. --- plugins/python/uwsgiplugin.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/plugins/python/uwsgiplugin.py b/plugins/python/uwsgiplugin.py index 7de8457376..47d30dd11b 100644 --- a/plugins/python/uwsgiplugin.py +++ b/plugins/python/uwsgiplugin.py @@ -45,7 +45,8 @@ def get_python_version(): if 'UWSGI_PYTHON_NOLIB' not in os.environ: LIBS = sysconfig.get_config_var('LIBS').split() + sysconfig.get_config_var('SYSLIBS').split() # check if it is a non-shared build (but please, add --enable-shared to your python's ./configure script) - if not sysconfig.get_config_var('Py_ENABLE_SHARED'): + use_static_lib = not sysconfig.get_config_var('Py_ENABLE_SHARED') + if use_static_lib: libdir = sysconfig.get_config_var('LIBPL') # libdir does not exists, try to get it from the venv version = get_python_version() @@ -75,13 +76,17 @@ def get_python_version(): libpath = '%s/%s' % (libdir, sysconfig.get_config_var('LIBRARY')) if not os.path.exists(libpath): libpath = '%s/libpython%s.a' % (libdir, version) - LIBS.append(libpath) - # hack for messy linkers/compilers - if '-lutil' in LIBS: - LIBS.append('-lutil') - if '-lrt' in LIBS: - LIBS.append('-lrt') - else: + + if os.path.exists(libpath): + LIBS.append(libpath) + # hack for messy linkers/compilers + if '-lutil' in LIBS: + LIBS.append('-lutil') + if '-lrt' in LIBS: + LIBS.append('-lrt') + else: + use_static_lib = False + if not use_static_lib: try: libdir = sysconfig.get_config_var('LIBDIR') except Exception: From 4616b8feb652a4027ee06e9af939e51cdbfe5e19 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 28 Jan 2025 11:30:04 +0100 Subject: [PATCH 199/207] fix pypy tests when run from another dir --- t/pypy/config.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/pypy/config.ini b/t/pypy/config.ini index 8643589c46..1e9e71bd6c 100644 --- a/t/pypy/config.ini +++ b/t/pypy/config.ini @@ -3,5 +3,5 @@ plugin = pypy 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 From ceb027f22aa300a58769f158b950e88522f623c6 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Sat, 1 Feb 2025 21:32:48 +0100 Subject: [PATCH 200/207] plugins/rack: add support for rack 3 (#2704) --- plugins/rack/rack_plugin.c | 24 ++++++++++++++---------- t/rack/app.ru | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/plugins/rack/rack_plugin.c b/plugins/rack/rack_plugin.c index 1611deecde..792cc4dc78 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 6a4323e186..4603375e7d 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 From 4a05d335484961f3c3c9be48ba0ffcfdfbf1ae09 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Sun, 30 Mar 2025 16:46:29 +0200 Subject: [PATCH 201/207] ci: don't install distutils in Python 3.12 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 83e476188e..83112fce96 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,7 +54,7 @@ jobs: libpcre2-dev libjansson-dev libcap2-dev \ curl - name: Install distutils - if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11","3.12"]'), matrix.python-version) + if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11"]'), matrix.python-version) run: | sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-distutils \ - uses: actions/checkout@v4 From e265f9fb1931bdba63e135cab427ea6b6f3640f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Li=C5=A1ka?= Date: Sat, 5 Apr 2025 21:47:58 +0200 Subject: [PATCH 202/207] uwsgiconfig: disable executable stack The note is newly printed with latest binutils master: [ 14s] [gcc] legion_cache_fetch_plugin.so [ 14s] build time: 0 seconds [ 20s] /usr/lib64/gcc/x86_64-suse-linux/13/../../../../x86_64-suse-linux/bin/ld: warning: plugins/pypy/pypy_setup.py.o: missing .note.GNU-stack section implies executable stack [ 20s] /usr/lib64/gcc/x86_64-suse-linux/13/../../../../x86_64-suse-linux/bin/ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker [ 28s] *** legion_cache_fetch plugin built and available in legion_cache_fetch_plugin.so *** Please use -z noexecstack linker option in order to silent the warning. Otherwise, libffi_plugin.so will have executable stack enabled. --- uwsgiconfig.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uwsgiconfig.py b/uwsgiconfig.py index e390695991..4f6c8126d6 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -512,7 +512,7 @@ def build_uwsgi(uc, print_only=False, gcll=None): 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') @@ -1164,7 +1164,7 @@ def get_gcll(self): 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)) @@ -1183,7 +1183,7 @@ def get_gcll(self): 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: @@ -1193,7 +1193,7 @@ def get_gcll(self): 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)) From 66b12ce4a0c7323c1e76fd7987c0b17120e29d1e Mon Sep 17 00:00:00 2001 From: Juho Heikkinen Date: Tue, 6 Dec 2016 11:14:29 +0100 Subject: [PATCH 203/207] core/reader: fix uwsgi_request_body_readline without new lines found The language-independent readline implementation in uwsgi's core/reader.c appears to contain a flaw causing it to append extra bytes from the readline buffer to the final line returned, if the request body is not terminated by a newline. Reproducing this seems to require that the request body is at least 4KB in size. While this reproduction only causes extra data from the readline buffer to be returned, we have also seen cases where the returned extra bytes seem to be other unrelated data from the process heap. This has probably been caused by consume_body_for_readline() having realloced the buffer just before. Fix #1412 Thanks to Jonas Smedegaard for forwarding. xrmx: converted tests in python --- core/reader.c | 2 +- t/core/readline/__init__.py | 0 t/core/readline/app.py | 16 ++++++++++++++++ t/core/readline/client.py | 8 ++++++++ t/core/readline/requirements.txt | 2 ++ 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 t/core/readline/__init__.py create mode 100644 t/core/readline/app.py create mode 100644 t/core/readline/client.py create mode 100644 t/core/readline/requirements.txt diff --git a/core/reader.c b/core/reader.c index 7a6c4728ce..bc8e770a80 100644 --- a/core/reader.c +++ b/core/reader.c @@ -283,7 +283,7 @@ char *uwsgi_request_body_readline(struct wsgi_request *wsgi_req, ssize_t hint, s } // 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 --git a/t/core/readline/__init__.py b/t/core/readline/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/t/core/readline/app.py b/t/core/readline/app.py new file mode 100644 index 0000000000..168d6d01dd --- /dev/null +++ b/t/core/readline/app.py @@ -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 --git a/t/core/readline/client.py b/t/core/readline/client.py new file mode 100644 index 0000000000..0a8734d435 --- /dev/null +++ b/t/core/readline/client.py @@ -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 --git a/t/core/readline/requirements.txt b/t/core/readline/requirements.txt new file mode 100644 index 0000000000..82b955f6ef --- /dev/null +++ b/t/core/readline/requirements.txt @@ -0,0 +1,2 @@ +requests +Werkzeug From dbc28f076885f75c8492558838fb2ae1b48135fb Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 18 Feb 2025 08:52:22 +0100 Subject: [PATCH 204/207] fix build with gcc-15 and -Wincompatible-pointer-types see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1098052 https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1098054 --- core/fifo.c | 22 +++++++++++----------- core/master_utils.c | 2 +- plugins/fiber/fiber.c | 4 ++-- plugins/pypy/pypy_plugin.c | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/fifo.c b/core/fifo.c index 45b051138c..172f06dc88 100644 --- a/core/fifo.c +++ b/core/fifo.c @@ -78,25 +78,25 @@ void uwsgi_master_fifo_prepare() { 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 --git a/core/master_utils.c b/core/master_utils.c index 22977c07b7..d147f2d6c4 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -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 --git a/plugins/fiber/fiber.c b/plugins/fiber/fiber.c index 30603ae099..d68f7ccac7 100644 --- a/plugins/fiber/fiber.c +++ b/plugins/fiber/fiber.c @@ -15,12 +15,12 @@ struct uwsgi_option fiber_options[] = { }; -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 --git a/plugins/pypy/pypy_plugin.c b/plugins/pypy/pypy_plugin.c index 62d177dd3f..f674537004 100644 --- a/plugins/pypy/pypy_plugin.c +++ b/plugins/pypy/pypy_plugin.c @@ -303,7 +303,7 @@ static void uwsgi_pypy_enable_threads() { } } -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(); From 5d4dfdd56efa81453480cd7465b2e7e9fa829bf6 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Mon, 7 Apr 2025 10:18:13 +0200 Subject: [PATCH 205/207] fix build with gcc-15 and -Werror=unterminated-string-initialization --- core/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/init.c b/core/init.c index 62dfb0a8c1..267a3fe3f2 100644 --- a/core/init.c +++ b/core/init.c @@ -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; }; From e0bdbf5d95dc18035f68d58e613766453b088f29 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 9 Apr 2025 09:14:48 +0200 Subject: [PATCH 206/207] ci: run everything on ubuntu 22.04 Since 20.04 is gone. This requires to drop tests against 3.6 and bump ruby. --- .github/workflows/compile-test.yml | 8 +------- .github/workflows/test.yml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.github/workflows/compile-test.yml b/.github/workflows/compile-test.yml index 6b56aea31d..71678caa4e 100644 --- a/.github/workflows/compile-test.yml +++ b/.github/workflows/compile-test.yml @@ -11,18 +11,12 @@ jobs: strategy: matrix: libpcre: ["libpcre3-dev", "libpcre2-dev"] - os: ["ubuntu-20.04", "ubuntu-22.04"] + os: ["ubuntu-22.04"] cc: [gcc, clang] include: - - os: ubuntu-20.04 - php: "php7.4" - php-config: "php-config7.4" - os: ubuntu-22.04 php: "php8.1" php-config: "php-config8.1" - exclude: - - os: ubuntu-20.04 - cc: "clang" runs-on: ${{ matrix.os }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 83112fce96..7f6ae1ba6f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,13 +9,13 @@ on: jobs: unittest: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Install dependencies run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf \ - libpcre2-dev libjansson-dev libcap2-dev \ + libpcre3-dev libjansson-dev libcap2-dev \ check - uses: actions/checkout@v4 - name: Run unit tests @@ -39,10 +39,10 @@ jobs: run: make all tests python: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: - python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["2.7", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] test-suite: [python, deadlocks] steps: - name: Add deadnakes ppa @@ -54,7 +54,7 @@ jobs: libpcre2-dev libjansson-dev libcap2-dev \ curl - name: Install distutils - if: contains(fromJson('["3.6","3.7","3.8","3.9","3.10","3.11"]'), matrix.python-version) + if: contains(fromJson('["3.7","3.8","3.9","3.10","3.11"]'), matrix.python-version) run: | sudo apt install --no-install-recommends -qqyf python${{ matrix.python-version }}-distutils \ - uses: actions/checkout@v4 @@ -73,16 +73,16 @@ jobs: ./tests/gh-${{ matrix.test-suite }}.sh ${PYTHON_VERSION} rack: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: - rack-version: ["270"] + rack-version: ["300"] steps: - name: Install dependencies run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf python3-dev \ - libpcre2-dev libjansson-dev libcap2-dev ruby2.7-dev \ + libpcre2-dev libjansson-dev libcap2-dev ruby3.0-dev \ curl - uses: actions/checkout@v4 - name: Build uWSGI binary From 4e3e534420111801562a758ab25a88b3031b213e Mon Sep 17 00:00:00 2001 From: wszak Date: Fri, 15 Nov 2024 17:31:34 +0100 Subject: [PATCH 207/207] plugins/python: fix reload-os-env=true Fixes the following stacktrace with the options enabled: Traceback (most recent call last): File "/srv/knip/development/KnipKnap/lib/python3.4/os.py", line 637, in __setitem__ key = self.encodekey(key) File "/srv/knip/development/KnipKnap/lib/python3.4/os.py", line 706, in encode raise TypeError("str expected, not %s" % type(value).__name__) TypeError: str expected, not bytes --- plugins/python/pyloader.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/plugins/python/pyloader.c b/plugins/python/pyloader.c index dc816fa102..b4853939dd 100644 --- a/plugins/python/pyloader.c +++ b/plugins/python/pyloader.c @@ -129,21 +129,33 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre 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; } - env_value = PyString_FromString(p+1); +#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); continue; } - + #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)) { @@ -209,7 +221,7 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre if (multiapp < 1) { uwsgi_log("you have to define at least one app in the applications dictionary\n"); goto doh; - } + } PyObject *app_mnt = PyList_GetItem(app_list, 0); if (!PyString_Check(app_mnt)) { @@ -228,7 +240,7 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre if (PyString_Check((PyObject *) wi->callable)) { PyObject *callables_dict = get_uwsgi_pydict((char *)arg1); if (callables_dict) { - wi->callable = PyDict_GetItem(callables_dict, (PyObject *)wi->callable); + wi->callable = PyDict_GetItem(callables_dict, (PyObject *)wi->callable); if (!wi->callable) { uwsgi_log("skipping broken app %s\n", wsgi_req->appid); goto multiapp; @@ -377,7 +389,7 @@ int init_uwsgi_app(int loader, void *arg1, struct wsgi_request *wsgi_req, PyThre multiapp: if (multiapp > 1) { for(i=1;i