mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-08-29 12:43:56 +00:00
547 lines
13 KiB
C
547 lines
13 KiB
C
#include "psgi.h"
|
|
|
|
extern char **environ;
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
#ifdef __APPLE__
|
|
extern struct uwsgi_perl uperl;
|
|
#else
|
|
struct uwsgi_perl uperl;
|
|
#endif
|
|
|
|
struct uwsgi_option uwsgi_perl_options[] = {
|
|
|
|
{"psgi", required_argument, 0, "load a psgi app", uwsgi_opt_set_str, &uperl.psgi, 0},
|
|
{"perl-local-lib", required_argument, 0, "set perl locallib path", uwsgi_opt_set_str, &uperl.locallib, 0},
|
|
{0, 0, 0, 0, 0, 0, 0},
|
|
|
|
};
|
|
|
|
extern struct http_status_codes hsc[];
|
|
|
|
SV *uwsgi_perl_obj_new(char *class, size_t class_len) {
|
|
|
|
SV *newobj;
|
|
|
|
dSP;
|
|
|
|
ENTER;
|
|
SAVETMPS;
|
|
PUSHMARK(SP);
|
|
XPUSHs(sv_2mortal(newSVpv( class, class_len)));
|
|
PUTBACK;
|
|
|
|
call_method( "new", G_SCALAR);
|
|
|
|
SPAGAIN;
|
|
|
|
newobj = POPs;
|
|
PUTBACK;
|
|
FREETMPS;
|
|
LEAVE;
|
|
|
|
return newobj;
|
|
|
|
}
|
|
|
|
SV *uwsgi_perl_call_stream(SV *func) {
|
|
|
|
SV *ret = NULL;
|
|
struct wsgi_request *wsgi_req = current_wsgi_req();
|
|
struct uwsgi_app *wi = &uwsgi_apps[wsgi_req->app_id];
|
|
|
|
dSP;
|
|
ENTER;
|
|
SAVETMPS;
|
|
PUSHMARK(SP);
|
|
XPUSHs( sv_2mortal(newRV((SV*) ((SV **)wi->responder0)[wsgi_req->async_id])));
|
|
PUTBACK;
|
|
|
|
call_sv( func, G_SCALAR | G_EVAL);
|
|
|
|
SPAGAIN;
|
|
if(SvTRUE(ERRSV)) {
|
|
uwsgi_log("[uwsgi-perl error] %s\n", SvPV_nolen(ERRSV));
|
|
}
|
|
else {
|
|
ret = SvREFCNT_inc(POPs);
|
|
}
|
|
|
|
PUTBACK;
|
|
FREETMPS;
|
|
LEAVE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int uwsgi_perl_obj_can(SV *obj, char *method, size_t len) {
|
|
|
|
int ret;
|
|
|
|
dSP;
|
|
|
|
ENTER;
|
|
SAVETMPS;
|
|
PUSHMARK(SP);
|
|
XPUSHs(obj);
|
|
XPUSHs(sv_2mortal(newSVpv(method, len)));
|
|
PUTBACK;
|
|
|
|
call_method( "can", G_SCALAR);
|
|
|
|
SPAGAIN;
|
|
|
|
ret = SvROK(POPs);
|
|
PUTBACK;
|
|
FREETMPS;
|
|
LEAVE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
SV *uwsgi_perl_obj_call(SV *obj, char *method) {
|
|
|
|
SV *ret = NULL;
|
|
|
|
dSP;
|
|
|
|
ENTER;
|
|
SAVETMPS;
|
|
PUSHMARK(SP);
|
|
|
|
XPUSHs(obj);
|
|
|
|
PUTBACK;
|
|
|
|
call_method( method, G_SCALAR | G_EVAL);
|
|
|
|
SPAGAIN;
|
|
if(SvTRUE(ERRSV)) {
|
|
uwsgi_log("%s\n", SvPV_nolen(ERRSV));
|
|
}
|
|
else {
|
|
ret = SvREFCNT_inc(POPs);
|
|
}
|
|
|
|
PUTBACK;
|
|
FREETMPS;
|
|
LEAVE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
AV *psgi_call(struct wsgi_request *wsgi_req, SV *psgi_func, SV *env) {
|
|
|
|
AV *ret = NULL;
|
|
|
|
dSP;
|
|
|
|
ENTER;
|
|
SAVETMPS;
|
|
PUSHMARK(SP);
|
|
XPUSHs(env);
|
|
PUTBACK;
|
|
|
|
call_sv(psgi_func, G_SCALAR | G_EVAL);
|
|
|
|
SPAGAIN;
|
|
|
|
if(SvTRUE(ERRSV)) {
|
|
internal_server_error(wsgi_req, "exception raised");
|
|
uwsgi_log("[uwsgi-perl error] %s\n", SvPV_nolen(ERRSV));
|
|
}
|
|
else {
|
|
ret = (AV *) SvREFCNT_inc(SvRV(POPs));
|
|
}
|
|
|
|
PUTBACK;
|
|
FREETMPS;
|
|
LEAVE;
|
|
|
|
return (AV *)ret;
|
|
|
|
}
|
|
|
|
SV *build_psgi_env(struct wsgi_request *wsgi_req) {
|
|
int i;
|
|
HV *env = newHV();
|
|
|
|
// fill perl hash
|
|
for(i=0;i<wsgi_req->var_cnt;i++) {
|
|
if (wsgi_req->hvec[i+1].iov_len > 0) {
|
|
|
|
// check for multiline header
|
|
if (hv_exists(env, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len)) {
|
|
SV **already_avalable_header = hv_fetch(env, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, 0);
|
|
STRLEN hlen;
|
|
char *old_value = SvPV(*already_avalable_header, hlen );
|
|
char *multiline_header = uwsgi_concat3n(old_value, hlen, ", ", 2, wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len);
|
|
if (!hv_store(env, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len,
|
|
newSVpv(multiline_header, hlen+wsgi_req->hvec[i+1].iov_len+2), 0)) { free(multiline_header); goto clear;}
|
|
free(multiline_header);
|
|
|
|
|
|
}
|
|
else {
|
|
if (!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)) goto clear;
|
|
}
|
|
}
|
|
else {
|
|
if (!hv_store(env, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, newSVpv("", 0), 0)) goto clear;
|
|
}
|
|
//uwsgi_log("%.*s = %.*s\n", wsgi_req->hvec[i].iov_len, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i+1].iov_len, wsgi_req->hvec[i+1].iov_base);
|
|
i++;
|
|
}
|
|
|
|
// psgi.version
|
|
AV *av = newAV();
|
|
av_store( av, 0, newSViv(1));
|
|
av_store( av, 1, newSViv(1));
|
|
if (!hv_store(env, "psgi.version", 12, newRV_noinc((SV *)av ), 0)) goto clear;
|
|
|
|
if (uwsgi.numproc > 1) {
|
|
if (!hv_store(env, "psgi.multiprocess", 17, newSViv(1), 0)) goto clear;
|
|
}
|
|
else {
|
|
if (!hv_store(env, "psgi.multiprocess", 17, newSViv(0), 0)) goto clear;
|
|
}
|
|
|
|
if (uwsgi.threads > 1) {
|
|
if (!hv_store(env, "psgi.multithread", 16, newSViv(1), 0)) goto clear;
|
|
}
|
|
else {
|
|
if (!hv_store(env, "psgi.multithread", 16, newSViv(0), 0)) goto clear;
|
|
}
|
|
|
|
if (!hv_store(env, "psgi.run_once", 13, newSViv(0), 0)) goto clear;
|
|
|
|
#ifdef UWSGI_ASYNC
|
|
if (uwsgi.async > 1) {
|
|
if (!hv_store(env, "psgi.nonblocking", 16, newSViv(1), 0)) goto clear;
|
|
}
|
|
else {
|
|
#else
|
|
if (!hv_store(env, "psgi.nonblocking", 16, newSViv(0), 0)) goto clear;
|
|
#endif
|
|
|
|
#ifdef UWSGI_ASYNC
|
|
}
|
|
#endif
|
|
|
|
if (!hv_store(env, "psgi.streaming", 14, newSViv(1), 0)) goto clear;
|
|
|
|
SV *us;
|
|
// psgi.url_scheme, honour HTTPS var or UWSGI_SCHEME
|
|
if (wsgi_req->scheme_len > 0) {
|
|
us = newSVpv(wsgi_req->scheme, wsgi_req->scheme_len);
|
|
}
|
|
else if (wsgi_req->https_len > 0) {
|
|
if (!strncasecmp(wsgi_req->https, "on", 2) || wsgi_req->https[0] == '1') {
|
|
us = newSVpv("https", 5);
|
|
}
|
|
else {
|
|
us = newSVpv("http", 4);
|
|
}
|
|
}
|
|
else {
|
|
us = newSVpv("http", 4);
|
|
}
|
|
|
|
if (!hv_store(env, "psgi.url_scheme", 15, us, 0)) goto clear;
|
|
|
|
|
|
SV *pi = uwsgi_perl_obj_new("uwsgi::input", 12);
|
|
if (!hv_store(env, "psgi.input", 10, pi, 0)) goto clear;
|
|
|
|
if (!hv_store(env, "psgix.input.buffered", 20, newSViv(wsgi_req->body_as_file), 0)) goto clear;
|
|
|
|
if (uwsgi.master_process) {
|
|
if (!hv_store(env, "psgix.harakiri", 14, newSViv(1), 0)) goto clear;
|
|
}
|
|
|
|
SV *pe = uwsgi_perl_obj_new("uwsgi::error", 12);
|
|
if (!hv_store(env, "psgi.errors", 11, pe, 0)) goto clear;
|
|
|
|
return newRV_noinc((SV *)env);
|
|
|
|
clear:
|
|
SvREFCNT_dec((SV *)env);
|
|
return NULL;
|
|
}
|
|
|
|
int uwsgi_perl_init(){
|
|
|
|
|
|
struct http_status_codes *http_sc;
|
|
|
|
int argc;
|
|
int i;
|
|
|
|
uperl.embedding[0] = "";
|
|
uperl.embedding[1] = "-e";
|
|
uperl.embedding[2] = "0";
|
|
|
|
#ifndef USE_ITHREADS
|
|
if (uwsgi.threads > 1) {
|
|
uwsgi_log("your Perl environment does not support threads\n");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
if (setenv("PLACK_ENV", "uwsgi", 0)) {
|
|
uwsgi_error("setenv()");
|
|
}
|
|
|
|
if (setenv("PLACK_SERVER", "uwsgi", 0)) {
|
|
uwsgi_error("setenv()");
|
|
}
|
|
|
|
argc = 3;
|
|
|
|
PERL_SYS_INIT3(&argc, (char ***) &uperl.embedding, &environ);
|
|
|
|
uperl.main = uwsgi_calloc(sizeof(PerlInterpreter *) * uwsgi.threads);
|
|
|
|
uperl.main[0] = uwsgi_perl_new_interpreter();
|
|
if (!uperl.main[0]) {
|
|
return -1;
|
|
}
|
|
|
|
for(i=1;i<uwsgi.threads;i++) {
|
|
uperl.main[i] = uwsgi_perl_new_interpreter();
|
|
if (!uperl.main[i]) {
|
|
uwsgi_log("unable to create new perl interpreter for thread %d\n", i+1);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
PERL_SET_CONTEXT(uperl.main[0]);
|
|
|
|
// filling http status codes
|
|
for (http_sc = hsc; http_sc->message != NULL; http_sc++) {
|
|
http_sc->message_size = strlen(http_sc->message);
|
|
}
|
|
|
|
#ifdef PERL_VERSION_STRING
|
|
uwsgi_log("initialized Perl %s main interpreter at %p\n", PERL_VERSION_STRING, uperl.main[0]);
|
|
#else
|
|
uwsgi_log("initialized Perl main interpreter at %p\n", uperl.main[0]);
|
|
#endif
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
int uwsgi_perl_request(struct wsgi_request *wsgi_req) {
|
|
|
|
SV **harakiri;
|
|
|
|
#ifdef UWSGI_ASYNC
|
|
if (wsgi_req->async_status == UWSGI_AGAIN) {
|
|
return psgi_response(wsgi_req, wsgi_req->async_placeholder);
|
|
}
|
|
#endif
|
|
/* Standard PSGI request */
|
|
if (!wsgi_req->uh.pktsize) {
|
|
uwsgi_log("Invalid PSGI request. skip.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (uwsgi_parse_vars(wsgi_req)) {
|
|
return -1;
|
|
}
|
|
|
|
wsgi_req->app_id = uwsgi_get_app_id(wsgi_req->appid, wsgi_req->appid_len, 5);
|
|
// if it is -1, try to load a dynamic app
|
|
if (wsgi_req->app_id == -1) {
|
|
if (wsgi_req->dynamic) {
|
|
if (uwsgi.threads > 1) {
|
|
pthread_mutex_lock(&uperl.lock_loader);
|
|
}
|
|
|
|
if (wsgi_req->script_len > 0) {
|
|
wsgi_req->app_id = init_psgi_app(wsgi_req, wsgi_req->script, wsgi_req->script_len, NULL);
|
|
}
|
|
else if (wsgi_req->file_len > 0) {
|
|
wsgi_req->app_id = init_psgi_app(wsgi_req, wsgi_req->file, wsgi_req->file_len, NULL);
|
|
}
|
|
|
|
if (uwsgi.threads > 1) {
|
|
pthread_mutex_unlock(&uperl.lock_loader);
|
|
}
|
|
}
|
|
|
|
if (wsgi_req->app_id == -1) {
|
|
internal_server_error(wsgi_req, "Perl application not found");
|
|
// nothing to clear/free
|
|
return UWSGI_OK;
|
|
}
|
|
}
|
|
|
|
struct uwsgi_app *wi = &uwsgi_apps[wsgi_req->app_id];
|
|
wi->requests++;
|
|
|
|
if (((PerlInterpreter **)wi->interpreter)[wsgi_req->async_id] != uperl.main[wsgi_req->async_id]) {
|
|
PERL_SET_CONTEXT(((PerlInterpreter **)wi->interpreter)[wsgi_req->async_id]);
|
|
}
|
|
|
|
ENTER;
|
|
SAVETMPS;
|
|
|
|
wsgi_req->async_environ = build_psgi_env(wsgi_req);
|
|
if (!wsgi_req->async_environ) goto clear;
|
|
|
|
wsgi_req->async_result = psgi_call(wsgi_req, ((SV **)wi->callable)[wsgi_req->async_id], wsgi_req->async_environ);
|
|
if (!wsgi_req->async_result) goto clear;
|
|
|
|
if (SvTYPE((AV *)wsgi_req->async_result) == SVt_PVCV) {
|
|
SV *stream_result = uwsgi_perl_call_stream((SV*)wsgi_req->async_result);
|
|
if (!stream_result) {
|
|
internal_server_error(wsgi_req, "exception raised");
|
|
}
|
|
else {
|
|
SvREFCNT_dec(stream_result);
|
|
}
|
|
goto clear2;
|
|
}
|
|
|
|
while (psgi_response(wsgi_req, wsgi_req->async_result) != UWSGI_OK) {
|
|
#ifdef UWSGI_ASYNC
|
|
if (uwsgi.async > 1) {
|
|
FREETMPS;
|
|
LEAVE;
|
|
return UWSGI_AGAIN;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
clear2:
|
|
// check for psgix.harakiri
|
|
harakiri = hv_fetch((HV*)SvRV( (SV*)wsgi_req->async_environ), "psgix.harakiri.commit", 21, 0);
|
|
if (harakiri) {
|
|
if (SvTRUE(*harakiri)) wsgi_req->async_plagued = 1;
|
|
}
|
|
|
|
SvREFCNT_dec(wsgi_req->async_environ);
|
|
SvREFCNT_dec(wsgi_req->async_result);
|
|
clear:
|
|
|
|
FREETMPS;
|
|
LEAVE;
|
|
|
|
// restore main interpreter if needed
|
|
if (((PerlInterpreter **)wi->interpreter)[wsgi_req->async_id] != uperl.main[wsgi_req->async_id]) {
|
|
PERL_SET_CONTEXT(uperl.main[wsgi_req->async_id]);
|
|
}
|
|
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
|
|
void uwsgi_perl_after_request(struct wsgi_request *wsgi_req) {
|
|
|
|
if (uwsgi.shared->options[UWSGI_OPTION_LOGGING])
|
|
log_request(wsgi_req);
|
|
|
|
if (wsgi_req->async_plagued) {
|
|
uwsgi_log("*** psgix.harakiri.commit requested ***\n");
|
|
goodbye_cruel_world();
|
|
}
|
|
|
|
}
|
|
|
|
int uwsgi_perl_magic(char *mountpoint, char *lazy) {
|
|
|
|
if (!strcmp(lazy+strlen(lazy)-5, ".psgi")) {
|
|
uperl.psgi = lazy;
|
|
return 1;
|
|
}
|
|
else if (!strcmp(lazy+strlen(lazy)-3, ".pl")) {
|
|
uperl.psgi = lazy;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// taken from Torsten Foertsch AfterFork.xs
|
|
void uwsgi_perl_post_fork() {
|
|
/*
|
|
GV *tmpgv;
|
|
|
|
PL_ppid = (IV)getppid();
|
|
hv_clear(PL_pidstatus);
|
|
|
|
tmpgv = gv_fetchpv("$", TRUE, SVt_PV);
|
|
if (tmpgv) {
|
|
SvREADONLY_off(GvSV(tmpgv));
|
|
sv_setiv(GvSV(tmpgv), (IV)getpid());
|
|
SvREADONLY_on(GvSV(tmpgv));
|
|
}
|
|
*/
|
|
}
|
|
|
|
int uwsgi_perl_mount_app(char *mountpoint, char *app, int regexp) {
|
|
|
|
if (uwsgi_endswith(app, ".pl") || uwsgi_endswith(app, ".psgi")) {
|
|
uwsgi.wsgi_req->appid = mountpoint;
|
|
uwsgi.wsgi_req->appid_len = strlen(mountpoint);
|
|
|
|
return init_psgi_app(uwsgi.wsgi_req, app, strlen(app), NULL);
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
void uwsgi_perl_init_thread(int core_id) {
|
|
|
|
#ifdef USE_ITHREADS
|
|
PERL_SET_CONTEXT(uperl.main[core_id]);
|
|
#endif
|
|
}
|
|
|
|
void uwsgi_perl_pthread_prepare(void) {
|
|
pthread_mutex_lock(&uperl.lock_loader);
|
|
}
|
|
|
|
void uwsgi_perl_pthread_parent(void) {
|
|
pthread_mutex_unlock(&uperl.lock_loader);
|
|
}
|
|
|
|
void uwsgi_perl_pthread_child(void) {
|
|
pthread_mutex_init(&uperl.lock_loader, NULL);
|
|
}
|
|
|
|
|
|
void uwsgi_perl_enable_threads(void) {
|
|
#ifdef USE_ITHREADS
|
|
pthread_mutex_init(&uperl.lock_loader, NULL);
|
|
pthread_atfork(uwsgi_perl_pthread_prepare, uwsgi_perl_pthread_parent, uwsgi_perl_pthread_child);
|
|
#endif
|
|
}
|
|
|
|
struct uwsgi_plugin psgi_plugin = {
|
|
|
|
.name = "psgi",
|
|
.modifier1 = 5,
|
|
.init = uwsgi_perl_init,
|
|
.options = uwsgi_perl_options,
|
|
|
|
.init_apps = uwsgi_psgi_app,
|
|
.mount_app = uwsgi_perl_mount_app,
|
|
|
|
.init_thread = uwsgi_perl_init_thread,
|
|
|
|
.post_fork = uwsgi_perl_post_fork,
|
|
.request = uwsgi_perl_request,
|
|
.after_request = uwsgi_perl_after_request,
|
|
.enable_threads = uwsgi_perl_enable_threads,
|
|
|
|
.magic = uwsgi_perl_magic,
|
|
};
|