pion-net  4.0.9
net/include/pion/net/HTTPWriter.hpp
00001 // ------------------------------------------------------------------
00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
00003 // ------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Distributed under the Boost Software License, Version 1.0.
00007 // See http://www.boost.org/LICENSE_1_0.txt
00008 //
00009 
00010 #ifndef __PION_HTTPWRITER_HEADER__
00011 #define __PION_HTTPWRITER_HEADER__
00012 
00013 #include <vector>
00014 #include <string>
00015 #include <boost/shared_ptr.hpp>
00016 #include <boost/function.hpp>
00017 #include <boost/function/function0.hpp>
00018 #include <boost/function/function2.hpp>
00019 #include <boost/asio.hpp>
00020 #include <boost/noncopyable.hpp>
00021 #include <pion/PionConfig.hpp>
00022 #include <pion/PionLogger.hpp>
00023 #include <pion/net/HTTPMessage.hpp>
00024 #include <pion/net/TCPConnection.hpp>
00025 
00026 
00027 namespace pion {    // begin namespace pion
00028 namespace net {     // begin namespace net (Pion Network Library)
00029 
00033 class PION_NET_API HTTPWriter :
00034     private boost::noncopyable
00035 {
00036 protected:
00037     
00039     typedef boost::function1<void,const boost::system::error_code&> FinishedHandler;
00040 
00042     typedef boost::function2<void,const boost::system::error_code&,std::size_t> WriteHandler;
00043     
00044     
00051     HTTPWriter(TCPConnectionPtr& tcp_conn, FinishedHandler handler)
00052         : m_logger(PION_GET_LOGGER("pion.net.HTTPWriter")),
00053         m_tcp_conn(tcp_conn), m_content_length(0), m_stream_is_empty(true), 
00054         m_client_supports_chunks(true), m_sending_chunks(false),
00055         m_sent_headers(false), m_finished(handler)
00056     {}
00057     
00064     virtual void handleWrite(const boost::system::error_code& write_error,
00065                      std::size_t bytes_written) = 0;
00066 
00067     
00073     virtual void prepareBuffersForSend(HTTPMessage::WriteBuffers& write_buffers) = 0;
00074                                       
00076     virtual WriteHandler bindToWriteHandler(void) = 0;
00077     
00079     inline void finishedWriting(const boost::system::error_code& ec) {
00080         if (m_finished) m_finished(ec);
00081     }
00082     
00083     
00084 public:
00085 
00087     virtual ~HTTPWriter() {}
00088 
00090     inline void clear(void) {
00091         m_content_buffers.clear();
00092         m_binary_cache.clear();
00093         m_text_cache.clear();
00094         m_content_stream.str("");
00095         m_stream_is_empty = true;
00096         m_content_length = 0;
00097     }
00098 
00104     template <typename T>
00105     inline void write(const T& data) {
00106         m_content_stream << data;
00107         if (m_stream_is_empty) m_stream_is_empty = false;
00108     }
00109 
00116     inline void write(const void *data, size_t length) {
00117         if (length != 0) {
00118             flushContentStream();
00119             m_content_buffers.push_back(m_binary_cache.add(data, length));
00120             m_content_length += length;
00121         }
00122     }
00123     
00131     inline void writeNoCopy(const std::string& data) {
00132         if (! data.empty()) {
00133             flushContentStream();
00134             m_content_buffers.push_back(boost::asio::buffer(data));
00135             m_content_length += data.size();
00136         }
00137     }
00138     
00146     inline void writeNoCopy(void *data, size_t length) {
00147         if (length > 0) {
00148             flushContentStream();
00149             m_content_buffers.push_back(boost::asio::buffer(data, length));
00150             m_content_length += length;
00151         }
00152     }
00153 
00154     
00160     inline void send(void) {
00161         sendMoreData(false, bindToWriteHandler());
00162     }
00163     
00173     template <typename SendHandler>
00174     inline void send(SendHandler send_handler) {
00175         sendMoreData(false, send_handler);
00176     }
00177     
00188     template <typename SendHandler>
00189     inline void sendChunk(SendHandler send_handler) {
00190         m_sending_chunks = true;
00191         if (!supportsChunkedMessages()) {
00192             // sending data in chunks, but the client does not support chunking;
00193             // make sure that the connection will be closed when we are all done
00194             m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
00195         }
00196         // send more data
00197         sendMoreData(false, send_handler);
00198     }
00199 
00211     template <typename SendHandler>
00212     inline void sendFinalChunk(SendHandler send_handler) {
00213         m_sending_chunks = true;
00214         sendMoreData(true, send_handler);
00215     }
00216     
00224     inline void sendFinalChunk(void) {
00225         m_sending_chunks = true;
00226         sendMoreData(true, bindToWriteHandler());
00227     }
00228     
00229     
00231     inline TCPConnectionPtr& getTCPConnection(void) { return m_tcp_conn; }
00232 
00234     inline size_t getContentLength(void) const { return m_content_length; }
00235 
00237     inline void supportsChunkedMessages(bool b) { m_client_supports_chunks = b; }
00238     
00240     inline bool supportsChunkedMessages() const { return m_client_supports_chunks; }
00241 
00243     inline bool sendingChunkedMessage() const { return m_sending_chunks; }
00244     
00246     inline void setLogger(PionLogger log_ptr) { m_logger = log_ptr; }
00247     
00249     inline PionLogger getLogger(void) { return m_logger; }
00250 
00251     
00252 private:
00253 
00260     template <typename SendHandler>
00261     inline void sendMoreData(const bool send_final_chunk, SendHandler send_handler)
00262     {
00263         // make sure that we did not lose the TCP connection
00264         if (! m_tcp_conn->is_open())
00265             finishedWriting(boost::asio::error::connection_reset);
00266         // make sure that the content-length is up-to-date
00267         flushContentStream();
00268         // prepare the write buffers to be sent
00269         HTTPMessage::WriteBuffers write_buffers;
00270         prepareWriteBuffers(write_buffers, send_final_chunk);
00271         // send data in the write buffers
00272         m_tcp_conn->async_write(write_buffers, send_handler);
00273     }
00274     
00281     void prepareWriteBuffers(HTTPMessage::WriteBuffers &write_buffers,
00282                              const bool send_final_chunk);
00283     
00285     inline void flushContentStream(void) {
00286         if (! m_stream_is_empty) {
00287             std::string string_to_add(m_content_stream.str());
00288             if (! string_to_add.empty()) {
00289                 m_content_stream.str("");
00290                 m_content_length += string_to_add.size();
00291                 m_text_cache.push_back(string_to_add);
00292                 m_content_buffers.push_back(boost::asio::buffer(m_text_cache.back()));
00293             }
00294             m_stream_is_empty = true;
00295         }
00296     }
00297     
00298     
00300     class BinaryCache : public std::vector<std::pair<const char *, size_t> > {
00301     public:
00302         ~BinaryCache() {
00303             for (iterator i=begin(); i!=end(); ++i) {
00304                 delete[] i->first;
00305             }
00306         }
00307         inline boost::asio::const_buffer add(const void *ptr, const size_t size) {
00308             char *data_ptr = new char[size];
00309             memcpy(data_ptr, ptr, size);
00310             push_back( std::make_pair(data_ptr, size) );
00311             return boost::asio::buffer(data_ptr, size);
00312         }
00313     };
00314     
00316     typedef std::list<std::string>              TextCache;
00317 
00318     
00320     PionLogger                              m_logger;
00321 
00323     TCPConnectionPtr                        m_tcp_conn;
00324     
00326     HTTPMessage::WriteBuffers               m_content_buffers;
00327     
00329     BinaryCache                             m_binary_cache;
00330 
00332     TextCache                               m_text_cache;
00333     
00335     std::ostringstream                      m_content_stream;
00336     
00338     size_t                                  m_content_length;
00339 
00341     bool                                    m_stream_is_empty;
00342     
00344     bool                                    m_client_supports_chunks;
00345     
00347     bool                                    m_sending_chunks;
00348     
00350     bool                                    m_sent_headers;
00351 
00353     FinishedHandler                         m_finished;
00354 };
00355 
00356 
00358 typedef boost::shared_ptr<HTTPWriter>   HTTPWriterPtr;
00359 
00360 
00362 template <typename T>
00363 HTTPWriterPtr& operator<<(HTTPWriterPtr& writer, const T& data) {
00364     writer->write(data);
00365     return writer;
00366 }
00367 
00368 
00369 }   // end namespace net
00370 }   // end namespace pion
00371 
00372 #endif