Commit | Line | Data |
c124bb9b |
1 | // |
e52a7487 |
2 | // $Id: fcgio.cpp,v 1.3 2000/07/27 12:21:54 robs Exp $ |
c124bb9b |
3 | // |
98e2ddaa |
4 | // Allows you communicate with FastCGI streams using C++ iostream |
5 | // objects |
c124bb9b |
6 | // |
98e2ddaa |
7 | // ORIGINAL AUTHOR: George Feinberg |
8 | // REWRITTEN BY: Michael Richards 06/20/1999 |
9 | // REWRITTEN AGAIN BY: Michael Shell 02/23/2000 |
c124bb9b |
10 | // |
98e2ddaa |
11 | // Special Thanks to Dietmar Kuehl for his help and the numerous custom |
12 | // streambuf examples on his web site. |
c124bb9b |
13 | // |
98e2ddaa |
14 | // see the header file fcgio2.h for lotsa docs and a code example. |
15 | // |
16 | // Copyright (c) 2000 Tux the Linux Penguin |
c124bb9b |
17 | // |
18 | // You are free to use this software without charge or royalty |
19 | // as long as this notice is not removed or altered, and recognition |
98e2ddaa |
20 | // is given to the author(s) |
21 | // |
22 | // This code is offered as-is without any warranty either expressed or |
23 | // implied; without even the implied warranty of MERCHANTABILITY or |
24 | // FITNESS FOR A PARTICULAR PURPOSE. |
25 | |
26 | /*------------------------------------------------------------------*/ |
27 | |
28 | |
e52a7487 |
29 | #include "fcgio.h" |
98e2ddaa |
30 | |
31 | |
32 | // **** fcgio_streambuf |
33 | |
34 | // default constructor |
35 | fcgi_streambuf::fcgi_streambuf(void) |
36 | { |
37 | // we setup the buffer |
38 | setb(buffer,buffer + (buffersize * sizeof(char)),0); |
39 | // but we do not know yet if we are a reader or |
40 | // a writer |
41 | setp(0,0); |
42 | setg(0,0,0); |
43 | |
44 | // good idea to declare buffer status |
45 | if (buffersize < 1) unbuffered(1); |
46 | else unbuffered(0); |
47 | |
48 | // disarm till initialized |
49 | fcgx_strm = NULL; |
50 | defined = 0; |
51 | } |
52 | |
53 | |
54 | // destructor |
55 | fcgi_streambuf::~fcgi_streambuf(void) {} |
56 | |
57 | |
58 | // do nothing |
59 | int fcgi_streambuf::doallocate() |
60 | { |
61 | return (0); |
62 | } |
63 | |
64 | |
65 | // drain the get area |
66 | // this method provides an advanced feature and is |
67 | // considered experimental, see the header file for docs |
68 | // it is provided as it may save somebody someday |
69 | int fcgi_streambuf::drain_strm(char *s, int n) |
70 | { |
71 | int numinget; // number of chars in get buffer |
72 | int numtoextract; // number of chars to pull out of the |
73 | // get area |
74 | |
75 | int i; // counter |
76 | |
77 | // no meaning if not initialized |
78 | if (!defined) return (EOF); |
79 | // is already drained if no buffer |
80 | if (unbuffered()) return (0); |
81 | |
82 | numinget = egptr() - gptr(); // calculate how much stuff is in |
83 | // the get buffer |
84 | |
85 | // bogus requests deserve bogus answers |
86 | if (n<1) return (0); |
87 | |
88 | // an empty get area is an already drained get area |
89 | if (numinget<1) return(0); |
90 | |
91 | // the number we extract is the lesser of the number of chars |
92 | // in the get area and the number of chars the user's array |
93 | // can hold. Maybe should use a ? operator here. |
94 | if (numinget<n) numtoextract = numinget; |
95 | else numtoextract = n; |
96 | |
97 | // copy over the data |
98 | // adjust the get pointer |
99 | // probably could use memcpy() to speed things up, |
100 | // however this may not be portable and if you are |
101 | // using drain(), performance is gonna take a back seat |
102 | for (i=0;i<numtoextract;i++) |
103 | { |
104 | s[i] = *gptr(); |
105 | gbump(1); |
106 | } |
107 | // all done |
108 | return (numtoextract); |
109 | } |
110 | |
111 | |
112 | // little routine so that you can tell if the streambuf |
113 | // was ever initialized |
114 | int fcgi_streambuf::isstrmdefined(void) |
115 | { |
116 | return defined; |
117 | } |
118 | |
119 | |
120 | // overflow is called for flush and every time the put buffer |
121 | // is full. Returns EOF on error |
122 | // This is the only method that actually writes data to |
123 | // the FCGI interface. |
124 | int fcgi_streambuf::overflow(int c) |
125 | { |
126 | int numinbuf; // number of chars in put buffer |
127 | |
128 | // we will not allow a bogus fcgx_strm (because the user |
129 | // forgot to attach()) to do even more harm. |
130 | if (!defined) return (EOF); |
131 | |
132 | // get the number of chars in the put buffer |
133 | numinbuf = pptr() - pbase(); |
134 | |
135 | // send out the entire put buffer |
136 | if (numinbuf > 0 && !unbuffered()) |
137 | if (FCGX_PutStr(pbase(), numinbuf, fcgx_strm) < 0) return (EOF); |
138 | |
139 | // reset the put buffer to empty |
140 | setp(pbase(),epptr()); |
141 | |
142 | // if we are given an overflow char, send it out too. |
143 | if (c >= 0) |
144 | if (FCGX_PutChar(c, fcgx_strm) < 0) return (EOF); |
145 | |
146 | // all done |
147 | return (0); |
148 | } |
149 | |
150 | |
151 | // we have our own methods to setup buffering |
152 | // in case somebody calls this, ignore it |
153 | streambuf * fcgi_streambuf::setbuf(char *s, int n) |
154 | { |
155 | // tell them what they want to hear to make them |
156 | // go away |
157 | return this; |
158 | } |
159 | |
160 | |
161 | // just sets up the pointer and declares things defined. |
162 | // used by fcgi_iostream attach() |
163 | void fcgi_streambuf::stream_initialize(FCGX_Stream *str, int dir) |
164 | { |
165 | // setup the main buffer |
166 | setb(buffer,buffer + (buffersize * sizeof(char)),0); |
167 | setp(0,0); |
168 | setg(0,0,0); |
169 | |
170 | if (buffersize < 1) unbuffered(1); |
171 | else // setup the get or put buffers |
172 | { |
173 | unbuffered(0); |
174 | if (dir) // if writer |
175 | { |
176 | setg(0,0,0); // no get buffer |
177 | setp(base(),ebuf()); // buffer is all put |
178 | } |
179 | else // reader |
180 | { |
181 | setg(base(),ebuf(),ebuf()); // buffer is all get |
182 | setp(0,0); // no put buffer |
183 | } |
184 | } |
185 | |
186 | // set the FCGI interface |
187 | fcgx_strm = str; |
188 | // we are ready for action |
189 | defined = 1; |
190 | } |
191 | |
192 | |
193 | // flush all the output |
194 | int fcgi_streambuf::sync() |
195 | { |
196 | // flush the put area |
197 | if (overflow(-1) < 0) return (EOF); |
198 | |
199 | // now flush FCGX |
200 | return FCGX_FFlush(fcgx_strm); |
201 | } |
c124bb9b |
202 | |
203 | |
204 | // Underflow is called to get characters to fill up the get buffer |
205 | // so something that uses the stream can get the data. If there |
98e2ddaa |
206 | // is nothing else to read, this returns EOF. This is the only method |
207 | // which reads from the FCGI interface. |
208 | int fcgi_streambuf::underflow() |
209 | { |
210 | int numread; |
211 | |
212 | // we will not allow a bogus fcgx_strm (because the user |
213 | // forgot to attach()) to do even more harm. |
214 | if (!defined) return (EOF); |
215 | |
216 | // if it is unbuffered, then get & return a char |
217 | if (unbuffered()) return FCGX_GetChar(fcgx_strm); |
c124bb9b |
218 | |
98e2ddaa |
219 | // read as much data as we can, then adjust the get area so it |
220 | // reflects the data we just read |
221 | numread=FCGX_GetStr(base(), blen(), fcgx_strm); |
222 | if (numread<=0) return EOF; |
223 | setg(base(),base(),base()+numread); |
c124bb9b |
224 | |
98e2ddaa |
225 | // We avoid a common bug. You NEED the unsigned char cast or |
226 | // else this routine will return a false EOF when reading |
227 | // control chars outside the normal ascii range! |
228 | // i.e. "negative" chars map to positive ints and we |
229 | // reserve negative return values for EOF and error |
230 | // conditions. Now, even binary data will come through |
231 | // perfectly. |
232 | // The reason the get buffer uses chars rather |
233 | // than unsigned chars is a mystery from AT&T history. |
234 | // It probably has to due with the old 7bit text limits. |
235 | return ((unsigned char)(*eback())); |
236 | } |
c124bb9b |
237 | |
238 | |
c124bb9b |
239 | |
98e2ddaa |
240 | // here comes the higher level iostream stuff |
c124bb9b |
241 | |
98e2ddaa |
242 | // **** Istream |
c124bb9b |
243 | |
98e2ddaa |
244 | // parameterless constructor allows us to create first, then |
245 | // initialize later via attach() |
246 | fcgi_istream::fcgi_istream() {} |
c124bb9b |
247 | |
248 | |
98e2ddaa |
249 | // or we can create and initialize in one step |
c124bb9b |
250 | // constructor calls ios::init to assign our streambuf to |
98e2ddaa |
251 | // the istream derived class. Also sets up buffering. |
252 | fcgi_istream::fcgi_istream(FCGX_Stream *str) |
253 | { |
254 | // initialize as a reader, use all buffering for the |
255 | // get area |
256 | fcgi_strmbuf.stream_initialize(str,0); |
257 | // init is a protected member of ios |
258 | init(&fcgi_strmbuf); |
259 | } |
260 | |
261 | |
262 | // destructor |
263 | fcgi_istream::~fcgi_istream() {} |
c124bb9b |
264 | |
265 | |
98e2ddaa |
266 | // does everything the constructor with parameter does at the |
267 | // request of the programmer |
268 | void fcgi_istream::attach(FCGX_Stream *str) |
269 | { |
270 | // initialize as a reader, use all buffering for the |
271 | // get area |
272 | fcgi_strmbuf.stream_initialize(str,0); |
273 | // init is a protected member of ios |
274 | init(&fcgi_strmbuf); |
275 | } |
276 | |
277 | |
278 | // experimental method to drain the get buffer. Allows you |
279 | // to sync reads with FCGI library (non-istream reads). |
280 | // reads upto n chars into the s array from the get buffer, does |
281 | // not call underflow(). Returned is the number of chars extracted |
282 | // or EOF on error. If n>0 (normal use) and returns a nonnegative |
283 | // value less than n, the get buffer is empty and it is safe to |
284 | // use an FCGI library read. |
285 | // see the header file for more info |
286 | int fcgi_istream::drain(char *s, int n) |
287 | { |
288 | return (fcgi_strmbuf.drain_strm(s,n)); |
289 | } |
290 | |
291 | |
292 | // little routine so that you can tell if the streambuf |
293 | // was ever defined |
294 | int fcgi_istream::isdefined(void) |
295 | { |
296 | return (fcgi_strmbuf.isstrmdefined()); |
297 | } |
298 | |
299 | |
300 | |
301 | // **** Ostream |
302 | |
303 | // parameterless constructor allows us to create first, then |
304 | // initialize later via attach() |
305 | fcgi_ostream::fcgi_ostream() {} |
306 | |
307 | |
308 | // or we can create and initialize in one step |
c124bb9b |
309 | // constructor calls ios::init to assign our streambuf to |
310 | // the ostream derived class. Also sets up buffering |
98e2ddaa |
311 | fcgi_ostream::fcgi_ostream(FCGX_Stream *str) |
312 | { |
313 | // initialize as a writer, use all buffering for the |
314 | // put area |
315 | fcgi_strmbuf.stream_initialize(str,1); |
c124bb9b |
316 | // init is a protected member of ios |
98e2ddaa |
317 | init(&fcgi_strmbuf); |
318 | } |
319 | |
320 | |
321 | // destructor |
322 | fcgi_ostream::~fcgi_ostream() |
323 | { |
324 | // don't blowup if the user never defined the |
325 | // stream |
326 | if (fcgi_strmbuf.isstrmdefined()) |
327 | { |
328 | // this may protect a few poor souls who forgot to flush |
329 | // before deleting. Always flush when you are done. |
330 | fcgi_strmbuf.sync(); |
331 | } |
332 | } |
333 | |
334 | |
335 | // does everything the constructor with parameter does at the |
336 | // request of the programmer |
337 | void fcgi_ostream::attach(FCGX_Stream *str) |
338 | { |
339 | // initialize as a writer, use all buffering for the |
340 | // put area |
341 | fcgi_strmbuf.stream_initialize(str,1); |
342 | // init is a protected member of ios |
343 | init(&fcgi_strmbuf); |
344 | } |
345 | |
c124bb9b |
346 | |
98e2ddaa |
347 | // helper function to find out if this |
348 | // stream was ever initialized. |
349 | int fcgi_ostream::isdefined(void) |
350 | { |
351 | return (fcgi_strmbuf.isstrmdefined()); |
352 | } |
c124bb9b |
353 | |