add attach()/detach() support
[catagits/fcgi2.git] / perl / FCGI.XL
CommitLineData
0833402a 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__
849cab1e 15/* $Id: FCGI.XL,v 1.9 2002/11/11 13:51:20 skimo Exp $ */
0833402a 16
17#include "EXTERN.h"
18#include "perl.h"
19#include "XSUB.h"
20
21#include "fcgi_config.h"
22#include "fcgiapp.h"
23#include "fastcgi.h"
24
25#ifndef FALSE
26#define FALSE (0)
27#endif
28
29#ifndef TRUE
30#define TRUE (1)
31#endif
32
33#ifndef dTHX
34#define dTHX
35#endif
36
0c1fdec9 37#ifndef INT2PTR
38#define INT2PTR(a,b) ((a) (b))
39#endif
40
0833402a 41#ifdef USE_SFIO
42typedef struct
43{
44 Sfdisc_t disc;
45 FCGX_Stream *stream;
46} FCGI_Disc;
47
48static ssize_t
49sffcgiread(f, buf, n, disc)
50Sfio_t* f; /* stream involved */
51Void_t* buf; /* buffer to read into */
52size_t n; /* number of bytes to read */
53Sfdisc_t* disc; /* discipline */
54{
55 return FCGX_GetStr(buf, n, ((FCGI_Disc *)disc)->stream);
56}
57
58static ssize_t
59sffcgiwrite(f, buf, n, disc)
60Sfio_t* f; /* stream involved */
61const Void_t* buf; /* buffer to read into */
62size_t n; /* number of bytes to read */
63Sfdisc_t* disc; /* discipline */
64{
65 n = FCGX_PutStr(buf, n, ((FCGI_Disc *)disc)->stream);
66 FCGX_FFlush(((FCGI_Disc *)disc)->stream);
67 return n;
68}
69
70Sfdisc_t *
71sfdcnewfcgi(stream)
72 FCGX_Stream *stream;
73{
74 FCGI_Disc* disc;
75
76 New(1000,disc,1,FCGI_Disc);
77 if (!disc) return (Sfdisc_t *)disc;
78
79 disc->disc.exceptf = (Sfexcept_f)NULL;
80 disc->disc.seekf = (Sfseek_f)NULL;
81 disc->disc.readf = sffcgiread;
82 disc->disc.writef = sffcgiwrite;
83 disc->stream = stream;
84 return (Sfdisc_t *)disc;
85}
86
87Sfdisc_t *
88sfdcdelfcgi(disc)
89 Sfdisc_t* disc;
90{
91 Safefree(disc);
92 return 0;
93}
94#endif
95
96#if defined(USE_LOCKING) && defined(USE_THREADS)
97static perl_mutex accept_mutex;
98#endif
99
100typedef struct FCGP_Request {
101 int accepted;
102 int bound;
103 SV* svin;
104 SV* svout;
105 SV* sverr;
106 GV* gv[3];
107 HV* hvEnv;
108 FCGX_Request* requestPtr;
109#ifdef USE_SFIO
110 int sfcreated[3];
111 IO* io[3];
112#endif
113} FCGP_Request;
114
115static void FCGI_Finish(FCGP_Request* request);
116
9b60732b 117static void
0833402a 118FCGI_Flush(FCGP_Request* request)
119{
120 dTHX;
121
122 if(!request->bound) {
123 return;
9b60732b 124 }
0833402a 125#ifdef USE_SFIO
126 sfsync(IoOFP(GvIOp(request->gv[1])));
127 sfsync(IoOFP(GvIOp(request->gv[2])));
128#else
9c31872c 129 FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->svout))));
130 FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->sverr))));
0833402a 131#endif
132}
133
134static void
135FCGI_UndoBinding(FCGP_Request* request)
136{
137 dTHX;
138
139#ifdef USE_SFIO
140 sfdcdelfcgi(sfdisc(IoIFP(request->io[0]), SF_POPDISC));
141 sfdcdelfcgi(sfdisc(IoOFP(request->io[1]), SF_POPDISC));
142 sfdcdelfcgi(sfdisc(IoOFP(request->io[2]), SF_POPDISC));
143#else
849cab1e 144# ifdef USE_PERLIO
145 sv_unmagic((SV *)GvIOp(request->gv[0]), 'q');
146 sv_unmagic((SV *)GvIOp(request->gv[1]), 'q');
147 sv_unmagic((SV *)GvIOp(request->gv[2]), 'q');
148# else
0833402a 149 sv_unmagic((SV *)request->gv[0], 'q');
150 sv_unmagic((SV *)request->gv[1], 'q');
151 sv_unmagic((SV *)request->gv[2], 'q');
849cab1e 152# endif
0833402a 153#endif
154 request->bound = FALSE;
155}
156
157static void
158FCGI_Bind(FCGP_Request* request)
159{
160 dTHX;
161
162#ifdef USE_SFIO
163 sfdisc(IoIFP(request->io[0]), sfdcnewfcgi(request->requestPtr->in));
164 sfdisc(IoOFP(request->io[1]), sfdcnewfcgi(request->requestPtr->out));
165 sfdisc(IoOFP(request->io[2]), sfdcnewfcgi(request->requestPtr->err));
166#else
6d7e3fb2 167# ifdef USE_PERLIO
168 /* For tied filehandles, we apply tiedscalar magic to the IO
169 slot of the GP rather than the GV itself. */
170
171 if (!GvIOp(request->gv[1]))
172 GvIOp(request->gv[1]) = newIO();
173 if (!GvIOp(request->gv[2]))
174 GvIOp(request->gv[2]) = newIO();
175 if (!GvIOp(request->gv[0]))
176 GvIOp(request->gv[0]) = newIO();
177
178 sv_magic((SV *)GvIOp(request->gv[1]), request->svout, 'q', Nullch, 0);
179 sv_magic((SV *)GvIOp(request->gv[2]), request->sverr, 'q', Nullch, 0);
180 sv_magic((SV *)GvIOp(request->gv[0]), request->svin, 'q', Nullch, 0);
181# else
0833402a 182 sv_magic((SV *)request->gv[1], request->svout, 'q', Nullch, 0);
183 sv_magic((SV *)request->gv[2], request->sverr, 'q', Nullch, 0);
184 sv_magic((SV *)request->gv[0], request->svin, 'q', Nullch, 0);
6d7e3fb2 185# endif
0833402a 186#endif
187 request->bound = TRUE;
188}
189
190static void
191populate_env(envp, hv)
192char **envp;
193HV *hv;
194{
195 int i;
196 char *p, *p1;
197 SV *sv;
198 dTHX;
199
200 hv_clear(hv);
201 for(i = 0; ; i++) {
202 if((p = envp[i]) == NULL) {
203 break;
204 }
205 p1 = strchr(p, '=');
206 assert(p1 != NULL);
207 sv = newSVpv(p1 + 1, 0);
208 /* call magic for this value ourselves */
209 hv_store(hv, p, p1 - p, sv, 0);
210 SvSETMAGIC(sv);
211 }
212}
213
214static int
215FCGI_IsFastCGI(FCGP_Request* request)
216{
217 static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: CGI */
218
219 if (request->requestPtr->listen_sock == FCGI_LISTENSOCK_FILENO) {
220 if (isCGI == -1)
221 isCGI = FCGX_IsCGI();
222 return !isCGI;
223 }
224
225 /* A explicit socket is being used -> assume FastCGI */
226 return 1;
227}
228
229static int
230FCGI_Accept(FCGP_Request* request)
231{
232 dTHX;
233
234 if (!FCGI_IsFastCGI(request)) {
235 static int been_here = 0;
236
237 /*
238 * Not first call to FCGI_Accept and running as CGI means
239 * application is done.
240 */
241 if (been_here)
242 return EOF;
243
244 been_here = 1;
245 } else {
246#ifdef USE_SFIO
247 int i;
248#endif
249 FCGX_Request *fcgx_req = request->requestPtr;
250 int acceptResult;
251
252 FCGI_Finish(request);
253#if defined(USE_LOCKING) && defined(USE_THREADS)
254 MUTEX_LOCK(&accept_mutex);
255#endif
256 acceptResult = FCGX_Accept_r(fcgx_req);
257#if defined(USE_LOCKING) && defined(USE_THREADS)
258 MUTEX_UNLOCK(&accept_mutex);
259#endif
260 if(acceptResult < 0) {
261 return acceptResult;
262 }
263
264 populate_env(fcgx_req->envp, request->hvEnv);
265
266#ifdef USE_SFIO
267 for (i = 0; i < 3; ++i) {
268 request->io[i] = GvIOn(request->gv[i]);
269 if (!(i == 0 ? IoIFP(request->io[i])
270 : IoOFP(request->io[i]))) {
271 IoIFP(request->io[i]) = sftmp(0);
272 /*IoIFP(request->io[i]) = sfnew(NULL, NULL, SF_UNBOUND, 0,
273 SF_STRING | (i ? SF_WRITE : SF_READ));*/
274 if (i != 0)
275 IoOFP(request->io[i]) = IoIFP(request->io[i]);
276 request->sfcreated[i] = TRUE;
277 }
278 }
279#else
280 if (!request->svout) {
281 newSVrv(request->svout = newSV(0), "FCGI::Stream");
282 newSVrv(request->sverr = newSV(0), "FCGI::Stream");
283 newSVrv(request->svin = newSV(0), "FCGI::Stream");
284 }
9c31872c 285 sv_setiv(SvRV(request->svout), INT2PTR(IV, fcgx_req->out));
286 sv_setiv(SvRV(request->sverr), INT2PTR(IV, fcgx_req->err));
287 sv_setiv(SvRV(request->svin), INT2PTR(IV, fcgx_req->in));
0833402a 288#endif
289 FCGI_Bind(request);
290 request->accepted = TRUE;
291 }
292 return 0;
293}
294
295static void
296FCGI_Finish(FCGP_Request* request)
297{
298#ifdef USE_SFIO
299 int i;
300#endif
301 int was_bound;
302 dTHX;
303
304 if(!request->accepted) {
305 return;
306 }
307
308 if (was_bound = request->bound) {
309 FCGI_UndoBinding(request);
310 }
311#ifdef USE_SFIO
312 for (i = 0; i < 3; ++i) {
313 if (request->sfcreated[i]) {
314 sfclose(IoIFP(request->io[i]));
315 IoIFP(request->io[i]) = IoOFP(request->io[i]) = Nullfp;
316 request->sfcreated[i] = FALSE;
317 }
318 }
319#endif
320 if (was_bound)
321 FCGX_Finish_r(request->requestPtr);
322 else
bef7e0fc 323 FCGX_Free(request->requestPtr, 1);
0833402a 324 request->accepted = FALSE;
325}
326
327static int
328FCGI_StartFilterData(FCGP_Request* request)
329{
330 return request->requestPtr->in ?
331 FCGX_StartFilterData(request->requestPtr->in) : -1;
332}
333
334static FCGP_Request *
335FCGI_Request(in, out, err, env, socket, flags)
336 GV* in;
337 GV* out;
338 GV* err;
339 HV* env;
340 int socket;
341 int flags;
342{
343 FCGX_Request* fcgx_req;
344 FCGP_Request* req;
345
346 Newz(551, fcgx_req, 1, FCGX_Request);
347 FCGX_InitRequest(fcgx_req, socket, flags);
348 Newz(551, req, 1, FCGP_Request);
349 req->requestPtr = fcgx_req;
a6b22a6e 350 SvREFCNT_inc(in);
0833402a 351 req->gv[0] = in;
a6b22a6e 352 SvREFCNT_inc(out);
0833402a 353 req->gv[1] = out;
a6b22a6e 354 SvREFCNT_inc(err);
0833402a 355 req->gv[2] = err;
a6b22a6e 356 SvREFCNT_inc(env);
0833402a 357 req->hvEnv = env;
358
359 return req;
360}
361
362static void
363FCGI_Release_Request(FCGP_Request *req)
364{
a6b22a6e 365 SvREFCNT_dec(req->gv[0]);
366 SvREFCNT_dec(req->gv[1]);
367 SvREFCNT_dec(req->gv[2]);
368 SvREFCNT_dec(req->hvEnv);
0833402a 369 FCGI_Finish(req);
370 Safefree(req->requestPtr);
371 Safefree(req);
372}
373
374static void
375FCGI_Init()
376{
377#if defined(USE_LOCKING) && defined(USE_THREADS)
378 dTHX;
379
380 MUTEX_INIT(&accept_mutex);
381#endif
382
383 FCGX_Init();
384}
385
386typedef FCGX_Stream * FCGI__Stream;
387typedef FCGP_Request * FCGI;
388typedef GV* GLOBREF;
389typedef HV* HASHREF;
390
391MODULE = FCGI PACKAGE = FCGI PREFIX = FCGI_
392
393BOOT:
394 FCGI_Init();
395
396SV *
397RequestX(in, out, err, env, socket, flags)
398 GLOBREF in;
399 GLOBREF out;
400 GLOBREF err;
401 HASHREF env;
402 int socket;
403 int flags;
404
405 PROTOTYPE: ***$$$
406 CODE:
407 RETVAL = sv_setref_pv(newSV(0), "FCGI",
408 FCGI_Request(in, out, err, env, socket, flags));
409
410 OUTPUT:
411 RETVAL
412
413int
414OpenSocket(path, backlog)
415 char* path;
416 int backlog;
417
418 PROTOTYPE: $$
419 CODE:
420 RETVAL = FCGX_OpenSocket(path, backlog);
421 OUTPUT:
422 RETVAL
423
424void
425CloseSocket(socket)
426 int socket;
427
428 PROTOTYPE: $
429 CODE:
430 close(socket);
431
432int
433FCGI_Accept(request)
434
435 FCGI request;
436
437 PROTOTYPE: $
438
439void
440FCGI_Finish(request)
441 FCGI request;
442
443 PROTOTYPE: $
444
445void
446FCGI_Flush(request)
447 FCGI request;
448
449 PROTOTYPE: $
450
451HV *
452GetEnvironment(request)
453 FCGI request;
454
455 PROTOTYPE: $
456
457 CODE:
458 RETVAL = request->hvEnv;
459
460 OUTPUT:
461 RETVAL
462
463void
464GetHandles(request)
465 FCGI request;
466
467 PROTOTYPE: $
468
469 PREINIT:
470 int i;
471
472 PPCODE:
473 EXTEND(sp,3);
474 for (i = 0; i < 3; ++i)
475 PUSHs(sv_2mortal(newRV((SV *) request->gv[i])));
476
477int
478FCGI_IsFastCGI(request)
479 FCGI request;
480
481 PROTOTYPE: $
482
483void
484Detach(request)
0833402a 485 FCGI request;
486
487 PROTOTYPE: $
488
489 CODE:
490 if (request->accepted && request->bound)
491 FCGI_UndoBinding(request);
492
493void
494Attach(request)
0833402a 495 FCGI request;
496
497 PROTOTYPE: $
498
499 CODE:
500 if (request->accepted && !request->bound)
501 FCGI_Bind(request);
502
7fa2de73 503void
504LastCall(request)
505 FCGI request;
506
507 PROTOTYPE: $
508
509 CODE:
510 FCGX_ShutdownPending();
0833402a 511
512int
513FCGI_StartFilterData(request)
514
515 FCGI request;
516
517 PROTOTYPE: $
518
519void
520DESTROY(request)
521 FCGI request;
522
523 CODE:
524 FCGI_Release_Request(request);
525
526
527
528MODULE = FCGI PACKAGE = FCGI::Stream
529
530#ifndef USE_SFIO
531
532void
533PRINT(stream, ...)
534 FCGI::Stream stream;
535
536 PREINIT:
537 int n;
538
539 CODE:
540 for (n = 1; n < items; ++n) {
541 STRLEN len;
542 register char *tmps = (char *)SvPV(ST(n),len);
543 FCGX_PutStr(tmps, len, stream);
544 }
545 if (SvTRUEx(perl_get_sv("|", FALSE)))
546 FCGX_FFlush(stream);
547
548int
549WRITE(stream, bufsv, len, ...)
550 FCGI::Stream stream;
551 SV * bufsv;
552 int len;
553
554 PREINIT:
555 int offset;
556 char * buf;
557 STRLEN blen;
558 int n;
559
560 CODE:
561 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
562 buf = SvPV(bufsv, blen);
563 if (offset < 0) offset += blen;
564 if (len > blen - offset)
565 len = blen - offset;
566 if (offset < 0 || offset >= blen ||
567 (n = FCGX_PutStr(buf+offset, len, stream)) < 0)
568 ST(0) = &PL_sv_undef;
569 else {
570 ST(0) = sv_newmortal();
571 sv_setpvf(ST(0), "%c", n);
572 }
573
574int
575READ(stream, bufsv, len, ...)
576 FCGI::Stream stream;
577 SV * bufsv;
578 int len;
579
580 PREINIT:
581 int offset;
582 char * buf;
583
584 CODE:
585 offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
586 if (! SvOK(bufsv))
587 sv_setpvn(bufsv, "", 0);
588 buf = SvGROW(bufsv, len+offset+1);
589 len = FCGX_GetStr(buf+offset, len, stream);
590 SvCUR_set(bufsv, len+offset);
591 *SvEND(bufsv) = '\0';
592 (void)SvPOK_only(bufsv);
593 SvSETMAGIC(bufsv);
594 RETVAL = len;
595
596 OUTPUT:
597 RETVAL
598
599SV *
600GETC(stream)
601 FCGI::Stream stream;
602
603 PREINIT:
604 int retval;
605
606 CODE:
607 if ((retval = FCGX_GetChar(stream)) != -1) {
608 ST(0) = sv_newmortal();
609 sv_setpvf(ST(0), "%c", retval);
610 } else ST(0) = &PL_sv_undef;
611
612bool
613CLOSE(stream)
614 FCGI::Stream stream;
615
616# ALIAS:
617# DESTROY = 1
618
619 CODE:
620 RETVAL = FCGX_FClose(stream) != -1;
621
622 OUTPUT:
623 RETVAL
624
625#endif