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