Compare commits

...
155 Commits
Author SHA1 Message Date
Unbit 7604c67018 ready to release 2.0.4 2014-04-22 08:22:00 +02:00
Roberto De Ioris afc962709f fixed respawn when in on-demand mode 2014-04-21 07:49:57 +02:00
Unbit c11e0654a0 added php session support over cache 2014-04-19 18:42:52 +02:00
Unbit 6e4f47cc76 the corerouter error reporting is still wrong :( 2014-04-19 11:36:06 +02:00
Unbit 20c9f0ef40 another error report fix for the corerouter 2014-04-19 11:25:52 +02:00
Unbit ef3c8a5bd9 fixed tornado plugin 2014-04-19 11:01:14 +02:00
Unbit 76d0438fee added asyncio build profile 2014-04-19 10:58:50 +02:00
Unbit 18fd338908 fixed corerouter error reporting 2014-04-19 10:27:58 +02:00
Unbit 2b35e95706 removed some useless debug 2014-04-19 09:29:43 +02:00
Unbit 4761146599 the asyncio plugin (experimental) 2014-04-19 09:21:19 +02:00
Unbit 3924cc639e added Yu Zhao to CONTRIBUTORS 2014-04-18 06:29:29 +02:00
unbit 5f09de0469 Merge pull request #597 from getcwd/lru
Implement LRU for cache framework
2014-04-18 06:28:10 +02:00
Yu Zhao 4f0e3fb79f Implement LRU for cache framework
If "purge_lru" option is used when creating cache store, the
"expires" parameter for cache_set/cache_update will be ignored.
Instead, all items are placed on an LRU list and when the store
is full, the least recent used one will be evicted.
2014-04-17 20:26:41 -07:00
unbit 05b6baf173 Merge pull request #594 from getcwd/master
cache/sweeper ehancement
2014-04-16 07:03:53 +02:00
Yu Zhao 149823aac4 Only scan cached items when there is expired one
This is an optimization for the sweeper algorithm. By remembering
the least expiration time, we can determine whether we need to go
over all items in a cache store. If we have to, we find the new
least expiration time by examining those that are not expired yet.
2014-04-15 13:57:57 -07:00
Yu Zhao 85d86dc10e Only allocate one sweeper thread
Thread is not cheap on Linux since it involves context switch in the
kernel. And allocating multiple threads is not necessary even on SMP
since the granularity of time is second.
2014-04-15 10:58:29 -07:00
Yu Zhao e97d921909 Optimization for sweeper creation loop
cache_no_expire is a global flag, if it's set, return without further
check when trying to create sweepers.
2014-04-15 08:56:34 -07:00
unbit c5e20bf4da Merge pull request #593 from getcwd/patch-1
add ${HTTPS} to https_socket
2014-04-15 06:15:45 +02:00
getcwd a09d25ccaa add ${HTTPS} to https_socket
https plugin sets ${HTTPS} so should https_socket proto -- admins can write consistent HTTP/HTTPS routing rules for both.
2014-04-14 14:22:31 -07:00
Unbit bf01cea062 added micros() api function 2014-04-12 18:34:57 +02:00
unbit b6459be3e4 Merge pull request #591 from prymitive/ssl_opts
always generate key if (EC)DH is used
2014-04-12 12:25:29 +02:00
Łukasz Mierzwa d42788c17b always generate key if (EC)DH is used 2014-04-12 11:16:05 +02:00
Unbit 872730c116 added mongodb2 imperial monitor 2014-04-11 11:15:17 +02:00
Unbit 6fce0aee84 fixed a typo 2014-04-11 10:36:08 +02:00
Unbit e3d8a02614 add a memo for future offload improvements 2014-04-09 11:24:02 +02:00
Unbit b58696399e added support for http proxy connect in router_http 2014-04-09 10:49:17 +02:00
Unbit 519c9f58b3 Merge branch 'master' of ssh://github.com/unbit/uwsgi 2014-04-09 05:53:48 +02:00
Unbit 2c77c5c639 fixed http and uwsgi internal routers (by Guido Notari) 2014-04-09 05:53:26 +02:00
unbit fb9e5a1891 Merge pull request #587 from prymitive/lseek
check lseek return value in uwsgi_check_logrotate()
2014-04-06 20:11:20 +02:00
unbit 5aa7d1a79f Merge pull request #586 from prymitive/logto2
uwsgi_setup_log_master must always be called after logto()
2014-04-06 20:10:38 +02:00
Łukasz Mierzwa bc3442ba29 uwsgi_setup_log_master must always be called after logto(), this was not happening with --logto2 2014-04-06 15:08:03 +02:00
Łukasz Mierzwa 3061e36089 check lseek return value in uwsgi_check_logrotate() 2014-04-06 15:03:17 +02:00
Unbit 4f8c41cf61 fix headers parsing in http router 2014-04-06 10:15:24 +02:00
unbit b0065c379e Merge pull request #585 from stestagg/master
Fix uwsgi_route_var_mime
2014-04-05 15:58:06 +02:00
Steve Stagg 57f71c974d Fix some bugs introduced in c57baae477 preventing the 'mime' routing function from working with request variables, and actually return the found mimetype
The uwsgi_route_var_mime function had two errors preventing it from (afaics ever) working:
 1. Redefine the 'ret' variable inside an if block. This meant that if the uwsgi_get_mime_type function ever actually found a matching mimetype, the length of the matching value would be corectly set, but the function returns NULL (the value of the function-level ret variable)
 2. The function was looking up the request variable correctly, BUT then passing in the raw config string to uwsgi_get_mime_type NOT the resolved varible value, making the lookup just a waste of cycles.
2014-04-05 13:36:53 +01:00
Unbit b820fefbbd prepare for 2.0.4 2014-04-05 10:15:02 +02:00
Unbit 5f759f0d26 allow duplicate headers in http router 2014-04-05 10:14:11 +02:00
Unbit f7eb9ed1cd improved on_demand mode 2014-04-05 08:56:39 +02:00
Unbit 3a82732b19 advanced http timeout management 2014-04-05 07:57:01 +02:00
Unbit d74e826f71 another patch for reducing emperor race conditions 2014-04-02 09:15:05 +02:00
unbit c846a9b388 Merge pull request #582 from prymitive/cache_full_ignore
allow disabling cache warnings
2014-04-02 00:33:49 +02:00
Łukasz Mierzwa 001e4b6e39 allow disabling cache warnings in --cache2 2014-04-01 20:00:13 +02:00
Unbit a51a396f47 fixed #581 2014-04-01 12:04:31 +02:00
Unbit 17965ea870 faster restart of vassals 2014-04-01 10:52:32 +02:00
Unbit 8a032b41ec combine headers with the same name in HTTP native mode 2014-03-31 10:32:18 +02:00
Roberto De Ioris a489cfc2c1 fixed additional sources 2014-03-28 07:08:15 +01:00
unbit 3b32672ff9 Merge pull request #579 from prymitive/spdy_fix
merge duplicated headers when SPDY is enabled, fixes #572
2014-03-28 06:18:26 +01:00
Łukasz Mierzwa 858131ba57 merge duplicated headers when SPDY is enabled, fixes #572 2014-03-27 21:32:51 +01:00
Unbit 8a8d2f8675 support embedded config on FreeBSD 2014-03-26 16:54:31 +01:00
Unbit 9d37725323 added setmodifier1 and setmodifier2 2014-03-23 07:26:14 +01:00
Unbit e973492267 added no_headers option to keyval based static routers, fixes #576 2014-03-22 18:23:18 +01:00
Unbit 105a68bce9 fixed fatal hooks management 2014-03-22 13:10:00 +01:00
Unbit 0587a0ca53 fixed #557 for --lazy-apps 2014-03-22 13:01:14 +01:00
Unbit d4b259d5e0 fixed #575 2014-03-20 05:48:05 +01:00
Unbit 9e591c587f added rpc hook 2014-03-20 05:38:39 +01:00
Unbit e7cce7b785 fixed segfault for unnamed loggers 2014-03-18 16:16:12 +01:00
unbit 3285b224c2 Merge pull request #573 from prymitive/spdy_fix
Lowercase only header keys, not values.
2014-03-17 09:07:20 +01:00
Roberto De Ioris e4a4942980 prepare for 2.0.3 release 2014-03-17 06:33:04 +01:00
Roberto De Ioris 6f671936fc added uwsgi::add_var to perl 2014-03-17 06:18:15 +01:00
Roberto De Ioris 7d1d992d58 fix tnetstring support #562 2014-03-17 06:02:44 +01:00
Łukasz Mierzwa 2bba4cf15a Lowercase only header keys, not values. Fixes #572 2014-03-16 23:43:28 +01:00
unbit 930f71c562 Merge pull request #571 from avar/avar/fix-2.0.1-perl-refactoring-regression
perl: fix one-off error in 2.0-103-gf041d10 causing warnings in e.g. Pla...
2014-03-16 05:53:54 +01:00
unbit 959bb11f77 Merge pull request #570 from avar/avar/add-sanity-check-to-offset-test
perl: add sanity checks to t/perl/test_input_with_offset.pl
2014-03-16 05:52:19 +01:00
Ævar Arnfjörð Bjarmason 13d8505738 perl: fix one-off error in 2.0-103-gf041d10 causing warnings in e.g. Plack::Request
This newly added support for read() offsets started causing "Use of
uninitialized value in subroutine entry" warnings.

This is all because there was a test for the number of items on the
stack, which ignored that the first argument is always the object, so 3
arguments to read() actually yields 4 arguments on the stack, not 3.

As a result we'd be calling SvIV() on a stack item that wasn't actually
passed in.
2014-03-15 21:05:18 +00:00
Ævar Arnfjörð Bjarmason 93fe463ca8 perl: add sanity checks to t/perl/test_input_with_offset.pl 2014-03-15 20:41:54 +00:00
unbit 5c64c21745 Merge pull request #569 from avar/avar/add-generated-file-to-gitignore
.gitignore: add /core/config_py.c to ignore
2014-03-15 15:16:12 +01:00
Ævar Arnfjörð Bjarmason f825ab542d .gitignore: add /core/config_py.c to ignore
This file added in e4903ee is generated by the build system and should
be in the ignore file.
2014-03-15 14:15:35 +00:00
unbit e744aef144 Merge pull request #568 from avar/avar/perl-add-streaming-test
perl: add a trivial streaming test program
2014-03-15 15:15:23 +01:00
Ævar Arnfjörð Bjarmason 4ded485a8e perl: add a trivial streaming test program
This came out of working on this debugging session:
https://github.com/unbit/uwsgi/issues/567
2014-03-15 14:10:20 +00:00
unbit 2ce5ec3986 Merge pull request #566 from avar/avar/psgi-spelling-error-fix
perl: fix trivial spelling error in psgi_loader.c error message
2014-03-15 14:48:35 +01:00
Ævar Arnfjörð Bjarmason af95f90fb0 perl: fix trivial spelling error in psgi_loader.c error message 2014-03-15 13:47:07 +00:00
unbit 7989dc748b Update CONTRIBUTORS 2014-03-15 14:36:51 +01:00
unbit f85f3e8178 Merge pull request #565 from avar/avar/fix-perl-stacktrace
perl: fix a regression with caller() not indicating the starting *.psgi ...
2014-03-15 14:28:14 +01:00
Ævar Arnfjörð Bjarmason 97809192c8 perl: fix a regression with caller() not indicating the starting *.psgi program
In 2.0.1-41-g3480c30 I introduced a regression with how the top-level
stackframe would appear within Perl programs. Before we'd show the
filename of the *.psgi file, but after we just showed "-e".

We can retain the bugfix I added in 2.0.1-41-g3480c30 while having a
sensible stacktrace by overriding the file via the #line directive.
2014-03-15 13:10:30 +00:00
Roberto De Ioris 5f74e6d964 fixed stupid bug with python add_var 2014-03-15 12:51:15 +01:00
Roberto De Ioris ae87fdd0f7 added uwsgi.add_var 2014-03-15 12:47:59 +01:00
Roberto De Ioris 36c44c83af added disableheaders routing action 2014-03-15 12:04:01 +01:00
Roberto De Ioris 48e13bfc65 add a check for already initialized python vm 2014-03-15 08:48:07 +01:00
Unbit 16cd55637d added support for mongrel2 URL_SCHEME #562 2014-03-14 11:48:36 +01:00
Unbit 39cf2887c4 added SIGURG to the Emperor 2014-03-14 11:28:53 +01:00
Unbit 671bf8a258 try to avoid vassal pipe to have invalid fd numbers 2014-03-14 11:21:20 +01:00
Unbit 89a8ceea81 allows building plugins from git repositories 2014-03-13 14:11:12 +01:00
Roberto De Ioris 6699f4046e better error reporting on websockets errors 2014-03-13 07:35:18 +01:00
Roberto De Ioris 88474b3d96 do not wait for a whole Emperor cycle before checking for each waitpid 2014-03-13 07:03:50 +01:00
unbit 32b6463d06 Merge pull request #559 from hynek/master
Set ECDHE curve independently from DHE parameters
2014-03-13 06:19:11 +01:00
Hynek Schlawack 63cc29a61d Set ECDHE curve independently from DHE parameters 2014-03-12 21:19:52 +01:00
Unbit d58f79eeab fixed #558 2014-03-11 16:18:32 +01:00
Unbit 7b24d2f5de allows vassals to request a config chunk from the emperor 2014-03-11 07:04:23 +01:00
Unbit a93db73d0c Merge branch 'master' of github.com:unbit/uwsgi 2014-03-10 14:52:35 +01:00
Unbit c118c75bfe beter management of failed emperor kills 2014-03-10 14:52:30 +01:00
Unbit 624631d443 fixed tuntap 2014-03-08 13:57:15 +01:00
Unbit b681eab385 fixed SIGWINCH in emperor and added emperor rescan master fifo command 2014-03-08 08:13:30 +01:00
Unbit 91dfe779ff fixed typo 2014-03-08 06:38:50 +01:00
Unbit 0ad50711c0 report key in corerouters 2014-03-08 06:36:38 +01:00
Unbit df6ed26479 more debug for tuntap 2014-03-08 06:24:45 +01:00
Unbit d08778b39c removed useless tuntap debug 2014-03-08 05:59:59 +01:00
Unbit 0c8c2932cf Merge branch 'master' of https://github.com/unbit/uwsgi 2014-03-08 05:44:05 +01:00
Unbit 131bd7da2f try to improve nb tuntap gateway 2014-03-08 05:43:54 +01:00
Unbit d247424442 more useful log alarm 2014-03-07 16:09:04 +01:00
Roberto De Ioris 332fab2150 hack for avoiding libmongoclient to crash on broken cursor 2014-03-07 07:31:42 +01:00
Roberto De Ioris 62d74951db support for git-based plugins 2014-03-05 08:28:17 +01:00
Roberto De Ioris 839638a0ad report socket name on invalid zeromq syntax 2014-03-04 06:38:42 +01:00
Roberto De Ioris 536ae8d013 fixed mongrel2 post_fork hook 2014-03-04 06:32:40 +01:00
Roberto De Ioris 367c4b9bab added on_demand sockets support to mongodb Emperor plugin 2014-03-04 06:08:16 +01:00
Roberto De Ioris 3965c01880 on __APPLE__ use LOG_NOTICE for syslog plugin 2014-03-04 05:44:18 +01:00
Unbit 7e4341a7cd force on demand unix socket to 666 2014-03-02 10:14:58 +01:00
Unbit 8c118c8f62 fixed (and improved) on demand emperor socket 2014-03-02 09:36:17 +01:00
Unbit 5d76b63fac fixed #555 2014-03-01 08:25:35 +01:00
Unbit b0159ea23b prepare for 2.0.2 2014-02-26 17:06:47 +01:00
Unbit d3493df032 do not expode if Devel::StackTrace is missing 2014-02-26 17:06:21 +01:00
Unbit 757c76b569 fix -bash in perl hooks 2014-02-26 12:12:41 +01:00
Unbit 30eebfd335 better perl do usage and --perl-no-plack flag 2014-02-26 12:07:00 +01:00
Unbit c5d7d30439 try to better address #553 2014-02-26 11:39:00 +01:00
unbit 04bc3e117d Merge pull request #553 from avar/avar/do-not-run-psgi-begin-blocks-twice
perl: Don't run BEGIN blocks twice in the provided *.psgi
2014-02-26 08:24:11 +01:00
Ævar Arnfjörð Bjarmason 3480c30674 perl: Don't run BEGIN blocks twice in the provided *.psgi
The psgi loaded was calling perl_parse() with the script ostensibly to
set up xsinit.

However it would also call perl_parse() with the path to our *.psgi
file, whith the result that any BEGIN block in the *.psgi file would be
run twice, but anything outside BEGIN blocks would only run once.

This means that any code within explicit BEGIN blocks will run twice,
and any "use" statement in the *.psgi file will run its import() routine
twice, but due to the module being in %INC already we won't actually
compile things twice.

The previous behavior dates all the way back to the initial introduction
of the PSGI plugin in 299fd9c.

Then when support for local::lib was added in 7cbe751 we initially did a
perl_eval_pv() of a "use" statement like I'm doing here again now, but
later on in 1561dd3 changed it to call perl_parse with the commit
message "another PSGI loading fix".

Since there's no info on what that fixed or what was broken before I
have no idea if I'm introducing a regression here, but I don't see why
this way of loding local::lib shouldn't work, and it correctly munges
@INC for me when I try it.

We may still have this bug in the remaining perl_parse() calls that
remain for supporting "preinit" and "mule".

I haven't tested those modes (I don't use them), but when we load the
Perl apps we should only perl_parse() once with -e1, and then
perl_eval_pv() to actually load the application. We should not call
perl_parse() on code that we're just about to perl_eval_pv(), or we'll
run into this bug.

To test this just run:

    ./uwsgi --http 127.0.0.1:8080 --psgi ./t/perl/test.psgi

It'll no longer PANIC on the BEGIN block being run twice now, at least
in that simplistic non-"preinit" non-"mule" mode.
2014-02-25 18:07:42 +00:00
Unbit a7e6dfc469 fixed python 3 on old compilers, fixes #551 2014-02-25 07:05:46 +01:00
Unbit 5e276b43ad fix poll() usage in #546 2014-02-24 09:14:14 +01:00
Roberto De Ioris 9e6a21602a more cgi improvements 2014-02-22 11:40:23 +01:00
Roberto De Ioris 952c4ce854 minor fixes in non-blocking reads 2014-02-22 11:13:50 +01:00
Roberto De Ioris 951f24e711 fixed #546 2014-02-22 10:46:46 +01:00
Unbit 7bda4c9b63 allow starting in spooler mode only 2014-02-21 08:48:21 +01:00
Unbit 1b25516ced spawn spoolers if requested 2014-02-21 08:42:22 +01:00
Unbit b3020e60f9 fixed long standing bug with caches 2014-02-20 14:19:40 +01:00
Unbit 808d37f33a fixed ftime log var 2014-02-20 10:52:05 +01:00
Unbit 8c8a7f77d1 added async remote signal management 2014-02-20 10:48:30 +01:00
Unbit 3f601af740 fix end-for and end-if 2014-02-20 10:28:30 +01:00
Unbit 7b901ab81e fixed #536 2014-02-20 09:19:41 +01:00
Roberto De Ioris 930eb1e6aa fixed #541 2014-02-20 07:52:47 +01:00
Unbit 77e5931da7 avoid loop in response and error internal routing 2014-02-15 09:35:38 +01:00
Unbit 3e6ddbb3d5 added --php-exec-before/--php-exec-after 2014-02-15 08:59:56 +01:00
Unbit 623bce274a prepare for 2.0.2 2014-02-15 07:31:39 +01:00
Unbit 6c986f8082 fixed pypy execute_source usage 2014-02-15 07:30:17 +01:00
Unbit 10a02064ba Merge branch 'master' of ssh://github.com/unbit/uwsgi 2014-02-13 06:25:18 +01:00
Unbit 774f5a81a5 added --geoip-use-disk, fixes #519 2014-02-13 06:25:07 +01:00
unbit e270593cc0 Merge pull request #533 from ramm/master
typo fix
2014-02-13 06:24:13 +01:00
Unbit b559354845 more tests for bitmap cache 2014-02-13 06:16:02 +01:00
Unbit 46a2fefb04 added overlap check for cache 2014-02-13 06:06:29 +01:00
Unbit c6c9baa7b5 initial test suite for bitmap cache mode 2014-02-12 18:54:39 +01:00
Danila Shtan c37710f4c2 Add myself to contributors
as agreed at https://github.com/unbit/uwsgi/pull/530#issuecomment-34859953
2014-02-12 22:35:10 +05:00
Danila Shtan 943c20b88c typo fix 2014-02-12 22:33:40 +05:00
Unbit a442614db0 fixed #531 2014-02-12 17:00:47 +01:00
Unbit 5830af37ba restore back needed_bytes 2014-02-12 16:25:02 +01:00
unbit de4c563169 Merge pull request #530 from ramm/master
fixed bitmap allocation bugs
2014-02-12 16:22:56 +01:00
Danila Shtan 77f008e24c fix unmark interval inside cache_update 2014-02-12 18:20:39 +05:00
Danila Shtan df0bd832b4 bitmap allocation fix
fix cache_mark offset bug and change cache_unmark according to previous changes)
2014-02-12 17:36:40 +05:00
Danila Shtan 07c42c262f fix comment typos 2014-02-12 15:44:41 +05:00
Danila Shtan cb131836ae remove extra bit in bitmap_pos optimisation
looks like it was added because there was an error in counting last_byte_bit position in cache_mark_blocks()
2014-02-12 15:42:46 +05:00
Danila Shtan 2c6855a969 fix bitmap allocation 2014-02-12 15:37:44 +05:00
Roberto De Ioris 79ca1940de fixed #527 2014-02-12 06:59:24 +01:00
Roberto De Ioris d46879dca9 fixed #528 2014-02-12 06:33:47 +01:00
Roberto De Ioris 9b889f7b77 another spooler-related fix (fd leak) 2014-02-12 06:28:49 +01:00
Roberto De Ioris 1128d09187 fixed #526 2014-02-12 06:05:29 +01:00
Roberto De Ioris d3d4d04fac fixed #524 2014-02-11 06:27:01 +01:00
unbit 4a554db63b Merge pull request #521 from methane/patch-1
logpipe: Don't setsid() twice.
2014-02-10 12:02:27 +01:00
INADA Naoki 8b799b4f9f logpipe: Don't setsid() twice. 2014-02-10 20:00:50 +09:00
75 changed files with 2611 additions and 700 deletions
+1
View File
@@ -7,6 +7,7 @@
/uwsgi
/uwsgibuild.*
/core/config_py.c
/t/ring/target
+3
View File
@@ -25,3 +25,6 @@ Mingli Yuan
Natanael Copa
Roberto Leandrini
Ryan Petrello
Danila Shtan <danila@shtan.ru>
Ævar Arnfjörð Bjarmason
Yu Zhao (getcwd)
+3
View File
@@ -0,0 +1,3 @@
[uwsgi]
main_plugin = python,asyncio,greenlet
inherit = base
+4 -4
View File
@@ -18,18 +18,18 @@ void uwsgi_alarm_init_log(struct uwsgi_alarm_instance *uai) {
void uwsgi_alarm_func_log(struct uwsgi_alarm_instance *uai, char *msg, size_t len) {
if (msg[len-1] != '\n') {
if (uai->arg && strlen(uai->arg) > 0) {
uwsgi_log_alarm("] %s %.*s\n", uai->arg, len, msg);
uwsgi_log_verbose("ALARM: %s %.*s\n", uai->arg, len, msg);
}
else {
uwsgi_log_alarm("] %.*s\n", len, msg);
uwsgi_log_verbose("ALARM: %.*s\n", len, msg);
}
}
else {
if (uai->arg && strlen(uai->arg) > 0) {
uwsgi_log_alarm("] %s %.*s", uai->arg, len, msg);
uwsgi_log_verbose("ALARM: %s %.*s", uai->arg, len, msg);
}
else {
uwsgi_log_alarm("] %.*s", len, msg);
uwsgi_log_verbose("ALARM: %.*s", len, msg);
}
}
}
+18
View File
@@ -387,6 +387,23 @@ end:
}
static int uwsgi_async_wait_milliseconds_hook(int timeout) {
struct wsgi_request *wsgi_req = current_wsgi_req();
timeout = timeout / 1000;
if (!timeout) timeout = 1;
async_add_timeout(wsgi_req, timeout);
wsgi_req->async_force_again = 1;
if (uwsgi.schedule_to_main) {
uwsgi.schedule_to_main(wsgi_req);
}
if (wsgi_req->async_timed_out) {
wsgi_req->async_timed_out = 0;
return 0;
}
return -1;
}
void async_loop() {
if (uwsgi.async < 2) {
@@ -414,6 +431,7 @@ void async_loop() {
uwsgi.wait_write_hook = async_wait_fd_write;
uwsgi.wait_read_hook = async_wait_fd_read;
uwsgi.wait_read2_hook = async_wait_fd_read2;
uwsgi.wait_milliseconds_hook = uwsgi_async_wait_milliseconds_hook;
if (uwsgi.signal_socket > -1) {
event_queue_add_fd_read(uwsgi.async_queue, uwsgi.signal_socket);
+242 -117
View File
@@ -32,7 +32,7 @@ static uint64_t uwsgi_cache_find_free_blocks(struct uwsgi_cache *uc, uint64_t ne
// which is the first free bit?
uint64_t bitmap_byte = 0;
uint8_t bitmap_bit = 0;
if (uc->blocks_bitmap_pos > 0) {
bitmap_byte = uc->blocks_bitmap_pos/8;
bitmap_bit = uc->blocks_bitmap_pos % 8;
@@ -45,6 +45,8 @@ static uint64_t uwsgi_cache_find_free_blocks(struct uwsgi_cache *uc, uint64_t ne
uint64_t j;
uint64_t found = 0;
uint64_t need_to_scan = uc->blocks_bitmap_size;
// we make an addition round for the corner case of a single byte map not starting from 0
if (bitmap_bit > 0) need_to_scan++;
j = bitmap_byte;
//uwsgi_log("start scanning %llu bytes starting from %llu need: %llu\n", (unsigned long long) need_to_scan, (unsigned long long) bitmap_byte, (unsigned long long) needed_blocks);
while(need_to_scan) {
@@ -73,7 +75,9 @@ static uint64_t uwsgi_cache_find_free_blocks(struct uwsgi_cache *uc, uint64_t ne
}
found++;
if (found == needed_blocks) {
//printf("found %llu consecutive bit starting from byte %llu\n", found, base);
#ifdef UWSGI_DEBUG
printf("found %llu consecutive bit starting from byte %llu\n", (unsigned long long) found, (unsigned long long) base);
#endif
return ((base*8) + base_bit);
}
}
@@ -82,12 +86,14 @@ static uint64_t uwsgi_cache_find_free_blocks(struct uwsgi_cache *uc, uint64_t ne
}
j++;
need_to_scan--;
// check for overlap
if (j >= need_to_scan) {
// check for overlap (that is not supported)
if (j >= uc->blocks_bitmap_size) {
j = 0;
found = 0;
base = 0xffffffffffffffffLLU;
base_bit = 0;
// we use bitmap_bit only at the first round
bitmap_bit = 0;
}
}
@@ -102,19 +108,27 @@ static uint64_t cache_mark_blocks(struct uwsgi_cache *uc, uint64_t index, uint64
uint64_t first_byte = index/8;
uint8_t first_byte_bit = index % 8;
uint64_t last_byte = (index+needed_blocks)/8;
uint8_t last_byte_bit = (index+needed_blocks) % 8;
// offset starts with 0, so actual last bit is index + needed_blocks - 1
uint64_t last_byte = (index + needed_blocks - 1) / 8;
uint8_t last_byte_bit = (index + needed_blocks - 1) % 8;
uint64_t needed_bytes = (last_byte - first_byte) + 1;
//uwsgi_log("%llu %u %llu %u\n", first_byte, first_byte_bit, last_byte, last_byte_bit);
uint8_t mask = 0xff >> first_byte_bit;
if (needed_bytes == 1) {
// kinda hacky, but it does the job
mask >>= (7 - last_byte_bit);
mask <<= (7 - last_byte_bit);
}
uc->blocks_bitmap[first_byte] |= mask;
uint64_t needed_bytes = (last_byte - first_byte)+1;
if (needed_bytes > 1) {
mask = 0xff << (8 - last_byte_bit);
uc->blocks_bitmap[last_byte-1] |= mask;
mask = 0xff << (7 - last_byte_bit);
uc->blocks_bitmap[last_byte] |= mask;
}
if (needed_bytes > 2) {
@@ -130,19 +144,28 @@ static void cache_unmark_blocks(struct uwsgi_cache *uc, uint64_t index, uint64_t
uint64_t first_byte = index/8;
uint8_t first_byte_bit = index % 8;
uint64_t last_byte = (index+needed_blocks)/8;
uint8_t last_byte_bit = (index+needed_blocks) % 8;
// offset starts with 0, so actual last bit is index + needed_blocks - 1
uint64_t last_byte = (index + needed_blocks - 1)/8;
uint8_t last_byte_bit = (index + needed_blocks - 1) % 8;
// here we use XOR (0+0 = 0 | 1+0 = 1 | 0+1 = 1| 1+1 = 0|
uint64_t needed_bytes = (last_byte - first_byte) + 1;
uint8_t mask = 0xff >> first_byte_bit;
uc->blocks_bitmap[first_byte] ^= mask;
uint8_t mask = 0xff >> first_byte_bit;
uint64_t needed_bytes = (last_byte - first_byte)+1;
if (needed_bytes == 1) {
// kinda hacky, but it does the job
mask >>= (7 - last_byte_bit);
mask <<= (7 - last_byte_bit);
}
// here we use AND (0+0 = 0 | 1+0 = 0 | 0+1 = 0| 1+1 = 1)
// 0 in mask means "unmark", 1 in mask means "do not change"
// so we need to invert the mask
uc->blocks_bitmap[first_byte] &= ~mask;
if (needed_bytes > 1) {
mask = 0xff << (8 - last_byte_bit);
uc->blocks_bitmap[last_byte-1] ^= mask;
mask = 0xff << (7 - last_byte_bit);
uc->blocks_bitmap[last_byte] &= ~mask;
}
if (needed_bytes > 2) {
@@ -283,16 +306,24 @@ next2:
void uwsgi_cache_init(struct uwsgi_cache *uc) {
uc->hashtable = uwsgi_calloc_shared(sizeof(uint64_t) * uc->hashsize);
uc->unused_blocks_stack = uwsgi_calloc_shared(sizeof(uint64_t) * uc->blocks);
// the first cache item is always zero
uc->first_available_block = 1;
uc->unused_blocks_stack = uwsgi_calloc_shared(sizeof(uint64_t) * uc->max_items);
uc->unused_blocks_stack_ptr = 0;
uc->filesize = ( (sizeof(struct uwsgi_cache_item)+uc->keysize) * uc->max_items) + (uc->blocksize * uc->blocks);
uint64_t i;
for (i = 1; i < uc->max_items; i++) {
uc->unused_blocks_stack_ptr++;
uc->unused_blocks_stack[uc->unused_blocks_stack_ptr] = i;
}
if (uc->use_blocks_bitmap) {
uc->blocks_bitmap_size = uc->blocks/8;
if (uc->blocks % 8 > 0) uc->blocks_bitmap_size++;
uint8_t m = uc->blocks % 8;
if (m > 0) uc->blocks_bitmap_size++;
uc->blocks_bitmap = uwsgi_calloc_shared(uc->blocks_bitmap_size);
if (m > 0) {
uc->blocks_bitmap[uc->blocks_bitmap_size-1] = 0xff >> m;
}
}
//uwsgi.cache_items = (struct uwsgi_cache_item *) mmap(NULL, sizeof(struct uwsgi_cache_item) * uwsgi.cache_max_items, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
@@ -361,7 +392,7 @@ void uwsgi_cache_init(struct uwsgi_cache *uc) {
uc->name,
(unsigned long long) uc->filesize / (1024 * 1024),
(unsigned long long) sizeof(struct uwsgi_cache_item)+uc->keysize,
(unsigned long long) ((sizeof(struct uwsgi_cache_item)+uc->keysize) * uc->max_items), (unsigned long long) (uc->blocksize * uc->max_items),
(unsigned long long) ((sizeof(struct uwsgi_cache_item)+uc->keysize) * uc->max_items), (unsigned long long) (uc->blocksize * uc->blocks),
(unsigned long long) uc->blocks_bitmap_size);
uwsgi_cache_setup_nodes(uc);
@@ -441,6 +472,38 @@ uint32_t uwsgi_cache_exists2(struct uwsgi_cache *uc, char *key, uint16_t keylen)
return uwsgi_cache_get_index(uc, key, keylen);
}
static void lru_remove_item(struct uwsgi_cache *uc, uint64_t index)
{
struct uwsgi_cache_item *prev, *next, *curr = cache_item(index);
if (curr->next) {
next = cache_item(curr->next);
next->prev = curr->prev;
} else
uc->lru_tail = curr->prev;
if (curr->prev) {
prev = cache_item(curr->prev);
prev->next = curr->next;
} else
uc->lru_head = curr->next;
}
static void lru_add_item(struct uwsgi_cache *uc, uint64_t index)
{
struct uwsgi_cache_item *prev, *curr = cache_item(index);
if (uc->lru_tail) {
prev = cache_item(uc->lru_tail);
prev->next = index;
} else
uc->lru_head = index;
curr->next = 0;
curr->prev = uc->lru_tail;
uc->lru_tail = index;
}
char *uwsgi_cache_get2(struct uwsgi_cache *uc, char *key, uint16_t keylen, uint64_t * valsize) {
uint64_t index = uwsgi_cache_get_index(uc, key, keylen);
@@ -450,6 +513,10 @@ char *uwsgi_cache_get2(struct uwsgi_cache *uc, char *key, uint16_t keylen, uint6
if (uci->flags & UWSGI_CACHE_FLAG_UNGETTABLE)
return NULL;
*valsize = uci->valsize;
if (uc->purge_lru) {
lru_remove_item(uc, index);
lru_add_item(uc, index);
}
uci->hits++;
uc->hits++;
return uc->data + (uci->first_block * uc->blocksize);
@@ -489,6 +556,10 @@ char *uwsgi_cache_get3(struct uwsgi_cache *uc, char *key, uint16_t keylen, uint6
*valsize = uci->valsize;
if (expires)
*expires = uci->expires;
if (uc->purge_lru) {
lru_remove_item(uc, index);
lru_add_item(uc, index);
}
uci->hits++;
uc->hits++;
return uc->data + (uci->first_block * uc->blocksize);
@@ -523,50 +594,56 @@ char *uwsgi_cache_get4(struct uwsgi_cache *uc, char *key, uint16_t keylen, uint6
int uwsgi_cache_del2(struct uwsgi_cache *uc, char *key, uint16_t keylen, uint64_t index, uint16_t flags) {
struct uwsgi_cache_item *uci;
int ret = -1;
if (!index)
index = uwsgi_cache_get_index(uc, key, keylen);
if (!index) index = uwsgi_cache_get_index(uc, key, keylen);
if (index) {
uci = cache_item(index);
if (uci->keysize > 0) {
// unmark blocks
if (uc->blocks_bitmap) cache_unmark_blocks(uc, uci->first_block, uci->valsize);
// put back the block in unused stack
uc->unused_blocks_stack_ptr++;
uc->unused_blocks_stack[uc->unused_blocks_stack_ptr] = index;
// unlink prev and next (if any)
if (uci->prev) {
struct uwsgi_cache_item *ucii = cache_item(uci->prev);
ucii->next = uci->next;
}
else {
// set next as the new entry point (could be 0)
uc->hashtable[uci->hash % uc->hashsize] = uci->next;
}
if (uci->next) {
struct uwsgi_cache_item *ucii = cache_item(uci->next);
ucii->prev = uci->prev;
}
if (!uci->prev && !uci->next) {
// reset hashtable entry
uc->hashtable[uci->hash % uc->hashsize] = 0;
}
if (uc->purge_lru)
lru_remove_item(uc, index);
uc->n_items--;
}
ret = 0;
uci->keysize = 0;
uci->valsize = 0;
uc->unused_blocks_stack_ptr++;
uc->unused_blocks_stack[uc->unused_blocks_stack_ptr] = index;
// unmark blocks
if (uc->blocks_bitmap) {
cache_unmark_blocks(uc, uci->first_block, uci->valsize);
}
ret = 0;
// relink collisioned entry
if (uci->prev) {
struct uwsgi_cache_item *ucii = cache_item(uci->prev);
ucii->next = uci->next;
}
else {
// set next as the new entry point (could be 0)
uc->hashtable[uci->hash % uc->hashsize] = uci->next;
}
if (uci->next) {
struct uwsgi_cache_item *ucii = cache_item(uci->next);
ucii->prev = uci->prev;
}
if (!uci->prev && !uci->next) {
// reset hashtable entry
//uwsgi_log("!!! resetted hashtable entry !!!\n");
uc->hashtable[uci->hash % uc->hashsize] = 0;
}
uci->hash = 0;
uci->prev = 0;
uci->next = 0;
uci->expires = 0;
uc->n_items--;
if (uc->use_last_modified) {
uc->last_modified_at = uwsgi_now();
}
@@ -584,7 +661,10 @@ void uwsgi_cache_fix(struct uwsgi_cache *uc) {
uint64_t i;
unsigned long long restored = 0;
for (i = 0; i < uc->max_items; i++) {
// reset unused blocks
uc->unused_blocks_stack_ptr = 0;
for (i = 1; i < uc->max_items; i++) {
// valid record ?
struct uwsgi_cache_item *uci = cache_item(i);
if (uci->keysize) {
@@ -596,7 +676,6 @@ void uwsgi_cache_fix(struct uwsgi_cache *uc) {
}
else {
// put this record in unused stack
uc->first_available_block = i;
uc->unused_blocks_stack_ptr++;
uc->unused_blocks_stack[uc->unused_blocks_stack_ptr] = i;
}
@@ -613,7 +692,6 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v
struct uwsgi_cache_item *uci, *ucii;
// used to reset key allocation in bitmap mode
uint8_t rollback_mode = 0;
int ret = -1;
time_t now = 0;
@@ -631,56 +709,53 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v
//uwsgi_log("putting cache data in key %.*s %d\n", keylen, key, vallen);
index = uwsgi_cache_get_index(uc, key, keylen);
if (!index) {
if (uc->first_available_block >= uc->max_items && !uc->unused_blocks_stack_ptr) {
uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name);
uc->full++;
goto end;
}
if (uc->unused_blocks_stack_ptr) {
//uwsgi_log("!!! REUSING CACHE SLOT !!! (faci: %llu)\n", (unsigned long long) uwsgi.shared->cache_first_available_block);
index = uc->unused_blocks_stack[uc->unused_blocks_stack_ptr];
uc->unused_blocks_stack_ptr--;
}
else {
rollback_mode = 1;
index = uc->first_available_block;
if (uc->first_available_block < uc->max_items) {
rollback_mode = 2;
uc->first_available_block++;
if (!uc->unused_blocks_stack_ptr) {
if (!uc->ignore_full) {
if (uc->purge_lru)
uwsgi_log("LRU item will be purged from cache \"%s\"\n", uc->name);
else
uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name);
}
uc->full++;
if (uc->purge_lru && uc->lru_head)
uwsgi_cache_del2(uc, NULL, 0, uc->lru_head, UWSGI_CACHE_FLAG_LOCAL);
if (!uc->unused_blocks_stack_ptr)
goto end;
}
index = uc->unused_blocks_stack[uc->unused_blocks_stack_ptr];
uc->unused_blocks_stack_ptr--;
uci = cache_item(index);
if (!uc->blocks_bitmap) {
uci->first_block = index;
}
else {
uci->first_block = uwsgi_cache_find_free_blocks(uc, vallen);
//uwsgi_log("first block = %llu\n", uci->first_block);
if (uci->first_block == 0xffffffffffffffffLLU) {
uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name);
if (!uc->ignore_full)
uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name);
uc->full++;
if (rollback_mode == 0) {
uc->unused_blocks_stack_ptr++;
}
else if (rollback_mode == 2) {
uc->first_available_block--;
}
uc->unused_blocks_stack_ptr++;
goto end;
}
// mark used blocks;
uint64_t needed_blocks = cache_mark_blocks(uc, uci->first_block, vallen);
// optimize the scan
if (uc->blocks_bitmap_pos + (needed_blocks+1) > uc->blocks) {
if (uc->blocks_bitmap_pos + needed_blocks > uc->blocks) {
uc->blocks_bitmap_pos = 0;
}
else {
uc->blocks_bitmap_pos = uci->first_block + needed_blocks + 1;
uc->blocks_bitmap_pos = uci->first_block + needed_blocks;
}
}
if (expires && !(flags & UWSGI_CACHE_FLAG_ABSEXPIRE)) {
if (uc->purge_lru)
lru_add_item(uc, index);
else if (expires && !(flags & UWSGI_CACHE_FLAG_ABSEXPIRE)) {
now = uwsgi_now();
expires += now;
if (!uc->next_scan || uc->next_scan > expires)
uc->next_scan = expires;
}
uci->expires = expires;
uci->hash = uc->hash->func(key, keylen);
@@ -745,9 +820,16 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v
}
else if (flags & UWSGI_CACHE_FLAG_UPDATE) {
uci = cache_item(index);
if (expires && !(flags & UWSGI_CACHE_FLAG_ABSEXPIRE) && !(flags & UWSGI_CACHE_FLAG_FIXEXPIRE)) {
now = uwsgi_now();
expires += now;
if (!(flags & UWSGI_CACHE_FLAG_FIXEXPIRE)) {
if (uc->purge_lru) {
lru_remove_item(uc, index);
lru_add_item(uc, index);
} else if (expires && !(flags & UWSGI_CACHE_FLAG_ABSEXPIRE)) {
now = uwsgi_now();
expires += now;
if (!uc->next_scan || uc->next_scan > expires)
uc->next_scan = expires;
}
uci->expires = expires;
}
if (uc->blocks_bitmap) {
@@ -755,7 +837,8 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v
uint64_t old_first_block = uci->first_block;
uci->first_block = uwsgi_cache_find_free_blocks(uc, vallen);
if (uci->first_block == 0xffffffffffffffffLLU) {
uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name);
if (!uc->ignore_full)
uwsgi_log("*** DANGER cache \"%s\" is FULL !!! ***\n", uc->name);
uc->full++;
uci->first_block = old_first_block;
goto end;
@@ -763,14 +846,14 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v
// mark used blocks;
uint64_t needed_blocks = cache_mark_blocks(uc, uci->first_block, vallen);
// optimize the scan
if (uc->blocks_bitmap_pos + (needed_blocks+1) > uc->blocks) {
if (uc->blocks_bitmap_pos + needed_blocks > uc->blocks) {
uc->blocks_bitmap_pos = 0;
}
else {
uc->blocks_bitmap_pos = uci->first_block + needed_blocks + 1;
uc->blocks_bitmap_pos = uci->first_block + needed_blocks;
}
// unmark the old blocks
cache_unmark_blocks(uc, uci->first_block, uci->valsize);
cache_unmark_blocks(uc, old_first_block, uci->valsize);
}
if ( !(flags & UWSGI_CACHE_FLAG_MATH)) {
memcpy(((char *) uc->data) + (uci->first_block * uc->blocksize), val, vallen);
@@ -965,39 +1048,66 @@ void *cache_udp_server_loop(void *ucache) {
return NULL;
}
static uint64_t cache_sweeper_free_items(struct uwsgi_cache *uc) {
uint64_t i;
uint64_t freed_items = 0;
if (uc->no_expire || uc->purge_lru)
return 0;
uwsgi_rlock(uc->lock);
if (!uc->next_scan || uc->next_scan > (uint64_t)uwsgi.current_time) {
uwsgi_rwunlock(uc->lock);
return 0;
}
uwsgi_rwunlock(uc->lock);
// skip the first slot
for (i = 1; i < uc->max_items; i++) {
struct uwsgi_cache_item *uci = cache_item(i);
uwsgi_wlock(uc->lock);
// we reset next scan time first, then we find the least
// expiration time from those that are NOT expired yet.
if (i == 1)
uc->next_scan = 0;
if (uci->expires) {
if (uci->expires <= (uint64_t)uwsgi.current_time) {
uwsgi_cache_del2(uc, NULL, 0, i, UWSGI_CACHE_FLAG_LOCAL);
freed_items++;
} else if (!uc->next_scan || uc->next_scan > uci->expires) {
uc->next_scan = uci->expires;
}
}
uwsgi_rwunlock(uc->lock);
}
return freed_items;
}
static void *cache_sweeper_loop(void *ucache) {
uint64_t i;
// block all signals
sigset_t smask;
sigfillset(&smask);
pthread_sigmask(SIG_BLOCK, &smask, NULL);
struct uwsgi_cache *uc = (struct uwsgi_cache *) ucache;
if (!uwsgi.cache_expire_freq)
uwsgi.cache_expire_freq = 3;
// remove expired cache items TODO use rb_tree timeouts
for (;;) {
struct uwsgi_cache *uc;
for (uc = (struct uwsgi_cache *)ucache; uc; uc = uc->next) {
uint64_t freed_items = cache_sweeper_free_items(uc);
if (uwsgi.cache_report_freed_items && freed_items)
uwsgi_log("freed %llu items for cache \"%s\"\n", (unsigned long long)freed_items, uc->name);
}
sleep(uwsgi.cache_expire_freq);
uint64_t freed_items = 0;
// skip the first slot
for (i = 1; i < uc->max_items; i++) {
uwsgi_wlock(uc->lock);
struct uwsgi_cache_item *uci = cache_item(i);
if (uci->expires) {
if (uci->expires < (uint64_t) uwsgi.current_time) {
uwsgi_cache_del2(uc, NULL, 0, i, UWSGI_CACHE_FLAG_LOCAL);
freed_items++;
}
}
uwsgi_rwunlock(uc->lock);
}
if (uwsgi.cache_report_freed_items && freed_items > 0) {
uwsgi_log("freed %llu items for cache \"%s\"\n", (unsigned long long) freed_items, uc->name);
}
};
}
return NULL;
}
@@ -1017,16 +1127,21 @@ void uwsgi_cache_sync_all() {
void uwsgi_cache_start_sweepers() {
struct uwsgi_cache *uc = uwsgi.caches;
if (uwsgi.cache_no_expire)
return;
while(uc) {
pthread_t cache_sweeper;
if (!uwsgi.cache_no_expire && !uc->no_expire) {
if (pthread_create(&cache_sweeper, NULL, cache_sweeper_loop, (void *) uc)) {
if (!uc->no_expire && !uc->purge_lru) {
if (pthread_create(&cache_sweeper, NULL, cache_sweeper_loop, uwsgi.caches)) {
uwsgi_error("pthread_create()");
uwsgi_log("unable to run the sweeper for cache \"%s\" !!!\n", uc->name);
uwsgi_log("unable to run the sweeper!!!\n");
}
else {
uwsgi_log("sweeper thread enabled for cache \"%s\"\n", uc->name);
uwsgi_log("sweeper thread enabled\n");
}
break;
}
uc = uc->next;
}
@@ -1103,6 +1218,8 @@ struct uwsgi_cache *uwsgi_cache_create(char *arg) {
char *c_bitmap = NULL;
char *c_use_last_modified = NULL;
char *c_math_initial = NULL;
char *c_ignore_full = NULL;
char *c_purge_lru = NULL;
if (uwsgi_kvlist_parse(arg, strlen(arg), ',', '=',
"name", &c_name,
@@ -1130,6 +1247,8 @@ struct uwsgi_cache *uwsgi_cache_create(char *arg) {
"bitmap", &c_bitmap,
"lastmod", &c_use_last_modified,
"math_initial", &c_math_initial,
"ignore_full", &c_ignore_full,
"purge_lru", &c_purge_lru,
NULL)) {
uwsgi_log("unable to parse cache definition\n");
exit(1);
@@ -1177,6 +1296,7 @@ struct uwsgi_cache *uwsgi_cache_create(char *arg) {
uc->max_item_size = uc->blocksize * uc->blocks;
}
if (c_use_last_modified) uc->use_last_modified = 1;
if (c_ignore_full) uc->ignore_full = 1;
if (c_math_initial) uc->math_initial = strtol(c_math_initial, NULL, 10);
@@ -1211,6 +1331,8 @@ struct uwsgi_cache *uwsgi_cache_create(char *arg) {
}
}
if (c_purge_lru)
uc->purge_lru = 1;
}
uwsgi_cache_init(uc);
@@ -1466,7 +1588,10 @@ char *uwsgi_cache_magic_get(char *key, uint16_t keylen, uint64_t *vallen, uint64
// we have a local cache !!!
if (uc) {
uwsgi_rlock(uc->lock);
if (uc->purge_lru)
uwsgi_wlock(uc->lock);
else
uwsgi_rlock(uc->lock);
char *value = uwsgi_cache_get3(uc, key, keylen, vallen, expires);
if (!value) {
uwsgi_rwunlock(uc->lock);
@@ -1788,7 +1913,7 @@ int uwsgi_cache_magic_clear(char *cache) {
if (uc) {
uint64_t i;
uwsgi_wlock(uc->lock);
for (i = 1; i < uwsgi.caches->max_items; i++) {
for (i = 1; i < uc->max_items; i++) {
if (uwsgi_cache_del2(uc, NULL, 0, i, 0)) {
uwsgi_rwunlock(uc->lock);
return -1;
+2 -1
View File
@@ -400,7 +400,8 @@ void add_exported_option_do(char *key, char *value, int configured, int placehol
if (uwsgi.logic_opt_running)
goto add;
if (!strcmp(key, "end") || !strcmp(key, "endfor") || !strcmp(key, "endif")) {
if (!strcmp(key, "end") || !strcmp(key, "endfor") || !strcmp(key, "endif")
|| !strcmp(key, "end-if") || !strcmp(key, "end-for")) {
if (uwsgi.logic_opt_data) {
free(uwsgi.logic_opt_data);
}
+198 -40
View File
@@ -39,6 +39,70 @@ struct uwsgi_emperor_blacklist_item {
struct uwsgi_emperor_blacklist_item *emperor_blacklist;
/*
this should be placed in core/socket.c but we realized it was needed
only after 2.0 so we cannot change uwsgi.h
basically it is a stripped down bind_to_tcp/bind_to_unix with rollback
*/
static int on_demand_bind(char *socket_name) {
union uwsgi_sockaddr us;
socklen_t addr_len = sizeof(struct sockaddr_un);
char *is_tcp = strchr(socket_name, ':');
int af_family = is_tcp ? AF_INET : AF_UNIX;
int fd = socket(af_family, SOCK_STREAM, 0);
if (fd < 0) return -1;
memset(&us, 0, sizeof(union uwsgi_sockaddr));
if (is_tcp) {
int reuse = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &reuse, sizeof(int)) < 0) {
goto error;
}
us.sa_in.sin_family = AF_INET;
us.sa_in.sin_port = htons(atoi(is_tcp+1));
*is_tcp = 0;
if (socket_name[0] != 0) {
us.sa_in.sin_addr.s_addr = inet_addr(socket_name);
}
else {
us.sa_in.sin_addr.s_addr = INADDR_ANY;
}
*is_tcp = ':';
addr_len = sizeof(struct sockaddr_in);
}
else {
if (unlink(socket_name) != 0 && errno != ENOENT) {
goto error;
}
us.sa_un.sun_family = AF_UNIX;
memcpy(us.sa_un.sun_path, socket_name, UMIN(strlen(socket_name), 102));
addr_len = strlen(socket_name) + ((void *) us.sa_un.sun_path - (void *) &us.sa_un);
}
if (bind(fd, (struct sockaddr *) &us, addr_len) != 0) {
goto error;
}
if (!is_tcp) {
if (chmod(socket_name, 0666)) {
goto error;
}
}
if (listen(fd, uwsgi.listen_queue) != 0) {
goto error;
}
return fd;
error:
close(fd);
return -1;
}
struct uwsgi_emperor_blacklist_item *uwsgi_emperor_blacklist_check(char *id) {
struct uwsgi_emperor_blacklist_item *uebi = emperor_blacklist;
while (uebi) {
@@ -157,7 +221,7 @@ static char *emperor_check_on_demand_socket(char *filename) {
if (fd < 0) return NULL;
char *ret = uwsgi_read_fd(fd, &len, 1);
close(fd);
// change the first non prinabel character to 0
// change the first non printable character to 0
size_t i;
for(i=0;i<len;i++) {
if (ret[i] < 32) {
@@ -629,16 +693,39 @@ void emperor_del(struct uwsgi_instance *c_ui) {
free(c_ui->socket_name);
}
if (c_ui->on_demand_fd != -1) {
close(c_ui->on_demand_fd);
}
free(c_ui);
}
void emperor_back_to_ondemand(struct uwsgi_instance *c_ui) {
if (c_ui->status > 0) return;
// remove uWSGI instance
if (c_ui->pid != -1) {
if (write(c_ui->pipe[0], "\0", 1) != 1) {
uwsgi_error("emperor_stop()/write()");
}
}
c_ui->status = 2;
c_ui->cursed_at = uwsgi_now();
uwsgi_log_verbose("[emperor] bringing back instance %s to on-demand mode\n", c_ui->name);
}
void emperor_stop(struct uwsgi_instance *c_ui) {
if (c_ui->status == 1) return;
// remove uWSGI instance
if (write(c_ui->pipe[0], "\0", 1) != 1) {
uwsgi_error("emperor_stop()/write()");
if (c_ui->pid != -1) {
if (write(c_ui->pipe[0], "\0", 1) != 1) {
uwsgi_error("emperor_stop()/write()");
}
}
c_ui->status = 1;
@@ -651,38 +738,55 @@ void emperor_curse(struct uwsgi_instance *c_ui) {
if (c_ui->status == 1) return;
// curse uWSGI instance
c_ui->status = 1;
// take in account on-demand mode
if (c_ui->status == 0) c_ui->status = 1;
c_ui->cursed_at = uwsgi_now();
uwsgi_log_verbose("[emperor] curse the uwsgi instance %s (pid: %d)\n", c_ui->name, (int) c_ui->pid);
}
// send configuration (if required to the vassal)
static void emperor_push_config(struct uwsgi_instance *c_ui) {
struct uwsgi_header uh;
if (c_ui->use_config) {
uh.modifier1 = 115;
uh.pktsize = c_ui->config_len;
uh.modifier2 = 0;
if (write(c_ui->pipe_config[0], &uh, 4) != 4) {
uwsgi_error("[uwsgi-emperor] write() header config");
}
else {
if (write(c_ui->pipe_config[0], c_ui->config, c_ui->config_len) != (long) c_ui->config_len) {
uwsgi_error("[uwsgi-emperor] write() config");
}
}
}
}
void emperor_respawn(struct uwsgi_instance *c_ui, time_t mod) {
struct uwsgi_header uh;
// if the vassal is being destroyed, do not honour respawns
if (c_ui->status > 0) return;
// check if we are in on_demand mode (the respawn will be ignored)
if (c_ui->pid == -1 && c_ui->on_demand_fd > -1) {
c_ui->last_mod = mod;
uwsgi_log_verbose("[emperor] updated configuration for \"on demand\" instance %s\n", c_ui->name);
return;
}
// reload the uWSGI instance
if (write(c_ui->pipe[0], "\1", 1) != 1) {
// the vassal could be already dead, better to curse it
uwsgi_error("emperor_respawn/write()");
emperor_curse(c_ui);
return;
}
// push the config to the config pipe (if needed)
if (c_ui->use_config) {
uh.modifier1 = 115;
uh.pktsize = c_ui->config_len;
uh.modifier2 = 0;
if (write(c_ui->pipe_config[0], &uh, 4) != 4) {
uwsgi_error("[uwsgi-emperor] write() header config");
}
else {
if (write(c_ui->pipe_config[0], c_ui->config, c_ui->config_len) != (long) c_ui->config_len) {
uwsgi_error("[uwsgi-emperor] write() config");
}
}
}
emperor_push_config(c_ui);
c_ui->respawns++;
c_ui->last_mod = mod;
@@ -794,21 +898,12 @@ void emperor_add(struct uwsgi_emperor_scanner *ues, char *name, time_t born, cha
}
n_ui->pid = -1;
n_ui->pipe[0] = -1;
n_ui->pipe[1] = -1;
// ok here we check if we need to bind to the specified socket or continue with the activation
if (socket_name) {
char *tcp_port = strchr(socket_name, ':');
if (tcp_port) {
// disable deferred accept for this socket
int current_defer_accept = uwsgi.no_defer_accept;
uwsgi.no_defer_accept = 1;
n_ui->on_demand_fd = bind_to_tcp(socket_name, uwsgi.listen_queue, tcp_port);
uwsgi.no_defer_accept = current_defer_accept;
}
else {
n_ui->on_demand_fd = bind_to_unix(socket_name, uwsgi.listen_queue, uwsgi.chmod_socket, uwsgi.abstract_socket);
}
n_ui->on_demand_fd = on_demand_bind(socket_name);
if (n_ui->on_demand_fd < 0) {
uwsgi_error("emperor_add()/bind()");
free(n_ui);
@@ -838,6 +933,7 @@ int uwsgi_emperor_vassal_start(struct uwsgi_instance *n_ui) {
uwsgi_error("socketpair()");
return -1;
}
uwsgi_socket_nb(n_ui->pipe[0]);
event_queue_add_fd_read(uwsgi.emperor_queue, n_ui->pipe[0]);
@@ -846,6 +942,7 @@ int uwsgi_emperor_vassal_start(struct uwsgi_instance *n_ui) {
uwsgi_error("socketpair()");
return -1;
}
uwsgi_socket_nb(n_ui->pipe_config[0]);
}
if (n_ui->zerg) {
@@ -873,11 +970,14 @@ int uwsgi_emperor_vassal_start(struct uwsgi_instance *n_ui) {
n_ui->pid = pid;
// close the right side of the pipe
close(n_ui->pipe[1]);
/* THE ON-DEMAND file descriptir is left mapped to the emperor to allow fast-respawn
// TODO add an option to force closing it
// close the "on demand" socket
if (n_ui->on_demand_fd > -1) {
close(n_ui->on_demand_fd);
n_ui->on_demand_fd = -1;
}
*/
if (n_ui->use_config) {
close(n_ui->pipe_config[1]);
}
@@ -1452,6 +1552,17 @@ int uwsgi_emperor_scanner_event(int fd) {
}
static void emperor_wakeup(int sn) {}
static void emperor_cleanup() {
uwsgi_log_verbose("[uwsgi-emperor] cleaning up blacklist ...\n");
struct uwsgi_instance *ui_current = ui;
while (ui_current->ui_next) {
uwsgi_emperor_blacklist_remove(ui_current->name);
ui_current = ui_current->ui_next;
}
}
void emperor_loop() {
// monitor a directory
@@ -1480,11 +1591,13 @@ void emperor_loop() {
}
signal(SIGPIPE, SIG_IGN);
signal(SIGWINCH, emperor_wakeup);
uwsgi_unix_signal(SIGINT, royal_death);
uwsgi_unix_signal(SIGTERM, royal_death);
uwsgi_unix_signal(SIGQUIT, royal_death);
uwsgi_unix_signal(SIGUSR1, emperor_stats);
uwsgi_unix_signal(SIGHUP, emperor_massive_reload);
uwsgi_unix_signal(SIGURG, emperor_cleanup);
memset(&ui_base, 0, sizeof(struct uwsgi_instance));
@@ -1552,7 +1665,11 @@ void emperor_loop() {
ui_current = ui->ui_next;
while (ui_current) {
uwsgi_log_verbose("[emperor] NO MERCY for vassal %s !!!\n", ui_current->name);
kill(ui_current->pid, SIGKILL);
if (kill(ui_current->pid, SIGKILL) < 0) {
uwsgi_error("[emperor] kill()");
emperor_del(ui_current);
break;
}
ui_current = ui_current->ui_next;
}
break;
@@ -1600,6 +1717,10 @@ void emperor_loop() {
if (rlen <= 0) {
// SAFE
event_queue_del_fd(uwsgi.emperor_queue, interesting_fd, event_queue_read());
if (ui_current->status > 0) {
// temporarily set frequency to a low value , so we can eventually fast-restart the instance
freq = ui_current->status;
}
emperor_curse(ui_current);
}
else {
@@ -1616,7 +1737,13 @@ void emperor_loop() {
ui_current->last_heartbeat = uwsgi_now();
}
else if (byte == 22) {
emperor_stop(ui_current);
// command 22 changes meaning when in "on_demand" mode
if (ui_current->on_demand_fd != -1) {
emperor_back_to_ondemand(ui_current);
}
else {
emperor_stop(ui_current);
}
}
else if (byte == 30 && uwsgi.emperor_broodlord > 0 && uwsgi.emperor_broodlord_count < uwsgi.emperor_broodlord) {
uwsgi_log_verbose("[emperor] going in broodlord mode: launching zergs for %s\n", ui_current->name);
@@ -1635,6 +1762,9 @@ void emperor_loop() {
ui_current->last_ready = uwsgi_now();
uwsgi_log_verbose("[emperor] vassal %s has been spawned\n", ui_current->name);
}
else if (byte == 2) {
emperor_push_config(ui_current);
}
}
}
else {
@@ -1667,8 +1797,10 @@ void emperor_loop() {
// set last_heartbeat to 0 avoiding races
ui_current->last_heartbeat = 0;
if (ui_current->pid > 0) {
if (kill(ui_current->pid, SIGKILL)) {
if (kill(ui_current->pid, SIGKILL) < 0) {
uwsgi_error("[emperor] kill()");
emperor_del(ui_current);
break;
}
}
}
@@ -1676,6 +1808,8 @@ void emperor_loop() {
ui_current = ui_current->ui_next;
}
recheck:
// check for removed instances
ui_current = ui;
has_children = 0;
@@ -1720,7 +1854,7 @@ void emperor_loop() {
while (ui_current->ui_next) {
ui_current = ui_current->ui_next;
time_t now = uwsgi_now();
if (ui_current->pid == diedpid) {
if (diedpid > 0 && ui_current->pid == diedpid) {
if (ui_current->status == 0) {
// respawn an accidentally dead instance if its exit code is not UWSGI_EXILE_CODE
if (WIFEXITED(waitpid_status) && WEXITSTATUS(waitpid_status) == UWSGI_EXILE_CODE) {
@@ -1731,6 +1865,8 @@ void emperor_loop() {
// UNSAFE
emperor_add(ui_current->scanner, ui_current->name, ui_current->last_mod, ui_current->config, ui_current->config_len, ui_current->uid, ui_current->gid, ui_current->socket_name);
emperor_del(ui_current);
// temporarily set frequency to 0, so we can eventually fast-restart the instance
freq = 0;
}
break;
}
@@ -1740,18 +1876,40 @@ void emperor_loop() {
free(ui_current->config);
// SAFE
emperor_del(ui_current);
// temporarily set frequency to 0, so we can eventually fast-restart the instance
freq = 0;
break;
}
// back to on_demand mode ...
else if (ui_current->status == 2) {
event_queue_add_fd_read(uwsgi.emperor_queue, ui_current->on_demand_fd);
ui_current->pid = -1;
ui_current->status = 0;
ui_current->cursed_at = 0;
uwsgi_log("[uwsgi-emperor] %s -> back to \"on demand\" mode, waiting for connections on socket \"%s\" ...\n", ui_current->name, ui_current->socket_name);
break;
}
}
else if (ui_current->cursed_at > 0 && now - ui_current->cursed_at >= uwsgi.emperor_curse_tolerance) {
ui_current->cursed_at = now;
if (kill(ui_current->pid, SIGKILL)) {
uwsgi_error("[emperor] kill");
else if (ui_current->cursed_at > 0) {
if (ui_current->pid == -1) {
emperor_del(ui_current);
break;
}
else if (now - ui_current->cursed_at >= uwsgi.emperor_curse_tolerance) {
ui_current->cursed_at = now;
if (kill(ui_current->pid, SIGKILL) < 0) {
uwsgi_error("[emperor] kill()");
// delete the vassal, something is seriously wrong better to not leak memory...
emperor_del(ui_current);
}
break;
}
break;
}
}
// if waitpid returned an item, let's check for another (potential) one
if (diedpid > 0) goto recheck;
}
+9
View File
@@ -48,6 +48,14 @@ static void subscriptions_blocker() {
}
}
static void emperor_rescan() {
if (uwsgi.emperor_pid > 0) {
if (kill(uwsgi.emperor_pid, SIGWINCH)) {
uwsgi_error("emperor_rescan()/kill()");
}
}
}
/*
this is called as soon as possibile allowing plugins (or hooks) to override it
@@ -74,6 +82,7 @@ void uwsgi_master_fifo_prepare() {
uwsgi_fifo_table['+'] = uwsgi_cheaper_increase;
uwsgi_fifo_table['c'] = uwsgi_chain_reload;
uwsgi_fifo_table['C'] = 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;
+73
View File
@@ -485,6 +485,60 @@ static int uwsgi_hook_callret(char *arg) {
return func();
}
static int uwsgi_hook_rpc(char *arg) {
int ret = -1;
size_t i, argc = 0;
char **rargv = uwsgi_split_quoted(arg, strlen(arg), " \t", &argc);
if (!argc) goto end;
if (argc > 256) goto destroy;
char *argv[256];
uint16_t argvs[256];
char *node = NULL;
char *func = rargv[0];
char *at = strchr(func, '@');
if (at) {
*at = 0;
node = at + 1;
}
for(i=0;i<(argc-1);i++) {
size_t a_len = strlen(rargv[i+1]);
if (a_len > 0xffff) goto destroy;
argv[i] = rargv[i+1] ;
argvs[i] = a_len;
}
uint64_t size = 0;
// response must be always freed
char *response = uwsgi_do_rpc(node, func, argc-1, argv, argvs, &size);
if (response) {
if (at) *at = '@';
uwsgi_log("[rpc result from \"%s\"] %.*s\n", rargv[0], size, response);
free(response);
ret = 0;
}
destroy:
for(i=0;i<argc;i++) {
free(rargv[i]);
}
end:
free(rargv);
return ret;
}
static int uwsgi_hook_retryrpc(char *arg) {
for(;;) {
int ret = uwsgi_hook_rpc(arg);
if (!ret) break;
sleep(2);
}
return 0;
}
void uwsgi_register_base_hooks() {
uwsgi_register_hook("cd", uwsgi_hook_chdir);
@@ -523,6 +577,9 @@ void uwsgi_register_base_hooks() {
uwsgi_register_hook("alarm", uwsgi_hook_alarm);
uwsgi_register_hook("rpc", uwsgi_hook_rpc);
uwsgi_register_hook("retryrpc", uwsgi_hook_retryrpc);
// for testing
uwsgi_register_hook("exit", uwsgi_hook_exit);
uwsgi_register_hook("print", uwsgi_hook_print);
@@ -561,6 +618,22 @@ void uwsgi_hooks_run(struct uwsgi_string_list *l, char *phase, int fatal) {
int ret = uh->func(colon+1);
if (fatal && ret != 0) {
uwsgi_log_verbose("FATAL hook failed, destroying instance\n");
if (uwsgi.master_process) {
if (uwsgi.workers) {
if (uwsgi.workers[0].pid == getpid()) {
kill_them_all(0);
return;
}
else {
if (kill(uwsgi.workers[0].pid, SIGINT)) {
uwsgi_error("uwsgi_hooks_run()/kill()");
exit(1);
}
return;
}
}
}
exit(1);
}
}
+22 -3
View File
@@ -517,12 +517,28 @@ fallback:
goto end;
}
// is it a potential virtual file (/proc, /sys...) ?
int is_virtual = 0;
if (sb.st_size == 0) {
is_virtual = 1;
sb.st_size = 4096;
}
buffer = uwsgi_malloc(sb.st_size + add_zero);
len = read(fd, buffer, sb.st_size);
if (len != sb.st_size) {
uwsgi_error("read()");
exit(1);
if (!is_virtual) {
if (len != sb.st_size) {
}
}
else {
if (len >= 0) {
sb.st_size = len;
}
else {
uwsgi_error("read()");
exit(1);
}
}
close(fd);
@@ -951,6 +967,7 @@ int uwsgi_read_nb(int fd, char *buf, size_t remains, int timeout) {
ssize_t uwsgi_read_true_nb(int fd, char *buf, size_t len, int timeout) {
int ret;
errno = 0;
ssize_t rlen = read(fd, buf, len);
if (rlen > 0) {
return rlen;
@@ -961,8 +978,10 @@ ssize_t uwsgi_read_true_nb(int fd, char *buf, size_t len, int timeout) {
}
return -1;
wait:
errno = 0;
ret = uwsgi.wait_read_hook(fd, timeout);
if (ret > 0) {
errno = 0;
rlen = read(fd, buf, len);
if (rlen > 0) {
return rlen;
+10 -4
View File
@@ -477,13 +477,19 @@ void uwsgi_check_logrotate(void) {
int need_rotation = 0;
int need_reopen = 0;
off_t logsize;
if (uwsgi.log_master) {
uwsgi.shared->logsize = lseek(uwsgi.original_log_fd, 0, SEEK_CUR);
logsize = lseek(uwsgi.original_log_fd, 0, SEEK_CUR);
}
else {
uwsgi.shared->logsize = lseek(2, 0, SEEK_CUR);
logsize = lseek(2, 0, SEEK_CUR);
}
if (logsize < 0) {
uwsgi_error("uwsgi_check_logrotate()/lseek()");
return;
}
uwsgi.shared->logsize = logsize;
if (uwsgi.log_maxsize > 0 && (uint64_t) uwsgi.shared->logsize > uwsgi.log_maxsize) {
need_rotation = 1;
@@ -917,7 +923,7 @@ struct uwsgi_logger *uwsgi_get_logger_from_id(char *id) {
struct uwsgi_logger *ul = uwsgi.choosen_logger;
while (ul) {
if (!strcmp(ul->id, id)) {
if (ul->id && !strcmp(ul->id, id)) {
return ul;
}
ul = ul->next;
@@ -1116,7 +1122,7 @@ static ssize_t uwsgi_lf_ltime(struct wsgi_request * wsgi_req, char **buf) {
}
static ssize_t uwsgi_lf_ftime(struct wsgi_request * wsgi_req, char **buf) {
if (!uwsgi.log_strftime) {
if (!uwsgi.logformat_strftime || !uwsgi.log_strftime) {
return uwsgi_lf_ltime(wsgi_req, buf);
}
*buf = uwsgi_malloc(64);
+32 -10
View File
@@ -43,13 +43,6 @@ void uwsgi_destroy_processes() {
uwsgi_signal_spoolers(SIGKILL);
if (uwsgi.emperor_pid >= 0) {
kill(uwsgi.emperor_pid, SIGKILL);
waitpid(uwsgi.emperor_pid, &waitpid_status, 0);
uwsgi_log("The Emperor has been buried (pid: %d)\n", (int) uwsgi.emperor_pid);
}
uwsgi_detach_daemons();
for (i = 0; i < ushared->gateways_cnt; i++) {
@@ -68,6 +61,26 @@ void uwsgi_destroy_processes() {
uwsgi_log("mule %d has been buried (pid: %d)\n", i, (int) uwsgi.mules[i].pid);
}
}
if (uwsgi.emperor_pid > 0) {
kill(uwsgi.emperor_pid, SIGINT);
time_t timeout = uwsgi_now() + (uwsgi.reload_mercy ? uwsgi.reload_mercy : 3);
// increase timeout for being more tolerant
timeout+=2;
int waitpid_status;
while (uwsgi_now() < timeout) {
pid_t diedpid = waitpid(uwsgi.emperor_pid, &waitpid_status, WNOHANG);
if (diedpid == uwsgi.emperor_pid) {
goto nomoremperor;
}
uwsgi_log("waiting for Emperor death...\n");
sleep(1);
}
kill(uwsgi.emperor_pid, SIGKILL);
waitpid(uwsgi.emperor_pid, &waitpid_status, 0);
nomoremperor:
uwsgi_log("The Emperor has been buried (pid: %d)\n", (int) uwsgi.emperor_pid);
}
}
@@ -399,9 +412,17 @@ void uwsgi_reload(char **argv) {
uwsgi_log("fork()'ing uWSGI...\n");
}
// ask for configuration (if needed)
if (uwsgi.has_emperor && uwsgi.emperor_fd_config > -1) {
char byte = 2;
if (write(uwsgi.emperor_fd, &byte, 1) != 1) {
uwsgi_error("uwsgi_reload()/write()");
}
}
uwsgi_log("chdir() to %s\n", uwsgi.cwd);
if (chdir(uwsgi.cwd)) {
uwsgi_error("chdir()");
uwsgi_error("uwsgi_reload()/chdir()");
}
/* check fd table (a module can obviosly open some fd on initialization...) */
@@ -546,9 +567,10 @@ void uwsgi_fixup_fds(int wid, int muleid, struct uwsgi_gateway *ug) {
}
}
if (uwsgi.shared->spooler_signal_pipe[0] != -1)
close(uwsgi.shared->spooler_signal_pipe[0]);
if (uwsgi.i_am_a_spooler && uwsgi.i_am_a_spooler->pid != getpid()) {
if (uwsgi.shared->spooler_signal_pipe[0] != -1)
close(uwsgi.shared->spooler_signal_pipe[0]);
if (uwsgi.shared->spooler_signal_pipe[1] != -1)
close(uwsgi.shared->spooler_signal_pipe[1]);
}
+2 -2
View File
@@ -330,10 +330,10 @@ next:
}
int ret = poll(mulepoll, count + farms_count, timeout);
if (ret <= 0) {
if (ret < 0) {
uwsgi_error("poll");
}
else {
else if (ret > 0 ) {
if (mulepoll[0].revents & POLLIN) {
len = read(uwsgi.mules[uwsgi.muleid - 1].queue_pipe[1], message, buffer_size);
}
+8
View File
@@ -237,6 +237,7 @@ static void uwsgi_offload_loop(struct uwsgi_thread *ut) {
void *events = event_queue_alloc(uwsgi.offload_threads_events);
for (;;) {
// TODO make timeout tunable
int nevents = event_queue_wait_multi(ut->queue, -1, events, uwsgi.offload_threads_events);
for (i = 0; i < nevents; i++) {
int interesting_fd = event_queue_interesting_fd(events, i);
@@ -456,6 +457,13 @@ static int u_offload_transfer_do(struct uwsgi_thread *ut, struct uwsgi_offload_r
// write event (or just connected)
case 1:
if (fd == uor->fd) {
// maybe we want only a connection...
if (uor->ubuf->pos == 0) {
uor->status = 2;
if (event_queue_add_fd_read(ut->queue, uor->s)) return -1;
if (event_queue_fd_write_to_read(ut->queue, uor->fd)) return -1;
return 0;
}
rlen = write(uor->fd, uor->ubuf->buf + uor->written, uor->ubuf->pos-uor->written);
if (rlen > 0) {
uor->written += rlen;
+40 -1
View File
@@ -312,6 +312,7 @@ int uwsgi_apply_error_routes(struct wsgi_request *wsgi_req) {
int uwsgi_apply_response_routes(struct wsgi_request *wsgi_req) {
if (!uwsgi.response_routes) return 0;
if (wsgi_req->response_routes_applied) return 0;
@@ -942,6 +943,20 @@ static int uwsgi_router_clearheaders(struct uwsgi_route *ur, char *arg) {
return 0;
}
// disable headers
static int uwsgi_router_disableheaders_func(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) {
wsgi_req->headers_sent = 1;
return UWSGI_ROUTE_NEXT;
}
static int uwsgi_router_disableheaders(struct uwsgi_route *ur, char *arg) {
ur->func = uwsgi_router_disableheaders_func;
ur->data = arg;
ur->data_len = strlen(arg);
return 0;
}
// signal route
static int uwsgi_router_signal_func(struct wsgi_request *wsgi_req, struct uwsgi_route *route) {
@@ -1144,6 +1159,26 @@ static int uwsgi_router_setscheme(struct uwsgi_route *ur, char *arg) {
return 0;
}
// setmodifiers
static int uwsgi_router_setmodifier1_func(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) {
wsgi_req->uh->modifier1 = ur->custom;
return UWSGI_ROUTE_NEXT;
}
static int uwsgi_router_setmodifier1(struct uwsgi_route *ur, char *arg) {
ur->func = uwsgi_router_setmodifier1_func;
ur->custom = atoi(arg);
return 0;
}
static int uwsgi_router_setmodifier2_func(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) {
wsgi_req->uh->modifier2 = ur->custom;
return UWSGI_ROUTE_NEXT;
}
static int uwsgi_router_setmodifier2(struct uwsgi_route *ur, char *arg) {
ur->func = uwsgi_router_setmodifier2_func;
ur->custom = atoi(arg);
return 0;
}
// setuser route
static int uwsgi_router_setuser_func(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) {
@@ -1669,7 +1704,7 @@ static char *uwsgi_route_var_mime(struct wsgi_request *wsgi_req, char *key, uint
char *var_value = uwsgi_get_var(wsgi_req, key, keylen, &var_vallen);
if (var_value) {
size_t mime_type_len = 0;
char *ret = uwsgi_get_mime_type(key, keylen, &mime_type_len);
ret = uwsgi_get_mime_type(var_value, var_vallen, &mime_type_len);
if (ret) *vallen = mime_type_len;
}
return ret;
@@ -1736,6 +1771,7 @@ void uwsgi_register_embedded_routers() {
uwsgi_register_router("remheader", uwsgi_router_remheader);
uwsgi_register_router("clearheaders", uwsgi_router_clearheaders);
uwsgi_register_router("resetheaders", uwsgi_router_clearheaders);
uwsgi_register_router("disableheaders", uwsgi_router_disableheaders);
uwsgi_register_router("signal", uwsgi_router_signal);
uwsgi_register_router("send", uwsgi_router_send);
uwsgi_register_router("send-crnl", uwsgi_router_send_crnl);
@@ -1753,6 +1789,9 @@ void uwsgi_register_embedded_routers() {
uwsgi_register_router("setprocname", uwsgi_router_setprocname);
uwsgi_register_router("alarm", uwsgi_router_alarm);
uwsgi_register_router("setmodifier1", uwsgi_router_setmodifier1);
uwsgi_register_router("setmodifier2", uwsgi_router_setmodifier2);
uwsgi_register_router("+", uwsgi_router_simple_math_plus);
uwsgi_register_router("-", uwsgi_router_simple_math_minus);
uwsgi_register_router("*", uwsgi_router_simple_math_multiply);
+5 -1
View File
@@ -104,8 +104,12 @@ char *uwsgi_do_rpc(char *node, char *func, uint8_t argc, char *argv[], uint16_t
if (node == NULL || !strcmp(node, "")) {
// allocate the whole buffer
if (!uwsgi.rpc_table) {
uwsgi_log("local rpc subsystem is still not initialized !!!\n");
return NULL;
}
*len = uwsgi_rpc(func, argc, argv, argvs, &buffer);
if (*buffer)
if (buffer)
return buffer;
return NULL;
}
+12 -10
View File
@@ -269,20 +269,22 @@ int uwsgi_remote_signal_send(char *addr, uint8_t sig) {
uh.pktsize = 0;
uh.modifier2 = sig;
int fd = uwsgi_connect(addr, uwsgi.socket_timeout, 0);
if (fd < 0)
return -1;
int fd = uwsgi_connect(addr, 0, 1);
if (fd < 0) return -1;
if (write(fd, (char *) &uh, 4) != 4) {
uwsgi_error("uwsgi_remote_signal_send()");
close(fd);
return -1;
}
// wait for connection
if (uwsgi.wait_write_hook(fd, uwsgi.socket_timeout) <= 0) goto end;
int ret = uwsgi_read_response(fd, &uh, uwsgi.socket_timeout, NULL);
if (uwsgi_write_true_nb(fd, (char *) &uh, 4, uwsgi.socket_timeout)) goto end;
if (uwsgi_read_whole_true_nb(fd, (char *) &uh, 4, uwsgi.socket_timeout)) goto end;
close(fd);
return ret;
return uh.modifier2;
end:
close(fd);
return -1;
}
+5
View File
@@ -190,6 +190,11 @@ static void spooler_req_parser_hook(char *key, uint16_t key_len, char *value, ui
}
if (!uwsgi_strncmp(key, key_len, "at", 2)) {
// at can be a float...
char *dot = memchr(value, '.', value_len);
if (dot) {
value_len = dot - value;
}
sr->at = uwsgi_str_num(value, value_len);
return;
}
+11 -7
View File
@@ -243,19 +243,23 @@ SSL_CTX *uwsgi_ssl_new_server_context(char *name, char *crt, char *key, char *ci
DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
if (dh) {
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_tmp_dh(ctx, dh);
DH_free(dh);
}
}
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#ifndef OPENSSL_NO_ECDH
#ifdef NID_X9_62_prime256v1
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
EC_KEY_free(ecdh);
#endif
#endif
#endif
}
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (ecdh) {
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
EC_KEY_free(ecdh);
}
#endif
#endif
#endif
if (crt_need_free) free(crt);
+2
View File
@@ -427,6 +427,8 @@ struct uwsgi_subscribe_node *uwsgi_add_subscribe_node(struct uwsgi_subscribe_slo
uwsgi_log("[uwsgi-subscription for pid %d] invalid (sniffed ?) packet sent for slot: %.*s node: %.*s unix_check: %lu\n", (int) uwsgi.mypid, usr->keylen, usr->key, usr->address_len, usr->address, (unsigned long) usr->unix_check);
return NULL;
}
// eventually the packet could be upgraded to sni...
uwsgi_subscription_sni_check(current_slot, usr);
#endif
// remove death mark and update cores and load
node->death_mark = 0;
+34 -28
View File
@@ -982,19 +982,44 @@ nonroot:
}
}
static void close_and_free_request(struct wsgi_request *wsgi_req) {
// close the connection with the client
if (!wsgi_req->fd_closed) {
// NOTE, if we close the socket before receiving eventually sent data, socket layer will send a RST
wsgi_req->socket->proto_close(wsgi_req);
}
if (wsgi_req->post_file) {
fclose(wsgi_req->post_file);
}
if (wsgi_req->post_read_buf) {
free(wsgi_req->post_read_buf);
}
if (wsgi_req->post_readline_buf) {
free(wsgi_req->post_readline_buf);
}
if (wsgi_req->proto_parser_buf) {
free(wsgi_req->proto_parser_buf);
}
}
// destroy a request
void uwsgi_destroy_request(struct wsgi_request *wsgi_req) {
wsgi_req->socket->proto_close(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);
}
memset(wsgi_req, 0, sizeof(struct wsgi_request));
if (uwsgi.threads > 1) {
// now the thread can die...
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &foo);
}
memset(wsgi_req, 0, sizeof(struct wsgi_request));
}
@@ -1053,27 +1078,8 @@ void uwsgi_close_request(struct wsgi_request *wsgi_req) {
uwsgi_apply_final_routes(wsgi_req);
#endif
// close the connection with the client
if (!wsgi_req->fd_closed) {
// NOTE, if we close the socket before receiving eventually sent data, socket layer will send a RST
wsgi_req->socket->proto_close(wsgi_req);
}
if (wsgi_req->post_file) {
fclose(wsgi_req->post_file);
}
if (wsgi_req->post_read_buf) {
free(wsgi_req->post_read_buf);
}
if (wsgi_req->post_readline_buf) {
free(wsgi_req->post_readline_buf);
}
if (wsgi_req->proto_parser_buf) {
free(wsgi_req->proto_parser_buf);
}
// close socket and free parsers-allocated memory
close_and_free_request(wsgi_req);
// after_request hook
if (!wsgi_req->is_raw && uwsgi.p[wsgi_req->uh->modifier1]->after_request)
+29 -11
View File
@@ -1916,6 +1916,17 @@ int main(int argc, char *argv[], char *envp[]) {
return uwsgi_run();
}
static char *uwsgi_at_file_read(char *filename) {
size_t size = 0;
char *buffer = uwsgi_open_and_read(filename, &size, 1, NULL);
if (size > 1) {
if (buffer[size-2] == '\n' || buffer[size-2] == '\r') {
buffer[size-2] = 0;
}
}
return buffer;
}
void uwsgi_setup(int argc, char *argv[], char *envp[]) {
#ifdef UWSGI_AS_SHARED_LIBRARY
#ifdef __APPLE__
@@ -2113,7 +2124,7 @@ void uwsgi_setup(int argc, char *argv[], char *envp[]) {
uwsgi_apply_config_pass('$', (char *(*)(char *)) getenv);
// third pass: FILEs
uwsgi_apply_config_pass('@', uwsgi_simple_file_read);
uwsgi_apply_config_pass('@', uwsgi_at_file_read);
// last pass: REFERENCEs
uwsgi_apply_config_pass('%', uwsgi_manage_placeholder);
@@ -2187,7 +2198,7 @@ void uwsgi_setup(int argc, char *argv[], char *envp[]) {
uwsgi_opt_flock(NULL, uwsgi.flock_wait2, NULL);
// setup master logging
if (uwsgi.log_master && !uwsgi.daemonize2)
if (uwsgi.log_master && !uwsgi.daemonize2 && !uwsgi.logto2)
uwsgi_setup_log_master();
// setup offload engines
@@ -2470,6 +2481,7 @@ int uwsgi_start(void *v_argv) {
if (uwsgi.logto2) {
if (!uwsgi.is_a_reload || uwsgi.log_reopen) {
logto(uwsgi.logto2);
uwsgi_setup_log_master();
}
}
@@ -2772,6 +2784,7 @@ int uwsgi_start(void *v_argv) {
!uwsgi.command_mode &&
!uwsgi.daemons_cnt &&
!uwsgi.crons &&
!uwsgi.spoolers &&
!uwsgi.emperor_proxy
#ifdef __linux__
&& !uwsgi.setns_socket
@@ -2930,7 +2943,7 @@ unsafe:
}
// initialize locks and socket as soon as possible, as the master could enqueue tasks
if (uwsgi.spoolers != NULL && (uwsgi.sockets || uwsgi.loop)) {
if (uwsgi.spoolers != NULL) {
create_signal_pipe(uwsgi.shared->spooler_signal_pipe);
struct uwsgi_spooler *uspool = uwsgi.spoolers;
while (uspool) {
@@ -3068,15 +3081,13 @@ next:
if (uwsgi.spoolers != NULL && (uwsgi.sockets || uwsgi.loop)) {
struct uwsgi_spooler *uspool = uwsgi.spoolers;
while (uspool) {
if (uspool->mode == UWSGI_SPOOLER_EXTERNAL)
goto next2;
uspool->pid = spooler_start(uspool);
struct uwsgi_spooler *uspool = uwsgi.spoolers;
while (uspool) {
if (uspool->mode == UWSGI_SPOOLER_EXTERNAL)
goto next2;
uspool->pid = spooler_start(uspool);
next2:
uspool = uspool->next;
}
uspool = uspool->next;
}
if (!uwsgi.master_process) {
@@ -3653,6 +3664,13 @@ 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 {
+9 -7
View File
@@ -1,4 +1,4 @@
#include "uwsgi.h"
#include <uwsgi.h>
/*
@@ -10,6 +10,8 @@
extern struct uwsgi_server uwsgi;
#define REQ_DATA wsgi_req->method_len, wsgi_req->method, wsgi_req->uri_len, wsgi_req->uri, wsgi_req->remote_addr_len, wsgi_req->remote_addr
static struct uwsgi_buffer *uwsgi_websocket_message(struct wsgi_request *wsgi_req, char *msg, size_t len, uint8_t opcode) {
struct uwsgi_buffer *ub = wsgi_req->websocket_send_buf;
if (!ub) {
@@ -61,7 +63,7 @@ static int uwsgi_websockets_check_pingpong(struct wsgi_request *wsgi_req) {
// pong not received ?
if (wsgi_req->websocket_last_pong < wsgi_req->websocket_last_ping) {
if (wsgi_req->websocket_last_ping - wsgi_req->websocket_last_pong > uwsgi.websockets_pong_tolerance) {
uwsgi_log("[uwsgi-websocket] no PONG received in %d seconds !!!\n", uwsgi.websockets_pong_tolerance);
uwsgi_log("[uwsgi-websocket] \"%.*s %.*s\" (%.*s) no PONG received in %d seconds !!!\n", REQ_DATA, uwsgi.websockets_pong_tolerance);
return -1;
}
return 0;
@@ -189,7 +191,7 @@ static ssize_t uwsgi_websockets_recv_pkt(struct wsgi_request *wsgi_req, int nb)
}
goto wait;
}
uwsgi_error("uwsgi_websockets_recv_pkt()");
uwsgi_req_error("uwsgi_websockets_recv_pkt()");
return -1;
}
@@ -201,7 +203,7 @@ wait:
if (rlen <= 0) return -1;
}
if (ret < 0) {
uwsgi_error("uwsgi_websockets_recv_pkt()");
uwsgi_req_error("uwsgi_websockets_recv_pkt()");
return -1;
}
// send unsolicited pong
@@ -254,11 +256,11 @@ static struct uwsgi_buffer *uwsgi_websocket_recv_do(struct wsgi_request *wsgi_re
wsgi_req->websocket_size = uwsgi_be64(wsgi_req->websocket_buf->buf+2);
}
else {
uwsgi_log("[uwsgi-websocket] BUG error in websocket parser\n");
uwsgi_log("[uwsgi-websocket] \"%.*s %.*s\" (%.*s) BUG error in websocket parser\n", REQ_DATA);
return NULL;
}
if (wsgi_req->websocket_size > (uwsgi.websockets_max_size*1024)) {
uwsgi_log("[uwsgi-websocket] invalid packet size received: %llu, max allowed: %llu\n", wsgi_req->websocket_size, uwsgi.websockets_max_size * 1024);
uwsgi_log("[uwsgi-websocket] \"%.*s %.*s\" (%.*s) invalid packet size received: %llu, max allowed: %llu\n", REQ_DATA, wsgi_req->websocket_size, uwsgi.websockets_max_size * 1024);
return NULL;
}
wsgi_req->websocket_phase = 2;
@@ -312,7 +314,7 @@ static struct uwsgi_buffer *uwsgi_websocket_recv_do(struct wsgi_request *wsgi_re
break;
// oops
default:
uwsgi_log("[uwsgi-websocket] BUG error in websocket parser\n");
uwsgi_log("[uwsgi-websocket] \"%.*s %.*s\" (%.*s) BUG error in websocket parser\n", REQ_DATA);
return NULL;
}
}
+31 -23
View File
@@ -82,12 +82,14 @@ int uwsgi_response_prepare_headers(struct wsgi_request *wsgi_req, char *status,
wsgi_req->status = uwsgi_str3_num(status);
#ifdef UWSGI_ROUTING
// apply error routes
if (uwsgi_apply_error_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
if (wsgi_req->is_error_routing == 0) {
if (uwsgi_apply_error_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
}
wsgi_req->is_error_routing = 0;
}
wsgi_req->is_error_routing = 0;
#endif
if (status_len <= 4) {
char *new_sc = NULL;
@@ -186,12 +188,14 @@ static int uwsgi_response_write_headers_do0(struct wsgi_request *wsgi_req) {
#ifdef UWSGI_ROUTING
// apply response routes
if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
}
wsgi_req->is_response_routing = 0;
if (wsgi_req->is_response_routing == 0) {
if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
}
wsgi_req->is_response_routing = 0;
}
#endif
struct uwsgi_string_list *ah = uwsgi.additional_headers;
@@ -352,12 +356,14 @@ int uwsgi_response_write_body_do(struct wsgi_request *wsgi_req, char *buf, size_
// special case here, we could need to set transformations before
if (!wsgi_req->headers_sent) {
// apply response routes
if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
}
wsgi_req->is_response_routing = 0;
if (wsgi_req->is_response_routing == 0) {
if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
}
wsgi_req->is_response_routing = 0;
}
}
#endif
@@ -435,12 +441,14 @@ int uwsgi_response_writev_body_do(struct wsgi_request *wsgi_req, struct iovec *i
// special case here, we could need to set transformations before
if (!wsgi_req->headers_sent) {
// apply response routes
if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
}
wsgi_req->is_response_routing = 0;
if (wsgi_req->is_response_routing == 0) {
if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) {
// from now on ignore write body requests...
wsgi_req->ignore_body = 1;
return -1;
}
wsgi_req->is_response_routing = 0;
}
}
#endif
+378
View File
@@ -0,0 +1,378 @@
#include "../python/uwsgi_python.h"
/*
python >= 3.4 asyncio (PEP 3156) loop engine
EXPERIMENTAL !!!
Author: Roberto De Ioris
*/
extern struct uwsgi_server uwsgi;
extern struct uwsgi_python up;
static struct uwsgi_asyncio {
PyObject *mod;
PyObject *loop;
PyObject *request;
PyObject *hook_fd;
PyObject *hook_timeout;
PyObject *hook_fix;
} uasyncio;
#define free_req_queue uwsgi.async_queue_unused_ptr++; uwsgi.async_queue_unused[uwsgi.async_queue_unused_ptr] = wsgi_req
static void uwsgi_opt_setup_asyncio(char *opt, char *value, void *null) {
// set async mode
uwsgi_opt_set_int(opt, value, &uwsgi.async);
if (uwsgi.socket_timeout < 30) {
uwsgi.socket_timeout = 30;
}
// set loop engine
uwsgi.loop = "asyncio";
}
static struct uwsgi_option asyncio_options[] = {
{"asyncio", required_argument, 0, "a shortcut enabling asyncio loop engine with the specified number of async cores and optimal parameters", uwsgi_opt_setup_asyncio, NULL, UWSGI_OPT_THREADS},
{0, 0, 0, 0, 0, 0, 0},
};
static void gil_asyncio_get() {
pthread_setspecific(up.upt_gil_key, (void *) PyGILState_Ensure());
}
static void gil_asyncio_release() {
PyGILState_Release((PyGILState_STATE) pthread_getspecific(up.upt_gil_key));
}
static int uwsgi_asyncio_wait_read_hook(int fd, int timeout) {
struct wsgi_request *wsgi_req = current_wsgi_req();
if (PyObject_CallMethod(uasyncio.loop, "add_reader", "iOl", fd, uasyncio.hook_fd,(long) wsgi_req) == NULL) {
goto error;
}
PyObject *ob_timeout = PyObject_CallMethod(uasyncio.loop, "call_later", "iOl", timeout, uasyncio.hook_timeout, (long)wsgi_req);
if (!ob_timeout) {
if (PyObject_CallMethod(uasyncio.loop, "remove_reader", "i", fd) == NULL) PyErr_Print();
goto error;
}
// back to loop
if (uwsgi.schedule_to_main) uwsgi.schedule_to_main(wsgi_req);
// back from loop
if (PyObject_CallMethod(uasyncio.loop, "remove_reader", "i", fd) == NULL) PyErr_Print();
if (PyObject_CallMethod(ob_timeout, "cancel", NULL) == NULL) PyErr_Print();
Py_DECREF(ob_timeout);
if (wsgi_req->async_timed_out) return 0;
return 1;
error:
PyErr_Print();
return -1;
}
static int uwsgi_asyncio_wait_write_hook(int fd, int timeout) {
struct wsgi_request *wsgi_req = current_wsgi_req();
if (PyObject_CallMethod(uasyncio.loop, "add_writer", "iOl", fd, uasyncio.hook_fd,(long) wsgi_req) == NULL) {
goto error;
}
PyObject *ob_timeout = PyObject_CallMethod(uasyncio.loop, "call_later", "iOl", timeout, uasyncio.hook_timeout, (long)wsgi_req);
if (!ob_timeout) {
if (PyObject_CallMethod(uasyncio.loop, "remove_writer", "i", fd) == NULL) PyErr_Print();
goto error;
}
// back to loop
if (uwsgi.schedule_to_main) {
uwsgi.schedule_to_main(wsgi_req);
}
// back from loop
if (PyObject_CallMethod(uasyncio.loop, "remove_writer", "i", fd) == NULL) PyErr_Print();
if (PyObject_CallMethod(ob_timeout, "cancel", NULL) == NULL) PyErr_Print();
Py_DECREF(ob_timeout);
if (wsgi_req->async_timed_out) return 0;
return 1;
error:
PyErr_Print();
return -1;
}
static PyObject *py_uwsgi_asyncio_request(PyObject *self, PyObject *args) {
long wsgi_req_ptr = 0;
int timed_out = 0;
if (!PyArg_ParseTuple(args, "l|i:uwsgi_asyncio_request", &wsgi_req_ptr, &timed_out)) {
uwsgi_log_verbose("[BUG] invalid arguments for asyncio callback !!!\n");
exit(1);
}
struct wsgi_request *wsgi_req = (struct wsgi_request *) wsgi_req_ptr;
uwsgi.wsgi_req = wsgi_req;
PyObject *ob_timeout = (PyObject *) wsgi_req->async_timeout;
if (PyObject_CallMethod(ob_timeout, "cancel", NULL) == NULL) PyErr_Print();
Py_DECREF(ob_timeout);
// avoid mess when closing the request
wsgi_req->async_timeout = NULL;
if (timed_out > 0) {
if (PyObject_CallMethod(uasyncio.loop, "remove_reader", "i", wsgi_req->fd) == NULL) PyErr_Print();
goto end;
}
int status = wsgi_req->socket->proto(wsgi_req);
if (status > 0) {
ob_timeout = PyObject_CallMethod(uasyncio.loop, "call_later", "iOli", uwsgi.socket_timeout, uasyncio.request, wsgi_req_ptr, 1);
if (!ob_timeout) {
if (PyObject_CallMethod(uasyncio.loop, "remove_reader", "i", wsgi_req->fd) == NULL) PyErr_Print();
goto end;
}
// trick for reference counting
wsgi_req->async_timeout = (struct uwsgi_rb_timer *) ob_timeout;
goto again;
}
if (PyObject_CallMethod(uasyncio.loop, "remove_reader", "i", wsgi_req->fd) == NULL) {
PyErr_Print();
goto end;
}
if (status == 0) {
// we call this two time... overengineering :(
uwsgi.async_proto_fd_table[wsgi_req->fd] = NULL;
uwsgi.schedule_to_req();
goto again;
}
end:
uwsgi.async_proto_fd_table[wsgi_req->fd] = NULL;
uwsgi_close_request(uwsgi.wsgi_req);
free_req_queue;
again:
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *py_uwsgi_asyncio_accept(PyObject *self, PyObject *args) {
long uwsgi_sock_ptr = 0;
if (!PyArg_ParseTuple(args, "l:uwsgi_asyncio_accept", &uwsgi_sock_ptr)) {
return NULL;
}
struct wsgi_request *wsgi_req = find_first_available_wsgi_req();
if (wsgi_req == NULL) {
uwsgi_async_queue_is_full(uwsgi_now());
goto end;
}
uwsgi.wsgi_req = wsgi_req;
struct uwsgi_socket *uwsgi_sock = (struct uwsgi_socket *) uwsgi_sock_ptr;
// fill wsgi_request structure
wsgi_req_setup(wsgi_req, wsgi_req->async_id, uwsgi_sock );
// mark core as used
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 1;
// accept the connection (since uWSGI 1.5 all of the sockets are non-blocking)
if (wsgi_req_simple_accept(wsgi_req, uwsgi_sock->fd)) {
// in case of errors (or thundering herd, just reset it)
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 0;
free_req_queue;
goto end;
}
wsgi_req->start_of_request = uwsgi_micros();
wsgi_req->start_of_request_in_sec = wsgi_req->start_of_request/1000000;
// enter harakiri mode
if (uwsgi.harakiri_options.workers > 0) {
set_harakiri(uwsgi.harakiri_options.workers);
}
uwsgi.async_proto_fd_table[wsgi_req->fd] = wsgi_req;
// add callback for protocol
if (PyObject_CallMethod(uasyncio.loop, "add_reader", "iOl", wsgi_req->fd, uasyncio.request, (long) wsgi_req) == NULL) {
free_req_queue;
PyErr_Print();
}
// add timeout
PyObject *ob_timeout = PyObject_CallMethod(uasyncio.loop, "call_later", "iOli", uwsgi.socket_timeout, uasyncio.request, (long)wsgi_req, 1);
if (!ob_timeout) {
if (PyObject_CallMethod(uasyncio.loop, "remove_reader", "i", wsgi_req->fd) == NULL) PyErr_Print();
free_req_queue;
}
else {
// trick for reference counting
wsgi_req->async_timeout = (struct uwsgi_rb_timer *) ob_timeout;
}
end:
Py_INCREF(Py_None);
return Py_None;
}
PyObject *py_uwsgi_asyncio_hook_fd(PyObject *self, PyObject *args) {
long wsgi_req_ptr = 0;
if (!PyArg_ParseTuple(args, "l:uwsgi_asyncio_hook_fd", &wsgi_req_ptr)) {
return NULL;
}
uwsgi.wsgi_req = (struct wsgi_request *) wsgi_req_ptr;
uwsgi.schedule_to_req();
Py_INCREF(Py_None);
return Py_None;
}
PyObject *py_uwsgi_asyncio_hook_timeout(PyObject *self, PyObject *args) {
long wsgi_req_ptr = 0;
if (!PyArg_ParseTuple(args, "l", &wsgi_req_ptr)) {
return NULL;
}
uwsgi.wsgi_req = (struct wsgi_request *) wsgi_req_ptr;
uwsgi.wsgi_req->async_timed_out = 1;
uwsgi.schedule_to_req();
Py_INCREF(Py_None);
return Py_None;
}
PyObject *py_uwsgi_asyncio_hook_fix(PyObject *self, PyObject *args) {
long wsgi_req_ptr = 0;
if (!PyArg_ParseTuple(args, "l", &wsgi_req_ptr)) {
return NULL;
}
uwsgi.wsgi_req = (struct wsgi_request *) wsgi_req_ptr;
uwsgi.schedule_to_req();
Py_INCREF(Py_None);
return Py_None;
}
PyMethodDef uwsgi_asyncio_accept_def[] = { {"uwsgi_asyncio_accept", py_uwsgi_asyncio_accept, METH_VARARGS, ""} };
PyMethodDef uwsgi_asyncio_request_def[] = { {"uwsgi_asyncio_request", py_uwsgi_asyncio_request, METH_VARARGS, ""} };
PyMethodDef uwsgi_asyncio_hook_fd_def[] = { {"uwsgi_asyncio_hook_fd", py_uwsgi_asyncio_hook_fd, METH_VARARGS, ""} };
PyMethodDef uwsgi_asyncio_hook_timeout_def[] = { {"uwsgi_asyncio_hook_timeout", py_uwsgi_asyncio_hook_timeout, METH_VARARGS, ""} };
PyMethodDef uwsgi_asyncio_hook_fix_def[] = { {"uwsgi_asyncio_hook_fix", py_uwsgi_asyncio_hook_fix, METH_VARARGS, ""} };
static void uwsgi_asyncio_schedule_fix(struct wsgi_request *wsgi_req) {
PyObject *cb = PyObject_CallMethod(uasyncio.loop, "call_soon", "Ol", uasyncio.hook_fix, (long) wsgi_req);
if (!cb) goto error;
Py_DECREF(cb);
return;
error:
PyErr_Print();
}
static void asyncio_loop() {
if (!uwsgi.has_threads && uwsgi.mywid == 1) {
uwsgi_log("!!! Running asyncio without threads IS NOT recommended, enable them with --enable-threads !!!\n");
}
if (uwsgi.socket_timeout < 30) {
uwsgi_log("!!! Running asyncio with a socket-timeout lower than 30 seconds is not recommended, tune it with --socket-timeout !!!\n");
}
if (!uwsgi.async_waiting_fd_table)
uwsgi.async_waiting_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd);
if (!uwsgi.async_proto_fd_table)
uwsgi.async_proto_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd);
// get the GIL
UWSGI_GET_GIL
up.gil_get = gil_asyncio_get;
up.gil_release = gil_asyncio_release;
uwsgi.wait_write_hook = uwsgi_asyncio_wait_write_hook;
uwsgi.wait_read_hook = uwsgi_asyncio_wait_read_hook;
uwsgi.schedule_fix = uwsgi_asyncio_schedule_fix;
if (uwsgi.async < 2) {
uwsgi_log("the asyncio loop engine requires async mode (--async <n>)\n");
exit(1);
}
if (!uwsgi.schedule_to_main) {
uwsgi_log("*** DANGER *** asyncio mode without coroutine/greenthread engine loaded !!!\n");
}
PyObject *asyncio = PyImport_ImportModule("asyncio");
if (!asyncio) uwsgi_pyexit;
uasyncio.mod = asyncio;
uasyncio.loop = PyObject_CallMethod(asyncio, "get_event_loop", NULL);
if (!uasyncio.loop) uwsgi_pyexit;
// main greenlet waiting for connection (one greenlet per-socket)
PyObject *asyncio_accept = PyCFunction_New(uwsgi_asyncio_accept_def, NULL);
Py_INCREF(asyncio_accept);
uasyncio.request = PyCFunction_New(uwsgi_asyncio_request_def, NULL);
if (!uasyncio.request) uwsgi_pyexit;
uasyncio.hook_fd = PyCFunction_New(uwsgi_asyncio_hook_fd_def, NULL);
if (!uasyncio.hook_fd) uwsgi_pyexit;
uasyncio.hook_timeout = PyCFunction_New(uwsgi_asyncio_hook_timeout_def, NULL);
if (!uasyncio.hook_timeout) uwsgi_pyexit;
uasyncio.hook_fix = PyCFunction_New(uwsgi_asyncio_hook_fix_def, NULL);
if (!uasyncio.hook_fix) uwsgi_pyexit;
Py_INCREF(uasyncio.request);
Py_INCREF(uasyncio.hook_fd);
Py_INCREF(uasyncio.hook_timeout);
Py_INCREF(uasyncio.hook_fix);
// call add_handler on each socket
struct uwsgi_socket *uwsgi_sock = uwsgi.sockets;
while(uwsgi_sock) {
if (PyObject_CallMethod(uasyncio.loop, "add_reader", "iOi", uwsgi_sock->fd, asyncio_accept, (long) uwsgi_sock) == NULL) {
uwsgi_pyexit;
}
uwsgi_sock = uwsgi_sock->next;
}
if (PyObject_CallMethod(uasyncio.loop, "run_forever", NULL) == NULL) {
uwsgi_pyexit;
}
// never here ?
}
static void asyncio_init() {
uwsgi_register_loop( (char *) "asyncio", asyncio_loop);
}
struct uwsgi_plugin asyncio_plugin = {
.name = "asyncio",
.options = asyncio_options,
.on_load = asyncio_init,
};
+8
View File
@@ -0,0 +1,8 @@
from distutils import sysconfig
NAME='asyncio'
CFLAGS = ['-I' + sysconfig.get_python_inc(), '-I' + sysconfig.get_python_inc(plat_specific=True)]
LDFLAGS = []
LIBS = []
GCC_LIST = ['asyncio']
+214 -169
View File
@@ -2,6 +2,8 @@
extern struct uwsgi_server uwsgi;
#define kill_on_error if (!uc.do_not_kill_on_error) { if (kill(cgi_pid, SIGKILL)) uwsgi_error("kill()");}
struct uwsgi_cgi {
struct uwsgi_dyn_dict *mountpoint;
struct uwsgi_dyn_dict *helpers;
@@ -16,6 +18,8 @@ struct uwsgi_cgi {
int has_mountpoints;
struct uwsgi_dyn_dict *default_cgi;
int path_info;
int do_not_kill_on_error;
int async_max_attempts;
} uc ;
static void uwsgi_opt_add_cgi(char *opt, char *value, void *foobar) {
@@ -62,6 +66,9 @@ struct uwsgi_option uwsgi_cgi_options[] = {
{"cgi-path-info", no_argument, 0, "disable PATH_INFO management in cgi scripts", uwsgi_opt_true, &uc.path_info, 0},
{"cgi-do-not-kill-on-error", no_argument, 0, "do not send SIGKILL to cgi script on errors", uwsgi_opt_true, &uc.do_not_kill_on_error, 0},
{"cgi-async-max-attempts", no_argument, 0, "max waitpid() attempts in cgi async mode (default 10)", uwsgi_opt_set_int, &uc.async_max_attempts, 0},
{0, 0, 0, 0, 0, 0, 0},
};
@@ -179,118 +186,154 @@ static char *uwsgi_cgi_get_helper(char *filename) {
}
static int uwsgi_cgi_parse(struct wsgi_request *wsgi_req, char *buf, size_t len) {
size_t i;
/*
start reading each line until Status or Location are found
-1 error
0 not found
1 found
*/
static int uwsgi_cgi_check_status(struct wsgi_request *wsgi_req, char *buf, size_t len) {
char *key = buf, *value = NULL;
size_t header_size = 0;
int status_sent = 0;
size_t i;
// Search for Status/Location headers
for(i=0;i<len;i++) {
// end of a line
if (buf[i] == '\n') {
// end of headers
if (key == NULL) {
// Default status
// end of a line
if (buf[i] == '\n') {
// end of headers
if (key == NULL) {
// Default status
#ifdef UWSGI_DEBUG
uwsgi_log("setting default Status header\n");
#endif
if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1;
break;
return 1;
}
// invalid header
else if (value == NULL) {
return -1;
}
header_size = (buf+i) - key;
// security check
if (buf+i > buf) {
if ((buf[i-1]) == '\r') {
header_size--;
}
// invalid header
else if (value == NULL) return -1;
header_size = (buf+i) - key;
// security check
if (buf+i > buf) {
// remove \r
if ((buf[i-1]) == '\r') {
header_size--;
}
}
// enough space for Status ?
if (header_size >= 11) {
// "Status: NNN"
if (!strncasecmp("Status: ", key, 8)) {
#ifdef UWSGI_DEBUG
uwsgi_log("found Status header: %.*s\n", header_size, key);
#endif
if (uwsgi_response_prepare_headers(wsgi_req, key+8, header_size - 8)) return -1;
return 1;
}
// Location: X
if (!strncasecmp("Location: ", key, 10)) {
#ifdef UWSGI_DEBUG
uwsgi_log("found Location header: %.*s\n", header_size, key);
#endif
if (uwsgi_response_prepare_headers(wsgi_req, "302 Found", 9)) return -1;
return 1;
}
}
if (header_size >= 11) {
// "Status: NNN"
if (!strncasecmp("Status: ", key, 8)) {
#ifdef UWSGI_DEBUG
uwsgi_log("found Status header: %.*s\n", header_size, key);
#endif
if (uwsgi_response_prepare_headers(wsgi_req, key+8, header_size - 8)) return -1;
break;
}
// Location: X
if (!strncasecmp("Location: ", key, 10)) {
#ifdef UWSGI_DEBUG
uwsgi_log("found Location header: %.*s\n", header_size, key);
#endif
if (uwsgi_response_prepare_headers(wsgi_req, "302 Found", 9)) return -1;
break;
}
}
key = NULL;
value = NULL;
key = NULL;
value = NULL;
}
else if (buf[i] == ':') {
else if (buf[i] == ':') {
value = buf+i;
}
else if (buf[i] != '\r') {
if (key == NULL) {
key = buf + i;
}
}
}
else if (buf[i] != '\r') {
if (key == NULL) key = buf + i;
}
}
key = buf;
value = NULL;
// no Status/Location found
return 0;
for(i=0;i<len;i++) {
// end of a line
if (buf[i] == '\n') {
// end of headers
if (key == NULL) {
i++;
goto send_body;
}
// invalid header
else if (value == NULL) {
return -1;
}
header_size = (buf+i) - key;
// security check
if (buf+i > buf) {
if ((buf[i-1]) == '\r') {
header_size--;
}
static int uwsgi_cgi_parse(struct wsgi_request *wsgi_req, int fd, char *buf, size_t blen) {
size_t i;
size_t header_size = 0;
int status_sent = 0;
size_t remains = blen;
char *ptr = buf;
size_t len = 0;
while(remains > 0) {
ssize_t rlen = uwsgi_read_true_nb(fd, ptr, remains, uc.timeout);
if (rlen < 0) {
if (!errno) return 1;
return -1;
}
// timed out
if (rlen == 0) return -1;
remains -= rlen;
len += rlen;
ptr += rlen;
// Search for Status/Location headers
if (!status_sent) {
status_sent = uwsgi_cgi_check_status(wsgi_req, buf, len);
if (status_sent < 0) return -1;
// need more data ?
if (status_sent == 0) continue;
}
// send headers
char *key = buf;
char *value = NULL;
for(i=0;i<len;i++) {
// end of a line
if (buf[i] == '\n') {
// end of headers
if (key == NULL) {
i++;
goto send_body;
}
// invalid header
else if (value == NULL) {
return -1;
}
header_size = (buf+i) - key;
// security check
if (buf+i > buf) {
if ((buf[i-1]) == '\r') {
header_size--;
}
}
}
#ifdef UWSGI_DEBUG
uwsgi_log("found CGI header: %.*s\n", header_size, key);
uwsgi_log("found CGI header: %.*s\n", header_size, key);
#endif
// Ignore "Status: NNN" header
if (status_sent == 0 && header_size >= 11) {
if (!strncasecmp("Status: ", key, 8)) {
status_sent = 1;
key = NULL;
value = NULL;
continue;
// Ignore "Status: NNN" header
if (header_size >= 11) {
if (!strncasecmp("Status: ", key, 8)) {
key = NULL;
value = NULL;
continue;
}
}
uwsgi_response_add_header(wsgi_req, NULL, 0, key, header_size);
key = NULL;
value = NULL;
}
uwsgi_response_add_header(wsgi_req, NULL, 0, key, header_size);
key = NULL;
value = NULL;
}
else if (buf[i] == ':') {
value = buf+i;
}
else if (buf[i] != '\r') {
if (key == NULL) {
key = buf + i;
else if (buf[i] == ':') {
value = buf+i;
}
else if (buf[i] != '\r') {
if (key == NULL) {
key = buf + i;
}
}
}
}
@@ -592,7 +635,6 @@ static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t do
int post_pipe[2];
int nargs = 0;
int waitpid_status;
ssize_t len;
int i;
char **argv;
@@ -630,126 +672,96 @@ static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t do
close(cgi_pipe[1]);
close(post_pipe[0]);
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) {
close(post_pipe[1]);
goto clear2;
}
if (buf == uwsgi.empty) break;
// write data to the node
if (uwsgi_write_true_nb(post_pipe[1], buf, rlen, uc.timeout)) {
close(post_pipe[1]);
goto clear2;
}
remains -= rlen;
}
close(post_pipe[1]);
// wait for data
char *headers_buf = uwsgi_malloc(uc.buffer_size);
char *ptr = headers_buf;
remains = uc.buffer_size;
int completed = 0;
while(remains > 0) {
int ret = uwsgi.wait_read_hook(cgi_pipe[0], uc.timeout);
if (ret > 0) {
len = read(cgi_pipe[0], ptr, remains);
if (len > 0) {
ptr+=len;
remains -= len;
}
else if (len == 0) {
completed = 1;
break;
}
else {
uwsgi_error("read()");
goto clear;
}
continue;
}
else if (ret == 0) {
uwsgi_log("CGI timeout !!!\n");
goto clear;
}
break;
}
char *buf = uwsgi_malloc(uc.buffer_size);
if (uwsgi_cgi_parse(wsgi_req, headers_buf, uc.buffer_size-remains)) {
uwsgi_log("invalid CGI output !!!\n");
int completed = uwsgi_cgi_parse(wsgi_req, cgi_pipe[0], buf, uc.buffer_size);
if (completed < 0) {
uwsgi_log("invalid CGI response !!!\n");
kill_on_error
goto clear;
}
while (!completed) {
int ret = uwsgi.wait_read_hook(cgi_pipe[0], uc.timeout);
if (ret > 0) {
len = read(cgi_pipe[0], headers_buf, uc.buffer_size);
if (len > 0) {
uwsgi_response_write_body_do(wsgi_req, headers_buf, len);
}
// end of output
else if (len == 0) {
break;
}
else {
uwsgi_error("read()");
ssize_t rlen = uwsgi_read_true_nb(cgi_pipe[0], buf, uc.buffer_size, uc.timeout);
if (rlen > 0) {
if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) {
kill_on_error
goto clear;
}
continue;
}
else if (ret == 0) {
uwsgi_log("CGI timeout !!!\n");
goto clear;
}
break;
else if (rlen == 0) {
uwsgi_log("CGI timeout !!!\n");
kill_on_error
goto clear;
}
else {
if (errno) {
uwsgi_req_error("error reading CGI response\n");
kill_on_error
}
goto clear;
}
}
clear:
free(headers_buf);
free(buf);
clear2:
close(cgi_pipe[0]);
close(post_pipe[1]);
// now wait for process exit/death
if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
uwsgi_error("waitpid()");
// in async mode we need a trick...
if (uwsgi.async > 1) {
pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG);
if (diedpid < 0) {
uwsgi_error("waitpid()");
}
else if (diedpid == 0) {
// pass the pid of the cgi to async_plagued (the after request hook will clear the process)
wsgi_req->async_plagued = (int) cgi_pid;
}
}
else {
if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
uwsgi_error("waitpid()");
}
}
return UWSGI_OK;
}
// close all the fd except wsgi_req->poll.fd and 2;
for(i=0;i< (int)uwsgi.max_fd;i++) {
if (post_pipe[0] == i) {
continue;
}
if (wsgi_req->post_file) {
if (fileno(wsgi_req->post_file) == i) {
continue;
}
}
if (i != wsgi_req->fd && i != 2 && i != cgi_pipe[1]) {
close(i);
}
}
// now map wsgi_req->poll.fd (or async_post) to 0 & cgi_pipe[1] to 1
if (post_pipe[0] != 0) {
dup2(post_pipe[0], 0);
close(post_pipe[0]);
}
#ifdef UWSGI_DEBUG
uwsgi_log("mapping cgi_pipe %d to 1\n", cgi_pipe[1]);
#endif
dup2(post_pipe[0], 0);
close(post_pipe[0]);
dup2(cgi_pipe[1],1);
close(cgi_pipe[1]);
// close all the fd > 2
for(i=3;i<(int)uwsgi.max_fd;i++) {
close(i);
}
// fill cgi env
for(i=0;i<wsgi_req->var_cnt;i++) {
// no need to free the putenv() memory
@@ -913,6 +925,39 @@ clear2:
static void uwsgi_cgi_after_request(struct wsgi_request *wsgi_req) {
if (wsgi_req->async_plagued > 0) {
int waitpid_status;
pid_t cgi_pid = (pid_t) wsgi_req->async_plagued;
int max_attempts = uc.async_max_attempts;
if (!max_attempts) max_attempts = 10;
while(max_attempts) {
pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG);
if (diedpid < 0) {
uwsgi_error("waitpid()");
break;
}
else if (diedpid == 0) {
int ret = uwsgi.wait_milliseconds_hook(1000);
if (ret < 0) {
kill_on_error
if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
uwsgi_error("waitpid()");
}
}
}
else {
break;
}
max_attempts--;
}
if (max_attempts == 0) {
kill_on_error
if (waitpid(cgi_pid, &waitpid_status, 0) < 0) {
uwsgi_error("waitpid()");
}
}
}
log_request(wsgi_req);
}
+7 -2
View File
@@ -45,6 +45,7 @@ struct corerouter_peer *uwsgi_cr_peer_add(struct corerouter_session *cs) {
if (!bufsize) bufsize = uwsgi.page_size;
peers->in = uwsgi_buffer_new(bufsize);
// add timeout
peers->current_timeout = cs->corerouter->socket_timeout;
peers->timeout = cr_add_timeout(cs->corerouter, peers);
peers->prev = old_peers;
@@ -582,8 +583,8 @@ struct corerouter_session *corerouter_alloc_session(struct uwsgi_corerouter *ucr
cs->corerouter = ucr;
cs->ugs = ugs;
// set initial timeout
peer->timeout = cr_add_timeout(ucr, ucr->cr_table[new_connection]);
// set initial timeout (could be overridden)
peer->current_timeout = ucr->socket_timeout;
ucr->active_sessions++;
@@ -622,6 +623,10 @@ struct corerouter_session *corerouter_alloc_session(struct uwsgi_corerouter *ucr
corerouter_close_session(ucr, cs);
cs = NULL;
}
else {
// truly set the timeout
peer->timeout = cr_add_timeout(ucr, ucr->cr_table[new_connection]);
}
return cs;
}
+6 -4
View File
@@ -3,12 +3,12 @@
#define COREROUTER_STATUS_RECV_HDR 2
#define COREROUTER_STATUS_RESPONSE 3
#define cr_add_timeout(u, x) uwsgi_add_rb_timer(u->timeouts, uwsgi_now()+u->socket_timeout, x)
#define cr_add_timeout_fast(u, x, t) uwsgi_add_rb_timer(u->timeouts, t+u->socket_timeout, x)
#define cr_add_timeout(u, x) uwsgi_add_rb_timer(u->timeouts, uwsgi_now()+x->current_timeout, x)
#define cr_add_timeout_fast(u, x, t) uwsgi_add_rb_timer(u->timeouts, t+x->current_timeout, x)
#define cr_del_timeout(u, x) uwsgi_del_rb_timer(u->timeouts, x->timeout); free(x->timeout);
#define uwsgi_cr_error(x, y) uwsgi_log("[uwsgi-%s client_addr: %s client_port: %s] %s: %s [%s line %d]\n", x->session->corerouter->short_name, x->session->client_address, x->session->client_port, y, strerror(errno), __FILE__, __LINE__)
#define uwsgi_cr_log(x, y, ...) uwsgi_log("[uwsgi-%s client_addr: %s client_port: %s]" y, x->session->corerouter->short_name, x->session->client_address, x->session->client_port, __VA_ARGS__)
#define uwsgi_cr_error(x, y) uwsgi_log("[uwsgi-%s key: %.*s client_addr: %s client_port: %s] %s: %s [%s line %d]\n", x->session->corerouter->short_name, (x == x->session->main_peer) ? (x->session->peers ? x->session->peers->key_len: 0) : x->key_len, (x == x->session->main_peer) ? (x->session->peers ? x->session->peers->key: "") : x->key, x->session->client_address, x->session->client_port, y, strerror(errno), __FILE__, __LINE__)
#define uwsgi_cr_log(x, y, ...) uwsgi_log("[uwsgi-%s key: %.*s client_addr: %s client_port: %s]" y, x->session->corerouter->short_name, (x == x->session->main_peer) ? (x->session->peers ? x->session->peers->key_len: 0) : x->key_len, (x == x->session->main_peer) ? (x->session->peers ? x->session->peers->key: "") : x->key, x->session->client_address, x->session->client_port, __VA_ARGS__)
#define cr_try_again if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {\
errno = EINPROGRESS;\
@@ -188,6 +188,8 @@ struct corerouter_peer {
struct corerouter_peer *prev;
struct corerouter_peer *next;
int current_timeout;
};
struct uwsgi_corerouter {
+54 -2
View File
@@ -10,6 +10,10 @@ struct uwsgi_emperor_mongodb_state {
char *address;
char *collection;
char *json;
char *database;
char *username;
char *password;
char *predigest;
};
@@ -20,7 +24,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues
try {
// requested fields
mongo::BSONObj p = BSON( "name" << 1 << "config" << 1 << "ts" << 1 << "uid" << 1 << "gid" << 1 );
mongo::BSONObj p = BSON( "name" << 1 << "config" << 1 << "ts" << 1 << "uid" << 1 << "gid" << 1 << "socket" << 1 );
mongo::BSONObj q = mongo::fromjson(uems->json);
// the connection object (will be automatically destroyed at each cycle)
mongo::DBClientConnection c;
@@ -29,9 +33,17 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues
// connect
c.connect(uems->address);
if (uems->database && uems->username && uems->password) {
std::string err;
if (c.auth(uems->database, uems->username, uems->password, err, uems->predigest ? false : true) == false) {
uwsgi_log_verbose("[emperor-mongodb] unabel to authenticate to db %s: %s\n", uems->database, err.c_str());
return;
}
}
// run the query
std::auto_ptr<mongo::DBClientCursor> cursor = c.query(uems->collection, q, 0, 0, &p);
while( cursor->more() ) {
while(cursor.get() && cursor->more() ) {
mongo::BSONObj p = cursor->next();
// checking for an empty string is not required, but we reduce the load
@@ -40,6 +52,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues
if (strlen(name) == 0) continue;
const char *config = p.getStringField("config");
if (strlen(config) == 0) config = NULL;
time_t vassal_ts = 0;
// ts must be a Date object !!!
@@ -61,6 +74,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues
}
const char *socket_name = p.getStringField("socket");
if (strlen(socket_name) == 0) socket_name = NULL;
uwsgi_emperor_simple_do(ues, (char *) name, (char *) config, vassal_ts/1000, vassal_uid, vassal_gid, (char *) socket_name);
}
@@ -76,6 +90,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues
b.append("name", c_ui->name);
mongo::BSONObj q2 = b.obj();
cursor = c.query(uems->collection, q2, 0, 0, &p);
if (!cursor.get()) return;
#ifdef UWSGI_DEBUG
uwsgi_log("JSON: %s\n", q2.toString().c_str());
#endif
@@ -121,3 +136,40 @@ done:
uwsgi_log("[emperor] enabled emperor MongoDB monitor for %s on collection %s\n", uems->address, uems->collection);
}
// setup a new mongodb imperial monitor (keyval based)
extern "C" void uwsgi_imperial_monitor_mongodb_init2(struct uwsgi_emperor_scanner *ues) {
// allocate a new state
ues->data = uwsgi_calloc(sizeof(struct uwsgi_emperor_mongodb_state));
size_t arg_len = strlen(ues->arg);
struct uwsgi_emperor_mongodb_state *uems = (struct uwsgi_emperor_mongodb_state *) ues->data;
// parse args/ set defaults
uems->address = (char *) "127.0.0.1:27017";
uems->collection = (char *) "uwsgi.emperor.vassals";
uems->json = (char *) "";
char *args = NULL;
if (arg_len <= 11) goto done;
args = ues->arg+11;
if (uwsgi_kvlist_parse(args, strlen(args), ',', '=',
"addr", &uems->address,
"address", &uems->address,
"server", &uems->address,
"collection", &uems->collection,
"coll", &uems->collection,
"json", &uems->json,
"database", &uems->database,
"db", &uems->database,
"username", &uems->username,
"password", &uems->password,
"predigest", &uems->predigest,
NULL)) {
uwsgi_log("[emperor-mongodb] invalid keyval syntax !\n");
exit(1);
}
done:
uwsgi_log("[emperor] enabled emperor MongoDB monitor for %s on collection %s\n", uems->address, uems->collection);
}
+2
View File
@@ -2,9 +2,11 @@
void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *);
void uwsgi_imperial_monitor_mongodb_init(struct uwsgi_emperor_scanner *);
void uwsgi_imperial_monitor_mongodb_init2(struct uwsgi_emperor_scanner *);
void emperor_mongodb_init(void) {
uwsgi_register_imperial_monitor("mongodb", uwsgi_imperial_monitor_mongodb_init, uwsgi_imperial_monitor_mongodb);
uwsgi_register_imperial_monitor("mongodb2", uwsgi_imperial_monitor_mongodb_init2, uwsgi_imperial_monitor_mongodb);
}
struct uwsgi_plugin emperor_mongodb_plugin = {
+5 -3
View File
@@ -8,18 +8,20 @@ struct uwsgi_geoip {
char *city_db;
GeoIP *country;
GeoIP *city;
int use_disk;
} ugeoip;
struct uwsgi_option uwsgi_geoip_options[] = {
{"geoip-country", required_argument, 0, "load the specified geoip country database", uwsgi_opt_set_str, &ugeoip.country_db, 0},
{"geoip-city", required_argument, 0, "load the specified geoip city database", uwsgi_opt_set_str, &ugeoip.city_db, 0},
{ 0, 0, 0, 0, 0, 0, 0 }
{"geoip-use-disk", no_argument, 0, "do not cache geoip databases in memory", uwsgi_opt_true, &ugeoip.use_disk, 0},
UWSGI_END_OF_OPTIONS
};
static int uwsgi_geoip_init() {
if (ugeoip.country_db) {
ugeoip.country = GeoIP_open(ugeoip.country_db, GEOIP_MEMORY_CACHE);
ugeoip.country = GeoIP_open(ugeoip.country_db, ugeoip.use_disk ? GEOIP_STANDARD : GEOIP_MEMORY_CACHE);
if (!ugeoip.country) {
uwsgi_log("unable to open GeoIP country database: %s\n", ugeoip.country_db);
exit(1);
@@ -27,7 +29,7 @@ static int uwsgi_geoip_init() {
}
if (ugeoip.city_db) {
ugeoip.city = GeoIP_open(ugeoip.city_db, GEOIP_MEMORY_CACHE);
ugeoip.city = GeoIP_open(ugeoip.city_db, ugeoip.use_disk ? GEOIP_STANDARD : GEOIP_MEMORY_CACHE);
if (!ugeoip.city) {
uwsgi_log("unable to open GeoIP city database: %s\n", ugeoip.city_db);
exit(1);
+6 -4
View File
@@ -4,6 +4,8 @@
extern struct uwsgi_server uwsgi;
extern struct uwsgi_python up;
#define is_not_python(x) strcmp(uwsgi.p[x]->name, "python")
struct ugreenlet {
int enabled;
PyObject *callable;
@@ -69,7 +71,7 @@ static void greenlet_schedule_to_req() {
}
// call it in the main core
if (uwsgi.p[modifier1]->suspend) {
if (is_not_python(modifier1) && uwsgi.p[modifier1]->suspend) {
uwsgi.p[modifier1]->suspend(NULL);
}
@@ -81,7 +83,7 @@ static void greenlet_schedule_to_req() {
}
Py_DECREF(ret);
if (uwsgi.p[modifier1]->resume) {
if (is_not_python(modifier1) && uwsgi.p[modifier1]->resume) {
uwsgi.p[modifier1]->resume(NULL);
}
@@ -93,7 +95,7 @@ static void greenlet_schedule_to_main(struct wsgi_request *wsgi_req) {
// ensure gil
UWSGI_GET_GIL
if (uwsgi.p[wsgi_req->uh->modifier1]->suspend) {
if (is_not_python(wsgi_req->uh->modifier1) && uwsgi.p[wsgi_req->uh->modifier1]->suspend) {
uwsgi.p[wsgi_req->uh->modifier1]->suspend(wsgi_req);
}
PyObject *ret = PyGreenlet_Switch(ugl.main, NULL, NULL);
@@ -103,7 +105,7 @@ static void greenlet_schedule_to_main(struct wsgi_request *wsgi_req) {
exit(1);
}
Py_DECREF(ret);
if (uwsgi.p[wsgi_req->uh->modifier1]->resume) {
if (is_not_python(wsgi_req->uh->modifier1) && uwsgi.p[wsgi_req->uh->modifier1]->resume) {
uwsgi.p[wsgi_req->uh->modifier1]->resume(wsgi_req);
}
uwsgi.wsgi_req = wsgi_req;
+2
View File
@@ -42,6 +42,8 @@ struct uwsgi_http {
int server_name_as_http_host;
int headers_timeout;
int connect_timeout;
};
struct http_session {
+120 -42
View File
@@ -60,43 +60,56 @@ struct uwsgi_option http_options[] = {
{"http-buffer-size", required_argument, 0, "set internal buffer size (default: page size)", uwsgi_opt_set_64bit, &uhttp.cr.buffer_size, 0},
{"http-server-name-as-http-host", required_argument, 0, "force SERVER_NAME to HTTP_HOST", uwsgi_opt_true, &uhttp.server_name_as_http_host, 0},
{"http-headers-timeout", required_argument, 0, "set internal http socket timeout for headers", uwsgi_opt_set_int, &uhttp.headers_timeout, 0},
{"http-connect-timeout", required_argument, 0, "set internal http socket timeout for backend connections", uwsgi_opt_set_int, &uhttp.connect_timeout, 0},
{0, 0, 0, 0, 0, 0, 0},
};
int http_add_uwsgi_header(struct corerouter_peer *peer, char *hh, uint16_t hhlen) {
static void http_set_timeout(struct corerouter_peer *peer, int timeout) {
if (peer->current_timeout == timeout) return;
peer->current_timeout = timeout;
peer->timeout = corerouter_reset_timeout(peer->session->corerouter, peer);
}
static char * http_header_to_cgi(char *hh, size_t hhlen, size_t *keylen, size_t *vallen, int *has_prefix) {
size_t i;
char *val = hh;
int status = 0;
for (i = 0; i < hhlen; i++) {
if (!status) {
hh[i] = toupper((int) hh[i]);
if (hh[i] == '-')
hh[i] = '_';
if (hh[i] == ':') {
status = 1;
*keylen = i;
}
}
else if (status == 1 && hh[i] != ' ') {
status = 2;
val += i;
*vallen+=1;
}
else if (status == 2) {
*vallen+=1;
}
}
if (!(*keylen))
return NULL;
if (uwsgi_strncmp("CONTENT_LENGTH", 14, hh, *keylen) && uwsgi_strncmp("CONTENT_TYPE", 12, hh, *keylen)) {
*has_prefix = 0x02;
}
return val;
}
static int http_add_uwsgi_header(struct corerouter_peer *peer, char *hh, size_t keylen, char *val, size_t vallen, int prefix) {
struct uwsgi_buffer *out = peer->out;
struct http_session *hr = (struct http_session *) peer->session;
int i;
int status = 0;
char *val = hh;
uint16_t keylen = 0, vallen = 0;
int prefix = 0;
for (i = 0; i < hhlen; i++) {
if (!status) {
hh[i] = toupper((int) hh[i]);
if (hh[i] == '-')
hh[i] = '_';
if (hh[i] == ':') {
status = 1;
keylen = i;
}
}
else if (status == 1 && hh[i] != ' ') {
status = 2;
val += i;
vallen++;
}
else if (status == 2) {
vallen++;
}
}
if (!keylen)
return -1;
if (hr->websockets) {
if (!uwsgi_strncmp("UPGRADE", 7, hh, keylen)) {
if (!uwsgi_strnicmp(val, vallen, "websocket", 9)) {
@@ -151,11 +164,7 @@ int http_add_uwsgi_header(struct corerouter_peer *peer, char *hh, uint16_t hhlen
#endif
done:
if (uwsgi_strncmp("CONTENT_TYPE", 12, hh, keylen) && uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen)) {
keylen += 5;
prefix = 1;
}
if (prefix) keylen += 5;
if (uwsgi_buffer_u16le(out, keylen)) return -1;
@@ -163,7 +172,7 @@ done:
if (uwsgi_buffer_append(out, "HTTP_", 5)) return -1;
}
if (uwsgi_buffer_append(out, hh, keylen - (prefix * 5))) return -1;
if (uwsgi_buffer_append(out, hh, keylen - (prefix ? 5 : 0))) return -1;
if (uwsgi_buffer_u16le(out, vallen)) return -1;
if (uwsgi_buffer_append(out, val, vallen)) return -1;
@@ -325,6 +334,8 @@ int http_headers_parse(struct corerouter_peer *peer) {
//HEADERS
base = ptr;
struct uwsgi_string_list *headers = NULL, *usl = NULL;
while (ptr < watermark) {
if (*ptr == '\r') {
if (ptr + 1 >= watermark)
@@ -345,13 +356,53 @@ int http_headers_parse(struct corerouter_peer *peer) {
hr->send_expect_100 = 1;
}
}
if (http_add_uwsgi_header(peer, base, ptr - base)) return -1;
size_t key_len = 0, value_len = 0;
int has_prefix = 0;
// last line, do not waste time
if (ptr - base == 0) break;
char *value = http_header_to_cgi(base, ptr - base, &key_len, &value_len, &has_prefix);
if (!value) goto clear;
usl = uwsgi_string_list_has_item(headers, base, key_len);
// there is already a HTTP header with the same name, let's merge them
if (usl) {
char *old_value = usl->custom_ptr;
usl->custom_ptr = uwsgi_concat3n(old_value, (size_t) usl->custom, ", ", 2, value, value_len);
usl->custom += 2 + value_len;
if (usl->custom2 & 0x01) free(old_value);
usl->custom2 |= 0x01;
}
else {
// add an entry
usl = uwsgi_string_new_list(&headers, NULL);
usl->value = base;
usl->len = key_len;
usl->custom_ptr = value;
usl->custom = value_len;
usl->custom2 = has_prefix;
}
ptr++;
base = ptr + 1;
}
ptr++;
}
usl = headers;
int broken = 0;
while(usl) {
if (!broken) {
if (http_add_uwsgi_header(peer, usl->value, usl->len, usl->custom_ptr, (size_t) usl->custom, usl->custom2 & 0x02)) broken = 1;
}
if (usl->custom2 & 0x01) {
free(usl->custom_ptr);
}
struct uwsgi_string_list *tmp_usl = usl;
usl = usl->next;
free(tmp_usl);
}
if (broken) return -1;
struct uwsgi_string_list *hv = uhttp.http_vars;
while (hv) {
char *equal = strchr(hv->value, '=');
@@ -363,6 +414,18 @@ int http_headers_parse(struct corerouter_peer *peer) {
return 0;
clear:
usl = headers;
while(usl) {
if (usl->custom2 & 0x01) {
free(usl->custom_ptr);
}
struct uwsgi_string_list *tmp_usl = usl;
usl = usl->next;
free(tmp_usl);
}
return -1;
}
@@ -446,6 +509,7 @@ ssize_t hr_write(struct corerouter_peer *main_peer) {
return 0;
}
if (main_peer->session->connect_peer_after_write) {
http_set_timeout(main_peer->session->connect_peer_after_write, uhttp.connect_timeout);
cr_connect(main_peer->session->connect_peer_after_write, hr_instance_connected);
main_peer->session->connect_peer_after_write = NULL;
return len;
@@ -459,6 +523,9 @@ ssize_t hr_write(struct corerouter_peer *main_peer) {
ssize_t hr_instance_connected(struct corerouter_peer* peer) {
cr_peer_connected(peer, "hr_instance_connected()");
// set the default timeout
http_set_timeout(peer, uhttp.cr.socket_timeout);
// we are connected, we cannot retry anymore
peer->can_retry = 0;
@@ -523,10 +590,7 @@ ssize_t hr_instance_read(struct corerouter_peer *peer) {
hr->has_gzip = 0;
#endif
if (uhttp.keepalive > 1) {
int orig_timeout = peer->session->corerouter->socket_timeout;
peer->session->corerouter->socket_timeout = uhttp.keepalive;
peer->session->main_peer->timeout = corerouter_reset_timeout(peer->session->corerouter, peer->session->main_peer);
peer->session->corerouter->socket_timeout = orig_timeout;
http_set_timeout(peer->session->main_peer, uhttp.keepalive);
}
}
#ifdef UWSGI_ZLIB
@@ -655,6 +719,9 @@ ssize_t http_parse(struct corerouter_peer *main_peer) {
return 1;
}
// ensure the headers timeout is honoured
http_set_timeout(main_peer, uhttp.headers_timeout);
// read until \r\n\r\n is found
size_t j;
size_t len = main_peer->in->pos;
@@ -744,6 +811,10 @@ ssize_t http_parse(struct corerouter_peer *main_peer) {
hr->raw_body = 1;
}
new_peer->can_retry = 1;
// reset main timeout
http_set_timeout(main_peer, uhttp.cr.socket_timeout);
// set peer timeout
http_set_timeout(new_peer, uhttp.connect_timeout);
cr_connect(new_peer, hr_instance_connected);
break;
}
@@ -830,12 +901,17 @@ static int hr_retry(struct corerouter_peer *peer) {
retry:
// start async connect (again)
http_set_timeout(peer, uhttp.connect_timeout);
cr_connect(peer, hr_instance_connected);
return 0;
}
int http_alloc_session(struct uwsgi_corerouter *ucr, struct uwsgi_gateway_socket *ugs, struct corerouter_session *cs, struct sockaddr *sa, socklen_t s_len) {
if (!uhttp.headers_timeout) uhttp.headers_timeout = uhttp.cr.socket_timeout;
if (!uhttp.connect_timeout) uhttp.connect_timeout = uhttp.cr.socket_timeout;
// set the retry hook
cs->retry = hr_retry;
struct http_session *hr = (struct http_session *) cs;
@@ -845,6 +921,9 @@ int http_alloc_session(struct uwsgi_corerouter *ucr, struct uwsgi_gateway_socket
// default hook
cs->main_peer->last_hook_read = hr_read;
// headers timeout
cs->main_peer->current_timeout = uhttp.headers_timeout;
if (uhttp.raw_body) {
hr->raw_body = 1;
}
@@ -906,7 +985,6 @@ int http_init() {
uhttp.cr.socket_num = 0;
}
uwsgi_corerouter_init((struct uwsgi_corerouter *) &uhttp);
return 0;
}
+46 -5
View File
@@ -180,8 +180,10 @@ struct uwsgi_buffer *spdy_http_to_spdy(char *buf, size_t len, uint32_t *hh) {
if (!key) return ub;
uint32_t h_len = 0;
// headers (key lowercase...)
// merge header values and ensure keys are all lowercase
struct uwsgi_string_list *hr=NULL, *usl=NULL;
char *line_value;
size_t line_key_len, line_value_len;
for(i=next;i<len;i++) {
if (key) {
if (buf[i] == '\r' || buf[i] == '\n') {
@@ -192,10 +194,37 @@ struct uwsgi_buffer *spdy_http_to_spdy(char *buf, size_t len, uint32_t *hh) {
// tolower !!!
size_t j;
for(j=0;j<h_len;j++) {
key[j] = tolower((int) key[j]);
// don't lowercase values, only keys
if (key[j] == ':') break;
key[j] = tolower((int) key[j]);
}
line_key_len = colon - key;
key[line_key_len] = 0;
line_value_len = h_len - 2 - line_key_len;
line_value = uwsgi_strncopy(colon+2, line_value_len);
if (hr) {
// check if we already store values for this key
usl = uwsgi_string_list_has_item(hr, key, line_key_len);
if (usl) {
// we have this key, append new value
char *oldval = usl->custom_ptr;
usl->custom_ptr = uwsgi_concat3n(usl->custom_ptr, usl->custom, "\0", 1, line_value, line_value_len);
usl->custom = usl->custom + 1 + line_value_len;
free(oldval);
}
else {
// this key is new
usl = uwsgi_string_new_list(&hr, key);
usl->custom_ptr = line_value;
usl->custom = line_value_len;
}
}
else {
// this is first key
usl = uwsgi_string_new_list(&hr, key);
usl->custom_ptr = line_value;
usl->custom = line_value_len;
}
if (uwsgi_buffer_append_keyval32(ub, key, colon-key, colon+2, h_len-((colon-key)+2))) goto end;
*hh+=1;
key = NULL;
h_len = 0;
}
@@ -210,6 +239,18 @@ struct uwsgi_buffer *spdy_http_to_spdy(char *buf, size_t len, uint32_t *hh) {
}
}
}
// append all merged header lines to buffer and free memory
struct uwsgi_string_list *ohr;
while (hr) {
if (uwsgi_buffer_append_keyval32(ub, hr->value, hr->len, hr->custom_ptr, hr->custom)) goto end;
*hh+=1;
ohr = hr;
hr = hr->next;
free(ohr->custom_ptr);
free(ohr);
}
return ub;
end:
-1
View File
@@ -28,7 +28,6 @@ static ssize_t uwsgi_pipe_logger(struct uwsgi_logger *ul, char *message, size_t
}
else {
// child
setsid();
if (setsid() < 0) {
uwsgi_error("setsid()");
exit(1);
+46 -33
View File
@@ -102,6 +102,10 @@ static int uwsgi_mongrel2_json_parse(json_t * root, struct wsgi_request *wsgi_re
}
}
if ((json_val = uwsgi_mongrel2_json_get_string(root, "URL_SCHEME"))) {
wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "UWSGI_SCHEME", 12, json_val, strlen(json_val));
}
if ((json_val = uwsgi_mongrel2_json_get_string(root, "host"))) {
char *colon = strchr(json_val, ':');
if (colon) {
@@ -210,6 +214,9 @@ static int uwsgi_mongrel2_tnetstring_parse(struct wsgi_request *wsgi_req, char *
wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "REQUEST_URI", 11, val, vallen);
}
}
else if (!uwsgi_strncmp("URL_SCHEME", 10, key, keylen)) {
wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "UWSGI_SCHEME", 12, val, vallen);
}
}
else {
// add header
@@ -299,6 +306,13 @@ void uwsgi_proto_zeromq_thread_fixup(struct uwsgi_socket *uwsgi_sock, int async_
// fake function, the body is in a file or completely in memory
ssize_t uwsgi_proto_zeromq_read_body(struct wsgi_request *wsgi_req, char *buf, size_t len) {
size_t remains = wsgi_req->post_cl - wsgi_req->proto_parser_status;
if (remains > 0) {
if (len > remains) len = remains;
memcpy(buf, wsgi_req->proto_parser_buf + wsgi_req->proto_parser_buf_size + wsgi_req->proto_parser_status, len);
wsgi_req->proto_parser_status += len;
return len;
}
return 0;
}
@@ -410,7 +424,8 @@ int uwsgi_proto_zeromq_accept(struct wsgi_request *wsgi_req, int fd) {
}
// pre-build the mongrel2 response_header
wsgi_req->proto_parser_buf = uwsgi_malloc(req_uuid_len + 1 + 11 + 1 + req_id_len + 1 + 1);
wsgi_req->proto_parser_buf_size = req_uuid_len + 1 + 11 + 1 + req_id_len + 1 + 1;
wsgi_req->proto_parser_buf = uwsgi_malloc(wsgi_req->proto_parser_buf_size);
memcpy(wsgi_req->proto_parser_buf, req_uuid, req_uuid_len);
((char *) wsgi_req->proto_parser_buf)[req_uuid_len] = ' ';
resp_id_len = uwsgi_num2str2(req_id_len, wsgi_req->proto_parser_buf + req_uuid_len + 1);
@@ -422,13 +437,22 @@ int uwsgi_proto_zeromq_accept(struct wsgi_request *wsgi_req, int fd) {
wsgi_req->proto_parser_pos = (uint64_t) req_uuid_len + 1 + resp_id_len + 1 + req_id_len + 1 + 1;
// handle post data (in memory)
// reallocate wsgi_req->proto_parser_buf and change its size to be able to store request body
// the parser status holds the current position for read_body hook
if (wsgi_req->post_cl > 0 && !wsgi_req->post_file) {
if (uwsgi_netstring(post_data, message_size - (post_data - message_ptr), &message_ptr, &wsgi_req->post_cl)) {
char *tmp = realloc(wsgi_req->proto_parser_buf, wsgi_req->proto_parser_buf_size + wsgi_req->post_cl);
if (!tmp) {
uwsgi_error("realloc()");
exit(1);
}
wsgi_req->proto_parser_buf = tmp;
// status is an offset...
wsgi_req->proto_parser_status = 0;
#ifdef UWSGI_DEBUG
uwsgi_log("post_size: %d\n", wsgi_req->post_cl);
#endif
wsgi_req->post_read_buf = uwsgi_malloc(wsgi_req->post_cl);
memcpy(wsgi_req->post_read_buf, message_ptr, wsgi_req->post_cl);
memcpy(wsgi_req->proto_parser_buf + wsgi_req->proto_parser_buf_size, message_ptr, wsgi_req->post_cl);
}
}
@@ -452,33 +476,7 @@ retry:
return -1;
}
void uwsgi_proto_zeromq_close(struct wsgi_request *wsgi_req) {
zmq_msg_t reply;
// check for already freed wsgi_req->proto_parser_buf/wsgi_req->proto_parser_pos
if (!wsgi_req->proto_parser_pos)
return;
// no need to pass a free function (the buffer will be freed during cloe_request)
zmq_msg_init_data(&reply, wsgi_req->proto_parser_buf, wsgi_req->proto_parser_pos, NULL, NULL);
if (uwsgi.threads > 1)
pthread_mutex_lock(&wsgi_req->socket->lock);
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3,0,0)
if (zmq_sendmsg(wsgi_req->socket->pub, &reply, 0)) {
uwsgi_error("uwsgi_proto_zeromq_close()/zmq_sendmsg()");
#else
if (zmq_send(wsgi_req->socket->pub, &reply, 0)) {
uwsgi_error("uwsgi_proto_zeromq_close()/zmq_send()");
#endif
}
if (uwsgi.threads > 1)
pthread_mutex_unlock(&wsgi_req->socket->lock);
zmq_msg_close(&reply);
}
int uwsgi_proto_zeromq_write(struct wsgi_request *wsgi_req, char *buf, size_t len) {
static int uwsgi_proto_zeromq_write_do(struct wsgi_request *wsgi_req, char *buf, size_t len) {
zmq_msg_t reply;
if (zmq_msg_init_size(&reply, wsgi_req->proto_parser_pos + len)) {
@@ -489,7 +487,8 @@ int uwsgi_proto_zeromq_write(struct wsgi_request *wsgi_req, char *buf, size_t le
char *zmq_body = zmq_msg_data(&reply);
memcpy(zmq_body, wsgi_req->proto_parser_buf, wsgi_req->proto_parser_pos);
memcpy(zmq_body + wsgi_req->proto_parser_pos, buf, len);
if (len > 0)
memcpy(zmq_body + wsgi_req->proto_parser_pos, buf, len);
if (uwsgi.threads > 1)
pthread_mutex_lock(&wsgi_req->socket->lock);
@@ -510,6 +509,18 @@ int uwsgi_proto_zeromq_write(struct wsgi_request *wsgi_req, char *buf, size_t le
return UWSGI_OK;
}
int uwsgi_proto_zeromq_write(struct wsgi_request *wsgi_req, char *buf, size_t len) {
int ret = uwsgi_proto_zeromq_write_do(wsgi_req, buf, len);
if (ret == UWSGI_OK) {
wsgi_req->write_pos += len;
}
return ret;
}
void uwsgi_proto_zeromq_close(struct wsgi_request *wsgi_req) {
uwsgi_proto_zeromq_write_do(wsgi_req, "", 0);
}
/*
we have a problem... recent Mongrel2 releases introduced a ring buffer that limit the amount of messages we can send (or better, the amount of
@@ -532,7 +543,7 @@ int uwsgi_proto_zeromq_sendfile(struct wsgi_request *wsgi_req, int fd, size_t po
return -1;
}
wsgi_req->write_pos += rlen;
if (uwsgi_proto_zeromq_write(wsgi_req, tmp_buf, rlen) < 0) {
if (uwsgi_proto_zeromq_write_do(wsgi_req, tmp_buf, rlen) < 0) {
free(tmp_buf);
return -1;
}
@@ -574,6 +585,7 @@ static void mongrel2_register_proto() {
static void mongrel2_connect() {
struct uwsgi_socket *uwsgi_sock = uwsgi.sockets;
while(uwsgi_sock) {
if (uwsgi_sock->proto != uwsgi_proto_zeromq_parser) goto next;
uwsgi_sock->ctx = zmq_init(1);
if (!uwsgi_sock->ctx) {
uwsgi_error("mongrel2_connect()/zmq_init()");
@@ -581,7 +593,7 @@ static void mongrel2_connect() {
}
char *responder = strchr(uwsgi_sock->name, ',');
if (!responder) {
uwsgi_log("invalid zeromq address\n");
uwsgi_log("invalid zeromq address: %s\n", uwsgi_sock->name);
exit(1);
}
uwsgi_sock->receiver = uwsgi_concat2n(uwsgi_sock->name, responder - uwsgi_sock->name, "", 0);
@@ -653,6 +665,7 @@ static void mongrel2_connect() {
#else
uwsgi_sock->recv_flag = ZMQ_NOBLOCK;
#endif
next:
uwsgi_sock = uwsgi_sock->next;
}
}
+12
View File
@@ -0,0 +1,12 @@
#include "php.h"
#include "SAPI.h"
#include "php_main.h"
#include "php_variables.h"
#include "ext/standard/php_smart_str.h"
#include "ext/standard/info.h"
#include "ext/session/php_session.h"
#include <uwsgi.h>
+24 -14
View File
@@ -1,12 +1,4 @@
#include "php.h"
#include "SAPI.h"
#include "php_main.h"
#include "php_variables.h"
#include "ext/standard/php_smart_str.h"
#include "ext/standard/info.h"
#include "../../uwsgi.h"
#include "common.h"
extern struct uwsgi_server uwsgi;
@@ -31,6 +23,9 @@ struct uwsgi_php {
int dump_config;
char *server_software;
size_t server_software_len;
struct uwsgi_string_list *exec_before;
struct uwsgi_string_list *exec_after;
} uphp;
void uwsgi_opt_php_ini(char *opt, char *value, void *foobar) {
@@ -59,8 +54,11 @@ struct uwsgi_option uwsgi_php_options[] = {
#endif
{"php-var", required_argument, 0, "add/overwrite a CGI variable at each request", uwsgi_opt_add_string_list, &uphp.vars, 0},
{"php-dump-config", no_argument, 0, "dump php config (if modified via --php-set or append options)", uwsgi_opt_true, &uphp.dump_config, 0},
{0, 0, 0, 0, 0, 0, 0},
{"php-exec-before", required_argument, 0, "run specified php code before the requested script", uwsgi_opt_add_string_list, &uphp.exec_before, 0},
{"php-exec-begin", required_argument, 0, "run specified php code before the requested script", uwsgi_opt_add_string_list, &uphp.exec_before, 0},
{"php-exec-after", required_argument, 0, "run specified php code after the requested script", uwsgi_opt_add_string_list, &uphp.exec_after, 0},
{"php-exec-end", required_argument, 0, "run specified php code after the requested script", uwsgi_opt_add_string_list, &uphp.exec_after, 0},
UWSGI_END_OF_OPTIONS
};
@@ -227,8 +225,9 @@ void uwsgi_php_set(char *opt) {
uwsgi_sapi_module.ini_entries[uphp.ini_size] = 0;
}
// future implementation...
extern ps_module ps_mod_uwsgi;
PHP_MINIT_FUNCTION(uwsgi_php_minit) {
php_session_register_module(&ps_mod_uwsgi);
return SUCCESS;
}
@@ -335,7 +334,7 @@ PHP_FUNCTION(uwsgi_cache_set) {
RETURN_NULL();
}
if (uwsgi_cache_magic_set(key, keylen, value, vallen, expires, 0, cache)) {
if (!uwsgi_cache_magic_set(key, keylen, value, vallen, expires, 0, cache)) {
RETURN_TRUE;
}
RETURN_NULL();
@@ -357,7 +356,7 @@ PHP_FUNCTION(uwsgi_cache_update) {
RETURN_NULL();
}
if (uwsgi_cache_magic_set(key, keylen, value, vallen, expires, UWSGI_CACHE_FLAG_UPDATE, cache)) {
if (!uwsgi_cache_magic_set(key, keylen, value, vallen, expires, UWSGI_CACHE_FLAG_UPDATE, cache)) {
RETURN_TRUE;
}
RETURN_NULL();
@@ -890,8 +889,19 @@ secure3:
return -1;
}
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;
}
php_execute_script(&file_handle TSRMLS_CC);
uwsgi_foreach(usl, uphp.exec_after) {
if (zend_eval_string_ex(usl->value, NULL, "uWSGI php exec after", 1 TSRMLS_CC) == FAILURE) goto end;
}
end:
php_request_shutdown(NULL);
return 0;
+50
View File
@@ -0,0 +1,50 @@
#include "common.h"
PS_OPEN_FUNC(uwsgi) {
PS_SET_MOD_DATA((char *)save_path);
return SUCCESS;
}
PS_CLOSE_FUNC(uwsgi) {
return SUCCESS;
}
PS_READ_FUNC(uwsgi) {
char *cache = PS_GET_MOD_DATA();
uint64_t valsize = 0;
char *value = uwsgi_cache_magic_get((char *)key, strlen(key), &valsize, NULL, cache);
if (!value) return FAILURE;
char *new_val = emalloc(valsize);
memcpy(new_val, value, valsize);
free(value);
*val = new_val;
*vallen = valsize;
return SUCCESS;
}
PS_WRITE_FUNC(uwsgi) {
char *cache = PS_GET_MOD_DATA();
if (vallen == 0) return SUCCESS;
if (!uwsgi_cache_magic_set((char *)key, strlen(key), (char *)val, vallen, 0, UWSGI_CACHE_FLAG_UPDATE, cache)) {
return SUCCESS;
}
return FAILURE;
}
PS_DESTROY_FUNC(uwsgi) {
char *cache = PS_GET_MOD_DATA();
if (!uwsgi_cache_magic_del((char *)key, strlen(key), cache)) {
return SUCCESS;
}
return FAILURE;
}
PS_GC_FUNC(uwsgi) {
return SUCCESS;
}
ps_module ps_mod_uwsgi = {
PS_MOD(uwsgi)
};
+1 -1
View File
@@ -25,4 +25,4 @@ phplibdir = os.environ.get('UWSGICONFIG_PHPLIBDIR')
if phplibdir:
LIBS.append('-Wl,-rpath=%s' % phplibdir)
GCC_LIST = ['php_plugin']
GCC_LIST = ['php_plugin', 'session']
+2
View File
@@ -65,6 +65,8 @@ struct uwsgi_perl {
int shell_oneshot;
CV *spooler;
int no_plack;
};
void init_perl_embedded_module(void);
+52 -39
View File
@@ -54,7 +54,7 @@ XS(XS_psgix_logger) {
psgi_check_args(1);
HV *hv_args = (HV *) (SvRV(ST(0)));
if (!hv_exists(hv_args, "level", 5) || !hv_exists(hv_args, "message", 7)) {
Perl_croak(aTHX_ "psgix.logger requires bot level and message items");
Perl_croak(aTHX_ "psgix.logger requires both level and message items");
}
char *level = SvPV_nolen(*(hv_fetch(hv_args, "level", 5, 0)));
char *message = SvPV_nolen(*(hv_fetch(hv_args, "message", 7, 0)));
@@ -108,7 +108,7 @@ XS(XS_input_read) {
unsigned long arg_len = SvIV(ST(2));
long offset = 0;
if (items > 2) {
if (items > 3) {
offset = (long) SvIV(ST(3));
}
@@ -313,9 +313,6 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe
char *app_name = uwsgi_concat2n(app, app_len, "", 0);
size_t size;
char *buf = uwsgi_open_and_read(app_name, &size, 1, NULL);
if (uwsgi_file_exists(app_name)) {
// prepare for $0 (if the file is local)
uperl.embedding[1] = app_name;
@@ -368,38 +365,34 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe
uperl.tmp_current_i = i;
if (uperl.locallib) {
uwsgi_log("using %s as local::lib directory\n", uperl.locallib);
uperl.embedding[1] = uwsgi_concat2("-Mlocal::lib=", uperl.locallib);
uperl.embedding[2] = app_name;
if (perl_parse(interpreters[i], xs_init, 3, uperl.embedding, NULL)) {
// what to do here ? i hope no-one will use threads with dynamic apps... but clear the whole stuff...
free(uperl.embedding[1]);
uperl.embedding[1] = app_name;
free(callables);
uwsgi_perl_free_stashes();
goto clear;
}
free(uperl.embedding[1]);
uperl.embedding[1] = app_name;
}
else {
if (perl_parse(interpreters[i], xs_init, 2, uperl.embedding, NULL)) {
// We need to initialize the interpreter to execute
// our xs_init hook, but we're *not* calling it with
// uperl.embedding as an argument so we won't execute
// BEGIN blocks in app_name twice.
{
char *perl_e_arg = uwsgi_concat2("#line 0 ", app_name);
char *perl_init_arg[] = { "", "-e", perl_e_arg };
if (perl_parse(interpreters[i], xs_init, 3, perl_init_arg, NULL)) {
// what to do here ? i hope no-one will use threads with dynamic apps... but clear the whole stuff...
free(callables);
free(perl_e_arg);
uwsgi_perl_free_stashes();
goto clear;
}
} else {
free(perl_e_arg);
}
}
if (uperl.locallib) {
uwsgi_log("using %s as local::lib directory\n", uperl.locallib);
char *local_lib_use = uwsgi_concat3("use local::lib qw(", uperl.locallib, ");");
perl_eval_pv(local_lib_use, 1);
free(local_lib_use);
}
perl_eval_pv("use IO::Handle;", 1);
perl_eval_pv("use IO::File;", 1);
perl_eval_pv("use IO::Socket;", 1);
perl_eval_pv("use Scalar::Util;", 1);
if (!uperl.no_die_catch) {
perl_eval_pv("use Devel::StackTrace; $SIG{__DIE__} = sub { print Devel::StackTrace->new()->as_string() };", 0);
}
if (uperl.argv_items || uperl.argv_item) {
AV *uperl_argv = GvAV(PL_argvgv);
@@ -417,12 +410,32 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe
}
}
SV *dollar_zero = get_sv("0", GV_ADD);
sv_setsv(dollar_zero, newSVpv(app, app_len));
callables[i] = perl_eval_pv(uwsgi_concat4("#line 1 ", app_name, "\n", buf), 0);
if (!callables[i]) {
SV *has_plack = NULL;
if (!uperl.no_plack) {
has_plack = perl_eval_pv("use Plack::Util;", 0);
}
if (!has_plack || SvTRUE(ERRSV)) {
if (!uperl.no_plack) {
uwsgi_log("Plack::Util is not installed, using \"do\" instead of \"load_psgi\"\n");
}
char *code = uwsgi_concat3("my $app = do '", app_name, "'; if ( !$app && ( my $error = $@ || $! )) { die $error; }; $app");
callables[i] = perl_eval_pv(code, 0);
free(code);
}
else {
char *code = uwsgi_concat3("Plack::Util::load_psgi '", app_name , "';");
callables[i] = perl_eval_pv(code, 0);
free(code);
}
if (!callables[i] || SvTYPE(callables[i]) == SVt_NULL || SvTRUE(ERRSV)) {
if (SvTRUE(ERRSV)) {
uwsgi_log("%s", SvPV_nolen(ERRSV));
}
uwsgi_log("unable to find PSGI function entry point.\n");
// what to do here ? i hope no-one will use threads with dynamic apps...
free(callables);
@@ -430,18 +443,16 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe
goto clear;
}
if (!uperl.no_die_catch) {
perl_eval_pv("use Devel::StackTrace; $SIG{__DIE__} = sub { print Devel::StackTrace->new()->as_string() };", 0);
if(SvTRUE(ERRSV)) {
uwsgi_log("%s", SvPV_nolen(ERRSV));
}
}
PERL_SET_CONTEXT(interpreters[0]);
}
free(buf);
if(SvTRUE(ERRSV)) {
uwsgi_log("%s", SvPV_nolen(ERRSV));
free(callables);
uwsgi_perl_free_stashes();
goto clear;
}
if (uwsgi_apps_cnt >= uwsgi.max_apps) {
uwsgi_log("ERROR: you cannot load more than %d apps in a worker\n", uwsgi.max_apps);
goto clear;
@@ -504,6 +515,8 @@ void uwsgi_psgi_preinit_apps() {
perl_parse(uperl.main[0], xs_init, 3, uperl.embedding, NULL);
struct uwsgi_string_list *usl;
uwsgi_foreach(usl, uperl.exec) {
SV *dollar_zero = get_sv("0", GV_ADD);
sv_setsv(dollar_zero, newSVpv(usl->value, usl->len));
uwsgi_perl_exec(usl->value);
}
}
+4
View File
@@ -44,6 +44,8 @@ struct uwsgi_option uwsgi_perl_options[] = {
{"plshell", optional_argument, 0, "run a perl interactive shell", uwsgi_opt_plshell, NULL, 0},
{"plshell-oneshot", no_argument, 0, "run a perl interactive shell (one shot)", uwsgi_opt_plshell, NULL, 0},
{"perl-no-plack", no_argument, 0, "force the use of do instead of Plack::Util::load_psgi", uwsgi_opt_true, &uperl.no_plack, 0},
{0, 0, 0, 0, 0, 0, 0},
};
@@ -705,6 +707,8 @@ void uwsgi_perl_post_fork() {
struct uwsgi_string_list *usl;
uwsgi_foreach(usl, uperl.exec_post_fork) {
SV *dollar_zero = get_sv("0", GV_ADD);
sv_setsv(dollar_zero, newSVpv(usl->value, usl->len));
uwsgi_perl_exec(usl->value);
}
+23
View File
@@ -955,6 +955,27 @@ XS(XS_spool) {
XSRETURN_UNDEF;
}
XS(XS_add_var) {
dXSARGS;
psgi_check_args(2);
struct wsgi_request *wsgi_req = current_wsgi_req();
STRLEN keylen;
char *key = SvPV(ST(0), keylen);
STRLEN vallen;
char *val = SvPV(ST(1), vallen);
if (!uwsgi_req_append(wsgi_req, key, keylen, val, vallen)) {
croak("unable to add request var, check your buffer size");
XSRETURN_UNDEF;
}
XSRETURN_YES;
}
void init_perl_embedded_module() {
psgi_xs(reload);
@@ -1016,5 +1037,7 @@ void init_perl_embedded_module() {
psgi_xs(spooler);
psgi_xs(spool);
psgi_xs(add_var);
}
+22 -25
View File
@@ -35,6 +35,7 @@ void (*u_pypy_thread_attach)(void);
void (*u_pypy_init_threads)(void);
// the hooks you can override with pypy
void (*uwsgi_pypy_hook_execute_source)(char *);
void (*uwsgi_pypy_hook_loader)(char *);
void (*uwsgi_pypy_hook_file_loader)(char *);
void (*uwsgi_pypy_hook_paste_loader)(char *);
@@ -186,23 +187,21 @@ ready:
static void uwsgi_pypy_preinit_apps() {
struct uwsgi_string_list *usl = upypy.eval;
while(usl) {
if (u_pypy_execute_source(usl->value)) {
exit(1);
}
usl = usl->next;
if (!uwsgi_pypy_hook_execute_source) {
uwsgi_log("*** WARNING your pypy setup code does not expose a callback for \"execute_source\" ***\n");
return;
}
usl = upypy.exec;
while(usl) {
struct uwsgi_string_list *usl = NULL;
uwsgi_foreach(usl, upypy.eval) {
uwsgi_pypy_hook_execute_source(usl->value);
}
uwsgi_foreach(usl, upypy.exec) {
size_t rlen = 0;
char *buffer = uwsgi_open_and_read(usl->value, &rlen, 1, NULL);
if (u_pypy_execute_source(buffer)) {
exit(1);
}
uwsgi_pypy_hook_execute_source(buffer);
free(buffer);
usl = usl->next;
}
}
@@ -305,22 +304,15 @@ static uint64_t uwsgi_pypy_rpc(void *func, uint8_t argc, char **argv, uint16_t a
static void uwsgi_pypy_post_fork() {
pthread_mutex_init(&upypy.attach_thread_lock, NULL);
struct uwsgi_string_list *usl = upypy.eval_post_fork;
while(usl) {
if (u_pypy_execute_source(usl->value)) {
exit(1);
}
usl = usl->next;
struct uwsgi_string_list *usl = NULL;
uwsgi_foreach(usl, upypy.eval_post_fork) {
uwsgi_pypy_hook_execute_source(usl->value);
}
usl = upypy.exec_post_fork;
while(usl) {
uwsgi_foreach(usl, upypy.exec_post_fork) {
size_t rlen = 0;
char *buffer = uwsgi_open_and_read(usl->value, &rlen, 1, NULL);
if (u_pypy_execute_source(buffer)) {
exit(1);
}
uwsgi_pypy_hook_execute_source(buffer);
free(buffer);
usl = usl->next;
}
if (uwsgi_pypy_post_fork_hook) {
@@ -337,10 +329,15 @@ static void uwsgi_pypy_onload() {
static int uwsgi_pypy_mule(char *opt) {
if (!uwsgi_pypy_hook_execute_source) {
uwsgi_log("!!! no \"execute_source\" callback in your pypy setup code !!!\n");
exit(1);
}
if (uwsgi_endswith(opt, ".py")) {
size_t rlen = 0;
char *buffer = uwsgi_open_and_read(opt, &rlen, 1, NULL);
u_pypy_execute_source(buffer);
uwsgi_pypy_hook_execute_source(buffer);
free(buffer);
return 1;
}
+10
View File
@@ -24,6 +24,7 @@ ssize_t read(int, void *, size_t);
ssize_t write(int, const void *, size_t);
int close(int);
void (*uwsgi_pypy_hook_execute_source)(char *);
void (*uwsgi_pypy_hook_loader)(char *);
void (*uwsgi_pypy_hook_file_loader)(char *);
void (*uwsgi_pypy_hook_paste_loader)(char *);
@@ -277,6 +278,14 @@ wsgi_application = None
if len(sys.argv) == 0:
sys.argv.insert(0, ffi.string(lib.uwsgi_binary_path()))
"""
execute source, we expose it as cffi callback to avoid deadlocks
after GIL initialization
"""
@ffi.callback("void(char *)")
def uwsgi_pypy_execute_source(s):
source = ffi.string(s)
exec(source)
"""
load a wsgi module
@@ -480,6 +489,7 @@ def uwsgi_pypy_wsgi_handler(wsgi_req):
if hasattr(response, 'close'):
response.close()
lib.uwsgi_pypy_hook_execute_source = uwsgi_pypy_execute_source
lib.uwsgi_pypy_hook_loader = uwsgi_pypy_loader
lib.uwsgi_pypy_hook_file_loader = uwsgi_pypy_file_loader
lib.uwsgi_pypy_hook_paste_loader = uwsgi_pypy_paste_loader
+8
View File
@@ -197,6 +197,12 @@ int uwsgi_python_init() {
uwsgi_log_initial("Python version: %.*s %s\n", pyversion-Py_GetVersion(), Py_GetVersion(), Py_GetCompiler()+1);
}
if (Py_IsInitialized()) {
uwsgi_log("--- Python VM already initialized ---\n");
PyGILState_Ensure();
goto ready;
}
if (up.home != NULL) {
#ifdef PYTHREE
// check for PEP 405 virtualenv (starting from python 3.3)
@@ -251,6 +257,8 @@ pep405:
Py_Initialize();
ready:
if (!uwsgi.has_threads) {
uwsgi_log_initial("*** Python threads support is disabled. You can enable it with --enable-threads ***\n");
}
+27
View File
@@ -4,6 +4,29 @@ extern struct uwsgi_server uwsgi;
extern struct uwsgi_python up;
extern struct uwsgi_plugin python_plugin;
static PyObject *py_uwsgi_add_var(PyObject * self, PyObject * args) {
char *key = NULL;
Py_ssize_t keylen = 0;
char *val = NULL;
Py_ssize_t vallen = 0;
struct wsgi_request *wsgi_req = py_current_wsgi_req();
if (!PyArg_ParseTuple(args, "s#s#", &key, &keylen, &val, &vallen)) {
return NULL;
}
if (!uwsgi_req_append(wsgi_req, key, keylen, val, vallen)) {
return PyErr_Format(PyExc_ValueError, "unable to add request var, check your buffer size");
}
Py_INCREF(Py_True);
return Py_True;
}
static PyObject *py_uwsgi_micros(PyObject * self, PyObject * args) {
return PyLong_FromUnsignedLongLong(uwsgi_micros());
}
static PyObject *py_uwsgi_signal_wait(PyObject * self, PyObject * args) {
struct wsgi_request *wsgi_req = py_current_wsgi_req();
@@ -2417,6 +2440,10 @@ static PyMethodDef uwsgi_advanced_methods[] = {
{"ready_fd", py_uwsgi_ready_fd, METH_VARARGS, ""},
{"add_var", py_uwsgi_add_var, METH_VARARGS, ""},
{"micros", py_uwsgi_micros, METH_VARARGS, ""},
{NULL, NULL},
};
+2 -1
View File
@@ -21,12 +21,13 @@ int uwsgi_python_send_body(struct wsgi_request *wsgi_req, PyObject *chunk) {
char *content = NULL;
size_t content_len = 0;
if (!up.wsgi_accept_buffer && !wsgi_req->is_raw) goto strict;
#if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER)
Py_buffer pbuf;
int has_buffer = 0;
#endif
if (!up.wsgi_accept_buffer && !wsgi_req->is_raw) goto strict;
#if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER)
if (PyObject_CheckBuffer(chunk)) {
if (!PyObject_GetBuffer(chunk, &pbuf, PyBUF_SIMPLE)) {
+38 -2
View File
@@ -13,6 +13,26 @@ pthread_mutex_t ur_basicauth_crypt_mutex;
extern struct uwsgi_server uwsgi;
static char *htpasswd_check_sha1(char *pwd) {
#ifdef UWSGI_SSL
char sha1[20];
uwsgi_sha1(pwd, strlen(pwd), sha1);
size_t len = 0;
char *b64 = uwsgi_base64_encode(sha1, 20, &len);
if (!b64) return NULL;
// we add a new line for being fgets-friendly
char *crypted = uwsgi_concat3n("{SHA}", 5, b64, len, "\n", 1);
free(b64);
return crypted;
#else
uwsgi_log("*** WARNING, rebuild uWSGI with SSL support for htpasswd sha1 feature ***\n");
return NULL;
#endif
}
static uint16_t htpasswd_check(char *filename, char *auth) {
char line[1024];
@@ -25,11 +45,23 @@ static uint16_t htpasswd_check(char *filename, char *auth) {
return 0;
}
while(fgets(line, 1024, htpasswd)) {
char *crypted = NULL;
int need_free = 0;
char *colon2 = strchr(line, ':');
if (!colon2) break;
char *cpwd = colon2+1;
size_t clen = strlen(cpwd);
// now we check which algo to use
// {SHA} ?
if (!uwsgi_starts_with(cpwd, clen, "{SHA}", 5)) {
crypted = htpasswd_check_sha1(colon+1);
if (crypted) need_free = 1;
goto check;
}
if (clen < 13) break;
if (clen > 13) cpwd[13] = 0;
@@ -39,20 +71,24 @@ static uint16_t htpasswd_check(char *filename, char *auth) {
cd.initialized = 0;
// we do as nginx here
cd.current_salt[0] = ~cpwd[0];
char *crypted = crypt_r( colon+1, cpwd, &cd);
crypted = crypt_r( colon+1, cpwd, &cd);
#else
if (uwsgi.threads > 1) pthread_mutex_lock(&ur_basicauth_crypt_mutex);
char *crypted = crypt( colon+1, cpwd);
crypted = crypt( colon+1, cpwd);
if (uwsgi.threads > 1) pthread_mutex_unlock(&ur_basicauth_crypt_mutex);
#endif
check:
if (!crypted) continue;
if (!strcmp( crypted, cpwd )) {
if (!uwsgi_strncmp(auth, colon-auth, line, colon2-line)) {
fclose(htpasswd);
if (need_free) free(crypted);
return colon-auth;
}
}
if (need_free) free(crypted);
}
fclose(htpasswd);
+33 -4
View File
@@ -5,6 +5,8 @@
extern struct uwsgi_server uwsgi;
static int uwsgi_routing_func_http(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) {
struct uwsgi_buffer *ub = NULL;
// mark a route request
wsgi_req->via = UWSGI_VIA_ROUTE;
@@ -24,9 +26,14 @@ static int uwsgi_routing_func_http(struct wsgi_request *wsgi_req, struct uwsgi_r
}
}
// convert the wsgi_request to an http proxy request
struct uwsgi_buffer *ub = uwsgi_to_http(wsgi_req, ur->data2, ur->data2_len, ub_url ? ub_url->buf : NULL, ub_url ? ub_url->pos : 0);
if (ur->custom & 0x02) {
ub = uwsgi_buffer_new(uwsgi.page_size);
}
else {
ub = uwsgi_to_http(wsgi_req, ur->data2, ur->data2_len, ub_url ? ub_url->buf : NULL, ub_url ? ub_url->pos : 0);
}
if (!ub) {
if (ub_url) uwsgi_buffer_destroy(ub_url);
uwsgi_log("unable to generate http request for %s\n", ub_addr->buf);
@@ -46,11 +53,12 @@ static int uwsgi_routing_func_http(struct wsgi_request *wsgi_req, struct uwsgi_r
uwsgi_buffer_destroy(ub_addr);
return UWSGI_ROUTE_NEXT;
}
wsgi_req->post_pos += wsgi_req->proto_parser_remains;
wsgi_req->proto_parser_remains = 0;
}
// ok now if have offload threads, directly use them
if (!wsgi_req->post_file && !ur->custom && wsgi_req->socket->can_offload) {
if (!wsgi_req->post_file && !(ur->custom & 0x01) && wsgi_req->socket->can_offload) {
// append buffered body
if (uwsgi.post_buffering > 0 && wsgi_req->post_cl > 0) {
if (uwsgi_buffer_append(ub, wsgi_req->post_buffering_buf, wsgi_req->post_cl)) {
@@ -60,6 +68,14 @@ static int uwsgi_routing_func_http(struct wsgi_request *wsgi_req, struct uwsgi_r
return UWSGI_ROUTE_NEXT;
}
}
// if we have a CONNECT request, let's confirm it to the client
if (ur->custom & 0x02) {
if (uwsgi_response_prepare_headers(wsgi_req, "200 Connection established", 26)) goto end;
// no need to check for return value
uwsgi_response_write_headers_do(wsgi_req);
}
if (!uwsgi_offload_request_net_do(wsgi_req, ub_addr->buf, ub)) {
wsgi_req->via = UWSGI_VIA_OFFLOAD;
wsgi_req->status = 202;
@@ -72,6 +88,7 @@ static int uwsgi_routing_func_http(struct wsgi_request *wsgi_req, struct uwsgi_r
uwsgi_log("error routing request to http server %s\n", ub_addr->buf);
}
end:
uwsgi_buffer_destroy(ub);
uwsgi_buffer_destroy(ub_addr);
@@ -102,15 +119,27 @@ static int uwsgi_router_http(struct uwsgi_route *ur, char *args) {
}
static int uwsgi_router_proxyhttp(struct uwsgi_route *ur, char *args) {
ur->custom = 1;
ur->custom = 0x1;
return uwsgi_router_http(ur, args);
}
static int uwsgi_router_proxyhttp_connect(struct uwsgi_route *ur, char *args) {
ur->custom = 0x1|0x02;
return uwsgi_router_http(ur, args);
}
static int uwsgi_router_http_connect(struct uwsgi_route *ur, char *args) {
ur->custom = 0x02;
return uwsgi_router_http(ur, args);
}
static void router_http_register(void) {
uwsgi_register_router("http", uwsgi_router_http);
uwsgi_register_router("proxyhttp", uwsgi_router_proxyhttp);
uwsgi_register_router("httpconnect", uwsgi_router_http_connect);
uwsgi_register_router("proxyhttpconnect", uwsgi_router_proxyhttp_connect);
}
struct uwsgi_plugin router_http_plugin = {
+17 -1
View File
@@ -18,6 +18,8 @@ struct uwsgi_router_file_conf {
char *mime;
char *no_cl;
char *no_headers;
};
int uwsgi_routing_func_static(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) {
@@ -38,6 +40,7 @@ int uwsgi_routing_func_file(struct wsgi_request *wsgi_req, struct uwsgi_route *u
char buf[32768];
struct stat st;
int ret = UWSGI_ROUTE_BREAK;
size_t remains = 0;
struct uwsgi_router_file_conf *urfc = (struct uwsgi_router_file_conf *) ur->data2;
@@ -61,6 +64,8 @@ int uwsgi_routing_func_file(struct wsgi_request *wsgi_req, struct uwsgi_route *u
struct uwsgi_buffer *ub_s = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urfc->status, urfc->status_len);
if (!ub_s) goto end2;
if (urfc->no_headers) goto send;
if (uwsgi_response_prepare_headers(wsgi_req, ub_s->buf, ub_s->pos)) {
uwsgi_buffer_destroy(ub_s);
goto end2;
@@ -82,8 +87,10 @@ int uwsgi_routing_func_file(struct wsgi_request *wsgi_req, struct uwsgi_route *u
else {
if (uwsgi_response_add_content_type(wsgi_req, urfc->content_type, urfc->content_type_len)) goto end2;
}
send:
size_t remains = st.st_size;
remains = st.st_size;
while(remains) {
ssize_t rlen = read(fd, buf, UMIN(32768, remains));
if (rlen <= 0) goto end2;
@@ -125,6 +132,8 @@ int uwsgi_routing_func_sendfile(struct wsgi_request *wsgi_req, struct uwsgi_rout
struct uwsgi_buffer *ub_s = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urfc->status, urfc->status_len);
if (!ub_s) goto end2;
if (urfc->no_headers) goto send;
if (uwsgi_response_prepare_headers(wsgi_req, ub_s->buf, ub_s->pos)) {
uwsgi_buffer_destroy(ub_s);
goto end2;
@@ -145,6 +154,8 @@ int uwsgi_routing_func_sendfile(struct wsgi_request *wsgi_req, struct uwsgi_rout
if (uwsgi_response_add_content_type(wsgi_req, urfc->content_type, urfc->content_type_len)) goto end2;
}
send:
if (!wsgi_req->headers_sent) {
if (uwsgi_response_write_headers_do(wsgi_req)) goto end2;
}
@@ -188,6 +199,8 @@ int uwsgi_routing_func_fastfile(struct wsgi_request *wsgi_req, struct uwsgi_rout
struct uwsgi_buffer *ub_s = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urfc->status, urfc->status_len);
if (!ub_s) goto end2;
if (urfc->no_headers) goto send;
if (uwsgi_response_prepare_headers(wsgi_req, ub_s->buf, ub_s->pos)) {
uwsgi_buffer_destroy(ub_s);
goto end2;
@@ -208,6 +221,8 @@ int uwsgi_routing_func_fastfile(struct wsgi_request *wsgi_req, struct uwsgi_rout
if (uwsgi_response_add_content_type(wsgi_req, urfc->content_type, urfc->content_type_len)) goto end2;
}
send:
if (!wsgi_req->headers_sent) {
if (uwsgi_response_write_headers_do(wsgi_req)) goto end2;
}
@@ -256,6 +271,7 @@ static int uwsgi_router_file(struct uwsgi_route *ur, char *args) {
"no_cl", &urfc->no_cl,
"no_content_length", &urfc->no_cl,
"mime", &urfc->mime,
"no_headers", &urfc->no_headers,
NULL)) {
uwsgi_log("invalid file route syntax: %s\n", args);
return -1;
+1
View File
@@ -54,6 +54,7 @@ static int uwsgi_routing_func_uwsgi_remote(struct wsgi_request *wsgi_req, struct
if (uwsgi_buffer_append(ub, wsgi_req->proto_parser_remains_buf, wsgi_req->proto_parser_remains)) {
goto end;
}
wsgi_req->post_pos += wsgi_req->proto_parser_remains;
wsgi_req->proto_parser_remains = 0;
}
+6 -5
View File
@@ -1,12 +1,12 @@
#include "../../uwsgi.h"
#include <uwsgi.h>
extern struct uwsgi_server uwsgi;
/* request 110 */
int uwsgi_request_signal(struct wsgi_request *wsgi_req) {
ssize_t len;
uint8_t ret_status = 1;
struct uwsgi_header uh;
if (uwsgi_signal_send(uwsgi.signal_socket, wsgi_req->uh->modifier2) < 0) {
ret_status = 0;
@@ -15,10 +15,11 @@ int uwsgi_request_signal(struct wsgi_request *wsgi_req) {
uh.modifier1 = 255;
uh.pktsize = 0;
uh.modifier2 = ret_status;
len = write(wsgi_req->fd, &uh, 4);
if (len != 4) {
uwsgi_error("write()");
if (uwsgi_response_write_body_do(wsgi_req, (char *) &uh, 4)) {
return -1;
}
return UWSGI_OK;
}
+4
View File
@@ -93,7 +93,11 @@ ssize_t uwsgi_syslog_logger(struct uwsgi_logger *ul, char *message, size_t len)
ul->configured = 1;
}
#ifdef __APPLE__
syslog(LOG_NOTICE, "%.*s", (int) len, message);
#else
syslog(LOG_INFO, "%.*s", (int) len, message);
#endif
return 0;
}
+5
View File
@@ -326,6 +326,11 @@ static void tornado_loop() {
uwsgi_log("!!! Running tornado with a socket-timeout lower than 30 seconds is not recommended, tune it with --socket-timeout !!!\n");
}
if (!uwsgi.async_waiting_fd_table)
uwsgi.async_waiting_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd);
if (!uwsgi.async_proto_fd_table)
uwsgi.async_proto_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd);
// get the GIL
UWSGI_GET_GIL
+18 -8
View File
@@ -2,6 +2,16 @@
extern struct uwsgi_tuntap utt;
// error reporting
void uwsgi_tuntap_error_do(struct uwsgi_tuntap_peer *uttp, char *msg, char *file, int line) {
if (uttp) {
uwsgi_log_verbose("[tuntap] peer fd: %d ip: %s %s: %s [%s line %d]\n", uttp->fd, uttp->ip, msg, strerror(errno), file, line);
}
else {
uwsgi_log_verbose("[tuntap] %s: %s [%s line %d]\n", msg, strerror(errno), file, line);
}
}
// create a new peer
struct uwsgi_tuntap_peer *uwsgi_tuntap_peer_create(struct uwsgi_tuntap_router *uttr, int fd, int is_router) {
@@ -183,7 +193,7 @@ int uwsgi_tuntap_register_addr(struct uwsgi_tuntap_router *uttr, struct uwsgi_tu
char ip[INET_ADDRSTRLEN + 1];
memset(ip, 0, INET_ADDRSTRLEN + 1);
if (!inet_ntop(AF_INET, &uttp->addr, ip, INET_ADDRSTRLEN)) {
uwsgi_error("uwsgi_tuntap_register_addr()/inet_ntop()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_register_addr()/inet_ntop()");
return -1;
}
if (uttp != tmp_uttp) {
@@ -205,7 +215,7 @@ int uwsgi_tuntap_peer_dequeue(struct uwsgi_tuntap_router *uttr, struct uwsgi_tun
if (rlen < 0) {
if (uwsgi_is_again())
return 0;
uwsgi_error("uwsgi_tuntap_peer_dequeue()/read()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_peer_dequeue()/read()");
return -1;
}
uttp->buf_pos += rlen;
@@ -265,7 +275,7 @@ enqueue:
if (rlen < 0) {
if (uwsgi_is_again())
return 0;
uwsgi_error("uwsgi_tuntap_peer_dequeue()/read()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_peer_dequeue()/read()");
return -1;
}
uttp->header_pos += rlen;
@@ -282,14 +292,14 @@ int uwsgi_tuntap_peer_enqueue(struct uwsgi_tuntap_router *uttr, struct uwsgi_tun
ssize_t rlen = write(uttp->fd, uttp->write_buf + uttp->written, uttp->write_buf_pktsize - uttp->written);
if (rlen == 0) {
uwsgi_error("uwsgi_tuntap_peer_enqueue()/write()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_peer_enqueue()/write()");
return -1;
}
if (rlen < 0) {
if (uwsgi_is_again())
goto retry;
uwsgi_error("uwsgi_tuntap_peer_enqueue()/write()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_peer_enqueue()/write()");
return -1;
}
@@ -303,13 +313,13 @@ int uwsgi_tuntap_peer_enqueue(struct uwsgi_tuntap_router *uttr, struct uwsgi_tun
if (uttr->wait_for_write) {
uttp->blocked_read = 1;
if (event_queue_del_fd(uttr->queue, uttp->fd, event_queue_write())) {
uwsgi_error("uwsgi_tuntap_peer_enqueue()/event_queue_del_fd()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_peer_enqueue()/event_queue_del_fd()");
return -1;
}
}
else {
if (event_queue_fd_readwrite_to_read(uttr->queue, uttp->fd)) {
uwsgi_error("uwsgi_tuntap_peer_enqueue()/event_queue_fd_write_to_read()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_peer_enqueue()/event_queue_fd_write_to_read()");
return -1;
}
}
@@ -324,7 +334,7 @@ int uwsgi_tuntap_peer_enqueue(struct uwsgi_tuntap_router *uttr, struct uwsgi_tun
retry:
if (!uttp->wait_for_write) {
if (event_queue_fd_read_to_readwrite(uttr->queue, uttp->fd)) {
uwsgi_error("uwsgi_tuntap_peer_enqueue()/event_queue_fd_read_to_write()");
uwsgi_tuntap_error(uttp, "uwsgi_tuntap_peer_enqueue()/event_queue_fd_read_to_write()");
return -1;
}
uttp->wait_for_write = 1;
+2
View File
@@ -116,3 +116,5 @@ int uwsgi_tuntap_register_addr(struct uwsgi_tuntap_router *, struct uwsgi_tuntap
void uwsgi_tuntap_peer_send_rules(int, struct uwsgi_tuntap_peer *);
int uwsgi_tuntap_peer_rules_check(struct uwsgi_tuntap_router *, struct uwsgi_tuntap_peer *, char *, size_t, int);
#define uwsgi_tuntap_error(x, y) uwsgi_tuntap_error_do(x, y, __FILE__, __LINE__)
void uwsgi_tuntap_error_do(struct uwsgi_tuntap_peer *, char *, char *, int);
+24 -5
View File
@@ -1,6 +1,7 @@
#include "common.h"
extern struct uwsgi_tuntap utt;
extern struct uwsgi_server uwsgi;
int uwsgi_tuntap_peer_rules_check(struct uwsgi_tuntap_router *uttr, struct uwsgi_tuntap_peer *uttp, char *pkt, size_t len, int direction) {
if (uttp->rules_cnt == 0) return 0;
@@ -51,7 +52,25 @@ int uwsgi_tuntap_peer_rules_check(struct uwsgi_tuntap_router *uttr, struct uwsgi
sin.sin_port = rule->target_port;
sin.sin_addr.s_addr = rule->target;
if (sendto(uttr->gateway_fd, pkt, len, 0, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
uwsgi_error("uwsgi_tuntap_route_check()/sendto()");
if (uwsgi_is_again()) {
// suspend and retry
struct pollfd pfd;
memset(&pfd, 0, sizeof(struct pollfd));
pfd.fd = uttr->gateway_fd;
pfd.events = POLLOUT;
int ret = poll(&pfd, 1, uwsgi.socket_timeout * 1000);
if (ret > 0) {
if (sendto(uttr->gateway_fd, pkt, len, 0, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
uwsgi_tuntap_error(uttp,"uwsgi_tuntap_route_check()/sendto()");
}
}
else {
uwsgi_tuntap_error(uttp,"uwsgi_tuntap_route_check()/poll()");
}
}
else {
uwsgi_tuntap_error(uttp,"uwsgi_tuntap_route_check()/sendto()");
}
}
}
return 2;
@@ -305,7 +324,7 @@ void uwsgi_tuntap_peer_send_rules(int fd, struct uwsgi_tuntap_peer *peer) {
*slash = 0;
}
if (inet_pton(AF_INET, argv[1], &utpr.src) != 1) {
uwsgi_error("uwsgi_tuntap_peer_send_rules()/inet_pton()");
uwsgi_tuntap_error(peer, "uwsgi_tuntap_peer_send_rules()/inet_pton()");
exit(1);
}
if (slash) *slash = '/';
@@ -317,7 +336,7 @@ void uwsgi_tuntap_peer_send_rules(int fd, struct uwsgi_tuntap_peer *peer) {
*slash = 0;
}
if (inet_pton(AF_INET, argv[2], &utpr.dst) != 1) {
uwsgi_error("uwsgi_tuntap_peer_send_rules()/inet_pton()");
uwsgi_tuntap_error(peer, "uwsgi_tuntap_peer_send_rules()/inet_pton()");
exit(1);
}
if (slash) *slash = '/';
@@ -352,7 +371,7 @@ void uwsgi_tuntap_peer_send_rules(int fd, struct uwsgi_tuntap_peer *peer) {
}
*colon = 0;
if (inet_pton(AF_INET, argv[4], &utpr.target) != 1) {
uwsgi_error("uwsgi_tuntap_peer_send_rules()/inet_pton()");
uwsgi_tuntap_error(peer, "uwsgi_tuntap_peer_send_rules()/inet_pton()");
exit(1);
}
*colon = ':';
@@ -370,7 +389,7 @@ void uwsgi_tuntap_peer_send_rules(int fd, struct uwsgi_tuntap_peer *peer) {
size_t len = ub->pos;
uwsgi_buffer_destroy(ub);
if (write(fd,peer->rules, len) != (ssize_t)len) {
uwsgi_error("uwsgi_tuntap_peer_send_rules()/write()");
uwsgi_tuntap_error(peer, "uwsgi_tuntap_peer_send_rules()/write()");
exit(1);
}
return;
+11 -4
View File
@@ -117,6 +117,13 @@ static void *uwsgi_tuntap_loop(void *arg) {
uwsgi_error("uwsgi_tuntap_loop()/read()");
exit(1);
}
// check for full write buffer
if (uttp->write_buf_pktsize + 4 + rlen > utt.buffer_size) {
uttp->dropped++;
continue;
}
uint16_t pktsize = rlen;
char *ptr = uttp->write_buf + uttp->write_buf_pktsize;
memcpy(ptr + 4, uttr->buf, rlen);
@@ -126,7 +133,7 @@ static void *uwsgi_tuntap_loop(void *arg) {
ptr[3] = 0;
uttp->write_buf_pktsize+= pktsize+4;
if (uwsgi_tuntap_peer_enqueue(uttr, uttp)) {
uwsgi_log("server disconnected...\n");
uwsgi_log_verbose("tuntap server disconnected...\n");
exit(1);
}
continue;
@@ -137,7 +144,7 @@ static void *uwsgi_tuntap_loop(void *arg) {
// read from the client
if (!uttp->wait_for_write) {
if (uwsgi_tuntap_peer_dequeue(uttr, uttp, 0)) {
uwsgi_log("server disconnected...\n");
uwsgi_log_verbose("tuntap server disconnected...\n");
exit(1);
}
}
@@ -149,7 +156,7 @@ static void *uwsgi_tuntap_loop(void *arg) {
// write to the client
if (uwsgi_tuntap_peer_enqueue(uttr, uttp)) {
uwsgi_log("server disconnected...\n");
uwsgi_log_verbose("tuntap server disconnected...\n");
exit(1);
}
}
@@ -507,7 +514,7 @@ void tuntaprouter_send_stats(struct uwsgi_tuntap_router *uttr) {
ssize_t res = write(client_fd, us->base + pos, remains);
if (res <= 0) {
if (res < 0) {
uwsgi_error("write()");
uwsgi_error("tuntaprouter_send_stats()/write()");
}
goto end0;
}
+110 -45
View File
@@ -4,52 +4,55 @@
extern struct uwsgi_server uwsgi;
static uint16_t http_add_uwsgi_header(struct wsgi_request *wsgi_req, char *hh, int hhlen) {
static char * http_header_to_cgi(char *hh, size_t hhlen, size_t *keylen, size_t *vallen, int *has_prefix) {
size_t i;
char *val = hh;
int status = 0;
for (i = 0; i < hhlen; i++) {
if (!status) {
hh[i] = toupper((int) hh[i]);
if (hh[i] == '-')
hh[i] = '_';
if (hh[i] == ':') {
status = 1;
*keylen = i;
}
}
else if (status == 1 && hh[i] != ' ') {
status = 2;
val += i;
*vallen+=1;
}
else if (status == 2) {
*vallen+=1;
}
}
if (!(*keylen))
return NULL;
if (uwsgi_strncmp("CONTENT_LENGTH", 14, hh, *keylen) && uwsgi_strncmp("CONTENT_TYPE", 12, hh, *keylen)) {
*has_prefix = 0x02;
}
return val;
}
static uint16_t http_add_uwsgi_header(struct wsgi_request *wsgi_req, char *hh, size_t hhlen, char *hv, size_t hvlen, int has_prefix) {
char *buffer = wsgi_req->buffer + wsgi_req->uh->pktsize;
char *watermark = wsgi_req->buffer + uwsgi.buffer_size;
int i;
int status = 0;
char *val = hh;
uint16_t keylen = 0, vallen = 0;
int prefix = 0;
char *ptr = buffer;
size_t keylen = hhlen;
for (i = 0; i < hhlen; i++) {
if (!status) {
hh[i] = toupper((int) hh[i]);
if (hh[i] == '-')
hh[i] = '_';
if (hh[i] == ':') {
status = 1;
keylen = i;
}
}
else if (status == 1 && hh[i] != ' ') {
status = 2;
val += i;
vallen++;
}
else if (status == 2) {
vallen++;
}
}
if (has_prefix) keylen += 5;
if (!keylen)
return 0;
if (uwsgi_strncmp("CONTENT_LENGTH", 14, hh, keylen) && uwsgi_strncmp("CONTENT_TYPE", 12, hh, keylen)) {
keylen += 5;
prefix = 1;
}
if (buffer + keylen + vallen + 2 + 2 >= watermark) {
if (prefix) {
uwsgi_log("[WARNING] unable to add HTTP_%.*s=%.*s to uwsgi packet, consider increasing buffer size\n", keylen, hh, vallen, val);
if (buffer + keylen + hvlen + 2 + 2 >= watermark) {
if (has_prefix) {
uwsgi_log("[WARNING] unable to add HTTP_%.*s=%.*s to uwsgi packet, consider increasing buffer size\n", keylen, hh, hvlen, hv);
}
else {
uwsgi_log("[WARNING] unable to add %.*s=%.*s to uwsgi packet, consider increasing buffer size\n", keylen, hh, vallen, val);
uwsgi_log("[WARNING] unable to add %.*s=%.*s to uwsgi packet, consider increasing buffer size\n", keylen, hh, hvlen, hv);
}
return 0;
}
@@ -58,7 +61,7 @@ static uint16_t http_add_uwsgi_header(struct wsgi_request *wsgi_req, char *hh, i
*ptr++ = (uint8_t) (keylen & 0xff);
*ptr++ = (uint8_t) ((keylen >> 8) & 0xff);
if (prefix) {
if (has_prefix) {
memcpy(ptr, "HTTP_", 5);
ptr += 5;
memcpy(ptr, hh, keylen - 5);
@@ -69,11 +72,11 @@ static uint16_t http_add_uwsgi_header(struct wsgi_request *wsgi_req, char *hh, i
ptr += keylen;
}
*ptr++ = (uint8_t) (vallen & 0xff);
*ptr++ = (uint8_t) ((vallen >> 8) & 0xff);
memcpy(ptr, val, vallen);
*ptr++ = (uint8_t) (hvlen & 0xff);
*ptr++ = (uint8_t) ((hvlen >> 8) & 0xff);
memcpy(ptr, hv, hvlen);
return 2 + keylen + 2 + vallen;
return 2 + keylen + 2 + hvlen;
}
static char *proxy1_parse(char *ptr, char *watermark, char **src, uint16_t *src_len, char **dst, uint16_t *dst_len, char **src_port, uint16_t *src_port_len, char **dst_port, uint16_t *dst_port_len) {
@@ -301,9 +304,15 @@ static int http_parse(struct wsgi_request *wsgi_req, char *watermark) {
}
}
if (wsgi_req->https_len > 0) {
wsgi_req->uh->pktsize += proto_base_add_uwsgi_var(wsgi_req, "HTTPS", 5, wsgi_req->https, wsgi_req->https_len);
}
//HEADERS
base = ptr;
struct uwsgi_string_list *headers = NULL, *usl = NULL;
while (ptr < watermark) {
if (*ptr == '\r') {
if (ptr + 1 >= watermark)
@@ -317,15 +326,71 @@ static int http_parse(struct wsgi_request *wsgi_req, char *watermark) {
continue;
}
}
wsgi_req->uh->pktsize += http_add_uwsgi_header(wsgi_req, base, ptr - base);
size_t key_len = 0, value_len = 0;
int has_prefix = 0;
// last line, do not waste time
if (ptr - base == 0) break;
char *value = http_header_to_cgi(base, ptr - base, &key_len, &value_len, &has_prefix);
if (!value) {
uwsgi_log_verbose("invalid HTTP request\n");
goto clear;
}
usl = uwsgi_string_list_has_item(headers, base, key_len);
// there is already a HTTP header with the same name, let's merge them
if (usl) {
char *old_value = usl->custom_ptr;
usl->custom_ptr = uwsgi_concat3n(old_value, (size_t) usl->custom, ", ", 2, value, value_len);
usl->custom += 2 + value_len;
if (usl->custom2 & 0x01) free(old_value);
usl->custom2 |= 0x01;
}
else {
// add an entry
usl = uwsgi_string_new_list(&headers, NULL);
usl->value = base;
usl->len = key_len;
usl->custom_ptr = value;
usl->custom = value_len;
usl->custom2 = has_prefix;
}
ptr++;
base = ptr + 1;
}
ptr++;
}
return 0;
usl = headers;
int broken = 0;
while(usl) {
if (!broken) {
uint16_t old_pktsize = wsgi_req->uh->pktsize;
wsgi_req->uh->pktsize += http_add_uwsgi_header(wsgi_req, usl->value, usl->len, usl->custom_ptr, (size_t) usl->custom, usl->custom2 & 0x02);
// if the packet remains unchanged, the buffer is full, mark the request as broken
if (old_pktsize == wsgi_req->uh->pktsize) {
broken = 1;
}
}
if (usl->custom2 & 0x01) {
free(usl->custom_ptr);
}
struct uwsgi_string_list *tmp_usl = usl;
usl = usl->next;
free(tmp_usl);
}
return broken;
clear:
usl = headers;
while(usl) {
if (usl->custom2 & 0x01) {
free(usl->custom_ptr);
}
struct uwsgi_string_list *tmp_usl = usl;
usl = usl->next;
free(tmp_usl);
}
return -1;
}
+12
View File
@@ -0,0 +1,12 @@
[uwsgi]
socket = /tmp/foo
cache2 = name=items_1,blocks=4,items=2,bitmap=1,blocksize=1
cache2 = name=items_2,blocks=4,items=3,bitmap=1,blocksize=1
cache2 = name=items_3,blocks=4,items=4,bitmap=1,blocksize=1
cache2 = name=items_4,blocks=5,items=5,bitmap=1,blocksize=1
cache2 = name=items_17,blocks=17,items=17,bitmap=1,blocksize=1
cache2 = name=items_4_10,blocks=5,items=5,bitmap=1,blocksize=10
cache2 = name=items_1_100000,blocks=1000,items=2,bitmap=1,blocksize=100
cache2 = name=items_non_bitmap,items=2,blocksize=20
pyrun = t/cachebitmap.py
+106
View File
@@ -0,0 +1,106 @@
import uwsgi
import unittest
import random
import string
class BitmapTest(unittest.TestCase):
__caches__ = ['items_1', 'items_2', 'items_3', 'items_4', 'items_17', 'items_4_10', 'items_1_100000', 'items_non_bitmap']
def setUp(self):
for cache in self.__caches__:
uwsgi.cache_clear(cache)
def test_failed_by_one(self):
self.assertIsNone(uwsgi.cache_update('key1', 'HELLO', 0, 'items_1'))
def test_ok_four_bytes(self):
self.assertTrue(uwsgi.cache_update('key1', 'HELL', 0, 'items_1'))
def test_two_items_using_four_blocks(self):
self.assertTrue(uwsgi.cache_update('key1', 'HE', 0, 'items_2'))
self.assertTrue(uwsgi.cache_update('key2', 'LL', 0, 'items_2'))
self.assertTrue(uwsgi.cache_del('key1', 'items_2'))
self.assertIsNone(uwsgi.cache_update('key1', 'HEL', 0, 'items_2'))
self.assertTrue(uwsgi.cache_update('key1', 'HE', 0, 'items_2'))
def test_overlapping(self):
self.assertTrue(uwsgi.cache_update('key1', 'HE', 0, 'items_2'))
self.assertIsNone(uwsgi.cache_update('key1', 'HELL', 0, 'items_2'))
self.assertTrue(uwsgi.cache_del('key1', 'items_2'))
self.assertTrue(uwsgi.cache_update('key1', 'HELL', 0, 'items_2'))
def test_big_item(self):
self.assertIsNone(uwsgi.cache_update('key1', 'HELLOHELLOHELLOHEL', 0, 'items_17'))
self.assertTrue(uwsgi.cache_update('key1', 'HELLOHELLOHELLOHE', 0, 'items_17'))
def test_set(self):
self.assertTrue(uwsgi.cache_set('key1', 'HELLO', 0, 'items_17'))
self.assertIsNone(uwsgi.cache_set('key1', 'HELLO', 0, 'items_17'))
self.assertTrue(uwsgi.cache_del('key1', 'items_17'))
self.assertTrue(uwsgi.cache_set('key1', 'HELLO', 0, 'items_17'))
self.assertIsNone(uwsgi.cache_set('key1', 'HELLO', 0, 'items_17'))
def test_too_much_items(self):
self.assertTrue(uwsgi.cache_set('key1', 'HELLO', 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_set('key2', 'HELLO', 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_set('key3', 'HELLO', 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_set('key4', 'HELLO', 0, 'items_4_10'))
self.assertIsNone(uwsgi.cache_set('key5', 'HELLO', 0, 'items_4_10'))
def test_big_delete(self):
self.assertTrue(uwsgi.cache_set('key1', 'X' * 50 , 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_del('key1', 'items_4_10'))
self.assertTrue(uwsgi.cache_set('key1', 'HELLOHELLO', 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_set('key2', 'HELLOHELLO', 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_set('key3', 'HELLOHELLO', 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_set('key4', 'HELLOHELLO', 0, 'items_4_10'))
self.assertIsNone(uwsgi.cache_set('key5', 'HELLOHELLO', 0, 'items_4_10'))
def test_big_update(self):
self.assertTrue(uwsgi.cache_set('key1', 'X' * 40 , 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_update('key1', 'X' * 10 , 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_del('key1', 'items_4_10'))
self.assertIsNone(uwsgi.cache_update('key1', 'X' * 51 , 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_update('key1', 'X' * 50 , 0, 'items_4_10'))
def test_multi_clear(self):
for i in range(0, 100):
self.assertTrue(uwsgi.cache_clear('items_4_10'))
def test_multi_delete(self):
for i in range(0, 100):
self.assertTrue(uwsgi.cache_set('key1', 'X' * 50 , 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_del('key1', 'items_4_10'))
for i in range(0, 100):
self.assertIsNone(uwsgi.cache_set('key1', 'X' * 51 , 0, 'items_4_10'))
self.assertIsNone(uwsgi.cache_del('key1', 'items_4_10'))
for i in range(0, 100):
self.assertTrue(uwsgi.cache_set('key1', 'X' * 50 , 0, 'items_4_10'))
self.assertTrue(uwsgi.cache_del('key1', 'items_4_10'))
def test_big_key(self):
self.assertTrue(uwsgi.cache_set('K' * 2048, 'X' * 50 , 0, 'items_4_10'))
self.assertIsNone(uwsgi.cache_set('K' * 2049, 'X' * 50 , 0, 'items_4_10'))
def rand_blob(self, n=32):
return ''.join([random.choice(string.ascii_letters + string.digits) for n in range(n)])
def test_big_random(self):
blob = self.rand_blob(100000)
self.assertTrue(uwsgi.cache_set('KEY', blob, 0, 'items_1_100000'))
get_blob = uwsgi.cache_get('KEY', 'items_1_100000')
self.assertEqual(blob, get_blob)
self.assertTrue(uwsgi.cache_del('KEY', 'items_1_100000'))
self.assertIsNone(uwsgi.cache_set('KEY', 'X' * 100001, 0, 'items_1_100000'))
self.assertTrue(uwsgi.cache_set('KEY', 'X' * 10000, 0, 'items_1_100000'))
def test_non_bitmap(self):
self.assertTrue(uwsgi.cache_set('KEY', 'X' * 20, 0, 'items_non_bitmap'))
self.assertTrue(uwsgi.cache_del('KEY', 'items_non_bitmap'))
self.assertIsNone(uwsgi.cache_set('KEY', 'X' * 21, 0, 'items_non_bitmap'))
self.assertTrue(uwsgi.cache_set('KEY', 'X' * 20, 0, 'items_non_bitmap'))
unittest.main()
+8
View File
@@ -1,5 +1,9 @@
use strict;
use warnings;
BEGIN {
die "PANIC: We should only load this once" if ++$main::count_BEGIN > 1;
}
die "PANIC: We should only run this once" if ++$main::count_runs > 1;
uwsgi::register_rpc('hello', sub {
my ($one, $two, $three) = @_;
@@ -55,6 +59,10 @@ my $app = sub {
uwsgi::signal(17);
uwsgi::signal(30);
my ($package, $filename, $line) = caller;
die "Expecting reasonable caller() return values, not [$package, $filename, $line]"
unless $package eq 'main' and $filename =~ /\btest\.psgi$/s and $line == 0;
if ($env->{'psgix.cleanup'}) {
print "cleanup supported\n";
push @{$env->{'psgix.cleanup.handlers'}}, $one;
+3 -1
View File
@@ -20,11 +20,13 @@ push @tests, ['1', 'HELLO', "oHELLOo_three_four"];
push @tests, ['3', 'HELLO', "oneHELLOthree_four"];
push @tests, ['30', 'HELLO', "one_two_three_four\0\0\0\0\0\0\0\0\0\0\0\0HELLO"];
@ARGV or die "You must provide a host to test on, e.g. localhost:8080";
foreach(@tests) {
print "testing: offset(".$_->[0].") body(".$_->[1].")\n";
my $req = "POST /?".$base." HTTP/1.0\r\nContent-Length: ".length($_->[1])."\r\nuWSGI-Offset: ".$_->[0]."\r\n\r\n".$_->[1];
my $s = IO::Socket::INET->new(PeerAddr => $ARGV[0]);
my $s = IO::Socket::INET->new(PeerAddr => $ARGV[0]) or die "PANIC: Unable to construct socket";
$s->send($req);
my $response = '';
+47
View File
@@ -0,0 +1,47 @@
use strict;
use warnings;
sub {
my $env = shift;
my $cl = $env->{CONTENT_LENGTH};
$env->{'psgi.input'}->seek(0,0);
my $content = '';
while ($cl) {
$env->{'psgi.input'}->read(my $chunk, $cl < 8192 ? $cl : 8192);
my $read = length $chunk;
$cl -= $read;
$content .= $chunk;
}
return [200, [], [ "Your content was: <$content>" ]];
};
__END__
This is a trival test that prints out a POST request, it's here to
test a regression introduced in 2.0-103-gf041d10 where doing reads
without offsets, e.g.:
$ http_proxy= curl -d '{ "what": "ever" }' http://localhost:8080/
Your content was: $VAR1 = '{ "what": "ever" }';
Would result in:
Use of uninitialized value in subroutine entry at
/home/v-perlbrew/perl5/perlbrew/perls/perl-5.19.6/lib/site_perl/5.19.6/Plack/Request.pm
line 280.
Which is due to this commit having a one-off error in counting stack
items.
$ git bisect good
f041d1095ddf7541c4b275e16d2ed3355a8e2be9 is the first bad commit
commit f041d1095ddf7541c4b275e16d2ed3355a8e2be9
Author: Unbit <info@unbit.it>
Date: Wed Feb 5 11:21:01 2014 +0100
perl refactoring
:040000 040000 98a25406b7edb9bd0b9be8bbcd351a99e7ce2d33 0087e3ca4b6bd65a087fade65d43a56085298ef0 M plugins
+48
View File
@@ -0,0 +1,48 @@
use strict;
use warnings;
sub {
my $env = shift;
return sub {
my $responder = shift;
my $writer = $responder->([ 200, [ 'Content-Type', 'text/plain' ]]);
sleep 3;
$writer->write("hello\n");
sleep 3;
$writer->write("world\n");
$writer->close;
return;
};
}
__END__
Making a request to this will give you:
$ date; curl -s -N -D - 'http://localhost:8080'
Sat Mar 15 14:08:25 UTC 2014
HTTP/1.1 200 OK
Content-Type: text/plain
hello
world
And monitoring it with tcpflow shows how the output (including
headers) is flushed right away:
$ sudo tcpflow -i lo -c port 8080 | perl -pe 's/^/localtime . " "/ge'
Sat Mar 15 14:09:08 2014 127.000.000.001.55058-127.000.000.001.08080: GET / HTTP/1.1
Sat Mar 15 14:09:08 2014 User-Agent: curl/7.35.0
Sat Mar 15 14:09:08 2014 Host: localhost:8080
Sat Mar 15 14:09:08 2014 Accept: */*
Sat Mar 15 14:09:08 2014
Sat Mar 15 14:09:08 2014
Sat Mar 15 14:09:08 2014 127.000.000.001.08080-127.000.000.001.55058: HTTP/1.1 200 OK
Sat Mar 15 14:09:08 2014 Content-Type: text/plain
Sat Mar 15 14:09:08 2014
Sat Mar 15 14:09:08 2014
Sat Mar 15 14:09:11 2014 127.000.000.001.08080-127.000.000.001.55058: hello
Sat Mar 15 14:09:11 2014
Sat Mar 15 14:09:14 2014 127.000.000.001.08080-127.000.000.001.55058: world
Sat Mar 15 14:09:14 2014
+1 -1
View File
@@ -1,4 +1,4 @@
#!./uwsgi --http-socket :9090 --gevent 100 --module tests.websocket_chat --gevent-monkey-patch
#!./uwsgi --http-socket :9090 --gevent 100 --module tests.websockets_chat --gevent-monkey-patch
import uwsgi
import time
import gevent.select
+140
View File
@@ -0,0 +1,140 @@
#!./uwsgi --http-socket :9090 --asyncio 100 --module tests.websockets_chat_asyncio --greenlet
import uwsgi
import asyncio
import asyncio_redis
import time
import greenlet
class GreenFuture(asyncio.Future):
def __init__(self):
super().__init__()
self.greenlet = greenlet.getcurrent()
self.add_done_callback(lambda f: f.greenlet.switch())
def result(self):
while True:
if self.done():
return super().result()
self.greenlet.parent.switch()
@asyncio.coroutine
def redis_open(f):
connection = yield from asyncio_redis.Connection.create(host='localhost', port=6379)
f.set_result(connection)
f.greenlet.switch()
@asyncio.coroutine
def redis_subscribe(f):
connection = yield from asyncio_redis.Connection.create(host='localhost', port=6379)
subscriber = yield from connection.start_subscribe()
yield from subscriber.subscribe([ 'foobar' ])
f.set_result(subscriber)
f.greenlet.switch()
def ws_recv_msg(g):
g.has_ws_msg = True
g.switch()
@asyncio.coroutine
def redis_wait(subscriber, f):
reply = yield from subscriber.next_published()
f.set_result(reply.value)
f.greenlet.switch()
@asyncio.coroutine
def redis_publish(connection, msg):
yield from connection.publish('foobar', msg.decode('utf-8'))
def application(env, sr):
ws_scheme = 'ws'
if 'HTTPS' in env or env['wsgi.url_scheme'] == 'https':
ws_scheme = 'wss'
if env['PATH_INFO'] == '/':
sr('200 OK', [('Content-Type','text/html')])
return ("""
<html>
<head>
<script language="Javascript">
var s = new WebSocket("%s://%s/foobar/");
s.onopen = function() {
alert("connected !!!");
s.send("ciao");
};
s.onmessage = function(e) {
var bb = document.getElementById('blackboard')
var html = bb.innerHTML;
bb.innerHTML = html + '<br/>' + e.data;
};
s.onerror = function(e) {
alert(e);
}
s.onclose = function(e) {
alert("connection closed");
}
function invia() {
var value = document.getElementById('testo').value;
s.send(value);
}
</script>
</head>
<body>
<h1>WebSocket</h1>
<input type="text" id="testo"/>
<input type="button" value="invia" onClick="invia();"/>
<div id="blackboard" style="width:640px;height:480px;background-color:black;color:white;border: solid 2px red;overflow:auto">
</div>
</body>
</html>
""" % (ws_scheme, env['HTTP_HOST'])).encode()
elif env['PATH_INFO'] == '/favicon.ico':
return b""
elif env['PATH_INFO'] == '/foobar/':
uwsgi.websocket_handshake()
print("websockets...")
# a future for waiting for redis connection
f = GreenFuture()
asyncio.Task(redis_subscribe(f))
# the result() method will switch greenlets if needed
subscriber = f.result()
# open another redis connection for publishing messages
f0 = GreenFuture()
t = asyncio.Task(redis_open(f0))
connection = f0.result()
myself = greenlet.getcurrent()
myself.has_ws_msg = False
# start monitoring websocket events
asyncio.get_event_loop().add_reader(uwsgi.connection_fd(), ws_recv_msg, myself)
# add a 4 seconds timer to manage ping/pong
asyncio.get_event_loop().call_later(4, ws_recv_msg, myself)
# add a coroutine for redis messages
f = GreenFuture()
asyncio.Task(redis_wait(subscriber, f))
# switch again
f.greenlet.parent.switch()
while True:
# any redis message in the queue ?
if f.done():
msg = f.result()
uwsgi.websocket_send("[%s] %s" % (time.time(), msg))
# restart coroutine
f = GreenFuture()
asyncio.Task(redis_wait(subscriber, f))
if myself.has_ws_msg:
myself.has_ws_msg = False
msg = uwsgi.websocket_recv_nb()
if msg:
asyncio.Task(redis_publish(connection, msg))
# switch again
f.greenlet.parent.switch()
+1 -1
View File
@@ -2,7 +2,7 @@ Gem::Specification.new do |s|
s.name = 'uwsgi'
s.license = 'GPL-2'
s.version = `python -c "import uwsgiconfig as uc; print uc.uwsgi_version"`.sub(/-dev-.*/,'')
s.date = '2014-02-09'
s.date = '2014-04-22'
s.summary = "uWSGI"
s.description = "The uWSGI server for Ruby/Rack"
s.authors = ["Unbit"]
+12
View File
@@ -765,6 +765,10 @@ struct uwsgi_cache_item {
uint64_t prev;
// next same-hash item
uint64_t next;
// previous lru item
uint64_t lru_prev;
// next lru item
uint64_t lru_next;
// key characters follows...
char key[];
} __attribute__ ((__packed__));
@@ -819,6 +823,13 @@ struct uwsgi_cache {
struct uwsgi_lock_item *lock;
struct uwsgi_cache *next;
int ignore_full;
uint64_t next_scan;
int purge_lru;
uint64_t lru_head;
uint64_t lru_tail;
};
struct uwsgi_option {
@@ -4044,6 +4055,7 @@ void emperor_stop(struct uwsgi_instance *);
void emperor_curse(struct uwsgi_instance *);
void emperor_respawn(struct uwsgi_instance *, time_t);
void emperor_add(struct uwsgi_emperor_scanner *, char *, time_t, char *, uint32_t, uid_t, gid_t, char *);
void emperor_back_to_ondemand(struct uwsgi_instance *);
void uwsgi_exec_command_with_args(char *);
+28 -9
View File
@@ -1,6 +1,6 @@
# uWSGI build system
uwsgi_version = '2.0.1'
uwsgi_version = '2.0.4'
import os
import re
@@ -331,6 +331,13 @@ def build_uwsgi(uc, print_only=False, gcll=None):
uwsgi_config_py = uwsgi_config_py_content.encode('hex')
open('core/config_py.c', 'w').write('char *uwsgi_config_py = "%s";\n' % uwsgi_config_py);
gcc_list.append('core/config_py')
additional_sources = os.environ.get('UWSGI_ADDITIONAL_SOURCES')
if not additional_sources:
additional_sources = uc.get('additional_sources')
if additional_sources:
for item in additional_sources.split(','):
gcc_list.append(item)
cflags.append('-DUWSGI_CFLAGS=\\"%s\\"' % uwsgi_cflags)
cflags.append('-DUWSGI_BUILD_DATE="\\"%s\\""' % time.strftime("%d %B %Y %H:%M:%S"))
@@ -367,6 +374,15 @@ def build_uwsgi(uc, print_only=False, gcll=None):
if len(kv) > 1:
p = kv[1]
p = p.strip()
if p.startswith('http://') or p.startswith('https://') or p.startswith('git://') or p.startswith('ssh://'):
git_dir = p.split('/').pop()
if not os.path.isdir(git_dir):
if os.system('git clone %s' % p) != 0:
sys.exit(1)
else:
if os.system('cd %s ; git pull' % git_dir) != 0:
sys.exit(1)
p = git_dir
path = os.path.abspath(p)
else:
p = kv[0]
@@ -510,13 +526,6 @@ def build_uwsgi(uc, print_only=False, gcll=None):
for ef in binary_list:
gcc_list.append("%s.o" % ef)
additional_sources = os.environ.get('UWSGI_ADDITIONAL_SOURCES')
if not additional_sources:
additional_sources = uc.get('additional_sources')
if additional_sources:
for item in additional_sources.split(','):
gcc_list.append(item)
if compile_queue:
for t in thread_compilers:
compile_queue.put((None, None))
@@ -1049,7 +1058,7 @@ class uConf(object):
self.embed_config = None
if uwsgi_os == 'Linux':
if uwsgi_os in ('Linux','FreeBSD'):
self.embed_config = os.environ.get('UWSGI_EMBED_CONFIG')
if not self.embed_config:
self.embed_config = self.get('embed_config')
@@ -1275,6 +1284,16 @@ def build_plugin(path, uc, cflags, ldflags, libs, name = None):
up = {}
if path.startswith('http://') or path.startswith('https://') or path.startswith('git://') or path.startswith('ssh://'):
git_dir = path.split('/').pop()
if not os.path.isdir(git_dir):
if os.system('git clone %s' % path) != 0:
sys.exit(1)
else:
if os.system('cd %s ; git pull' % git_dir) != 0:
sys.exit(1)
path = os.path.abspath(git_dir)
if os.path.isfile(path):
bname = os.path.basename(path)
# override path