s/\t/ /
[catagits/fcgi2.git] / perl / FCGI.XL
1 use Config;
2
3 open OUT, ">FCGI.xs";
4
5 print "Generating FCGI.xs for Perl version $]\n";
6 #unless (exists $Config{apiversion} && $Config{apiversion} >= 5.005) 
7 unless ($] >= 5.005) {
8     for (qw(sv_undef diehook warnhook in_eval)) {
9     print OUT "#define PL_$_ $_\n" 
10     }
11 }
12 print OUT while <DATA>;
13 close OUT;
14 __END__
15 /* $Id: FCGI.XL,v 1.10 2003/06/22 00:24:11 robs Exp $ */
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
37 #ifndef INT2PTR
38 #define INT2PTR(a,b) ((a) (b))
39 #endif
40
41 #ifdef USE_SFIO
42 typedef struct
43 {
44     Sfdisc_t    disc;
45     FCGX_Stream *stream;
46 } FCGI_Disc;
47
48 static ssize_t
49 sffcgiread(f, buf, n, disc)
50 Sfio_t*     f;      /* stream involved */
51 Void_t*     buf;    /* buffer to read into */
52 size_t      n;      /* number of bytes to read */
53 Sfdisc_t*   disc;   /* discipline */
54 {
55     return FCGX_GetStr(buf, n, ((FCGI_Disc *)disc)->stream);
56 }
57
58 static ssize_t
59 sffcgiwrite(f, buf, n, disc)
60 Sfio_t*     f;      /* stream involved */
61 const Void_t*   buf;    /* buffer to read into */
62 size_t      n;      /* number of bytes to read */
63 Sfdisc_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
70 Sfdisc_t *
71 sfdcnewfcgi(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
87 Sfdisc_t *
88 sfdcdelfcgi(disc)
89     Sfdisc_t*   disc;
90 {
91     Safefree(disc);
92     return 0;
93 }
94 #endif
95
96 #if defined(USE_LOCKING) && defined(USE_THREADS)
97 static perl_mutex   accept_mutex;
98 #endif
99
100 typedef 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
115 static void FCGI_Finish(FCGP_Request* request);
116
117 static void 
118 FCGI_Flush(FCGP_Request* request)
119 {
120     dTHX;
121
122     if(!request->bound) {
123     return;
124     }
125 #ifdef USE_SFIO
126     sfsync(IoOFP(GvIOp(request->gv[1])));
127     sfsync(IoOFP(GvIOp(request->gv[2])));
128 #else
129     FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->svout))));
130     FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->sverr))));
131 #endif
132 }
133
134 static void
135 FCGI_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
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
149     sv_unmagic((SV *)request->gv[0], 'q');
150     sv_unmagic((SV *)request->gv[1], 'q');
151     sv_unmagic((SV *)request->gv[2], 'q');
152 #  endif
153 #endif
154     request->bound = FALSE;
155 }
156
157 static void
158 FCGI_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
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
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);
185 #  endif
186 #endif
187     request->bound = TRUE;
188 }
189
190 static void
191 populate_env(envp, hv)
192 char **envp;
193 HV *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
214 static int
215 FCGI_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
229 static int 
230 FCGI_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     }
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));
288 #endif
289     FCGI_Bind(request);
290     request->accepted = TRUE;
291     }
292     return 0;
293 }
294
295 static void 
296 FCGI_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
323     FCGX_Free(request->requestPtr, 1);
324     request->accepted = FALSE;
325 }
326
327 static int 
328 FCGI_StartFilterData(FCGP_Request* request)
329 {
330     return request->requestPtr->in ? 
331         FCGX_StartFilterData(request->requestPtr->in) : -1;
332 }
333
334 static FCGP_Request *
335 FCGI_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;
350     SvREFCNT_inc(in);
351     req->gv[0] = in;
352     SvREFCNT_inc(out);
353     req->gv[1] = out;
354     SvREFCNT_inc(err);
355     req->gv[2] = err;
356     SvREFCNT_inc(env);
357     req->hvEnv = env;
358
359     return req;
360 }
361
362 static void
363 FCGI_Release_Request(FCGP_Request *req)
364 {
365     SvREFCNT_dec(req->gv[0]);
366     SvREFCNT_dec(req->gv[1]);
367     SvREFCNT_dec(req->gv[2]);
368     SvREFCNT_dec(req->hvEnv);
369     FCGI_Finish(req);
370     Safefree(req->requestPtr);
371     Safefree(req);
372 }
373
374 static void
375 FCGI_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
386 typedef FCGX_Stream *   FCGI__Stream;
387 typedef FCGP_Request *  FCGI;
388 typedef GV*     GLOBREF;
389 typedef HV*     HASHREF;
390
391 MODULE = FCGI       PACKAGE = FCGI      PREFIX = FCGI_
392
393 BOOT:
394     FCGI_Init();
395
396 SV *
397 RequestX(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
413 int
414 OpenSocket(path, backlog)
415     char* path;
416     int backlog;
417
418     PROTOTYPE: $$
419     CODE:
420     RETVAL = FCGX_OpenSocket(path, backlog);
421     OUTPUT:
422     RETVAL
423
424 void
425 CloseSocket(socket)
426     int socket;
427
428     PROTOTYPE: $
429     CODE:
430     close(socket);
431
432 int
433 FCGI_Accept(request)
434
435     FCGI    request;
436
437     PROTOTYPE: $
438
439 void
440 FCGI_Finish(request)
441     FCGI    request;
442
443     PROTOTYPE: $
444
445 void
446 FCGI_Flush(request)
447     FCGI    request;
448
449     PROTOTYPE: $
450
451 HV *
452 GetEnvironment(request)
453     FCGI    request;
454
455     PROTOTYPE: $
456
457     CODE:
458     RETVAL = request->hvEnv;
459
460     OUTPUT: 
461     RETVAL
462
463 void
464 GetHandles(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
477 int
478 FCGI_IsFastCGI(request)
479     FCGI    request;
480
481     PROTOTYPE: $
482
483 void
484 Detach(request)
485     FCGI    request;
486
487     PROTOTYPE: $
488
489     CODE:
490     if (request->accepted && request->bound) {
491         FCGI_UndoBinding(request);
492         FCGX_Detach(request->requestPtr);
493     }
494
495 void
496 Attach(request)
497     FCGI    request;
498
499     PROTOTYPE: $
500
501     CODE:
502     if (request->accepted && !request->bound) {
503         FCGI_Bind(request);
504         FCGX_Attach(request->requestPtr);
505     }
506
507 void
508 LastCall(request)
509     FCGI    request;
510
511     PROTOTYPE: $
512
513     CODE:
514     FCGX_ShutdownPending();
515
516 int
517 FCGI_StartFilterData(request)
518
519     FCGI    request;
520
521     PROTOTYPE: $
522
523 void
524 DESTROY(request)
525     FCGI    request;
526
527     CODE:
528     FCGI_Release_Request(request);
529
530
531
532 MODULE = FCGI       PACKAGE = FCGI::Stream
533
534 #ifndef USE_SFIO
535
536 SV *
537 PRINT(stream, ...)
538     FCGI::Stream    stream;
539
540     PREINIT:
541     int n;
542     STRLEN len;
543     register char *str;
544     bool ok = TRUE;
545
546     CODE:
547     for (n = 1; ok && n < items; ++n) {
548 #ifdef DO_UTF8
549         if (DO_UTF8(ST(n)) && !sv_utf8_downgrade(ST(n), 1) && ckWARN_d(WARN_UTF8))
550             Perl_warner(aTHX_ WARN_UTF8, "Wide character in FCGI::Stream::PRINT");
551 #endif
552         str = (char *)SvPV(ST(n),len);
553         if (FCGX_PutStr(str, len, stream) < 0)
554             ok = FALSE;
555     }
556
557     if (ok && SvTRUEx(perl_get_sv("|", FALSE)) && FCGX_FFlush(stream) < 0)
558         ok = FALSE;
559
560     RETVAL = ok ? &PL_sv_yes : &PL_sv_undef;
561     OUTPUT:
562         RETVAL
563
564 int
565 WRITE(stream, bufsv, len, ...)
566     FCGI::Stream    stream;
567     SV *    bufsv;
568     int len;
569
570     PREINIT:
571     int offset;
572     char *  buf;
573     STRLEN  blen;
574     int n;
575
576     CODE:
577     offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
578 #ifdef DO_UTF8
579     if (DO_UTF8(bufsv) && !sv_utf8_downgrade(bufsv, 1) && ckWARN_d(WARN_UTF8))
580          Perl_warner(aTHX_ WARN_UTF8, "Wide character in FCGI::Stream::WRITE");
581 #endif
582     buf = SvPV(bufsv, blen);
583     if (offset < 0) offset += blen;
584     if (len > blen - offset)
585         len = blen - offset;
586     if (offset < 0 || offset >= blen ||
587         (n = FCGX_PutStr(buf+offset, len, stream)) < 0) 
588         ST(0) = &PL_sv_undef;
589     else {
590         ST(0) = sv_newmortal();
591         sv_setiv(ST(0), n);
592     }
593
594 int
595 READ(stream, bufsv, len, ...)
596     FCGI::Stream    stream;
597     SV *    bufsv;
598     int len;
599
600     PREINIT:
601     int offset;
602     char *  buf;
603
604     CODE:
605     offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
606 #ifdef DO_UTF8
607     if (DO_UTF8(bufsv) && !sv_utf8_downgrade(bufsv, 1))
608          croak("Wide character in FCGI::Stream::READ");
609 #endif
610     if (! SvOK(bufsv))
611         sv_setpvn(bufsv, "", 0);
612     buf = SvGROW(bufsv, len+offset+1);
613     len = FCGX_GetStr(buf+offset, len, stream);
614     SvCUR_set(bufsv, len+offset);
615     *SvEND(bufsv) = '\0';
616     (void)SvPOK_only(bufsv);
617     SvSETMAGIC(bufsv);
618     RETVAL = len;
619
620     OUTPUT:
621     RETVAL
622
623 SV *
624 GETC(stream)
625     FCGI::Stream    stream;
626
627     PREINIT:
628     int retval;
629
630     CODE:
631     if ((retval = FCGX_GetChar(stream)) != -1) {
632         ST(0) = sv_newmortal();
633         sv_setpvf(ST(0), "%c", retval);
634     } else ST(0) = &PL_sv_undef;
635
636 bool
637 CLOSE(stream)
638     FCGI::Stream    stream;
639
640 #   ALIAS:
641 #   DESTROY = 1
642
643     CODE:
644     RETVAL = FCGX_FClose(stream) != -1;
645
646     OUTPUT:
647     RETVAL
648
649 #endif