From 3da5238ee5aa839041f7a37412cf47f0ef1d73d7 Mon Sep 17 00:00:00 2001 From: "roberto@sirius" Date: Mon, 14 Jun 2010 16:59:50 +0200 Subject: [PATCH] PSGI embedded support --- mojoapp.pl | 23 +++++++++ plugins.c | 83 ++++++++++++++++++++++++++++++++ plugins/example/example_plugin.c | 4 +- plugins/psgi/psgi_plugin.c | 44 +++++++++-------- psgi.py | 7 ++- simple_app.py | 5 ++ test.psgi | 1 - uwsgi.c | 16 +++++- uwsgi.h | 74 ++++++++++++++++++++++++++++ uwsgi_pymodule.c | 46 +++--------------- uwsgiconfig.py | 40 ++++++++++++--- 11 files changed, 269 insertions(+), 74 deletions(-) create mode 100644 mojoapp.pl create mode 100644 plugins.c diff --git a/mojoapp.pl b/mojoapp.pl new file mode 100644 index 00000000..221fca23 --- /dev/null +++ b/mojoapp.pl @@ -0,0 +1,23 @@ +use Mojolicious::Lite; + +# / +get '/' => 'index'; + +# /* +get '/:groovy' => sub { + my $self = shift; + $self->render_text($self->param('groovy'), layout => 'funky'); +}; + +app->start('psgi'); +__DATA__ + +@@ index.html.ep +% layout 'funky'; +Yea baby! + +@@ layouts/funky.html.ep + + Funky! + <%= content %> + diff --git a/plugins.c b/plugins.c new file mode 100644 index 00000000..7900f1a2 --- /dev/null +++ b/plugins.c @@ -0,0 +1,83 @@ +#include "uwsgi.h" + +void embed_plugins(struct uwsgi_server *uwsgi) { + + int ret ; + +#ifdef UWSGI_EMBED_PLUGIN_PSGI + if (uwsgi->plugin_arg_psgi) + ret = uwsgi_load_plugin(uwsgi, 5, "psgi_plugin.so", uwsgi->plugin_arg_psgi, 0); +#endif + +#ifdef UWSGI_EMBED_PLUGIN_LUA + if (uwsgi->plugin_arg_lua) + ret = uwsgi_load_plugin(uwsgi, 6, "lua_plugin.so", uwsgi->plugin_arg_lua, 0); +#endif + +#ifdef UWSGI_EMBED_PLUGIN_RACK + if (uwsgi->plugin_arg_rack) + ret = uwsgi_load_plugin(uwsgi, 7, "rack_plugin.so", uwsgi->plugin_arg_rack, 0); +#endif + +} + +int uwsgi_load_plugin(struct uwsgi_server *uwsgi, int modifier, char *plugin, char *pargs, int absolute) { + + char *plugin_name ; + + void *plugin_handle; + int (*plugin_init) (struct uwsgi_server *, char *); + int (*plugin_request) (struct uwsgi_server *, struct wsgi_request *); + void (*plugin_after_request) (struct uwsgi_server *, struct wsgi_request *); + + if (absolute) { + plugin_name = malloc(strlen(plugin) + 1); + strcpy(plugin_name, plugin); + } + else { + plugin_name = malloc(strlen(UWSGI_PLUGIN_DIR) + 1 + strlen(plugin) + 1); + if (!plugin_name) { + uwsgi_error("malloc()"); + return -1 ; + } + strcpy(plugin_name, UWSGI_PLUGIN_DIR); + strcat(plugin_name, "/"); + strcat(plugin_name, plugin); + } + plugin_handle = dlopen(plugin_name, RTLD_NOW | RTLD_GLOBAL); + free(plugin_name); + + if (!plugin_handle) { + uwsgi_log( "%s\n", dlerror()); + } + else { + plugin_init = dlsym(plugin_handle, "uwsgi_init"); + if (plugin_init) { + if ((*plugin_init) (uwsgi, pargs)) { + uwsgi_log( "plugin initialization returned error\n"); + if (dlclose(plugin_handle)) { + uwsgi_log( "unable to unload plugin\n"); + } + + return -1; + } + } + + plugin_request = dlsym(plugin_handle, "uwsgi_request"); + if (plugin_request) { + uwsgi->shared->hooks[modifier] = plugin_request; + plugin_after_request = dlsym(plugin_handle, "uwsgi_after_request"); + if (plugin_after_request) { + uwsgi->shared->after_hooks[modifier] = plugin_after_request; + } + return 0; + + } + else { + uwsgi_log( "%s\n", dlerror()); + } + } + + + return -1; +} diff --git a/plugins/example/example_plugin.c b/plugins/example/example_plugin.c index a3140e8d..3bf1de34 100644 --- a/plugins/example/example_plugin.c +++ b/plugins/example/example_plugin.c @@ -1,7 +1,7 @@ #include "../../uwsgi.h" int uwsgi_init(struct uwsgi_server *uwsgi, char *args){ - fprintf(stderr,"i am the example plugin initialization function with arg: %s\n", args); + uwsgi_log("i am the example plugin initialization function with arg: %s\n", args); return 0; } @@ -16,5 +16,5 @@ int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { void uwsgi_after_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { - fprintf(stderr,"i am the example plugin after request function\n"); + uwsgi_log("i am the example plugin after request function\n"); } diff --git a/plugins/psgi/psgi_plugin.c b/plugins/psgi/psgi_plugin.c index 42e0c427..2ec9f016 100644 --- a/plugins/psgi/psgi_plugin.c +++ b/plugins/psgi/psgi_plugin.c @@ -6,6 +6,8 @@ static PerlInterpreter *my_perl; static SV *psgi_func ; +extern char **environ; + /* statistically ordered */ static struct http_status_codes hsc[] = { @@ -76,7 +78,6 @@ xs_init(pTHX) int uwsgi_init(struct uwsgi_server *uwsgi, char *args){ - char *pargs[2] ; char *psgibuffer ; int fd ; @@ -84,20 +85,20 @@ int uwsgi_init(struct uwsgi_server *uwsgi, char *args){ struct http_status_codes *http_sc ; - char *embedding[] = { "", "-e", "0" }; + int argc = 4 ; + char *embedding[] = { "", args, "-e", "0" }; + char **argv = embedding ; + uwsgi_log("initializing Perl environment: %s\n", args); - fprintf(stderr,"initializing Perl environment: %s\n", args); - + PERL_SYS_INIT3(&argc, &argv, &environ); my_perl = perl_alloc(); if (!my_perl) { - fprintf(stderr,"unable to allocate perl interpreter\n"); + uwsgi_log("unable to allocate perl interpreter\n"); return -1; } - pargs[0] = "" ; - pargs[1] = args ; - + PL_perl_destruct_level = 1; perl_construct(my_perl); // filling http status codes @@ -106,31 +107,32 @@ int uwsgi_init(struct uwsgi_server *uwsgi, char *args){ } - perl_parse(my_perl, xs_init, 3, embedding, NULL); + PL_origalen = 1; + perl_parse(my_perl, xs_init, 4, embedding, NULL); perl_eval_pv("use IO::Handle;", 0); fd = open(args, O_RDONLY); if (fd < 0) { - perror("open()"); + uwsgi_error("open()"); goto clear ; } if (fstat(fd, &stat_psgi)) { - perror("fstat()"); + uwsgi_error("fstat()"); close(fd); goto clear; } psgibuffer = malloc(stat_psgi.st_size); if (!psgibuffer) { - perror("malloc()"); + uwsgi_error("malloc()"); close(fd); goto clear; } if (read(fd, psgibuffer, stat_psgi.st_size) != stat_psgi.st_size) { - perror("read()"); + uwsgi_error("read()"); close(fd); free(psgibuffer); goto clear; @@ -139,8 +141,9 @@ int uwsgi_init(struct uwsgi_server *uwsgi, char *args){ psgibuffer[stat_psgi.st_size] = 0 ; psgi_func = perl_eval_pv(psgibuffer, 0); + if (!psgi_func) { - fprintf(stderr,"unable to find PSGI function entry point.\n"); + uwsgi_log("unable to find PSGI function entry point.\n"); close(fd); free(psgibuffer); goto clear; @@ -175,13 +178,13 @@ int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { /* Standard PSGI request */ if (!wsgi_req->uh.pktsize) { - fprintf (stderr, "Invalid PSGI request. skip.\n"); + uwsgi_log("Invalid PSGI request. skip.\n"); return -1; } if (uwsgi_parse_vars(uwsgi, wsgi_req)) { - fprintf(stderr,"Invalid PSGI request. skip.\n"); + uwsgi_log("Invalid PSGI request. skip.\n"); return -1; } @@ -259,7 +262,6 @@ int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { // dereference output response = (AV *) SvRV( sv_2mortal(newSVsv(POPs)) ) ; - status_code = av_fetch(response, 0, 0); wsgi_req->hvec[0].iov_base = "HTTP/1.1 "; @@ -320,7 +322,7 @@ int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { if ( (wsgi_req->response_size = writev(wsgi_req->poll.fd, wsgi_req->hvec, vi+1)) < 0) { - perror("writev()"); + uwsgi_error("writev()"); } @@ -328,8 +330,7 @@ int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { io = *hitem; - - if (SvTYPE(SvRV(io)) == SVt_PVGV) { + if (SvTYPE(SvRV(io)) == SVt_PVGV || SvTYPE(SvRV(io)) == SVt_PVHV) { for(;;) { @@ -360,6 +361,9 @@ int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { } } + else { + uwsgi_log("unsupported response body type: %d\n", SvTYPE(SvRV(io))); + } FREETMPS; LEAVE; diff --git a/psgi.py b/psgi.py index aa249b4b..dd221e60 100644 --- a/psgi.py +++ b/psgi.py @@ -1,3 +1,8 @@ import uwsgi +import sys -uwsgi.load_plugin(0, "plugins/psgi/psgi_plugin.so", "test.psgi") +if uwsgi.load_plugin(0, "plugins/psgi/psgi_plugin.so", "mojoapp.pl"): + print "PSGI plugin loaded" +else: + print "unable to load PSGI plugin" + sys.exit(1) diff --git a/simple_app.py b/simple_app.py index ca4cd095..cdab876f 100644 --- a/simple_app.py +++ b/simple_app.py @@ -8,6 +8,11 @@ def ciao(): def ciao2(): print "nuovo uwsgi_server" +#if uwsgi.load_plugin(0, 'plugins/example/example_plugin.so', 'ciao'): +# print "example plugin loaded" +#else: +# print "unable to load example plugin" + #uwsgi.event_add(uwsgi.EVENT_FILE, "/tmp", ciao) #uwsgi.event_add(uwsgi.EVENT_DNSSD, "_uwsgi._tcp", ciao2) #uwsgi.event_add(uwsgi.EVENT_TIMER, 1000, ciao2) diff --git a/test.psgi b/test.psgi index 9698f23d..b5b42d3b 100644 --- a/test.psgi +++ b/test.psgi @@ -9,4 +9,3 @@ my $app = sub { [ "Hello World\r\n", $env->{'REQUEST_URI'} ], ]; }; - diff --git a/uwsgi.c b/uwsgi.c index bf470ad6..6c3eddff 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -378,6 +378,9 @@ int main(int argc, char *argv[], char *envp[]) { {"ugreen", no_argument, &uwsgi.ugreen, 1}, {"ugreen-stacksize", required_argument, 0, LONG_ARGS_UGREEN_PAGES}, #endif + UWSGI_PLUGIN_LONGOPT_PSGI + UWSGI_PLUGIN_LONGOPT_LUA + UWSGI_PLUGIN_LONGOPT_RACK {"logto", required_argument, 0, LONG_ARGS_LOGTO}, {"grunt", no_argument, &uwsgi.grunt, 1}, {"no-site", no_argument, &Py_NoSiteFlag, 1}, @@ -481,6 +484,10 @@ int main(int argc, char *argv[], char *envp[]) { } #endif +#ifdef UWSGI_DEBUG + uwsgi_log("***\n*** You are running a DEBUG version of uWSGI, plese disable DEBUG in uwsgiconfig.py and recompile it ***\n***\n"); +#endif + uwsgi_log("compiled with version: %s\n", __VERSION__); #ifdef __BIG_ENDIAN__ @@ -909,6 +916,10 @@ int main(int argc, char *argv[], char *envp[]) { uwsgi_log( "done.\n"); +#ifdef UWSGI_EMBED_PLUGINS + embed_plugins(&uwsgi); +#endif + #ifdef UWSGI_ERLANG if (uwsgi.erlang_node) { uwsgi.erlang_nodes = 1; @@ -951,7 +962,6 @@ int main(int argc, char *argv[], char *envp[]) { } - #ifndef UNBIT if (no_server) { uwsgi_log( "no-server mode requested. Goodbye.\n"); @@ -2214,7 +2224,6 @@ void uwsgi_wsgi_config(char *filename) { } } - wsgi_dict = PyModule_GetDict(wsgi_module); if (!wsgi_dict) { PyErr_Print(); @@ -2783,6 +2792,9 @@ void manage_opt(int i, char *optarg) { case 'i': uwsgi.single_interpreter = 1; break; + LONG_ARGS_PLUGIN_EMBED_PSGI + LONG_ARGS_PLUGIN_EMBED_LUA + LONG_ARGS_PLUGIN_EMBED_RACK #ifndef UNBIT case 'h': fprintf(stdout, "Usage: %s [options...]\n\ diff --git a/uwsgi.h b/uwsgi.h index f2a243d1..f55474b5 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -33,6 +33,10 @@ #include #endif +#ifndef UWSGI_PLUGIN_BASE +#define UWSGI_PLUGIN_BASE "" +#endif + #include #include #include @@ -556,6 +560,22 @@ struct uwsgi_server { #endif int no_orphans; + +#ifdef UWSGI_EMBED_PLUGINS + +#ifdef UWSGI_EMBED_PLUGIN_PSGI + char *plugin_arg_psgi; +#endif + +#ifdef UWSGI_EMBED_PLUGIN_LUA + char *plugin_arg_lua; +#endif + +#ifdef UWSGI_EMBED_PLUGIN_RACK + char *plugin_arg_rack; +#endif + +#endif }; struct uwsgi_cluster_node { @@ -880,3 +900,57 @@ void uwsgi_log(const char *, ...); #define EVDIS_TYPE_FILE #define EVDIS_TYPE_DNSSD #endif + +int uwsgi_load_plugin(struct uwsgi_server *, int, char *, char *, int); +void embed_plugins(struct uwsgi_server *); + + +// PLUGINS + +#define UWSGI_PLUGIN_LONGOPT_PSGI +#define UWSGI_PLUGIN_LONGOPT_LUA +#define UWSGI_PLUGIN_LONGOPT_RACK +#define LONG_ARGS_PLUGIN_EMBED_PSGI +#define LONG_ARGS_PLUGIN_EMBED_LUA +#define LONG_ARGS_PLUGIN_EMBED_RACK + + + +#ifdef UWSGI_EMBED_PLUGINS + + +#ifdef UWSGI_EMBED_PLUGIN_PSGI + +#undef UWSGI_PLUGIN_LONGOPT_PSGI +#define UWSGI_PLUGIN_LONGOPT_PSGI {"psgi", required_argument, 0, 30005}, + +#undef LONG_ARGS_PLUGIN_EMBED_PSGI +#define LONG_ARGS_PLUGIN_EMBED_PSGI case 30005:\ + uwsgi.plugin_arg_psgi = optarg;\ + break; +#endif + +#ifdef UWSGI_EMBED_PLUGIN_LUA + +#undef UWSGI_PLUGIN_LONGOPT_LUA +#define UWSGI_PLUGIN_LONGOPT_LUA {"lua", required_argument, 0, 30006}, + +#undef LONG_ARGS_PLUGIN_EMBED_LUA +#define LONG_ARGS_PLUGIN_EMBED_LUA case 30006:\ + uwsgi.plugin_arg_lua = optarg;\ + break; +#endif + +#ifdef UWSGI_EMBED_PLUGIN_RACK + +#undef UWSGI_PLUGIN_LONGOPT_RACK +#define UWSGI_PLUGIN_LONGOPT_RACK {"rack", required_argument, 0, 30007}, + +#undef LONG_ARGS_PLUGIN_EMBED_RACK +#define LONG_ARGS_PLUGIN_EMBED_RACK case 30007:\ + uwsgi.plugin_arg_rack = optarg;\ + break; +#endif + + +#endif diff --git a/uwsgi_pymodule.c b/uwsgi_pymodule.c index 43af9b05..40c01a69 100644 --- a/uwsgi_pymodule.c +++ b/uwsgi_pymodule.c @@ -583,55 +583,21 @@ PyObject *py_uwsgi_set_option(PyObject * self, PyObject * args) { } PyObject *py_uwsgi_load_plugin(PyObject * self, PyObject * args) { - uint8_t modifier; + int modifier; char *plugin_name = NULL; char *pargs = NULL; - void *plugin_handle; - int (*plugin_init) (struct uwsgi_server *, char *); - int (*plugin_request) (struct uwsgi_server *, struct wsgi_request *); - void (*plugin_after_request) (struct uwsgi_server *, struct wsgi_request *); - if (!PyArg_ParseTuple(args, "is|s:load_plugin", &modifier, &plugin_name, &pargs)) { return NULL; } - plugin_handle = dlopen(plugin_name, RTLD_NOW | RTLD_GLOBAL); - if (!plugin_handle) { - uwsgi_log( "%s\n", dlerror()); - } - else { - plugin_init = dlsym(plugin_handle, "uwsgi_init"); - if (plugin_init) { - if ((*plugin_init) (&uwsgi, pargs)) { - uwsgi_log( "plugin initialization returned error\n"); - if (dlclose(plugin_handle)) { - uwsgi_log( "unable to unload plugin\n"); - } - - Py_INCREF(Py_None); - return Py_None; - } - } - - plugin_request = dlsym(plugin_handle, "uwsgi_request"); - if (plugin_request) { - uwsgi.shared->hooks[modifier] = plugin_request; - plugin_after_request = dlsym(plugin_handle, "uwsgi_after_request"); - if (plugin_after_request) { - uwsgi.shared->after_hooks[modifier] = plugin_after_request; - } - Py_INCREF(Py_True); - return Py_True; - - } - else { - uwsgi_log( "%s\n", dlerror()); - } + if (uwsgi_load_plugin(&uwsgi, modifier, plugin_name, pargs, 1)) { + Py_INCREF(Py_None); + return Py_None; } - Py_INCREF(Py_None); - return Py_None; + Py_INCREF(Py_True); + return Py_True; } #ifdef UWSGI_MULTICAST diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 26f4c8c9..28368477 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -20,11 +20,15 @@ UGREEN=True EVDIS=True WSGI2=True STACKLESS=False +#PLUGINS = ['psgi'] PLUGINS = [] USWALLOW=False UNBIT=False DEBUG=True +EMBED_PLUGINS=True UWSGI_BIN_NAME = 'uwsgi' +UWSGI_PLUGIN_DIR = '.' + # specific compilation flags # libxml2 or expat @@ -55,7 +59,7 @@ GCC = os.environ.get('CC', sysconfig.get_config_var('CC')) if not GCC: GCC = 'gcc' -gcc_list = ['utils', 'pyutils', 'protocol', 'socket', 'logging', 'wsgi_handlers', 'wsgi_headers', 'uwsgi_handlers', 'uwsgi'] +gcc_list = ['utils', 'pyutils', 'protocol', 'socket', 'logging', 'wsgi_handlers', 'wsgi_headers', 'uwsgi_handlers', 'plugins', 'uwsgi'] # large file support try: @@ -107,9 +111,10 @@ def build_uwsgi(bin_name): sys.exit(1) if len(PLUGINS) > 0: - print("*** uWSGI embedding plugin ***") + print("*** uWSGI building plugins ***") for plugin in PLUGINS: - print(plugin) + print("*** building plugin: %s ***" % plugin) + build_plugin("plugins/%s" % plugin) print("*** uWSGI linking ***") ldline = "%s -o %s %s %s %s" % (GCC, bin_name, ' '.join(ldflags), ' '.join(map(add_o, gcc_list)), ' '.join(libs)) @@ -267,6 +272,14 @@ def parse_vars(): cflags.append(ERLANG_CFLAGS) gcc_list.append('erlang') + if UWSGI_PLUGIN_DIR is not None: + cflags.append("-DUWSGI_PLUGIN_DIR=\\\"%s\\\"" % UWSGI_PLUGIN_DIR) + + if len(PLUGINS) > 0 and EMBED_PLUGINS: + cflags.append("-DUWSGI_EMBED_PLUGINS") + for plugin in PLUGINS: + cflags.append("-DUWSGI_EMBED_PLUGIN_%s" % plugin.upper()) + if SCTP: libs.append("-lsctp") cflags.append("-DUWSGI_SCTP") @@ -281,6 +294,7 @@ def parse_vars(): if DEBUG: cflags.append("-DUWSGI_DEBUG") + cflags.append("-g") if UNBIT: cflags.append("-DUWSGI_UNBIT") @@ -291,14 +305,24 @@ def build_plugin(path): sys.path.insert(0, path) import uwsgiplugin as up - cflags.append(up.CFLAGS) - libs.append(up.LDFLAGS) + p_cflags = cflags[:] + p_libs = libs[:] + p_ldflags = ldflags[:] - cflags.insert(0, '-I.') + p_cflags.append(up.CFLAGS) + p_libs.append(up.LDFLAGS) + + p_cflags.insert(0, '-I.') plugin_base = path + '/' + up.NAME + '_plugin' + plugin_dest = UWSGI_PLUGIN_DIR + '/' + up.NAME + '_plugin' - gccline = "%s -fPIC -shared -o %s.so %s %s %s.c %s" % (GCC, plugin_base, ' '.join(cflags), ' '.join(ldflags), plugin_base, ' '.join(libs)) + shared_flag = '-shared' + + if uwsgi_os == 'Darwin': + shared_flag = '-dynamiclib -undefined dynamic_lookup' + + gccline = "%s -fPIC %s -o %s.so %s %s %s.c %s" % (GCC, shared_flag, plugin_dest, ' '.join(p_cflags), ' '.join(p_ldflags), plugin_base, ' '.join(p_libs)) print(gccline) ret = os.system(gccline) @@ -306,7 +330,7 @@ def build_plugin(path): print("*** unable to build %s plugin ***" % up.NAME) sys.exit(1) - print("*** %s plugin built and available in %s ***" % (up.NAME, plugin_base + '.so')) + print("*** %s plugin built and available in %s ***" % (up.NAME, plugin_dest + '.so'))