Version 0.73
[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 /* Deprecation added 2010-10-05.  The deprecated functionality should not be
42  * removed for at least a year after that. */
43 #define WIDE_CHAR_DEPRECATION_MSG "Use of wide characters in %s is deprecated" \
44   " and will stop wprking in a future version of FCGI"
45
46 #if defined(USE_LOCKING) && defined(USE_THREADS)
47 static perl_mutex accept_mutex;
48 #endif
49
50 typedef struct FCGP_Request {
51     int         accepted;
52     int         bound;
53     SV*         svin;
54     SV*         svout;
55     SV*         sverr;
56     GV*         gv[3];
57     HV*         hvEnv;
58     FCGX_Request*   requestPtr;
59 } FCGP_Request;
60
61 static void FCGI_Finish(FCGP_Request* request);
62
63 static void 
64 FCGI_Flush(FCGP_Request* request) {
65     dTHX;
66     if(!request->bound)
67         return;
68     FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->svout))));
69     FCGX_FFlush(INT2PTR(FCGX_Stream *, SvIV((SV*) SvRV(request->sverr))));
70 }
71
72 static void
73 FCGI_UndoBinding(FCGP_Request* request) {
74     dTHX;
75 #ifdef USE_PERLIO
76     sv_unmagic((SV *)GvIOp(request->gv[0]), 'q');
77     sv_unmagic((SV *)GvIOp(request->gv[1]), 'q');
78     sv_unmagic((SV *)GvIOp(request->gv[2]), 'q');
79 #else
80     sv_unmagic((SV *)request->gv[0], 'q');
81     sv_unmagic((SV *)request->gv[1], 'q');
82     sv_unmagic((SV *)request->gv[2], 'q');
83 #endif
84     request->bound = FALSE;
85 }
86
87 static void
88 FCGI_Bind(FCGP_Request* request) {
89     dTHX;
90 #ifdef USE_PERLIO
91     /* For tied filehandles, we apply tiedscalar magic to the IO
92        slot of the GP rather than the GV itself. */
93
94     if (!GvIOp(request->gv[1]))
95         GvIOp(request->gv[1]) = newIO();
96     if (!GvIOp(request->gv[2]))
97         GvIOp(request->gv[2]) = newIO();
98     if (!GvIOp(request->gv[0]))
99         GvIOp(request->gv[0]) = newIO();
100
101     sv_magic((SV *)GvIOp(request->gv[1]), request->svout, 'q', Nullch, 0);
102     sv_magic((SV *)GvIOp(request->gv[2]), request->sverr, 'q', Nullch, 0);
103     sv_magic((SV *)GvIOp(request->gv[0]), request->svin, 'q', Nullch, 0);
104 #else
105     sv_magic((SV *)request->gv[1], request->svout, 'q', Nullch, 0);
106     sv_magic((SV *)request->gv[2], request->sverr, 'q', Nullch, 0);
107     sv_magic((SV *)request->gv[0], request->svin, 'q', Nullch, 0);
108 #endif
109     request->bound = TRUE;
110 }
111
112 static void
113 populate_env(char **envp, HV *hv) {
114     int i;
115     char *p, *p1;
116     SV *sv;
117     dTHX;
118
119     hv_clear(hv);
120     for(i = 0; ; i++) {
121         if((p = envp[i]) == NULL)
122             break;
123         p1 = strchr(p, '=');
124         assert(p1 != NULL);
125         sv = newSVpv(p1 + 1, 0);
126         /* call magic for this value ourselves */
127         hv_store(hv, p, p1 - p, sv, 0);
128         SvSETMAGIC(sv);
129     }
130 }
131
132 static int
133 FCGI_IsFastCGI(FCGP_Request* request) {
134     static int isCGI = -1; /* -1: not checked; 0: FCGI; 1: CGI */
135
136     if (request->requestPtr->listen_sock == FCGI_LISTENSOCK_FILENO) {
137         if (isCGI == -1)
138             isCGI = FCGX_IsCGI();
139         return !isCGI;
140     }
141
142     /* A explicit socket is being used -> assume FastCGI */
143     return 1;
144 }
145
146 static int 
147 FCGI_Accept(FCGP_Request* request) {
148     dTHX;
149
150     if (!FCGI_IsFastCGI(request)) {
151         static int been_here = 0;
152
153         /*
154         * Not first call to FCGI_Accept and running as CGI means
155         * application is done.
156         */
157         if (been_here)
158             return EOF;
159         been_here = 1;
160     } 
161     else {
162         FCGX_Request *fcgx_req = request->requestPtr;
163         int acceptResult;
164
165         FCGI_Finish(request);
166 #if defined(USE_LOCKING) && defined(USE_THREADS)
167         MUTEX_LOCK(&accept_mutex);
168 #endif
169         acceptResult = FCGX_Accept_r(fcgx_req);
170 #if defined(USE_LOCKING) && defined(USE_THREADS)
171         MUTEX_UNLOCK(&accept_mutex);
172 #endif
173         if(acceptResult < 0) {
174             return acceptResult;
175         }
176
177         populate_env(fcgx_req->envp, request->hvEnv);
178
179         if (!request->svout) {
180             newSVrv(request->svout = newSV(0), "FCGI::Stream");
181             newSVrv(request->sverr = newSV(0), "FCGI::Stream");
182             newSVrv(request->svin = newSV(0), "FCGI::Stream");
183         }
184         sv_setiv(SvRV(request->svout), INT2PTR(IV, fcgx_req->out));
185         sv_setiv(SvRV(request->sverr), INT2PTR(IV, fcgx_req->err));
186         sv_setiv(SvRV(request->svin), INT2PTR(IV, fcgx_req->in));
187         FCGI_Bind(request);
188         request->accepted = TRUE;
189     }
190     return 0;
191 }
192
193 static void 
194 FCGI_Finish(FCGP_Request* request) {
195     int was_bound;
196     dTHX;
197
198     if(!request->accepted)
199         return;
200
201     if (was_bound = request->bound)
202         FCGI_UndoBinding(request);
203     if (was_bound)
204         FCGX_Finish_r(request->requestPtr);
205     else
206         FCGX_Free(request->requestPtr, 1);
207     request->accepted = FALSE;
208 }
209
210 static int 
211 FCGI_StartFilterData(FCGP_Request* request) {
212     return request->requestPtr->in ? 
213         FCGX_StartFilterData(request->requestPtr->in) : -1;
214 }
215
216 static FCGP_Request *
217 FCGI_Request(GV *in, GV *out, GV *err, HV *env, int socket, int flags) {
218     FCGX_Request* fcgx_req;
219     FCGP_Request* req;
220
221     Newz(551, fcgx_req, 1, FCGX_Request);
222     FCGX_InitRequest(fcgx_req, socket, flags);
223     Newz(551, req, 1, FCGP_Request);
224     req->requestPtr = fcgx_req;
225     SvREFCNT_inc(in);
226     req->gv[0] = in;
227     SvREFCNT_inc(out);
228     req->gv[1] = out;
229     SvREFCNT_inc(err);
230     req->gv[2] = err;
231     SvREFCNT_inc(env);
232     req->hvEnv = env;
233
234     return req;
235 }
236
237 static void
238 FCGI_Release_Request(FCGP_Request *req) {
239     SvREFCNT_dec(req->gv[0]);
240     SvREFCNT_dec(req->gv[1]);
241     SvREFCNT_dec(req->gv[2]);
242     SvREFCNT_dec(req->hvEnv);
243     FCGI_Finish(req);
244     Safefree(req->requestPtr);
245     Safefree(req);
246 }
247
248 static void
249 FCGI_Init() {
250 #if defined(USE_LOCKING) && defined(USE_THREADS)
251     dTHX;
252     MUTEX_INIT(&accept_mutex);
253 #endif
254     FCGX_Init();
255 }
256
257 typedef FCGX_Stream* FCGI__Stream;
258 typedef FCGP_Request* FCGI;
259 typedef GV* GLOBREF;
260 typedef HV* HASHREF;
261
262 MODULE = FCGI PACKAGE = FCGI PREFIX = FCGI_
263
264 BOOT:
265     FCGI_Init();
266
267 SV *
268 RequestX(in, out, err, env, socket, flags)
269     GLOBREF in;
270     GLOBREF out;
271     GLOBREF err;
272     HASHREF env;
273     int     socket;
274     int     flags;
275   PROTOTYPE: ***$$$
276   CODE:
277     RETVAL = sv_setref_pv(newSV(0), "FCGI", 
278         FCGI_Request(in, out, err, env, socket, flags));
279   OUTPUT:
280     RETVAL
281
282 int
283 OpenSocket(path, backlog)
284     char* path;
285     int backlog;
286   PROTOTYPE: $$
287   CODE:
288     RETVAL = FCGX_OpenSocket(path, backlog);
289   OUTPUT:
290     RETVAL
291
292 void
293 CloseSocket(socket)
294     int socket;
295   PROTOTYPE: $
296   CODE:
297     close(socket);
298
299 int
300 FCGI_Accept(request)
301     FCGI    request;
302   PROTOTYPE: $
303
304 void
305 FCGI_Finish(request)
306     FCGI request;
307   PROTOTYPE: $
308
309 void
310 FCGI_Flush(request)
311     FCGI request;
312   PROTOTYPE: $
313
314 HV *
315 GetEnvironment(request)
316     FCGI request;
317   PROTOTYPE: $
318   CODE:
319     RETVAL = request->hvEnv;
320   OUTPUT: 
321     RETVAL
322
323 void
324 GetHandles(request)
325     FCGI request;
326   PROTOTYPE: $
327   PREINIT:
328     int i;
329   PPCODE:
330     EXTEND(sp,3);
331     for (i = 0; i < 3; ++i)
332         PUSHs(sv_2mortal(newRV((SV *) request->gv[i])));
333
334 int
335 FCGI_IsFastCGI(request)
336     FCGI request;
337   PROTOTYPE: $
338
339 void
340 Detach(request)
341     FCGI request;
342   PROTOTYPE: $
343   CODE:
344     if (request->accepted && request->bound) {
345         FCGI_UndoBinding(request);
346         FCGX_Detach(request->requestPtr);
347     }
348
349 void
350 Attach(request)
351     FCGI request;
352   PROTOTYPE: $
353   CODE:
354     if (request->accepted && !request->bound) {
355         FCGI_Bind(request);
356         FCGX_Attach(request->requestPtr);
357     }
358
359 void
360 LastCall(request)
361     FCGI request;
362   PROTOTYPE: $
363   CODE:
364     FCGX_ShutdownPending();
365
366 int
367 FCGI_StartFilterData(request)
368     FCGI request;
369   PROTOTYPE: $
370
371 void
372 DESTROY(request)
373     FCGI request;
374   CODE:
375     FCGI_Release_Request(request);
376
377 MODULE = FCGI PACKAGE = FCGI::Stream
378
379 SV *
380 PRINT(stream, ...)
381     FCGI::Stream stream;
382   PREINIT:
383     int n;
384     STRLEN len;
385     register char *str;
386     bool ok = TRUE;
387   CODE:
388     for (n = 1; ok && n < items; ++n) {
389 #ifdef DO_UTF8
390         if (DO_UTF8(ST(n)) && !sv_utf8_downgrade(ST(n), 1) && ckWARN_d(WARN_UTF8))
391             Perl_warner(aTHX_ WARN_UTF8, WIDE_CHAR_DEPRECATION_MSG,
392                         "FCGI::Stream::PRINT");
393 #endif
394         str = (char *)SvPV(ST(n),len);
395         if (FCGX_PutStr(str, len, stream) < 0)
396             ok = FALSE;
397     }
398     if (ok && SvTRUEx(perl_get_sv("|", FALSE)) && FCGX_FFlush(stream) < 0)
399         ok = FALSE;
400     RETVAL = ok ? &PL_sv_yes : &PL_sv_undef;
401   OUTPUT:
402     RETVAL
403
404 int
405 WRITE(stream, bufsv, len, ...)
406     FCGI::Stream stream;
407     SV *bufsv;
408     int len;
409   PREINIT:
410     int offset;
411     char *buf;
412     STRLEN blen;
413     int n;
414   CODE:
415     offset = (items == 4) ? (int)SvIV(ST(3)) : 0;
416 #ifdef DO_UTF8
417     if (DO_UTF8(bufsv) && !sv_utf8_downgrade(bufsv, 1) && ckWARN_d(WARN_UTF8))
418          Perl_warner(aTHX_ WARN_UTF8, WIDE_CHAR_DEPRECATION_MSG,
419                      "FCGI::Stream::WRITE");
420 #endif
421     buf = SvPV(bufsv, blen);
422     if (offset < 0) offset += blen;
423     if (len > blen - offset)
424         len = blen - offset;
425     if (offset < 0 || offset >= blen ||
426         (n = FCGX_PutStr(buf+offset, len, stream)) < 0) 
427         ST(0) = &PL_sv_undef;
428     else {
429         ST(0) = sv_newmortal();
430         sv_setiv(ST(0), n);
431     }
432
433 void
434 READ(stream, bufsv, len, ...)
435     FCGI::Stream stream;
436     SV *bufsv;
437     int len;
438   PREINIT:
439     int offset = 0;
440     char *buf;
441     STRLEN blen;
442   CODE:
443     if (items < 3 || items > 4)
444         croak("Usage: FCGI::Stream::READ(STREAM, SCALAR, LENGTH [, OFFSET ])");
445     if (len < 0)
446         croak("Negative length");
447     if (!SvOK(bufsv))
448         sv_setpvn(bufsv, "", 0);
449 #ifdef DO_UTF8
450     if (DO_UTF8(bufsv) && !sv_utf8_downgrade(bufsv, 1) && ckWARN_d(WARN_UTF8))
451          Perl_warner(aTHX_ WARN_UTF8, WIDE_CHAR_DEPRECATION_MSG,
452                      "FCGI::Stream::READ");
453 #endif
454     buf = SvPV_force(bufsv, blen);
455     if (items == 4) {
456         offset = SvIV(ST(3));
457         if (offset < 0) {
458             if (-offset > (int)blen)
459                 croak("Offset outside string");
460             offset += blen;
461         }
462     }
463     buf = SvGROW(bufsv, len + offset + 1);
464     if (offset > blen)
465         Zero(buf + blen, offset - blen, char);
466     len = FCGX_GetStr(buf + offset, len, stream);
467     SvCUR_set(bufsv, len + offset);
468     *SvEND(bufsv) = '\0';
469     (void)SvPOK_only(bufsv);
470     SvSETMAGIC(bufsv);
471     XSRETURN_IV(len);
472
473 SV *
474 GETC(stream)
475     FCGI::Stream stream;
476   PREINIT:
477     int retval;
478   CODE:
479     if ((retval = FCGX_GetChar(stream)) != -1) {
480         ST(0) = sv_newmortal();
481         sv_setpvf(ST(0), "%c", retval);
482     }
483     else
484         ST(0) = &PL_sv_undef;
485
486 bool
487 CLOSE(stream)
488     FCGI::Stream stream;
489   CODE:
490     RETVAL = FCGX_FClose(stream) != -1;
491   OUTPUT:
492     RETVAL