Version 0.54 changes
[catagits/fcgi2.git] / perl / FCGI.PL
index be655fe..b148dcb 100644 (file)
@@ -12,13 +12,15 @@ unless ($] >= 5.005) {
 print OUT while <DATA>;
 close OUT;
 __END__
-/* $Id: FCGI.PL,v 1.6 1999/07/28 23:09:56 skimo Exp $ */
+/* $Id: FCGI.PL,v 1.15 2000/07/10 09:56:49 skimo Exp $ */
 
 #include "EXTERN.h"
 #include "perl.h"
 #include "XSUB.h"
 
+#include "fcgi_config.h"
 #include "fcgiapp.h"
+#include "fastcgi.h"
 
 #ifndef FALSE
 #define FALSE (0)
@@ -28,12 +30,10 @@ __END__
 #define TRUE  (1)
 #endif
 
-#ifdef WIN32
-#define environ _environ
+#ifndef dTHX
+#define dTHX
 #endif
 
-extern char **environ;
-
 #ifdef USE_SFIO
 typedef struct
 {
@@ -89,36 +89,38 @@ sfdcdelfcgi(disc)
 }
 #endif
 
-static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: FCGI */
-
-static FCGX_Request global_fcgx_request;
+#if defined(USE_LOCKING) && defined(USE_THREADS)
+static perl_mutex   accept_mutex;
+#endif
 
 typedef struct FCGP_Request {
-    int                    compat;
-    int                    acceptCalled;
-    int                    finishCalled;
+    int                    accepted;
+    int                    bound;
     SV*                    svin;
     SV*                    svout;
     SV*                    sverr;
     GV*                    gv[3];
     HV*                    hvEnv;
-    FCGX_Stream*    in;
-    FCGX_ParamArray envp;
     FCGX_Request*   requestPtr;
+#ifdef USE_SFIO
+    int                    sfcreated[3];
+    IO*                    io[3];
+#endif
 } FCGP_Request;
 
-static FCGP_Request global_request;
-static SV*         global_sverr;
+static void FCGI_Finish(FCGP_Request* request);
 
 static int 
 FCGI_Flush(FCGP_Request* request)
 {
-    if(!request->acceptCalled || isCGI) {
+    dTHX;
+
+    if(!request->bound) {
        return;
     }
 #ifdef USE_SFIO
-    sfsync(IoIFP(GvIOp(request->gv[1])));
-    sfsync(IoIFP(GvIOp(request->gv[2])));
+    sfsync(IoOFP(GvIOp(request->gv[1])));
+    sfsync(IoOFP(GvIOp(request->gv[2])));
 #else
     FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->svout)));
     FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->sverr)));
@@ -128,121 +130,190 @@ FCGI_Flush(FCGP_Request* request)
 static void
 FCGI_UndoBinding(FCGP_Request* request)
 {
+    dTHX;
+
 #ifdef USE_SFIO
-    sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[0])), SF_POPDISC));
-    sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[1])), SF_POPDISC));
-    sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[2])), SF_POPDISC));
+    sfdcdelfcgi(sfdisc(IoIFP(request->io[0]), SF_POPDISC));
+    sfdcdelfcgi(sfdisc(IoOFP(request->io[1]), SF_POPDISC));
+    sfdcdelfcgi(sfdisc(IoOFP(request->io[2]), SF_POPDISC));
 #else
-    FCGI_Flush(request);
     sv_unmagic((SV *)request->gv[0], 'q');
     sv_unmagic((SV *)request->gv[1], 'q');
     sv_unmagic((SV *)request->gv[2], 'q');
 #endif
+    request->bound = FALSE;
+}
+
+static void
+FCGI_Bind(FCGP_Request* request)
+{
+    dTHX;
+
+#ifdef USE_SFIO
+    sfdisc(IoIFP(request->io[0]), sfdcnewfcgi(request->requestPtr->in));
+    sfdisc(IoOFP(request->io[1]), sfdcnewfcgi(request->requestPtr->out));
+    sfdisc(IoOFP(request->io[2]), sfdcnewfcgi(request->requestPtr->err));
+#else
+    sv_magic((SV *)request->gv[1], request->svout, 'q', Nullch, 0);
+    sv_magic((SV *)request->gv[2], request->sverr, 'q', Nullch, 0);
+    sv_magic((SV *)request->gv[0], request->svin, 'q', Nullch, 0);
+#endif
+    request->bound = TRUE;
+}
+
+static void
+populate_env(envp, hv)
+char **envp;
+HV *hv;
+{
+    int i;
+    char *p, *p1;
+    SV   *sv;
+    dTHX;
+
+    hv_clear(hv);
+    for(i = 0; ; i++) {
+       if((p = envp[i]) == NULL) {
+           break;
+       }
+       p1 = strchr(p, '=');
+       assert(p1 != NULL);
+       sv = newSVpv(p1 + 1, 0);
+       /* call magic for this value ourselves */
+       hv_store(hv, p, p1 - p, sv, 0);
+       SvSETMAGIC(sv);
+    }
 }
 
 static int 
-FCGI_Accept(FCGP_Request* request, GV **gvp)
+FCGI_Accept(FCGP_Request* request)
 {
-    if(isCGI == -1) {
+    static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: FCGI */
+
+    int req_isCGI = 
+       request->requestPtr->listen_sock == FCGI_LISTENSOCK_FILENO ?
+       isCGI : 0;
+
+    dTHX;
+
+    if(req_isCGI == -1) {
         /*
          * First call to FCGI_Accept.  Is application running
          * as FastCGI or as CGI?
          */
-        isCGI = FCGX_IsCGI();
-    } else if(isCGI) {
+        req_isCGI = isCGI = FCGX_IsCGI();
+    } else if(req_isCGI) {
         /*
          * Not first call to FCGI_Accept and running as CGI means
          * application is done.
          */
         return(EOF);
     } 
-    if(request->acceptCalled && !request->finishCalled) {
-       FCGI_UndoBinding(request);
-    }
-    if(!isCGI) {
-       FCGX_Stream *out, *error;
-        int acceptResult = FCGX_Accept_r(&request->in, &out, &error, 
-                                        &request->envp,
-                                        request->requestPtr);
+    if(!req_isCGI) {
+#ifdef USE_SFIO
+       int i;
+#endif
+       FCGX_Request *fcgx_req = request->requestPtr;
+        int acceptResult;
+
+       FCGI_Finish(request);
+#if defined(USE_LOCKING) && defined(USE_THREADS)
+       MUTEX_LOCK(&accept_mutex);
+#endif
+       acceptResult = FCGX_Accept_r(fcgx_req);
+#if defined(USE_LOCKING) && defined(USE_THREADS)
+       MUTEX_UNLOCK(&accept_mutex);
+#endif
         if(acceptResult < 0) {
             return acceptResult;
         }
+
+       populate_env(fcgx_req->envp, request->hvEnv);
+
 #ifdef USE_SFIO
-        sfdisc(IoIFP(GvIOp(request->gv[0])), sfdcnewfcgi(request->in));
-        sfdisc(IoIFP(GvIOp(request->gv[1])), sfdcnewfcgi(out));
-        sfdisc(IoIFP(GvIOp(request->gv[2])), sfdcnewfcgi(error));
+       for (i = 0; i < 3; ++i) {
+           request->io[i] = GvIOn(request->gv[i]);
+           if (!(i == 0 ? IoIFP(request->io[i]) 
+                        : IoOFP(request->io[i]))) {
+               IoIFP(request->io[i]) = sftmp(0);
+               /*IoIFP(request->io[i]) = sfnew(NULL, NULL, SF_UNBOUND, 0, 
+                                    SF_STRING | (i ? SF_WRITE : SF_READ));*/
+               if (i != 0) 
+                   IoOFP(request->io[i]) = IoIFP(request->io[i]);
+               request->sfcreated[i] = TRUE;
+           }
+       }
 #else
        if (!request->svout) {
            newSVrv(request->svout = newSV(0), "FCGI::Stream");
            newSVrv(request->sverr = newSV(0), "FCGI::Stream");
            newSVrv(request->svin = newSV(0), "FCGI::Stream");
        }
-       sv_magic((SV *)request->gv[1] = gvp[1], request->svout, 'q', Nullch, 0);
-       sv_magic((SV *)request->gv[2] = gvp[2], request->sverr, 'q', Nullch, 0);
-       sv_magic((SV *)request->gv[0] = gvp[0], request->svin, 'q', Nullch, 0);
-       sv_setiv(SvRV(request->svout), (IV) out);
-       sv_setiv(SvRV(request->sverr), (IV) error);
-       sv_setiv(SvRV(request->svin), (IV) request->in);
-
-       if (request->compat) {
-           global_sverr = request->sverr;
-           if (PL_warnhook) SvREFCNT_dec(PL_warnhook);
-           PL_warnhook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN")));
-           if (PL_diehook) SvREFCNT_dec(PL_diehook);
-           PL_diehook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE")));
-       }
+       sv_setiv(SvRV(request->svout), (IV) fcgx_req->out);
+       sv_setiv(SvRV(request->sverr), (IV) fcgx_req->err);
+       sv_setiv(SvRV(request->svin), (IV) fcgx_req->in);
 #endif
-       request->finishCalled = FALSE;
+       FCGI_Bind(request);
+       request->accepted = TRUE;
     }
-    request->acceptCalled = TRUE;
     return 0;
 }
 
 static void 
 FCGI_Finish(FCGP_Request* request)
 {
-    if(!request->acceptCalled || isCGI) {
+#ifdef USE_SFIO
+    int i;
+#endif
+    dTHX;
+
+    if(!request->accepted) {
        return;
     }
-    FCGI_UndoBinding(request);
-    request->in = NULL;
-    FCGX_Finish_r(request->requestPtr);
-    request->finishCalled = TRUE;
-#ifndef USE_SFIO
-    if (request->compat) {
-       if (PL_warnhook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN"))) {
-           SvREFCNT_dec(PL_warnhook);
-           PL_warnhook = Nullsv;
-       }
-       if (PL_diehook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE"))) {
-           SvREFCNT_dec(PL_diehook);
-           PL_diehook = Nullsv;
+
+    if (request->bound) {
+       FCGI_UndoBinding(request);
+    }
+#ifdef USE_SFIO
+    for (i = 0; i < 3; ++i) {
+       if (request->sfcreated[i]) {
+           sfclose(IoIFP(request->io[i]));
+           IoIFP(request->io[i]) = IoOFP(request->io[i]) = Nullfp;
+           request->sfcreated[i] = FALSE;
        }
     }
 #endif
+    FCGX_Finish_r(request->requestPtr);
+    request->accepted = FALSE;
 }
 
 static int 
 FCGI_StartFilterData(FCGP_Request* request)
 {
-    return request->in ? FCGX_StartFilterData(request->in) : -1;
-}
-
-static void
-FCGI_SetExitStatus(FCGP_Request* request, int status)
-{
-    if (request->in) FCGX_SetExitStatus(status, request->in);
+    return request->requestPtr->in ? 
+           FCGX_StartFilterData(request->requestPtr->in) : -1;
 }
 
 static FCGP_Request *
-FCGI_Request()
+FCGI_Request(in, out, err, env, socket, flags)
+    GV*            in;
+    GV*            out;
+    GV*            err;
+    HV*            env;
+    int            socket;
+    int            flags;
 {
     FCGX_Request* fcgx_req;
     FCGP_Request* req;
 
     Newz(551, fcgx_req, 1, FCGX_Request);
+    FCGX_InitRequest(fcgx_req, socket, flags);
     Newz(551, req, 1, FCGP_Request);
     req->requestPtr = fcgx_req;
+    req->gv[0] = in;
+    req->gv[1] = out;
+    req->gv[2] = err;
+    req->hvEnv = env;
 
     return req;
 }
@@ -250,167 +321,91 @@ FCGI_Request()
 static void
 FCGI_Release_Request(FCGP_Request *req)
 {
+    FCGI_Finish(req);
     Safefree(req->requestPtr);
     Safefree(req);
 }
 
 static void
-populate_env(envp, hv)
-char **envp;
-HV *hv;
+FCGI_Init()
 {
-    int i;
-    char *p, *p1;
-    SV   *sv;
+#if defined(USE_LOCKING) && defined(USE_THREADS)
+    dTHX;
 
-    for(i = 0; ; i++) {
-       if((p = envp[i]) == NULL) {
-           break;
-       }
-       p1 = strchr(p, '=');
-       assert(p1 != NULL);
-       sv = newSVpv(p1 + 1, 0);
-       /* call magic for this value ourselves */
-       hv_store(hv, p, p1 - p, sv, 0);
-       SvSETMAGIC(sv);
-    }
-}
-
-/*
- * For each variable in the array envp, either set or unset it
- * in the global hash %ENV.
- */
-static void
-DoPerlEnv(envp, set)
-char **envp;
-int set;
-{
-    if (!set)
-       perl_eval_pv("%ENV = %FCGI::ENV", 0);
-    else {
-       perl_eval_pv("%FCGI::ENV = %ENV", 0);
-       populate_env(envp, perl_get_hv("ENV", TRUE));
-    }
+    MUTEX_INIT(&accept_mutex);
+#endif
 }
 
-#define REQUEST_ARG(arg,request) \
-       if (sv_isa(ST(arg), "FCGI")) { \
-           request = (FCGP_Request*) SvIV((SV*)SvRV(ST(arg))); \
-       } else \
-           croak("request is not of type FCGI")
-
 typedef FCGX_Stream *  FCGI__Stream;
 typedef FCGP_Request * FCGI;
+typedef        GV*             GLOBREF;
+typedef        HV*             HASHREF;
 
 MODULE = FCGI          PACKAGE = FCGI
 
 BOOT:
-    FCGX_Init();
-    FCGX_InitRequest(&global_fcgx_request);
-    memset(&global_request, 0, sizeof(global_request));
-    global_request.compat = 1;
-    global_request.requestPtr = &global_fcgx_request;
-    global_request.gv[0] = gv_fetchpv("STDIN",TRUE, SVt_PVIO);
-    global_request.gv[1] = gv_fetchpv("STDOUT",TRUE, SVt_PVIO);
-    global_request.gv[2] = gv_fetchpv("STDERR",TRUE, SVt_PVIO);
-
+    FCGI_Init();
 
 SV *
-request()
-
-    PROTOTYPE:
+RequestX(in, out, err, env, socket, flags)
+    GLOBREF in;
+    GLOBREF out;
+    GLOBREF err;
+    HASHREF env;
+    int            socket;
+    int            flags;
+
+    PROTOTYPE: ***$$$
     CODE:
-    RETVAL = Perl_sv_setref_pv(Perl_newSV(0), "FCGI", FCGI_Request());
+    RETVAL = sv_setref_pv(newSV(0), "FCGI", 
+               FCGI_Request(in, out, err, env, socket, flags));
+
+    OUTPUT:
+    RETVAL
+
+int
+OpenSocket(path, backlog)
+    char* path;
+    int backlog;
 
+    PROTOTYPE: $$
+    CODE:
+    RETVAL = FCGX_OpenSocket(path, backlog);
     OUTPUT:
     RETVAL
 
+void
+CloseSocket(socket)
+    int socket;
+
+    PROTOTYPE: $
+    CODE:
+    close(socket);
 
 int
-accept(...)
+Accept(request)
 
-    PROTOTYPE: ;$***$
+    FCGI    request;
 
-    PREINIT:
-    FCGP_Request* request = &global_request;
-    GV *gv[3];
+    PROTOTYPE: $
 
     CODE:
-    if (items != 0 && items != 5)
-       croak("Usage: FCGI::accept() or "
-             "FCGI::accept(request, IN, OUT, ERR, env)");
-    if (items) {
-       static const char* names[] = {"", "IN", "OUT", "ERR"};
-       int i;
+    RETVAL = FCGI_Accept(request);
 
-       REQUEST_ARG(0,request);
-       for(i = 1; i <= 3; ++i) {
-           if (SvROK(ST(i)) && isGV(SvRV(ST(i)))) {
-               gv[i-1] = (GV*)SvRV(ST(i));
-           } else
-               croak("%s is not a GLOB reference", names[i]);
-       }
-       if (SvROK(ST(4)) && SvTYPE(SvRV(ST(4))) == SVt_PVHV) {
-           request->hvEnv = (HV*)SvRV(ST(4));
-       } else
-           croak("env is not a reference to a hash");
-    }
-    {
-        int acceptStatus;
-        /*
-         * Unmake Perl variable settings for the request just completed.
-         */
-        if(request->envp != NULL) {
-            DoPerlEnv(request->envp, FALSE);
-            request->envp = NULL;
-        }
-        /*
-         * Call FCGI_Accept but preserve environ.
-         */
-        acceptStatus = FCGI_Accept(request, gv);
-        /*
-         * Make Perl variable settings for the new request.
-         */
-        if(acceptStatus >= 0 && !isCGI) {
-           if (request->compat)
-               DoPerlEnv(request->envp, TRUE);
-           else {
-               populate_env(request->envp, request->hvEnv);
-               request->envp = NULL;
-           }
-        } else {
-            request->envp = NULL;
-        }
-        RETVAL = acceptStatus;
-    }
     OUTPUT:
     RETVAL
 
 
 void
-finish(...)
+Finish(request)
 
-    PROTOTYPE: ;$
+    FCGI    request;
 
-    PREINIT:
-    FCGP_Request* request = &global_request;
+    PROTOTYPE: $
 
     CODE:
-    if (items != 0 && items != 1)
-       croak("Usage: FCGI::finish() or "
-             "FCGI::finish(request)");
-    if (items) {
-       REQUEST_ARG(0,request);
-    }
     {
         /*
-         * Unmake Perl variable settings for the completed request.
-         */
-        if(request->envp != NULL) {
-            DoPerlEnv(request->envp, FALSE);
-            request->envp = NULL;
-        }
-        /*
          * Finish the request.
          */
         FCGI_Finish(request);
@@ -418,59 +413,51 @@ finish(...)
 
 
 void
-flush(...)
+Flush(request)
 
-    PROTOTYPE: ;$
+    FCGI    request;
 
-    PREINIT:
-    FCGP_Request* request = &global_request;
+    PROTOTYPE: $
 
     CODE:
-    if (items != 0 && items != 1)
-       croak("Usage: FCGI::flush([request])");
-    if (items) {
-       REQUEST_ARG(0,request);
-    }
     FCGI_Flush(request);
 
 void
-set_exit_status(status,...)
+Detach(request)
 
-    int status;
+    FCGI    request;
 
-    PROTOTYPE: $;$
+    PROTOTYPE: $
 
-    PREINIT:
-    FCGP_Request* request = &global_request;
+    CODE:
+    if (request->accepted && request->bound)
+       FCGI_UndoBinding(request);
+
+void
+Attach(request)
+
+    FCGI    request;
+
+    PROTOTYPE: $
 
     CODE:
-    if (items != 1 && items != 2)
-       croak("Usage: FCGI::set_exit_status(status[,request])");
-    if (items == 2) {
-       REQUEST_ARG(1,request);
-    }
-    FCGI_SetExitStatus(request, status);
+    if (request->accepted && !request->bound)
+       FCGI_Bind(request);
+
 
 int
-start_filter_data(...)
+StartFilterData(request)
 
-    PROTOTYPE: ;$
+    FCGI    request;
 
-    PREINIT:
-    FCGP_Request* request = &global_request;
+    PROTOTYPE: $
 
     CODE:
-    if (items != 0 && items != 1)
-       croak("Usage: FCGI::flush([request])");
-    if (items) {
-       REQUEST_ARG(0,request);
-    }
     RETVAL = FCGI_StartFilterData(request);
 
     OUTPUT:
     RETVAL
 
-
 void
 DESTROY(request)
     FCGI    request;
@@ -483,20 +470,6 @@ DESTROY(request)
 MODULE = FCGI          PACKAGE = FCGI::Stream
 
 #ifndef USE_SFIO
-void
-DIE(msg)
-       char *  msg;
-
-       CODE:
-       if (!PL_in_eval)
-           FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
-
-void
-WARN(msg)
-       char *  msg;
-
-       CODE:
-       FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
 
 void
 PRINT(stream, ...)
@@ -582,8 +555,8 @@ bool
 CLOSE(stream)
        FCGI::Stream    stream;
 
-       ALIAS:
-       DESTROY = 1
+#      ALIAS:
+#      DESTROY = 1
 
        CODE:
        RETVAL = FCGX_FClose(stream) != -1;