Finish off thread support (fingers crossed)
[catagits/fcgi2.git] / perl / FCGI.PL
CommitLineData
1b64d24d 1use Config;
2
3open OUT, ">FCGI.xs";
4
5print "Generating FCGI.xs for Perl version $]\n";
6#unless (exists $Config{apiversion} && $Config{apiversion} >= 5.005)
7unless ($] >= 5.005) {
8 for (qw(sv_undef diehook warnhook in_eval)) {
9 print OUT "#define PL_$_ $_\n"
10 }
11}
12print OUT while <DATA>;
13close OUT;
14__END__
34bfd355 15/* $Id: FCGI.PL,v 1.6 1999/07/28 23:09:56 skimo Exp $ */
1b64d24d 16
17#include "EXTERN.h"
18#include "perl.h"
19#include "XSUB.h"
20
21#include "fcgiapp.h"
22
23#ifndef FALSE
24#define FALSE (0)
25#endif
26
27#ifndef TRUE
28#define TRUE (1)
29#endif
30
0074e702 31#ifdef WIN32
32#define environ _environ
33#endif
34
1b64d24d 35extern char **environ;
1b64d24d 36
37#ifdef USE_SFIO
38typedef struct
39{
40 Sfdisc_t disc;
41 FCGX_Stream *stream;
42} FCGI_Disc;
43
44static ssize_t
45sffcgiread(f, buf, n, disc)
46Sfio_t* f; /* stream involved */
47Void_t* buf; /* buffer to read into */
48size_t n; /* number of bytes to read */
49Sfdisc_t* disc; /* discipline */
50{
51 return FCGX_GetStr(buf, n, ((FCGI_Disc *)disc)->stream);
52}
53
54static ssize_t
55sffcgiwrite(f, buf, n, disc)
56Sfio_t* f; /* stream involved */
57const Void_t* buf; /* buffer to read into */
58size_t n; /* number of bytes to read */
59Sfdisc_t* disc; /* discipline */
60{
61 n = FCGX_PutStr(buf, n, ((FCGI_Disc *)disc)->stream);
62 FCGX_FFlush(((FCGI_Disc *)disc)->stream);
63 return n;
64}
65
66Sfdisc_t *
67sfdcnewfcgi(stream)
68 FCGX_Stream *stream;
69{
70 FCGI_Disc* disc;
71
72 New(1000,disc,1,FCGI_Disc);
73 if (!disc) return (Sfdisc_t *)disc;
74
75 disc->disc.exceptf = (Sfexcept_f)NULL;
76 disc->disc.seekf = (Sfseek_f)NULL;
77 disc->disc.readf = sffcgiread;
78 disc->disc.writef = sffcgiwrite;
79 disc->stream = stream;
80 return (Sfdisc_t *)disc;
81}
82
83Sfdisc_t *
84sfdcdelfcgi(disc)
85 Sfdisc_t* disc;
86{
87 Safefree(disc);
88 return 0;
89}
90#endif
91
90a18d65 92static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: FCGI */
93
94static FCGX_Request global_fcgx_request;
95
96typedef struct FCGP_Request {
97 int compat;
98 int acceptCalled;
99 int finishCalled;
100 SV* svin;
101 SV* svout;
102 SV* sverr;
eede4b76 103 GV* gv[3];
34bfd355 104 HV* hvEnv;
90a18d65 105 FCGX_Stream* in;
eede4b76 106 FCGX_ParamArray envp;
90a18d65 107 FCGX_Request* requestPtr;
108} FCGP_Request;
109
110static FCGP_Request global_request;
111static SV* global_sverr;
1b64d24d 112
113static int
90a18d65 114FCGI_Flush(FCGP_Request* request)
1b64d24d 115{
eede4b76 116 if(!request->acceptCalled || isCGI) {
1b64d24d 117 return;
118 }
119#ifdef USE_SFIO
eede4b76 120 sfsync(IoIFP(GvIOp(request->gv[1])));
121 sfsync(IoIFP(GvIOp(request->gv[2])));
1b64d24d 122#else
90a18d65 123 FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->svout)));
124 FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->sverr)));
1b64d24d 125#endif
126}
127
34bfd355 128static void
129FCGI_UndoBinding(FCGP_Request* request)
130{
131#ifdef USE_SFIO
132 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[0])), SF_POPDISC));
133 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[1])), SF_POPDISC));
134 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[2])), SF_POPDISC));
135#else
136 FCGI_Flush(request);
137 sv_unmagic((SV *)request->gv[0], 'q');
138 sv_unmagic((SV *)request->gv[1], 'q');
139 sv_unmagic((SV *)request->gv[2], 'q');
140#endif
141}
142
1b64d24d 143static int
34bfd355 144FCGI_Accept(FCGP_Request* request, GV **gvp)
1b64d24d 145{
90a18d65 146 if(isCGI == -1) {
1b64d24d 147 /*
148 * First call to FCGI_Accept. Is application running
149 * as FastCGI or as CGI?
150 */
151 isCGI = FCGX_IsCGI();
152 } else if(isCGI) {
153 /*
154 * Not first call to FCGI_Accept and running as CGI means
155 * application is done.
156 */
157 return(EOF);
eede4b76 158 }
159 if(request->acceptCalled && !request->finishCalled) {
34bfd355 160 FCGI_UndoBinding(request);
1b64d24d 161 }
162 if(!isCGI) {
1b64d24d 163 FCGX_Stream *out, *error;
eede4b76 164 int acceptResult = FCGX_Accept_r(&request->in, &out, &error,
165 &request->envp,
90a18d65 166 request->requestPtr);
1b64d24d 167 if(acceptResult < 0) {
168 return acceptResult;
169 }
170#ifdef USE_SFIO
eede4b76 171 sfdisc(IoIFP(GvIOp(request->gv[0])), sfdcnewfcgi(request->in));
172 sfdisc(IoIFP(GvIOp(request->gv[1])), sfdcnewfcgi(out));
173 sfdisc(IoIFP(GvIOp(request->gv[2])), sfdcnewfcgi(error));
1b64d24d 174#else
90a18d65 175 if (!request->svout) {
eede4b76 176 newSVrv(request->svout = newSV(0), "FCGI::Stream");
eede4b76 177 newSVrv(request->sverr = newSV(0), "FCGI::Stream");
eede4b76 178 newSVrv(request->svin = newSV(0), "FCGI::Stream");
1b64d24d 179 }
34bfd355 180 sv_magic((SV *)request->gv[1] = gvp[1], request->svout, 'q', Nullch, 0);
181 sv_magic((SV *)request->gv[2] = gvp[2], request->sverr, 'q', Nullch, 0);
182 sv_magic((SV *)request->gv[0] = gvp[0], request->svin, 'q', Nullch, 0);
90a18d65 183 sv_setiv(SvRV(request->svout), (IV) out);
184 sv_setiv(SvRV(request->sverr), (IV) error);
185 sv_setiv(SvRV(request->svin), (IV) request->in);
186
187 if (request->compat) {
188 global_sverr = request->sverr;
6ef7789b 189 if (PL_warnhook) SvREFCNT_dec(PL_warnhook);
190 PL_warnhook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN")));
6ef7789b 191 if (PL_diehook) SvREFCNT_dec(PL_diehook);
192 PL_diehook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE")));
193 }
1b64d24d 194#endif
90a18d65 195 request->finishCalled = FALSE;
1b64d24d 196 }
90a18d65 197 request->acceptCalled = TRUE;
1b64d24d 198 return 0;
199}
200
201static void
90a18d65 202FCGI_Finish(FCGP_Request* request)
1b64d24d 203{
90a18d65 204 if(!request->acceptCalled || isCGI) {
1b64d24d 205 return;
206 }
34bfd355 207 FCGI_UndoBinding(request);
90a18d65 208 request->in = NULL;
209 FCGX_Finish_r(request->requestPtr);
210 request->finishCalled = TRUE;
211#ifndef USE_SFIO
212 if (request->compat) {
213 if (PL_warnhook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN"))) {
214 SvREFCNT_dec(PL_warnhook);
215 PL_warnhook = Nullsv;
216 }
217 if (PL_diehook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE"))) {
218 SvREFCNT_dec(PL_diehook);
219 PL_diehook = Nullsv;
220 }
1b64d24d 221 }
222#endif
223}
224
225static int
90a18d65 226FCGI_StartFilterData(FCGP_Request* request)
227{
228 return request->in ? FCGX_StartFilterData(request->in) : -1;
229}
230
231static void
232FCGI_SetExitStatus(FCGP_Request* request, int status)
233{
234 if (request->in) FCGX_SetExitStatus(status, request->in);
235}
236
237static FCGP_Request *
238FCGI_Request()
1b64d24d 239{
90a18d65 240 FCGX_Request* fcgx_req;
241 FCGP_Request* req;
242
243 Newz(551, fcgx_req, 1, FCGX_Request);
244 Newz(551, req, 1, FCGP_Request);
245 req->requestPtr = fcgx_req;
246
247 return req;
1b64d24d 248}
249
250static void
90a18d65 251FCGI_Release_Request(FCGP_Request *req)
1b64d24d 252{
90a18d65 253 Safefree(req->requestPtr);
254 Safefree(req);
1b64d24d 255}
256
34bfd355 257static void
258populate_env(envp, hv)
259char **envp;
260HV *hv;
261{
262 int i;
263 char *p, *p1;
264 SV *sv;
265
266 for(i = 0; ; i++) {
267 if((p = envp[i]) == NULL) {
268 break;
269 }
270 p1 = strchr(p, '=');
271 assert(p1 != NULL);
272 sv = newSVpv(p1 + 1, 0);
273 /* call magic for this value ourselves */
274 hv_store(hv, p, p1 - p, sv, 0);
275 SvSETMAGIC(sv);
276 }
277}
278
1b64d24d 279/*
280 * For each variable in the array envp, either set or unset it
281 * in the global hash %ENV.
282 */
283static void
284DoPerlEnv(envp, set)
285char **envp;
286int set;
287{
1b64d24d 288 if (!set)
289 perl_eval_pv("%ENV = %FCGI::ENV", 0);
290 else {
291 perl_eval_pv("%FCGI::ENV = %ENV", 0);
34bfd355 292 populate_env(envp, perl_get_hv("ENV", TRUE));
1b64d24d 293 }
294}
295
eede4b76 296#define REQUEST_ARG(arg,request) \
297 if (sv_isa(ST(arg), "FCGI")) { \
298 request = (FCGP_Request*) SvIV((SV*)SvRV(ST(arg))); \
299 } else \
300 croak("request is not of type FCGI")
1b64d24d 301
eede4b76 302typedef FCGX_Stream * FCGI__Stream;
303typedef FCGP_Request * FCGI;
1b64d24d 304
305MODULE = FCGI PACKAGE = FCGI
306
90a18d65 307BOOT:
308 FCGX_Init();
309 FCGX_InitRequest(&global_fcgx_request);
310 memset(&global_request, 0, sizeof(global_request));
311 global_request.compat = 1;
312 global_request.requestPtr = &global_fcgx_request;
eede4b76 313 global_request.gv[0] = gv_fetchpv("STDIN",TRUE, SVt_PVIO);
314 global_request.gv[1] = gv_fetchpv("STDOUT",TRUE, SVt_PVIO);
315 global_request.gv[2] = gv_fetchpv("STDERR",TRUE, SVt_PVIO);
316
317
318SV *
319request()
320
321 PROTOTYPE:
322 CODE:
323 RETVAL = Perl_sv_setref_pv(Perl_newSV(0), "FCGI", FCGI_Request());
324
325 OUTPUT:
326 RETVAL
327
328
329int
330accept(...)
331
332 PROTOTYPE: ;$***$
333
334 PREINIT:
335 FCGP_Request* request = &global_request;
34bfd355 336 GV *gv[3];
eede4b76 337
338 CODE:
339 if (items != 0 && items != 5)
340 croak("Usage: FCGI::accept() or "
341 "FCGI::accept(request, IN, OUT, ERR, env)");
342 if (items) {
343 static const char* names[] = {"", "IN", "OUT", "ERR"};
344 int i;
345
346 REQUEST_ARG(0,request);
347 for(i = 1; i <= 3; ++i) {
348 if (SvROK(ST(i)) && isGV(SvRV(ST(i)))) {
34bfd355 349 gv[i-1] = (GV*)SvRV(ST(i));
eede4b76 350 } else
351 croak("%s is not a GLOB reference", names[i]);
352 }
34bfd355 353 if (SvROK(ST(4)) && SvTYPE(SvRV(ST(4))) == SVt_PVHV) {
354 request->hvEnv = (HV*)SvRV(ST(4));
355 } else
356 croak("env is not a reference to a hash");
eede4b76 357 }
358 {
eede4b76 359 int acceptStatus;
360 /*
361 * Unmake Perl variable settings for the request just completed.
362 */
363 if(request->envp != NULL) {
364 DoPerlEnv(request->envp, FALSE);
365 request->envp = NULL;
366 }
367 /*
368 * Call FCGI_Accept but preserve environ.
369 */
34bfd355 370 acceptStatus = FCGI_Accept(request, gv);
eede4b76 371 /*
372 * Make Perl variable settings for the new request.
373 */
34bfd355 374 if(acceptStatus >= 0 && !isCGI) {
375 if (request->compat)
376 DoPerlEnv(request->envp, TRUE);
377 else {
378 populate_env(request->envp, request->hvEnv);
379 request->envp = NULL;
380 }
eede4b76 381 } else {
382 request->envp = NULL;
383 }
384 RETVAL = acceptStatus;
385 }
386 OUTPUT:
387 RETVAL
388
389
390void
391finish(...)
392
393 PROTOTYPE: ;$
394
395 PREINIT:
396 FCGP_Request* request = &global_request;
397
398 CODE:
399 if (items != 0 && items != 1)
400 croak("Usage: FCGI::finish() or "
401 "FCGI::finish(request)");
402 if (items) {
403 REQUEST_ARG(0,request);
404 }
405 {
406 /*
407 * Unmake Perl variable settings for the completed request.
408 */
409 if(request->envp != NULL) {
410 DoPerlEnv(request->envp, FALSE);
411 request->envp = NULL;
412 }
413 /*
414 * Finish the request.
415 */
416 FCGI_Finish(request);
417 }
418
419
420void
421flush(...)
422
423 PROTOTYPE: ;$
424
425 PREINIT:
426 FCGP_Request* request = &global_request;
427
428 CODE:
429 if (items != 0 && items != 1)
430 croak("Usage: FCGI::flush([request])");
431 if (items) {
432 REQUEST_ARG(0,request);
433 }
434 FCGI_Flush(request);
435
436void
437set_exit_status(status,...)
438
439 int status;
440
441 PROTOTYPE: $;$
442
443 PREINIT:
444 FCGP_Request* request = &global_request;
445
446 CODE:
447 if (items != 1 && items != 2)
448 croak("Usage: FCGI::set_exit_status(status[,request])");
449 if (items == 2) {
450 REQUEST_ARG(1,request);
451 }
452 FCGI_SetExitStatus(request, status);
453
454int
455start_filter_data(...)
456
457 PROTOTYPE: ;$
458
459 PREINIT:
460 FCGP_Request* request = &global_request;
461
462 CODE:
463 if (items != 0 && items != 1)
464 croak("Usage: FCGI::flush([request])");
465 if (items) {
466 REQUEST_ARG(0,request);
467 }
468 RETVAL = FCGI_StartFilterData(request);
469
470 OUTPUT:
471 RETVAL
472
473
474void
475DESTROY(request)
476 FCGI request;
477
478 CODE:
479 FCGI_Release_Request(request);
480
481
482
483MODULE = FCGI PACKAGE = FCGI::Stream
90a18d65 484
1b64d24d 485#ifndef USE_SFIO
486void
487DIE(msg)
488 char * msg;
489
490 CODE:
491 if (!PL_in_eval)
90a18d65 492 FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
1b64d24d 493
494void
495WARN(msg)
496 char * msg;
497
498 CODE:
90a18d65 499 FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
1b64d24d 500
501void
502PRINT(stream, ...)
eede4b76 503 FCGI::Stream stream;
1b64d24d 504
505 PREINIT:
506 int n;
507
508 CODE:
509 for (n = 1; n < items; ++n) {
510 STRLEN len;
511 register char *tmps = (char *)SvPV(ST(n),len);
512 FCGX_PutStr(tmps, len, stream);
513 }
514 if (SvTRUEx(perl_get_sv("|", FALSE)))
515 FCGX_FFlush(stream);
516
517int
518WRITE(stream, bufsv, len, ...)
eede4b76 519 FCGI::Stream stream;
1b64d24d 520 SV * bufsv;
521 int len;
522
523 PREINIT:
524 int offset;
525 char * buf;
526 STRLEN blen;
527 int n;
528
529 CODE:
530 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
531 buf = SvPV(bufsv, blen);
532 if (offset < 0) offset += blen;
533 if (len > blen - offset)
534 len = blen - offset;
535 if (offset < 0 || offset >= blen ||
536 (n = FCGX_PutStr(buf+offset, len, stream)) < 0)
537 ST(0) = &PL_sv_undef;
538 else {
539 ST(0) = sv_newmortal();
540 sv_setpvf(ST(0), "%c", n);
541 }
542
543int
544READ(stream, bufsv, len, ...)
eede4b76 545 FCGI::Stream stream;
1b64d24d 546 SV * bufsv;
547 int len;
548
549 PREINIT:
550 int offset;
551 char * buf;
552
553 CODE:
554 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
555 if (! SvOK(bufsv))
556 sv_setpvn(bufsv, "", 0);
557 buf = SvGROW(bufsv, len+offset+1);
558 len = FCGX_GetStr(buf+offset, len, stream);
559 SvCUR_set(bufsv, len+offset);
560 *SvEND(bufsv) = '\0';
561 (void)SvPOK_only(bufsv);
562 SvSETMAGIC(bufsv);
563 RETVAL = len;
564
565 OUTPUT:
566 RETVAL
567
568SV *
569GETC(stream)
eede4b76 570 FCGI::Stream stream;
1b64d24d 571
572 PREINIT:
573 int retval;
574
575 CODE:
576 if ((retval = FCGX_GetChar(stream)) != -1) {
577 ST(0) = sv_newmortal();
578 sv_setpvf(ST(0), "%c", retval);
579 } else ST(0) = &PL_sv_undef;
580
581bool
582CLOSE(stream)
eede4b76 583 FCGI::Stream stream;
1b64d24d 584
585 ALIAS:
586 DESTROY = 1
587
588 CODE:
589 RETVAL = FCGX_FClose(stream) != -1;
590
591 OUTPUT:
592 RETVAL
593
594#endif