# 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