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