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)
#define TRUE (1)
#endif
-#ifdef WIN32
-#define environ _environ
+#ifndef dTHX
+#define dTHX
#endif
-extern char **environ;
-
#ifdef USE_SFIO
typedef struct
{
}
#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)));
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;
}
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);
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;
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, ...)
CLOSE(stream)
FCGI::Stream stream;
- ALIAS:
- DESTROY = 1
+# ALIAS:
+# DESTROY = 1
CODE:
RETVAL = FCGX_FClose(stream) != -1;