Hi folks,
robs [Fri, 21 Jul 2000 20:56:28 +0000 (20:56 +0000)]
The next two posts (subject fcgio2.h and fcgio2.cpp) contain the
documentation and source code for the new version of the fcgio
FCGI C++ iostream library.

The new version fixes two very bad bugs in the original version:

1. falsely indicating an EOF when uploading binary data as would
   be done with a <input type=file> form using multipart form
   encoding - or if a hacker were to send binary data instead of
   normal form data.

2. data loss when using different read methods, such as a getline()
   followed by a read().

There is also now full buffer support for both input and output
streams.

Two new methods have been added. Attach() lets you attach to
an fcgi stream pointer in your code, so that you can construct
your streams at the beginning of your code and attach() to the fcgi
interface pointers later (i.e. after accept())
Drain() is a method which provides a way to drain the get buffer
for those of you who need to mix istream code with the I/O
provided by the fcgiapp library (and hence sync the inputs).

The docs have been greatly expanded to include some example code
in the header file. See fcgio2.h for full details.

Note that the stream types are now fcgi_istream and fcgi_ostream
NOT fcgio_xxx as was done in the older version.

It is my hope that this code provides everyone with a higher
performance, more reliable C++ interface than that which previously
existed.

 Enjoy!

 Mike Shell

Modified Files:  libfcgi/fcgio.cpp include/fcgio.h

include/fcgio.h
libfcgi/fcgio.cpp

index f573c11..d647203 100644 (file)
-// fcgio.h - defines classes fcgio_buf, fcgio_ostream, fcgio_istream
-//           and replaces cin, cout and cerr with FastCGI versions
-//           you must include this file in any source file which 
-//           uses cin, cout or cerr. It must also appear at the end
-//           of your includes
 //
-// $Id: fcgio.h,v 1.1 1999/08/15 03:37:59 roberts Exp $
+// $Id: fcgio.h,v 1.2 2000/07/21 20:56:28 robs Exp $
 //
-// This work is based on routines written by George Feinberg. They 
-// have been mostly re-written and extensively changed by 
+// Allows you communicate with FastCGI streams using C++ iostream
+// objects
+//
+// defines classes fcgi_streambuf, fcgi_ostream, fcgi_istream
+// you can redefine cin, cout, cerr or use your own custom
+// FCGI stream names.
+//
+// ORIGINAL AUTHOR: George Feinberg
+//
+//
+// REWRITTEN BY: Michael Richards  06/20/1999
+//          -added cin support
+//          -added ability to replace cin/cout so existing code only
+//           needs to include the header file and use the normal
+//           cin/cout. This has been altered as of 2/2000, see below.
+//          -added buffered read support which is required for ungets.
+//
+//
+// REWRITTEN AGAIN BY: Michael Shell 02/23/2000
+//
+//            Previous versions of this code had problems.
+//            Two hellish bugs have been fixed and there is now full
+//            buffering for both input and output streams.
+//
+//          - fixed signed char bug in underflow() that would
+//            cause a false EOF to be flagged when reading binary
+//            data. Uploading short binary files via <input type=file
+//            which uses multipart/form-data encoding would reveal
+//            this bug. Also could be triggered by hackers
+//            sending binary data instead of legitimate form data,
+//            in which case the hung network connection, depending on
+//            how the FCGI application server handles things, could
+//            form the basis for a stupid denial of service attack.
+//          - fixed code to properly use the get and put buffers via
+//            underflow() and overflow() NOT xsgetn() and xsputn() as
+//            was done before. Because of this, the previous
+//            version would often drop data, especially if the
+//            user did an initial getline() followed by a read().
+//          - added the attach() method and a parameterless
+//            constructor so that you can declare fcgi_iostream
+//            objects and attach() to them later - after accept().
+//          - enhanced docs to include examples that actually work.
+//          - removed any predefined redefinitions of cin,cout,cerr.
+//            The example shows how you can use these names if you
+//            want (via properly placed #undefs) or use ones of your
+//            choosing (such as fin,fout,ferr). This may be very
+//            helpful when testing code. Also, as a result, you
+//            no longer have to place fcgio2.h in any special
+//            order in your #includes.
+//          - added an experimental method drain() to istream which
+//            allows the user to drain the get buffer. This is
+//            designed to provide users with a way to drain the get
+//            buffer prior to using a read function from the FCGI
+//            library in applications which mix I/O methods. i.e.
+//            it is the input equivalent to flush(). It does not
+//            read from the FCGI stream, but gets only characters
+//            already in the istream buffer. Mixing I/O methods is
+//            not recommended since this iostream implementation
+//            is complete and should provide you with everything
+//            you need.
+//
+//
+// NOTES: encapsulates the FastCGI protocol in an iostream via a
+// nice custom streambuf. Very nice, very robust, and very powerful.
+//
+// This work is based on routines written by George Feinberg. They
+// have been mostly re-written and extensively changed by
 // Michael Richards.
 //
-// Copyright (c) 1999 by Apollo Software. All rights reserved.
+// Rewritten again with bug fixes and numerous enhancements by
+// Michael Shell.
+//
+// Special Thanks to Dietmar Kuehl for his help and the numerous custom
+// streambuf examples on his web site.
+//
+// Copyright (c) 2000 Tux the Linux Penguin
 //
 // You are free to use this software without charge or royalty
-// as long as this notice is not removed or altered, and recognition 
+// as long as this notice is not removed or altered, and recognition
 // is given to the author(s)
 //
-   
+// This code is offered as-is without any warranty either expressed or
+// implied; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.
+// If it breaks, you get to keep both halves.
+
+
+// BEGIN EXAMPLE CODE: test_fcgio2.cpp
+/*
+// This code uses the fcgiapp interface to show a little bit more
+// complexity and to demonstrate the fact that with fcgio2 and just
+// a few more lines of code (like FCGX_Init etc.) you can easily
+// make FastCGI programs without needing wrapper functions.
+// You can use the fcgi_stdio interface if you you want a
+// simpler accept(). However note that the fcgio2 interface
+// removes the need for the fcgi_stdio wrapper functions.
+// i.e. Why override printf when you aren't going to use it anyway?
+// Also, be aware that because of iostream buffering, you must take
+// care when mixing FCGI iostream I/O with the FCGI library I/O
+// commands (such as printf). Be sure to flush() any open
+// fcgi_ostreams prior to using commands such as printf. This is true
+// even on systems which have the C I/O commands synced with C++
+// iostreams, such as Linux, because the FCGI wrapper printf, etc. are
+// not the same as the "normal" printf etc. It is recommended that you
+// not use any FCGI library input (read) commands if you use
+// fcgi_istream (cin) input as there is no easy way to "flush" (drain)
+// an istream get buffer. However, an experimental istream method
+// drain() has been provided to istream for those of you who need to
+// do mixed input. There should be no need to do mixed I/O as the
+// fcgio2 iostream implementation is complete.
+
+#include <stdlib.h>
+#include "fcgio2.h"  // fcgio2.h includes fcgiapp.h
+                     // however you must include fcgi_stdio.h if
+                     // you want to use it as fcgio2.h does not
+                     // include it for you
+
+#undef cin  // remember you have to undo the stuff predefined
+#undef cout // for cin, cout, cerr if you wish to use these
+#undef cerr // names for your FastCGI streams
+
+int main(void)
+{
+
+ int count = 0;
+
+ // I can create/declare my objects here, but I don't dare use them
+ // until I set them up with attach().
+
+ // note that version 1.0 of fcgio used fcgio_istream & fcgio_ostream
+ // we made a little change to the names in V2.00 for clarity.
+ fcgi_istream cin;  // you do not *HAVE* to use these names, you
+ fcgi_ostream cout; // could use other stream names of your choice
+ fcgi_ostream cerr; // don't forget that the input is
+                    // fcgi_*I*stream class
+
+ FCGX_Request request; // here is our request structure
+
+ // get everything ready for the customers
+ FCGX_Init();
+
+ FCGX_InitRequest(&request,0,0);
+
+
+ // let the games begin
+ while(FCGX_Accept_r(&request) >= 0)
+    {
+    count++;
+
+    cout.attach(request.out); // now I know my pointer values for
+    cerr.attach(request.err); // this request. attach to them
+    cin.attach(request.in);
+    // attach will initialize everything
+    // alternatively, you could declare the streams here and the
+    // constructor with the (FCGX_Stream *str) parameter would
+    // do the same job as attach
+
+    // If you are using fcgi_stdio.h, the equivalent command would
+    // be cout.attach(FCGI_stdout->fcgx_stream);
+    // and so forth for cin,cerr using FCGI_stdin and FCGI_stderr
+    // respectively.
+
+
+    // now I can fire at will:
+    cout << "Content-type: text/html\r\n\r\n"
+         << "<title> FastCGI cin, cout, cerr tester </title>\r\n"
+         << "<h1><center> FastCGI C++ IOstream test: "
+         << "It works! </center></h1>\r\n";
+    cout << "<h4><center><i> Total served by this task: "
+         << count << "</i></center></h4>\r\n";
+
+// didn't use cin or cerr in this example.
+
+// it is good practice to flush the buffers.
+// use cout.flush() if you don't want the line feed.
+
+   cout << endl;
+
+// there is no cxxx.close() and you do not need it with the fcgio
+// interface. You would need to call cxxx.close() if cxxx was an
+// fstream based object and attached to a file descripter you got
+// from a command like fd=open(blah). (GNU (Linux) based fstreams
+// support this) Then you need to call cxxx.close() before you
+// close the physical file with close(fd). If doing this with
+// fstream objects, you should call cxxx.clear() after
+// attach(fd) as the file descriptor attach is not as complete in
+// initialization as our fcgi_iostream attach()
+// If you don't understand any of this, don't worry about it and
+// forget I even mentioned it.
+
+   // all done with this request
+   FCGX_Finish_r(&request);
+
+   // do the next request
+   }
+ return (1);
+}
+
+*/
+
+// END EXAMPLE CODE
+
+/*------------------------------------------------------------------*/
+
+
 #ifndef FCGIO_H
 #define FCGIO_H
-   
+
 #include <iostream.h>
 #include <fcgiapp.h>
-   
 
-// FastCGI streambuf replacement. Implements low level io to the 
-// FastCGI c functions so our higher level iostreams will talk
-// in the fcgi protocol
-class fcgio_buf : public streambuf {
-  // construction
+
+// FastCGI streambuf replacement. Implements low level I/O to the
+// FastCGI C functions so our higher level iostreams will talk
+// in the FastCGI protocol
+class fcgi_streambuf : public streambuf
+{
   public:
-    fcgio_buf( FCGX_Stream *str) { fcgi_str = str; }
-    ~fcgio_buf() {}
-   
-    // from streambuf
-    // tells the stream to go flush itself
-    virtual int sync();
-    // handles allocation of buffers by failing since buffer support isn't used
-    virtual int doallocate() { return 0; }
-   
-    virtual int xsgetn(char *s, int n);
-    virtual int xsputn(const char* s, int n);
+    // constructor
+    fcgi_streambuf(void);
+    ~fcgi_streambuf();
+
+    // handles allocation of buffers by doing nothing since buffer
+    // allocation isn't needed
+    virtual int doallocate();
+
+    // gets data (upto the given maximum) from the get buffer and
+    // copies it to the given char array. Returns the number of chars
+    // written or -1 on error. A returned value less than the given
+    // maximum, assuming the user requested at least one char,
+    // indicates that the get buffer is empty. The underflow() method
+    // is never called to refill the get buffer, so this method can be
+    // used to drain the get buffer. It is used to form an istream
+    // drain() method which is the input equivalent to flush().
+    virtual int drain_strm(char *,int);
+
+    // let's us know if this strembuf has been initialized
+    virtual int isstrmdefined(void);
+
+    // (for writers) empties the put buffer and possibly an
+    // overflow char into the FCGI interface
     virtual int overflow(int);
+
+    // bogus routine in case somebody thinks they know
+    // better and calls it. We currently are happy with
+    // our static buffer.
+    virtual streambuf * setbuf(char *, int);
+
+    // initializes the buffering and FCGI interface
+    virtual void stream_initialize(FCGX_Stream *,int);
+
+    // (for writers) flushes the put buffer into the FCGI
+    // interface and then flushes the FCGI interface
+    virtual int sync();
+
+    // (for readers) fills the get buffer with data from the
+    // FCGI interface
     virtual int underflow();
-    virtual streambuf* setbuf(char* s, int n);
-   
+
   private:
-    FCGX_Stream * fcgi_str;
-};
+    // pointer to our underlying FCGI interface
+    FCGX_Stream * fcgx_strm;
 
-   
-// Here's the ostream class definition. All it has to do is
-// call ios::init() passing an instance of fcgio_buf
-class fcgio_ostream : public ostream {
-  public:
-    fcgio_ostream(FCGX_Stream *str);
-    ~fcgio_ostream() { buf.sync(); }
+    // our buffer
+    // we aren't pulling from the heap, so it is best not
+    // to make it too big
+    static const int buffersize=200;
+    char buffer[buffersize];
 
-  private:
-    fcgio_buf buf;
+    // little flag so that we can tell if the
+    // fcgi_str pointer was ever set
+    int defined;
 };
 
 
-// Here's the istream class definition. All it has to do is
-// call ios::init() passing an instance of fcgio_buf
-class fcgio_istream : public istream {
+
+// Here's the istream class definition.
+class fcgi_istream : public istream
+{
   public:
-    fcgio_istream(FCGX_Stream *str);
-    ~fcgio_istream() {}
+    fcgi_istream();
+    fcgi_istream(FCGX_Stream *str);
+    ~fcgi_istream();
+
+    // connects the fcgi_streambuf of  an existing fcgi_istream
+    // object to a given FCGI interface
+    virtual void attach(FCGX_Stream *str);
+
+    // allows you to drain down the streambuf buffer. It will not
+    // read any chars from the FCGI interface. You can repeatedly
+    // call drain() to empty the get buffer prior to using a
+    // FCGI library function to ensure syncronization of the
+    // reads. i.e. it flushes the input stream
+    // This method should be considered both nonstandard and
+    // experimental. It's use is entirely optional to the user,
+    // but could be very helpful for applications which use
+    // both istream and FCGI library based reads on a single
+    // FCGI interface and need a way to sync the reads.
+    // It copies upto the given number of chars into the given
+    // char array. It returns the number of chars written or
+    // -1 on error. If the number of chars written is less than
+    // the number the user requested, the get buffer is empty.
+    // This method does not alter or check any of the input
+    // status flags such as EOF or FAIL since it does not interact
+    // with the underlying FCGI interface at all - it only reads from
+    // the get buffer.
+    virtual int drain(char *,int);
+
+    // lets us know if this object has been initialized
+    virtual int isdefined(void);
 
   private:
-    // FastCGI stuff used for the FCGX_ functions
-    fcgio_buf buf;
-    // buffer for getting data
-    const int buffersize=100;
-    char buffer[buffersize];
+    // FastCGI streambuf
+    fcgi_streambuf fcgi_strmbuf;
+
 };
 
 
-// replace cin and cout with our own versions
-#ifndef USE_CIN_COUT_CERR
-  #undef cout   
-  #define cout (*FCGIX_cout)
-  extern fcgio_ostream *FCGIX_cout;
 
-  #undef cerr   
-  #define cerr (*FCGIX_cerr)
-  extern fcgio_ostream *FCGIX_cerr;
+// Here's the ostream class definition.
+class fcgi_ostream : public ostream
+{
+  public:
+    fcgi_ostream(void);
+    fcgi_ostream(FCGX_Stream *str);
+    ~fcgi_ostream();
+
+  // connects the fcgi_streambuf of an existing fcgi_ostream
+  // object to a given FCGI interface
+  virtual void attach(FCGX_Stream *str);
+
+  // lets us know if this object has been initialized
+  virtual int isdefined(void);
 
-  #undef cin
-  #define cin (*FCGIX_cin)
-  extern fcgio_istream *FCGIX_cin;
-#endif
+  private:
+    // FastCGI streambuf
+    fcgi_streambuf fcgi_strmbuf;
+};
 
-#endif __FCCIO_H__
+#endif FCGIO_H
index f43ac6c..66489ed 100644 (file)
-// FILE NAME: fcgio.cpp
 //
-// $Id: fcgio.cpp,v 1.1 1999/08/15 03:37:59 roberts Exp $
+// $Id: fcgio.cpp,v 1.2 2000/07/21 20:56:28 robs Exp $
 //
-// REWRITTEN BY: Michael Richards  06/20/1999
-//          -added cin support
-//          -added ability to replace cin/cout so existing code only
-//           needs to include the header file and use the normal cin/cout
-//          -added buffered read support which is required by getline
+// Allows you communicate with FastCGI streams using C++ iostream
+// objects
 //
-// ORIGINAL AUTHOR: George Feinberg
+// ORIGINAL AUTHOR:     George Feinberg
+// REWRITTEN BY:        Michael Richards  06/20/1999
+// REWRITTEN AGAIN BY:  Michael Shell     02/23/2000
 //
-// NOTES: encapsulates FCGI function in an ostream and replaces cin,
-//        cout and cerr
+// Special Thanks to Dietmar Kuehl for his help and the numerous custom
+// streambuf examples on his web site.
 //
-// This work is based on routines written by George Feinberg. They   
-// have been mostly re-written and extensively changed by
-// Michael Richards.
-//    
-// Copyright (c) 1999 by Apollo Software. All rights reserved.
+// see the header file fcgio2.h for lotsa docs and a code example.
+//
+// Copyright (c) 2000 Tux the Linux Penguin
 //
 // You are free to use this software without charge or royalty
 // as long as this notice is not removed or altered, and recognition
-// is given to the author(s)   
-//   
-
-#include <fcgiapp.h>
-#include "fcgio.h"
-
-
-// the definition that replaces cout and cin
-#ifndef USE_CIN_COUT_CERR
-  fcgio_ostream *FCGIX_cout;
-  fcgio_ostream *FCGIX_cerr;
-  fcgio_istream *FCGIX_cin;
-#endif
-   
-/*---------------------------------------------------------------------*/
-   
-
-// gets a number of characters at once for a more efficient implementation
-int fcgio_buf::xsgetn(char *s, int n) {
-  return FCGX_GetStr(s, n, fcgi_str);
-}
-   
-
-// writes a gob of data out, This is called for things like:
-// output << "hello world"
-// output.write(string, n);
-int fcgio_buf::xsputn(const char* s, int n) {
-  return FCGX_PutStr(s, n, fcgi_str);
-}
-   
-
-// overflow is called for "endl" and every time the put buffer
-// is full. Since we are using a single char put buffer, we just
-// pump the waiting character out and return the number of chars
-// we just got rid of (1). Returns EOF on error
-int fcgio_buf::overflow(int c) {
-  if (c)
-    return FCGX_PutChar(c, fcgi_str);
-  else
-    return EOF;
-}
+// is given to the author(s)
+//
+// This code is offered as-is without any warranty either expressed or
+// implied; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.
+
+/*------------------------------------------------------------------*/
+
+
+#include "fcgio2.h"
+
+
+// **** fcgio_streambuf
+
+// default constructor
+fcgi_streambuf::fcgi_streambuf(void)
+   {
+   // we setup the buffer
+   setb(buffer,buffer + (buffersize * sizeof(char)),0);
+   // but we do not know yet if we are a reader or
+   // a writer
+   setp(0,0);
+   setg(0,0,0);
+
+   // good idea to declare buffer status
+   if (buffersize < 1) unbuffered(1);
+   else unbuffered(0);
+
+   // disarm till initialized
+   fcgx_strm = NULL;
+   defined = 0;
+   }
+
+
+// destructor
+fcgi_streambuf::~fcgi_streambuf(void) {}
+
+
+// do nothing
+int fcgi_streambuf::doallocate()
+   {
+   return (0);
+   }
+
+
+// drain the get area
+// this method provides an advanced feature and is
+// considered experimental, see the header file for docs
+// it is provided as it may save somebody someday
+int fcgi_streambuf::drain_strm(char *s, int n)
+   {
+   int numinget;     // number of chars in get buffer
+   int numtoextract; // number of chars to pull out of the
+                     // get area
+
+   int i;            // counter
+
+   // no meaning if not initialized
+   if (!defined) return (EOF);
+   // is already drained if no buffer
+   if (unbuffered()) return (0);
+
+   numinget = egptr() - gptr(); // calculate how much stuff is in
+                                // the get buffer
+
+   // bogus requests deserve bogus answers
+   if (n<1) return (0);
+
+   // an empty get area is an already drained get area
+   if (numinget<1) return(0);
+
+   // the number we extract is the lesser of the number of chars
+   // in the get area and the number of chars the user's array
+   // can hold. Maybe should use a ? operator here.
+   if (numinget<n) numtoextract = numinget;
+   else numtoextract = n;
+
+   // copy over the data
+   // adjust the get pointer
+   // probably could use memcpy() to speed things up,
+   // however this may not be portable and if you are
+   // using drain(), performance is gonna take a back seat
+   for (i=0;i<numtoextract;i++)
+       {
+       s[i] = *gptr();
+       gbump(1);
+       }
+   // all done
+   return (numtoextract);
+   }
+
+
+// little routine so that you can tell if the streambuf
+// was ever initialized
+int fcgi_streambuf::isstrmdefined(void)
+   {
+   return defined;
+   }
+
+
+// overflow is called for flush and every time the put buffer
+// is full. Returns EOF on error
+// This is the only method that actually writes data to
+// the FCGI interface.
+int fcgi_streambuf::overflow(int c)
+  {
+  int numinbuf; // number of chars in put buffer
+
+  // we will not allow a bogus fcgx_strm (because the user
+  // forgot to attach()) to do even more harm.
+  if (!defined) return (EOF);
+
+  // get the number of chars in the put buffer
+  numinbuf = pptr() - pbase();
+
+  // send out the entire put buffer
+  if (numinbuf > 0 && !unbuffered())
+     if (FCGX_PutStr(pbase(), numinbuf, fcgx_strm) < 0) return (EOF);
+
+  // reset the put buffer to empty
+  setp(pbase(),epptr());
+
+  // if we are given an overflow char, send it out too.
+  if (c >= 0)
+     if (FCGX_PutChar(c, fcgx_strm) < 0) return (EOF);
+
+  // all done
+  return (0);
+  }
+
+
+// we have our own methods to setup buffering
+// in case somebody calls this, ignore it
+streambuf * fcgi_streambuf::setbuf(char *s, int n)
+   {
+   // tell them what they want to hear to make them
+   // go away
+   return this;
+   }
+
+
+// just sets up the pointer and declares things defined.
+// used by fcgi_iostream attach()
+void fcgi_streambuf::stream_initialize(FCGX_Stream *str, int dir)
+   {
+   // setup the main buffer
+   setb(buffer,buffer + (buffersize * sizeof(char)),0);
+   setp(0,0);
+   setg(0,0,0);
+
+   if (buffersize < 1) unbuffered(1);
+   else // setup the get or put buffers
+      {
+      unbuffered(0);
+      if (dir) // if writer
+         {
+         setg(0,0,0); // no get buffer
+         setp(base(),ebuf()); // buffer is all put
+         }
+      else // reader
+         {
+         setg(base(),ebuf(),ebuf()); // buffer is all get
+         setp(0,0); // no put buffer
+         }
+      }
+
+   // set the FCGI interface
+   fcgx_strm = str;
+   // we are ready for action
+   defined = 1;
+   }
+
+
+// flush all the output
+int fcgi_streambuf::sync()
+   {
+   // flush the put area
+   if (overflow(-1) < 0) return (EOF);
+
+   // now flush FCGX
+   return FCGX_FFlush(fcgx_strm);
+   }
 
 
 // Underflow is called to get characters to fill up the get buffer
 // so something that uses the stream can get the data. If there
-// is nothing else to read, this returns EOF
-int fcgio_buf::underflow() {
-  // if it is unbuffered, then get & return a char
-  if (unbuffered())
-    return FCGX_GetChar(fcgi_str);
+// is nothing else to read, this returns EOF. This is the only method
+// which reads from the FCGI interface.
+int fcgi_streambuf::underflow()
+   {
+   int numread;
+
+   // we will not allow a bogus fcgx_strm (because the user
+   // forgot to attach()) to do even more harm.
+   if (!defined) return (EOF);
+
+   // if it is unbuffered, then get & return a char
+   if (unbuffered()) return FCGX_GetChar(fcgx_strm);
 
-  // read as much data as we can, then adjust the get area so it
-  // reflects the data we just read
-  int numread=FCGX_GetStr(eback(), blen(), fcgi_str);
-  if (numread==0) return EOF;
-  setg(eback(),eback(),eback()+numread);
+   // read as much data as we can, then adjust the get area so it
+   // reflects the data we just read
+   numread=FCGX_GetStr(base(), blen(), fcgx_strm);
+   if (numread<=0) return EOF;
+   setg(base(),base(),base()+numread);
 
-  return *eback();
-}
+   // We avoid a common bug. You NEED the unsigned char cast or
+   // else this routine will return a false EOF when reading
+   // control chars outside the normal ascii range!
+   // i.e. "negative" chars map to positive ints and we
+   // reserve negative return values for EOF and error
+   // conditions. Now, even binary data will come through
+   // perfectly.
+   // The reason the get buffer uses chars rather
+   // than unsigned chars is a mystery from AT&T history.
+   // It probably has to due with the old 7bit text limits.
+   return ((unsigned char)(*eback()));
+   }
 
 
-// sets up any buffering used for input or output   
-streambuf* fcgio_buf::setbuf(char *p, int n) {
-  // if setbuf is offered a buffer, then use it, otherwise, set the stream to unbuffered
-  setb(p,p+n,0);
-  // output is not buffered
-  setp(0,0);
-  setg(p,p+n,p+n);
 
-  unbuffered(p==NULL);
-  return this;
-}
+// here comes the higher level iostream stuff
 
+// **** Istream
 
-// flush all the output using FCGX_FFlush
-int fcgio_buf::sync() {
-  return FCGX_FFlush(fcgi_str); 
-}
+// parameterless constructor allows us to create first, then
+// initialize later via attach()
+fcgi_istream::fcgi_istream() {}
 
 
+// or we can create and initialize in one step
 // constructor calls ios::init to assign our streambuf to
-// the ostream derived class. Also sets up buffering
-fcgio_ostream::fcgio_ostream(FCGX_Stream *str) : buf(str) {
-  // init is a protected member of ios
-  init(&buf);
-  buf.setbuf(NULL,0);
-}
+// the istream derived class. Also sets up buffering.
+fcgi_istream::fcgi_istream(FCGX_Stream *str)
+   {
+   // initialize as a reader, use all buffering for the
+   // get area
+   fcgi_strmbuf.stream_initialize(str,0);
+   // init is a protected member of ios
+   init(&fcgi_strmbuf);
+   }
+
+
+// destructor
+fcgi_istream::~fcgi_istream() {}
 
 
+// does everything the constructor with parameter does at the
+// request of the programmer
+void fcgi_istream::attach(FCGX_Stream *str)
+   {
+   // initialize as a reader, use all buffering for the
+   // get area
+   fcgi_strmbuf.stream_initialize(str,0);
+   // init is a protected member of ios
+   init(&fcgi_strmbuf);
+   }
+
+
+// experimental method to drain the get buffer. Allows you
+// to sync reads with FCGI library (non-istream reads).
+// reads upto n chars into the s array from the get buffer, does
+// not call underflow(). Returned is the number of chars extracted
+// or EOF on error. If n>0 (normal use) and returns a nonnegative
+// value less than n, the get buffer is empty and it is safe to
+// use an FCGI library read.
+// see the header file for more info
+int fcgi_istream::drain(char *s, int n)
+   {
+   return (fcgi_strmbuf.drain_strm(s,n));
+   }
+
+
+// little routine so that you can tell if the streambuf
+// was ever defined
+int fcgi_istream::isdefined(void)
+   {
+   return (fcgi_strmbuf.isstrmdefined());
+   }
+
+
+
+// **** Ostream
+
+// parameterless constructor allows us to create first, then
+// initialize later via attach()
+fcgi_ostream::fcgi_ostream() {}
+
+
+// or we can create and initialize in one step
 // constructor calls ios::init to assign our streambuf to
 // the ostream derived class. Also sets up buffering
-fcgio_istream::fcgio_istream(FCGX_Stream *str) : buf(str) {
+fcgi_ostream::fcgi_ostream(FCGX_Stream *str)
+  {
+  // initialize as a writer, use all buffering for the
+  // put area
+  fcgi_strmbuf.stream_initialize(str,1);
   // init is a protected member of ios
-  init(&buf);
-  // set up the buffers
-  buf.setbuf(buffer,buffersize);
-}
+  init(&fcgi_strmbuf);
+  }
+
+
+// destructor
+fcgi_ostream::~fcgi_ostream()
+   {
+   // don't blowup if the user never defined the
+   // stream
+   if (fcgi_strmbuf.isstrmdefined())
+      {
+      // this may protect a few poor souls who forgot to flush
+      // before deleting. Always flush when you are done.
+      fcgi_strmbuf.sync();
+      }
+   }
+
+
+// does everything the constructor with parameter does at the
+// request of the programmer
+void fcgi_ostream::attach(FCGX_Stream *str)
+   {
+   // initialize as a writer, use all buffering for the
+   // put area
+   fcgi_strmbuf.stream_initialize(str,1);
+   // init is a protected member of ios
+   init(&fcgi_strmbuf);
+   }
+
 
+// helper function to find out if this
+// stream was ever initialized.
+int fcgi_ostream::isdefined(void)
+   {
+   return (fcgi_strmbuf.isstrmdefined());
+   }