$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
From: lists.drrngrvy_at_[hidden]
Date: 2007-11-07 20:24:06
Author: drrngrvy
Date: 2007-11-07 20:24:05 EST (Wed, 07 Nov 2007)
New Revision: 40915
URL: http://svn.boost.org/trac/boost/changeset/40915
Log:
Working with cgi::response. It now separates the headers and the body, which can be reset at any time. The send() functions also should now work. The flush() and async_*() functions haven't been done yet. Streaming header and cookie classes to a response work now and can be sent in any order. A default-constructed header now stops any more headers being appended (BOOST_ASSERT is used for this), unless the ugly `unterminate_headers` function is called (that might be 'beyond ugly' though, it may have to change).
Text files modified: 
   sandbox/SOC/2007/cgi/branches/acceptor_work/README                 |     6 -                                       
   sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/cookie.hpp   |    22 +-                                      
   sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/header.hpp   |     4                                         
   sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/response.hpp |   235 ++++++++++++++++++++++----------------- 
   4 files changed, 148 insertions(+), 119 deletions(-)
Modified: sandbox/SOC/2007/cgi/branches/acceptor_work/README
==============================================================================
--- sandbox/SOC/2007/cgi/branches/acceptor_work/README	(original)
+++ sandbox/SOC/2007/cgi/branches/acceptor_work/README	2007-11-07 20:24:05 EST (Wed, 07 Nov 2007)
@@ -1,9 +1,3 @@
-Development has moved to cgi.sf.net for now (on the cgi/branches directory):
-
-  http://cgi.svn.sourceforge.net/viewvc/cgi/
-
-'Stable' updates will be uploaded to here, as and when available.
-
 Pre-built docs can be found at http://cgi.sf.net
 
 Comments/critique are welcome and can be directed to one of:
Modified: sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/cookie.hpp
==============================================================================
--- sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/cookie.hpp	(original)
+++ sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/cookie.hpp	2007-11-07 20:24:05 EST (Wed, 07 Nov 2007)
@@ -41,7 +41,7 @@
 
     /// Delete the cookie named `_name`.
     basic_cookie(const string_type& _name)
-      : content(_name)
+      : name(_name)
       , value()
       , expires("Fri, 05-Jun-1989 15:30:00 GMT")
       , path("/")
@@ -57,7 +57,7 @@
                 , const string_type& _domain = ""
                 , bool _secure = false
                 , bool HttpOnly = false)
-      : content(_name)
+      : name(_name)
       , value(_val)
       , expires(_expires)
       , path(_path)
@@ -83,7 +83,7 @@
      * - Parts of the cookie are delimited by '; '. ie. if there is no space,
      *   or multiple spaces after the semi-colon, this function won't work...
      */
-    /* Actually, I'm omitting these functions for now, just had a though...
+    /* Actually, I'm omitting these functions for now, just had a thought...
     static basic_cookie<string_type>
       from_string(const char* str, boost::system::error_code& ec)
     {
@@ -121,18 +121,18 @@
     /// Make a string out of the cookie
     std::string to_string()
     {
-      std::string str(ck.name + "=" + ck.value);
-      if (!ck.expires.empty()) str += "; expires=" += ck.expires;
-      if (!ck.path.empty()   ) str += "; path="    += ck.path;
-      if (!ck.domain.empty() ) str += "; domain="  += ck.domain;
-      if ( secure            ) str += "; secure";
-      if ( ck.http_only      ) str += "; HttpOnly";
+      std::string str(name + "=" + value);
+      if (!expires.empty()) str += ("; expires=" + expires);
+      if (!path.empty()   ) str += ("; path="    + path);
+      if (!domain.empty() ) str += ("; domain="  + domain);
+      if ( secure         ) str += "; secure";
+      if ( http_only      ) str += "; HttpOnly";
       return str;
     }
   };
 
-  template<typename OutStream, typename Cookie>
-    OutStream& operator<<(OutStream& os, Cookie ck)
+  template<typename OutStream, typename T>
+    OutStream& operator<<(OutStream& os, const basic_cookie<T>& ck)
   {
     return os<< ck.to_string();
   }
Modified: sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/header.hpp
==============================================================================
--- sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/header.hpp	(original)
+++ sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/header.hpp	2007-11-07 20:24:05 EST (Wed, 07 Nov 2007)
@@ -16,7 +16,7 @@
   struct header
   {
     header()
-      : content("\r\n")
+      : content()
     {
     }
 
@@ -36,7 +36,7 @@
     }
 
     header(const std::string& name, const std::string& val)
-      : content(name + ": " + val + "\r\n")
+      : content(name + ": " + val)
     {
     }
 
Modified: sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/response.hpp
==============================================================================
--- sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/response.hpp	(original)
+++ sandbox/SOC/2007/cgi/branches/acceptor_work/boost/cgi/response.hpp	2007-11-07 20:24:05 EST (Wed, 07 Nov 2007)
@@ -12,28 +12,56 @@
 #include "boost/cgi/detail/push_options.hpp"
 
 #include <string>
+#include <fstream> // only for testing
 
 //#include "boost/cgi/request_ostream.hpp"
 #include "boost/cgi/buffer.hpp"
 #include "boost/cgi/cookie.hpp"
 #include "boost/cgi/header.hpp"
+#include <boost/foreach.hpp>
 
-namespace cgi {
-
-  /// The response class: a helper for responding to requests
+/// This mess outputs a default Content-type header if the user hasn't set any.
+/**
+ * BOOST_CGI_ADD_DEFAULT_HEADER should not persiste beyond this file.
+ *
+ * It basically works like (default first):
+ *
+ * Debug mode:
+ * - Append a "Content-type: text/plain" header;
+ * - If BOOST_CGI_DEFAULT_CONTENT_TYPE is defined, set that as the
+ *   content-type;
+ * - If BOOST_CGI_NO_DEFAULT_CONTENT_TYPE is defined, do nothing.
+ *
+ * Release mode:
+ * - Do nothing.
+ */
+#if !defined(NDEBUG) && !defined(BOOST_CGI_NO_DEFAULT_CONTENT_TYPE)
+//{
+#  if !defined(BOOST_CGI_DEFAULT_CONTENT_TYPE)
+#    define BOOST_CGI_DEFAULT_CONTENT_TYPE "Content-type: text/plain"
+#  endif // !defined(BOOST_CGI_DEFAULT_CONTENT_TYPE)
+//}
+#  define BOOST_CGI_ADD_DEFAULT_HEADER   \
+      if (headers_.empty())              \
+        headers_.push_back(BOOST_CGI_DEFAULT_CONTENT_TYPE"\r\n");
+#else
+#  define BOOST_CGI_ADD_DEFAULT_HEADER
+#endif // !defined(NDEBUG) && !defined(BOOST_CGI_NO_DEFAULT_CONTENT_TYPE)
 
-  // The request_ostream is destined to become a basic_request_ostream
-//typedef request_ostream<> response_;
 
+namespace cgi {
 
+  /// The response class: a helper for responding to requests.
   class response
   {
   public:
+    typedef std::ostream ostream_type;
+
     response(http::status_code sc = http::ok)
       : buffer_(new ::cgi::streambuf())
       , ostream_(buffer_.get())
       , http_status_(sc)
-      , headers_sent_(false)
+      , headers_terminated_(false)
     {
     }
 
@@ -49,24 +77,6 @@
     {
     }
 
-    /// Construct, taking a buffer from an external source
-    /**
-     * Gets a buffer from the request/protocol service held by the request.
-     * <strike>
-     * Takes a buffer from T (can be a model of ProtocolService or
-     * CommonGatewayRequest) to use internally.
-     * </strike>
-     */
-//    template<typename CommonGatewayRequest>
-//    request_ostream(CommonGatewayRequest& req, http::status_code sc = http::ok)
-//      : request_(&req)
-//      , buffer_(new cgi::streambuf())
-//      , ostream_(buffer_.get()) //detail::take_buffer(req))
-//      , http_status_(sc)
-////    , destination_(destination)
-//    {
-//    }
-
     ~response()
     {
     } 
@@ -96,16 +106,6 @@
       return buf.size();
     }
 
-    /// Synchronously flush the data to the current request
-    /**
-     * If there is no error, the buffer is cleared.
-     */
-    //void flush()
-    //{
-    //  BOOST_ASSERT(request_ != NULL);
-    //  flush(*request_);
-    //}
-
     /// Synchronously flush the data to the supplied request
     /**
      * This call uses throwing semantics. ie. an exception will be thrown on
@@ -116,10 +116,10 @@
     void flush(CommonGatewayRequest& req)
     {
       /*
-      if (!headers_sent_)
+      if (!headers_terminated_)
       {
         ostream_<< "Content-type: text/plain\r\n\r\n";
-        headers_sent_ = true;
+        headers_terminated_ = true;
       }
       */
       ::cgi::write(req, headers_);
@@ -137,13 +137,11 @@
     boost::system::error_code&
       flush(CommonGatewayRequest& req, boost::system::error_code& ec)
     {
-      /*
-      if (!headers_sent_)
+      if (!headers_terminated_)
       {
         ostream_<< "Content-type: text/plain\r\n\r\n";
-        headers_sent_ = true;
+        headers_terminated_ = true;
       }
-      */
       if(!::cgi::write(req, rdbuf()->data(), ec))
         clear();
       return ec;
@@ -179,10 +177,10 @@
     void async_flush(CommonGatewayRequest& req, Handler handler)
     {
       /*
-      if (!headers_sent_)
+      if (!headers_terminated_)
       {
         ostream_<< "Content-type: text/plain\r\n\r\n";
-        headers_sent_ = true;
+        headers_terminated_ = true;
       }
       */
       ::cgi::async_write(req, rdbuf()->data()
@@ -221,7 +219,7 @@
     //}
 
 
-    /// Synchronously send the data via the supplied request
+    /// Synchronously send the data via the supplied request.
     /**
      * This call uses throwing semantics. ie. an exception will be thrown on
      * any failure.
@@ -230,15 +228,9 @@
     template<typename CommonGatewayRequest>
     void send(CommonGatewayRequest& req)
     {
-      /*
-      if (!headers_sent_)
-      {
-        ostream_<< "Content-type: text/plain\r\n\r\n";
-        headers_sent_ = true;
-      }
-      */
-      ::cgi::write(req.client(), rdbuf()->data());
-      req.set_status(http_status_);
+      boost::system::error_code ec;
+      send(req, ec);
+      detail::throw_error(ec);
     }
 
     /// Synchronously send the data via the supplied request
@@ -250,15 +242,31 @@
     boost::system::error_code&
       send(CommonGatewayRequest& req, boost::system::error_code& ec)
     {
-      /*
-      if (!headers_sent_)
+      //BOOST_CGI_ADD_DEFAULT_HEADER
+
+      // Terminate the headers.
+      headers_.push_back("\r\n");
+
+      //{ Construct a ConstBufferSequence out of the headers we have.
+      std::vector<boost::asio::const_buffer> headers;
+      typedef std::vector<std::string>::iterator iter;
+      for (iter i(headers_.begin()), e(headers_.end()); i != e; ++i)
       {
-        ostream_<< "Content-type: text/plain\r\n\r\n";
-        headers_sent_ = true;
+        headers.push_back(::cgi::buffer(*i));
       }
-      */
-      ::cgi::write(req.client(), rdbuf()->data(), ec);
+      //}
+      //std::ofstream("test_stuff", std::out);
+      //of<< headers_.front();
+      ::cgi::write(req.client(), headers
+                  , boost::asio::transfer_all(), ec);
+      headers_terminated_ = true;
+      ::cgi::write(req.client(), rdbuf()->data()
+                  , boost::asio::transfer_all(), ec);
       req.set_status(http_status_);
+
+      //BOOST_FOREACH(headers_.begin(), headers_.end()
+      //             , headers.push_back(::cgi::buffer(*_1)));
+
       return ec;
     }
 
@@ -271,10 +279,10 @@
     {
       req.set_status(http_status_);
       /*
-      if (!headers_sent_)
+      if (!headers_terminated_)
       {
         ostream_<< "Content-type: text/plain\r\n\r\n";
-        headers_sent_ = true;
+        headers_terminated_ = true;
       }
       */
       ::cgi::async_write(req, rdbuf()->data(), handler);
@@ -287,55 +295,61 @@
       return static_cast<::cgi::streambuf*>(ostream_.rdbuf());
     }
 
-    void set_status(const http::status_code& num)
+    /// Set the status code associated with the response.
+    response& set_status(const http::status_code& num)
     {
       http_status_ = num;
+      return *this;
     }
 
+    /// Get the status code associated with the response.
     http::status_code& get_status()
     {
       return http_status_;
     }
 
-    void set_header(const std::string& value)
+    /// Allow more headers to be added (note, avoid using this).
+    void unterminate_headers()
+    {
+      headers_terminated_ = false;
+    }
+
+    /// Add a header after appending the CRLF sequence.
+    response& set_header(const std::string& value)
     {
-      headers_.push_back(value);
+      BOOST_ASSERT(!headers_terminated_);
+      headers_.push_back(value + "\r\n");
+      return *this;
     }
 
-    void set_header(const std::string& name, const std::string& value)
+    /// Format and add a header given name and value, appending CRLF.
+    response& set_header(const std::string& name, const std::string& value)
     {
+      BOOST_ASSERT(!headers_terminated_);
       headers_.push_back(name + ": " + value + "\r\n");
+      return *this;
     }
   protected:
+    // Vector of all the headers, each followed by a CRLF
     std::vector<std::string> headers_;
-    boost::shared_ptr<::cgi::streambuf> buffer_;
-    std::ostream ostream_;
+
+    boost::shared_ptr<::cgi::streambuf> buffer_; // maybe scoped_ptr?
+
+    ostream_type ostream_;
+
     http::status_code http_status_;
-    bool headers_sent_;
+
+    // True if no more headers are to be appended. 
+    bool headers_terminated_;
 
     template<typename T>
     friend response& operator<<(response& resp, const T& t);
 
     //template<typename T>
     //friend response& operator<<(response& resp, T t);
-
-    /// Some helper functions for the basic CGI 1.1 meta-variables
-//     void auth_type(const std::string& value)
-//     {
-//       std::string str("Auth-type: ");
-//       str += value;
-//       this->headers_.push_back(cgi::buffer(str.c_str(), str.size()));
-//     }
-// 
-//     void content_length(const std::string& value)
-//     {
-//       std::string str("Content-length: ");
-//       str += value;
-//       this->headers_.push_back(cgi::buffer(str.c_str(), str.size()));
-//     }
-
   };
 
+  /// Generic ostream template
   template<typename T>
   response& operator<<(response& resp, const T& t)
   {
@@ -343,36 +357,57 @@
     return resp;
   }
 
-  //template<typename T>
-  //response& operator<<(response& resp, T t)
-  //{
-  //  resp.ostream_<< t;
-  //  return resp;
-  //}
-
-  template<typename T>
-  response& operator<<(response& resp, const header& hdr)
+  /// You can stream a cgi::header into a response.
+  /**
+   * This is just a more convenient way of doing:
+   *
+   * ``
+   * resp.set_header(header_content)
+   * ``
+   *
+   * [tip
+   * If you stream a default-constructed header to a response, it
+   * 'terminates' the headers. ie. You can do this if you want to ensure
+   * no further headers are added to the response. It has no other side-
+   * effects; for instance, it won't write any data to the client.
+   * ]
+   */
+  template<>
+  response& operator<<(response& resp, const ::cgi::header& hdr)
   {
     if (hdr.content.empty()) {
-      resp.headers_sent_ = true;
-      return resp<< "\r\n";
+      resp.headers_terminated_ = true;
+      return resp;
     }else{
-      resp.headers_.push_back(hdr.content);
+      // We shouldn't allow headers to be sent after they're explicitly ended.
+      BOOST_ASSERT(!resp.headers_terminated_);
+      resp.set_header(hdr.content);
       return resp;
     }
   }
 
+  /// You can stream a cgi::cookie into a response.
+  /**
+   * This is just a shorthand way of setting a header that will set a
+   * client-side cookie.
+   *
+   * You cannot stream a cookie to a response after the headers have been
+   * terminated. In this case, an alternative could be to use the HTML tag:
+   * <meta http-equiv="Set-cookie" ...> (see http://tinyurl.com/3bxftv or
+   * http://tinyurl.com/33znkj), but this is outside the scope of this
+   * library.
+   */
   template<typename T>
-  response& operator<<(response& resp, const basic_cookie<T>& ck)
+  response& operator<<(response& resp, basic_cookie<T> ck)
   {
-    // Note: the 'set-cookie' isn't part of the cookie object since
-    // the cookie can also be set after the headers have been sent.
-    // See http://tinyurl.com/33znkj
-    return resp<< "Set-cookie: " << ck.to_string() << "\r\n";
+    resp.set_header("Set-cookie", ck.to_string());
+    return resp;
   }
 
 } // namespace cgi
 
+#undef BOOST_CGI_ADD_DEFAULT_HEADER
+
 #include "boost/cgi/detail/pop_options.hpp"
 
 #endif // CGI_RESPONSE_HPP_INCLUDED__