From 299fd9cf8aad0b862adfdb2c0a169518da9f390b Mon Sep 17 00:00:00 2001 From: "roberto@sirius" Date: Tue, 13 Apr 2010 08:39:49 +0200 Subject: [PATCH] introduced open PSGI plugin for uWSGI --- plugins/example/example_plugin.c | 8 +- plugins/lua/lua_plugin.c | 2 +- plugins/psgi/psgi_plugin.c | 375 +++++++++++++++++++++++++++++++ plugins/psgi/uwsgiplugin.py | 5 + test.psgi | 12 + 5 files changed, 396 insertions(+), 6 deletions(-) create mode 100644 plugins/psgi/psgi_plugin.c create mode 100644 plugins/psgi/uwsgiplugin.py create mode 100644 test.psgi diff --git a/plugins/example/example_plugin.c b/plugins/example/example_plugin.c index b8a1ffe7..a3140e8d 100644 --- a/plugins/example/example_plugin.c +++ b/plugins/example/example_plugin.c @@ -1,17 +1,15 @@ #include "../../uwsgi.h" -/* gcc `python-config --cflags` `python ../../uwsgiconfig.py --cflags` -o example_plugin.so -fPIC -shared example_plugin.c */ - int uwsgi_init(struct uwsgi_server *uwsgi, char *args){ fprintf(stderr,"i am the example plugin initialization function with arg: %s\n", args); return 0; } int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { - char *http = "HTTP/1.1 200 Ok\r\nContent-type: text/html\r\n\r\n

Hello World

" ; - wsgi_req->response_size += write(uwsgi->poll.fd, http, strlen(http)); - fprintf(stderr,"UWSGI POLL: %p\n", &uwsgi->poll); + char *http = "HTTP/1.1 200 Ok\r\nContent-type: text/html\r\n\r\n

Hello World

" ; + + wsgi_req->response_size += write(wsgi_req->poll.fd, http, strlen(http)); return 0; } diff --git a/plugins/lua/lua_plugin.c b/plugins/lua/lua_plugin.c index 77029971..ca8305a9 100644 --- a/plugins/lua/lua_plugin.c +++ b/plugins/lua/lua_plugin.c @@ -93,7 +93,7 @@ int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { char *ptrbuf; /* Standard WSAPI request */ - if (!wsgi_req->size) { + if (!wsgi_req->uh.pktsize) { fprintf (stderr, "Invalid WSAPI request. skip.\n"); return -1; } diff --git a/plugins/psgi/psgi_plugin.c b/plugins/psgi/psgi_plugin.c new file mode 100644 index 00000000..42e0c427 --- /dev/null +++ b/plugins/psgi/psgi_plugin.c @@ -0,0 +1,375 @@ +#include + +#include +#include + +static PerlInterpreter *my_perl; +static SV *psgi_func ; + +/* statistically ordered */ +static struct http_status_codes hsc[] = { + + {"200", "OK"}, + {"302", "Found"}, + {"404", "Not Found"}, + {"500", "Internal Server Error"}, + {"301", "Moved Permanently"}, + {"304", "Not Modified"}, + {"303", "See Other"}, + {"403", "Forbidden"}, + {"307", "Temporary Redirect"}, + {"401", "Unauthorized"}, + {"400", "Bad Request"}, + {"405", "Method Not Allowed"}, + {"408", "Request Timeout"}, + + {"100", "Continue"}, + {"101", "Switching Protocols"}, + {"201", "Created"}, + {"202", "Accepted"}, + {"203", "Non-Authoritative Information"}, + {"204", "No Content"}, + {"205", "Reset Content"}, + {"206", "Partial Content"}, + {"300", "Multiple Choices"}, + {"305", "Use Proxy"}, + {"402", "Payment Required"}, + {"406", "Not Acceptable"}, + {"407", "Proxy Authentication Required"}, + {"409", "Conflict"}, + {"410", "Gone"}, + {"411", "Length Required"}, + {"412", "Precondition Failed"}, + {"413", "Request Entity Too Large"}, + {"414", "Request-URI Too Long"}, + {"415", "Unsupported Media Type"}, + {"416", "Requested Range Not Satisfiable"}, + {"417", "Expectation Failed"}, + {"501", "Not Implemented"}, + {"502", "Bad Gateway"}, + {"503", "Service Unavailable"}, + {"504", "Gateway Timeout"}, + {"505", "HTTP Version Not Supported"}, + { "", NULL }, +}; + + + +/* automatically generated */ + +EXTERN_C void xs_init (pTHX); + +EXTERN_C void boot_DynaLoader (pTHX_ CV* cv); + +EXTERN_C void +xs_init(pTHX) +{ + char *file = __FILE__; + dXSUB_SYS; + + /* DynaLoader is a special case */ + newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); +} + +/* end of automagically generated part */ + + +int uwsgi_init(struct uwsgi_server *uwsgi, char *args){ + + char *pargs[2] ; + char *psgibuffer ; + int fd ; + + struct stat stat_psgi; + + struct http_status_codes *http_sc ; + + char *embedding[] = { "", "-e", "0" }; + + + fprintf(stderr,"initializing Perl environment: %s\n", args); + + my_perl = perl_alloc(); + if (!my_perl) { + fprintf(stderr,"unable to allocate perl interpreter\n"); + return -1; + } + + pargs[0] = "" ; + pargs[1] = args ; + + perl_construct(my_perl); + + // filling http status codes + for (http_sc = hsc; http_sc->message != NULL; http_sc++) { + http_sc->message_size = strlen(http_sc->message); + } + + + perl_parse(my_perl, xs_init, 3, embedding, NULL); + + perl_eval_pv("use IO::Handle;", 0); + + fd = open(args, O_RDONLY); + if (fd < 0) { + perror("open()"); + goto clear ; + } + + if (fstat(fd, &stat_psgi)) { + perror("fstat()"); + close(fd); + goto clear; + } + + psgibuffer = malloc(stat_psgi.st_size); + if (!psgibuffer) { + perror("malloc()"); + close(fd); + goto clear; + } + + if (read(fd, psgibuffer, stat_psgi.st_size) != stat_psgi.st_size) { + perror("read()"); + close(fd); + free(psgibuffer); + goto clear; + } + + 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"); + close(fd); + free(psgibuffer); + goto clear; + } + + free(psgibuffer); + close(fd); + + return 0; + +clear: + perl_destruct(my_perl); + perl_free(my_perl); + return -1; + +} + +int uwsgi_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { + + HV *env ; + SV **item ; + + AV *response, *headers, *body ; + + SV **status_code, **hitem, *io_new, *io_err, *io, *chunk; + char *chitem ; + STRLEN hlen ; + + struct http_status_codes *http_sc; + + int i,vi, base ; + + /* Standard PSGI request */ + if (!wsgi_req->uh.pktsize) { + fprintf (stderr, "Invalid PSGI request. skip.\n"); + return -1; + } + + + if (uwsgi_parse_vars(uwsgi, wsgi_req)) { + fprintf(stderr,"Invalid PSGI request. skip.\n"); + return -1; + } + + + dSP; + + ENTER; + SAVETMPS; + + + env = (HV*)sv_2mortal((SV*)newHV()); + + // fill perl hash + for(i=0;ivar_cnt;i++) { + if (wsgi_req->hvec[i+1].iov_len > 0) { + + item = hv_store(env, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, + newSVpv(wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len), 0); + } + else { + item = hv_store(env, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, newSVpv("", 0), 0); + } + i++; + } + + item = hv_store(env, "psgi.url_scheme", 15, newSVpv("http", 4), 0); + + + PUSHMARK(SP); + XPUSHs( sv_2mortal( newSVpv( "IO::Handle", 10 ))); + PUTBACK; + perl_call_method( "new", G_SCALAR); + SPAGAIN; + io_new = newSVsv(POPs); + + + PUSHMARK(SP); + XPUSHs( sv_2mortal(io_new) ); + XPUSHs( sv_2mortal( newSViv( wsgi_req->poll.fd))); + XPUSHs( sv_2mortal( newSVpv( "r", 1))); + PUTBACK; + perl_call_method( "fdopen", G_SCALAR); + SPAGAIN; + + + item = hv_store(env, "psgi.input", 10, newSVsv(POPs), 0); + + PUSHMARK(SP); + XPUSHs( sv_2mortal( newSVpv( "IO::Handle", 10 ))); + PUTBACK; + perl_call_method( "new", G_SCALAR); + SPAGAIN; + io_err = newSVsv(POPs); + + PUSHMARK(SP); + XPUSHs( sv_2mortal(io_err) ); + XPUSHs( sv_2mortal( newSViv( 2 ))); + XPUSHs( sv_2mortal( newSVpv( "w", 1))); + PUTBACK; + perl_call_method( "fdopen", G_SCALAR); + SPAGAIN; + + + item = hv_store(env, "psgi.errors", 11, newSVsv(POPs), 0); + + + PUSHMARK(SP); + XPUSHs( sv_2mortal(newRV((SV *)env )) ); + PUTBACK; + + + perl_call_sv(psgi_func, G_SCALAR); + SPAGAIN; + + // 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 "; + wsgi_req->hvec[0].iov_len = 9 ; + + wsgi_req->hvec[1].iov_base = SvPV(*status_code, hlen); + wsgi_req->hvec[1].iov_len = 3 ; + + wsgi_req->hvec[2].iov_base = " "; + wsgi_req->hvec[2].iov_len = 1 ; + + wsgi_req->hvec[3].iov_len = 0 ; + + // get the status code + for (http_sc = hsc; http_sc->message != NULL; http_sc++) { + if (!strncmp(http_sc->key, wsgi_req->hvec[1].iov_base, 3)) { + wsgi_req->hvec[3].iov_base = http_sc->message ; + wsgi_req->hvec[3].iov_len = http_sc->message_size ; + break; + } + } + + if (wsgi_req->hvec[3].iov_len == 0) { + wsgi_req->hvec[3].iov_base = "Unknown" ; + wsgi_req->hvec[3].iov_len = 7; + } + + wsgi_req->hvec[4].iov_base = "\r\n"; + wsgi_req->hvec[4].iov_len = 2 ; + + hitem = av_fetch(response, 1, 0) ; + + headers = (AV *) SvRV(*hitem); + + base = 5 ; + + // put them in hvec + for(i=0; i<=av_len(headers); i++) { + + vi = (i*2)+base ; + hitem = av_fetch(headers,i,0); + chitem = SvPV(*hitem, hlen); + wsgi_req->hvec[vi].iov_base = chitem ; wsgi_req->hvec[vi].iov_len = hlen ; + + wsgi_req->hvec[vi+1].iov_base = ": " ; wsgi_req->hvec[vi+1].iov_len = 2 ; + + hitem = av_fetch(headers,i+1,0); + chitem = SvPV(*hitem, hlen); + wsgi_req->hvec[vi+2].iov_base = chitem ; wsgi_req->hvec[vi+2].iov_len = hlen ; + + wsgi_req->hvec[vi+3].iov_base = "\r\n" ; wsgi_req->hvec[vi+3].iov_len = 2 ; + + i++; + } + + vi = (i*2)+base ; + wsgi_req->hvec[vi].iov_base = "\r\n" ; wsgi_req->hvec[vi].iov_len = 2 ; + + + if ( (wsgi_req->response_size = writev(wsgi_req->poll.fd, wsgi_req->hvec, vi+1)) < 0) { + perror("writev()"); + } + + + hitem = av_fetch(response, 2, 0) ; + + io = *hitem; + + + if (SvTYPE(SvRV(io)) == SVt_PVGV) { + + for(;;) { + + PUSHMARK(SP); + XPUSHs(io) ; + PUTBACK; + perl_call_method("getline", G_SCALAR); + SPAGAIN; + chunk = sv_2mortal(newSVsv(POPs)); + + chitem = SvPV(chunk, hlen); + if (hlen <= 0) { + break; + } + wsgi_req->response_size += write(wsgi_req->poll.fd, chitem, hlen); + } + + + } + else if (SvTYPE(SvRV(io)) == SVt_PVAV) { + + body = (AV *) SvRV(io); + + for(i=0; i<=av_len(body); i++) { + hitem = av_fetch(body,i,0); + chitem = SvPV(*hitem, hlen); + wsgi_req->response_size += write(wsgi_req->poll.fd, chitem, hlen); + } + + } + + FREETMPS; + LEAVE; + + return 0; +} + + +void uwsgi_after_request(struct uwsgi_server *uwsgi, struct wsgi_request *wsgi_req) { + + if (uwsgi->shared->options[UWSGI_OPTION_LOGGING]) + log_request(wsgi_req); +} diff --git a/plugins/psgi/uwsgiplugin.py b/plugins/psgi/uwsgiplugin.py new file mode 100644 index 00000000..40ed048c --- /dev/null +++ b/plugins/psgi/uwsgiplugin.py @@ -0,0 +1,5 @@ +import os,sys + +NAME='psgi' +CFLAGS = os.popen('perl -MExtUtils::Embed -e ccopts').read().rstrip() +LDFLAGS = os.popen('perl -MExtUtils::Embed -e ldopts').read().rstrip() diff --git a/test.psgi b/test.psgi new file mode 100644 index 00000000..9698f23d --- /dev/null +++ b/test.psgi @@ -0,0 +1,12 @@ +use strict; +use warnings; + +my $app = sub { + my $env = shift; + return [ + '200', + [ 'Content-Type' => 'text/plain' ], + [ "Hello World\r\n", $env->{'REQUEST_URI'} ], + ]; +}; +