pion  5.0.6
include/pion/http/writer.hpp
00001 // ---------------------------------------------------------------------
00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
00003 // ---------------------------------------------------------------------
00004 // Copyright (C) 2007-2014 Splunk Inc.  (https://github.com/splunk/pion)
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_HTTP_WRITER_HEADER__
00011 #define __PION_HTTP_WRITER_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/config.hpp>
00022 #include <pion/logger.hpp>
00023 #include <pion/tcp/connection.hpp>
00024 #include <pion/http/message.hpp>
00025 
00026 
00027 namespace pion {    // begin namespace pion
00028 namespace http {    // begin namespace http
00029 
00030 
00034 class PION_API writer :
00035     private boost::noncopyable
00036 {
00037 protected:
00038     
00040     typedef boost::function1<void,const boost::system::error_code&> finished_handler_t;
00041 
00043     typedef boost::function2<void,const boost::system::error_code&,std::size_t> write_handler_t;
00044     
00045     
00052     writer(tcp::connection_ptr& tcp_conn, finished_handler_t handler)
00053         : m_logger(PION_GET_LOGGER("pion.http.writer")),
00054         m_tcp_conn(tcp_conn), m_content_length(0), m_stream_is_empty(true), 
00055         m_client_supports_chunks(true), m_sending_chunks(false),
00056         m_sent_headers(false), m_finished(handler)
00057     {}
00058     
00065     virtual void handle_write(const boost::system::error_code& write_error,
00066                               std::size_t bytes_written) = 0;
00067 
00068     
00074     virtual void prepare_buffers_for_send(http::message::write_buffers_t& write_buffers) = 0;
00075                                       
00077     virtual write_handler_t bind_to_write_handler(void) = 0;
00078     
00080     inline void finished_writing(const boost::system::error_code& ec) {
00081         if (m_finished) m_finished(ec);
00082     }
00083     
00084     
00085 public:
00086 
00088     virtual ~writer() {}
00089 
00091     inline void clear(void) {
00092         m_content_buffers.clear();
00093         m_binary_cache.clear();
00094         m_text_cache.clear();
00095         m_content_stream.str("");
00096         m_stream_is_empty = true;
00097         m_content_length = 0;
00098     }
00099 
00105     template <typename T>
00106     inline void write(const T& data) {
00107         m_content_stream << data;
00108         if (m_stream_is_empty) m_stream_is_empty = false;
00109     }
00110 
00111     inline void write(std::ostream& (*iomanip)(std::ostream&)) {
00112         m_content_stream << iomanip;
00113         if (m_stream_is_empty) m_stream_is_empty = false;
00114     }
00115 
00122     inline void write(const void *data, size_t length) {
00123         if (length != 0) {
00124             flush_content_stream();
00125             m_content_buffers.push_back(m_binary_cache.add(data, length));
00126             m_content_length += length;
00127         }
00128     }
00129     
00137     inline void write_no_copy(const std::string& data) {
00138         if (! data.empty()) {
00139             flush_content_stream();
00140             m_content_buffers.push_back(boost::asio::buffer(data));
00141             m_content_length += data.size();
00142         }
00143     }
00144     
00152     inline void write_no_copy(void *data, size_t length) {
00153         if (length > 0) {
00154             flush_content_stream();
00155             m_content_buffers.push_back(boost::asio::buffer(data, length));
00156             m_content_length += length;
00157         }
00158     }
00159 
00160     
00166     inline void send(void) {
00167         send_more_data(false, bind_to_write_handler());
00168     }
00169     
00179     template <typename SendHandler>
00180     inline void send(SendHandler send_handler) {
00181         send_more_data(false, send_handler);
00182     }
00183     
00194     template <typename SendHandler>
00195     inline void send_chunk(SendHandler send_handler) {
00196         m_sending_chunks = true;
00197         if (!supports_chunked_messages()) {
00198             // sending data in chunks, but the client does not support chunking;
00199             // make sure that the connection will be closed when we are all done
00200             m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
00201         }
00202         // send more data
00203         send_more_data(false, send_handler);
00204     }
00205 
00217     template <typename SendHandler>
00218     inline void send_final_chunk(SendHandler send_handler) {
00219         m_sending_chunks = true;
00220         send_more_data(true, send_handler);
00221     }
00222     
00230     inline void send_final_chunk(void) {
00231         m_sending_chunks = true;
00232         send_more_data(true, bind_to_write_handler());
00233     }
00234     
00235     
00237     inline tcp::connection_ptr& get_connection(void) { return m_tcp_conn; }
00238 
00240     inline size_t get_content_length(void) const { return m_content_length; }
00241 
00243     inline void supports_chunked_messages(bool b) { m_client_supports_chunks = b; }
00244     
00246     inline bool supports_chunked_messages() const { return m_client_supports_chunks; }
00247 
00249     inline bool sending_chunked_message() const { return m_sending_chunks; }
00250     
00252     inline void set_logger(logger log_ptr) { m_logger = log_ptr; }
00253     
00255     inline logger get_logger(void) { return m_logger; }
00256 
00257     
00258 private:
00259 
00266     template <typename SendHandler>
00267     inline void send_more_data(const bool send_final_chunk, SendHandler send_handler)
00268     {
00269         // make sure that we did not lose the TCP connection
00270         if (m_tcp_conn->is_open()) {
00271             // make sure that the content-length is up-to-date
00272             flush_content_stream();
00273             // prepare the write buffers to be sent
00274             http::message::write_buffers_t write_buffers;
00275             prepare_write_buffers(write_buffers, send_final_chunk);
00276             // send data in the write buffers
00277             m_tcp_conn->async_write(write_buffers, send_handler);
00278         } else {
00279             finished_writing(boost::asio::error::connection_reset);
00280         }
00281     }
00282     
00289     void prepare_write_buffers(http::message::write_buffers_t &write_buffers,
00290                                const bool send_final_chunk);
00291     
00293     inline void flush_content_stream(void) {
00294         if (! m_stream_is_empty) {
00295             std::string string_to_add(m_content_stream.str());
00296             if (! string_to_add.empty()) {
00297                 m_content_stream.str("");
00298                 m_content_length += string_to_add.size();
00299                 m_text_cache.push_back(string_to_add);
00300                 m_content_buffers.push_back(boost::asio::buffer(m_text_cache.back()));
00301             }
00302             m_stream_is_empty = true;
00303         }
00304     }
00305     
00306     
00308     class binary_cache_t : public std::vector<std::pair<const char *, size_t> > {
00309     public:
00310         ~binary_cache_t() {
00311             for (iterator i=begin(); i!=end(); ++i) {
00312                 delete[] i->first;
00313             }
00314         }
00315         inline boost::asio::const_buffer add(const void *ptr, const size_t size) {
00316             char *data_ptr = new char[size];
00317             memcpy(data_ptr, ptr, size);
00318             push_back( std::make_pair(data_ptr, size) );
00319             return boost::asio::buffer(data_ptr, size);
00320         }
00321     };
00322     
00324     typedef std::list<std::string>          text_cache_t;
00325 
00326     
00328     logger                                  m_logger;
00329 
00331     tcp::connection_ptr                     m_tcp_conn;
00332     
00334     http::message::write_buffers_t          m_content_buffers;
00335     
00337     binary_cache_t                          m_binary_cache;
00338 
00340     text_cache_t                            m_text_cache;
00341     
00343     std::ostringstream                      m_content_stream;
00344     
00346     size_t                                  m_content_length;
00347 
00349     bool                                    m_stream_is_empty;
00350     
00352     bool                                    m_client_supports_chunks;
00353     
00355     bool                                    m_sending_chunks;
00356     
00358     bool                                    m_sent_headers;
00359 
00361     finished_handler_t                      m_finished;
00362 };
00363 
00364 
00366 typedef boost::shared_ptr<writer>   writer_ptr;
00367 
00368 
00370 template <typename T>
00371 inline const writer_ptr& operator<<(const writer_ptr& writer, const T& data) {
00372     writer->write(data);
00373     return writer;
00374 }
00375 
00376 inline const writer_ptr& operator<<(const writer_ptr& writer, std::ostream& (*iomanip)(std::ostream&)) {
00377     writer->write(iomanip);
00378     return writer;
00379 }
00380 
00381 }   // end namespace http
00382 }   // end namespace pion
00383 
00384 #endif