Further threadification
[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__
eede4b76 15/* $Id: FCGI.PL,v 1.5 1999/07/28 19:23:08 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];
90a18d65 104 FCGX_Stream* in;
eede4b76 105 FCGX_ParamArray envp;
90a18d65 106 FCGX_Request* requestPtr;
107} FCGP_Request;
108
109static FCGP_Request global_request;
110static SV* global_sverr;
1b64d24d 111
112static int
90a18d65 113FCGI_Flush(FCGP_Request* request)
1b64d24d 114{
eede4b76 115 if(!request->acceptCalled || isCGI) {
1b64d24d 116 return;
117 }
118#ifdef USE_SFIO
eede4b76 119 sfsync(IoIFP(GvIOp(request->gv[1])));
120 sfsync(IoIFP(GvIOp(request->gv[2])));
1b64d24d 121#else
90a18d65 122 FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->svout)));
123 FCGX_FFlush((FCGX_Stream *) SvIV((SV*) SvRV(request->sverr)));
1b64d24d 124#endif
125}
126
127static int
90a18d65 128FCGI_Accept(FCGP_Request* request)
1b64d24d 129{
90a18d65 130 if(isCGI == -1) {
1b64d24d 131 /*
132 * First call to FCGI_Accept. Is application running
133 * as FastCGI or as CGI?
134 */
135 isCGI = FCGX_IsCGI();
136 } else if(isCGI) {
137 /*
138 * Not first call to FCGI_Accept and running as CGI means
139 * application is done.
140 */
141 return(EOF);
eede4b76 142 }
143 if(request->acceptCalled && !request->finishCalled) {
1b64d24d 144#ifdef USE_SFIO
eede4b76 145 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[0])), SF_POPDISC));
146 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[1])), SF_POPDISC));
147 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[2])), SF_POPDISC));
1b64d24d 148#else
eede4b76 149 FCGI_Flush(request);
1b64d24d 150#endif
1b64d24d 151 }
152 if(!isCGI) {
1b64d24d 153 FCGX_Stream *out, *error;
eede4b76 154 int acceptResult = FCGX_Accept_r(&request->in, &out, &error,
155 &request->envp,
90a18d65 156 request->requestPtr);
1b64d24d 157 if(acceptResult < 0) {
158 return acceptResult;
159 }
160#ifdef USE_SFIO
eede4b76 161 sfdisc(IoIFP(GvIOp(request->gv[0])), sfdcnewfcgi(request->in));
162 sfdisc(IoIFP(GvIOp(request->gv[1])), sfdcnewfcgi(out));
163 sfdisc(IoIFP(GvIOp(request->gv[2])), sfdcnewfcgi(error));
1b64d24d 164#else
90a18d65 165 if (!request->svout) {
eede4b76 166 newSVrv(request->svout = newSV(0), "FCGI::Stream");
167 sv_magic((SV *)request->gv[1], request->svout, 'q', Nullch, 0);
168 newSVrv(request->sverr = newSV(0), "FCGI::Stream");
169 sv_magic((SV *)request->gv[2], request->sverr, 'q', Nullch, 0);
170 newSVrv(request->svin = newSV(0), "FCGI::Stream");
171 sv_magic((SV *)request->gv[0], request->svin, 'q', Nullch, 0);
1b64d24d 172 }
90a18d65 173 sv_setiv(SvRV(request->svout), (IV) out);
174 sv_setiv(SvRV(request->sverr), (IV) error);
175 sv_setiv(SvRV(request->svin), (IV) request->in);
176
177 if (request->compat) {
178 global_sverr = request->sverr;
6ef7789b 179 if (PL_warnhook) SvREFCNT_dec(PL_warnhook);
180 PL_warnhook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN")));
6ef7789b 181 if (PL_diehook) SvREFCNT_dec(PL_diehook);
182 PL_diehook = SvREFCNT_inc(GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE")));
183 }
1b64d24d 184#endif
90a18d65 185 request->finishCalled = FALSE;
1b64d24d 186 }
90a18d65 187 request->acceptCalled = TRUE;
1b64d24d 188 return 0;
189}
190
191static void
90a18d65 192FCGI_Finish(FCGP_Request* request)
1b64d24d 193{
90a18d65 194 if(!request->acceptCalled || isCGI) {
1b64d24d 195 return;
196 }
90a18d65 197 if (request->compat) {
1b64d24d 198#ifdef USE_SFIO
eede4b76 199 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[0])), SF_POPDISC));
200 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[1])), SF_POPDISC));
201 sfdcdelfcgi(sfdisc(IoIFP(GvIOp(request->gv[2])), SF_POPDISC));
1b64d24d 202#else
90a18d65 203 FCGI_Flush(request);
1b64d24d 204#endif
1b64d24d 205 }
90a18d65 206 request->in = NULL;
207 FCGX_Finish_r(request->requestPtr);
208 request->finishCalled = TRUE;
209#ifndef USE_SFIO
210 if (request->compat) {
211 if (PL_warnhook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::WARN"))) {
212 SvREFCNT_dec(PL_warnhook);
213 PL_warnhook = Nullsv;
214 }
215 if (PL_diehook == (SV*)GvCV(gv_fetchmethod(Nullhv, "FCGI::DIE"))) {
216 SvREFCNT_dec(PL_diehook);
217 PL_diehook = Nullsv;
218 }
1b64d24d 219 }
220#endif
221}
222
223static int
90a18d65 224FCGI_StartFilterData(FCGP_Request* request)
225{
226 return request->in ? FCGX_StartFilterData(request->in) : -1;
227}
228
229static void
230FCGI_SetExitStatus(FCGP_Request* request, int status)
231{
232 if (request->in) FCGX_SetExitStatus(status, request->in);
233}
234
235static FCGP_Request *
236FCGI_Request()
1b64d24d 237{
90a18d65 238 FCGX_Request* fcgx_req;
239 FCGP_Request* req;
240
241 Newz(551, fcgx_req, 1, FCGX_Request);
242 Newz(551, req, 1, FCGP_Request);
243 req->requestPtr = fcgx_req;
244
245 return req;
1b64d24d 246}
247
248static void
90a18d65 249FCGI_Release_Request(FCGP_Request *req)
1b64d24d 250{
90a18d65 251 Safefree(req->requestPtr);
252 Safefree(req);
1b64d24d 253}
254
255/*
256 * For each variable in the array envp, either set or unset it
257 * in the global hash %ENV.
258 */
259static void
260DoPerlEnv(envp, set)
261char **envp;
262int set;
263{
264 int i;
265 char *p, *p1;
266 HV *hv;
267 SV *sv;
268 hv = perl_get_hv("ENV", TRUE);
269
270 if (!set)
271 perl_eval_pv("%ENV = %FCGI::ENV", 0);
272 else {
273 perl_eval_pv("%FCGI::ENV = %ENV", 0);
274 for(i = 0; ; i++) {
275 if((p = envp[i]) == NULL) {
276 break;
277 }
278 p1 = strchr(p, '=');
279 assert(p1 != NULL);
1b64d24d 280 sv = newSVpv(p1 + 1, 0);
281 /* call magic for this value ourselves */
282 hv_store(hv, p, p1 - p, sv, 0);
283 SvSETMAGIC(sv);
1b64d24d 284 }
285 }
286}
287
eede4b76 288#define REQUEST_ARG(arg,request) \
289 if (sv_isa(ST(arg), "FCGI")) { \
290 request = (FCGP_Request*) SvIV((SV*)SvRV(ST(arg))); \
291 } else \
292 croak("request is not of type FCGI")
1b64d24d 293
eede4b76 294typedef FCGX_Stream * FCGI__Stream;
295typedef FCGP_Request * FCGI;
1b64d24d 296
297MODULE = FCGI PACKAGE = FCGI
298
90a18d65 299BOOT:
300 FCGX_Init();
301 FCGX_InitRequest(&global_fcgx_request);
302 memset(&global_request, 0, sizeof(global_request));
303 global_request.compat = 1;
304 global_request.requestPtr = &global_fcgx_request;
eede4b76 305 global_request.gv[0] = gv_fetchpv("STDIN",TRUE, SVt_PVIO);
306 global_request.gv[1] = gv_fetchpv("STDOUT",TRUE, SVt_PVIO);
307 global_request.gv[2] = gv_fetchpv("STDERR",TRUE, SVt_PVIO);
308
309
310SV *
311request()
312
313 PROTOTYPE:
314 CODE:
315 RETVAL = Perl_sv_setref_pv(Perl_newSV(0), "FCGI", FCGI_Request());
316
317 OUTPUT:
318 RETVAL
319
320
321int
322accept(...)
323
324 PROTOTYPE: ;$***$
325
326 PREINIT:
327 FCGP_Request* request = &global_request;
328
329 CODE:
330 if (items != 0 && items != 5)
331 croak("Usage: FCGI::accept() or "
332 "FCGI::accept(request, IN, OUT, ERR, env)");
333 if (items) {
334 static const char* names[] = {"", "IN", "OUT", "ERR"};
335 int i;
336
337 REQUEST_ARG(0,request);
338 for(i = 1; i <= 3; ++i) {
339 if (SvROK(ST(i)) && isGV(SvRV(ST(i)))) {
340 request->gv[i-1] = (GV*)SvRV(ST(i));
341 } else
342 croak("%s is not a GLOB reference", names[i]);
343 }
344 }
345 {
346 char **savedEnviron;
347 int acceptStatus;
348 /*
349 * Unmake Perl variable settings for the request just completed.
350 */
351 if(request->envp != NULL) {
352 DoPerlEnv(request->envp, FALSE);
353 request->envp = NULL;
354 }
355 /*
356 * Call FCGI_Accept but preserve environ.
357 */
358 acceptStatus = FCGI_Accept(request);
359 /*
360 * Make Perl variable settings for the new request.
361 */
362 if(request->compat && acceptStatus >= 0 && !isCGI) {
363 DoPerlEnv(request->envp, TRUE);
364 } else {
365 request->envp = NULL;
366 }
367 RETVAL = acceptStatus;
368 }
369 OUTPUT:
370 RETVAL
371
372
373void
374finish(...)
375
376 PROTOTYPE: ;$
377
378 PREINIT:
379 FCGP_Request* request = &global_request;
380
381 CODE:
382 if (items != 0 && items != 1)
383 croak("Usage: FCGI::finish() or "
384 "FCGI::finish(request)");
385 if (items) {
386 REQUEST_ARG(0,request);
387 }
388 {
389 /*
390 * Unmake Perl variable settings for the completed request.
391 */
392 if(request->envp != NULL) {
393 DoPerlEnv(request->envp, FALSE);
394 request->envp = NULL;
395 }
396 /*
397 * Finish the request.
398 */
399 FCGI_Finish(request);
400 }
401
402
403void
404flush(...)
405
406 PROTOTYPE: ;$
407
408 PREINIT:
409 FCGP_Request* request = &global_request;
410
411 CODE:
412 if (items != 0 && items != 1)
413 croak("Usage: FCGI::flush([request])");
414 if (items) {
415 REQUEST_ARG(0,request);
416 }
417 FCGI_Flush(request);
418
419void
420set_exit_status(status,...)
421
422 int status;
423
424 PROTOTYPE: $;$
425
426 PREINIT:
427 FCGP_Request* request = &global_request;
428
429 CODE:
430 if (items != 1 && items != 2)
431 croak("Usage: FCGI::set_exit_status(status[,request])");
432 if (items == 2) {
433 REQUEST_ARG(1,request);
434 }
435 FCGI_SetExitStatus(request, status);
436
437int
438start_filter_data(...)
439
440 PROTOTYPE: ;$
441
442 PREINIT:
443 FCGP_Request* request = &global_request;
444
445 CODE:
446 if (items != 0 && items != 1)
447 croak("Usage: FCGI::flush([request])");
448 if (items) {
449 REQUEST_ARG(0,request);
450 }
451 RETVAL = FCGI_StartFilterData(request);
452
453 OUTPUT:
454 RETVAL
455
456
457void
458DESTROY(request)
459 FCGI request;
460
461 CODE:
462 FCGI_Release_Request(request);
463
464
465
466MODULE = FCGI PACKAGE = FCGI::Stream
90a18d65 467
1b64d24d 468#ifndef USE_SFIO
469void
470DIE(msg)
471 char * msg;
472
473 CODE:
474 if (!PL_in_eval)
90a18d65 475 FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
1b64d24d 476
477void
478WARN(msg)
479 char * msg;
480
481 CODE:
90a18d65 482 FCGX_PutS(msg, (FCGX_Stream *) SvIV((SV*) SvRV(global_sverr)));
1b64d24d 483
484void
485PRINT(stream, ...)
eede4b76 486 FCGI::Stream stream;
1b64d24d 487
488 PREINIT:
489 int n;
490
491 CODE:
492 for (n = 1; n < items; ++n) {
493 STRLEN len;
494 register char *tmps = (char *)SvPV(ST(n),len);
495 FCGX_PutStr(tmps, len, stream);
496 }
497 if (SvTRUEx(perl_get_sv("|", FALSE)))
498 FCGX_FFlush(stream);
499
500int
501WRITE(stream, bufsv, len, ...)
eede4b76 502 FCGI::Stream stream;
1b64d24d 503 SV * bufsv;
504 int len;
505
506 PREINIT:
507 int offset;
508 char * buf;
509 STRLEN blen;
510 int n;
511
512 CODE:
513 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
514 buf = SvPV(bufsv, blen);
515 if (offset < 0) offset += blen;
516 if (len > blen - offset)
517 len = blen - offset;
518 if (offset < 0 || offset >= blen ||
519 (n = FCGX_PutStr(buf+offset, len, stream)) < 0)
520 ST(0) = &PL_sv_undef;
521 else {
522 ST(0) = sv_newmortal();
523 sv_setpvf(ST(0), "%c", n);
524 }
525
526int
527READ(stream, bufsv, len, ...)
eede4b76 528 FCGI::Stream stream;
1b64d24d 529 SV * bufsv;
530 int len;
531
532 PREINIT:
533 int offset;
534 char * buf;
535
536 CODE:
537 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
538 if (! SvOK(bufsv))
539 sv_setpvn(bufsv, "", 0);
540 buf = SvGROW(bufsv, len+offset+1);
541 len = FCGX_GetStr(buf+offset, len, stream);
542 SvCUR_set(bufsv, len+offset);
543 *SvEND(bufsv) = '\0';
544 (void)SvPOK_only(bufsv);
545 SvSETMAGIC(bufsv);
546 RETVAL = len;
547
548 OUTPUT:
549 RETVAL
550
551SV *
552GETC(stream)
eede4b76 553 FCGI::Stream stream;
1b64d24d 554
555 PREINIT:
556 int retval;
557
558 CODE:
559 if ((retval = FCGX_GetChar(stream)) != -1) {
560 ST(0) = sv_newmortal();
561 sv_setpvf(ST(0), "%c", retval);
562 } else ST(0) = &PL_sv_undef;
563
564bool
565CLOSE(stream)
eede4b76 566 FCGI::Stream stream;
1b64d24d 567
568 ALIAS:
569 DESTROY = 1
570
571 CODE:
572 RETVAL = FCGX_FClose(stream) != -1;
573
574 OUTPUT:
575 RETVAL
576
577#endif