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