00001 
00002 
00003 
00004 
00005 
00006 
00007 
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 {    
00028 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             flushContentStream();
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             flushContentStream();
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             flushContentStream();
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             
00199             
00200             m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
00201         }
00202         
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         
00270         if (! m_tcp_conn->is_open())
00271             finished_writing(boost::asio::error::connection_reset);
00272         
00273         flushContentStream();
00274         
00275         http::message::write_buffers_t write_buffers;
00276         prepare_write_buffers(write_buffers, send_final_chunk);
00277         
00278         m_tcp_conn->async_write(write_buffers, send_handler);
00279     }
00280     
00287     void prepare_write_buffers(http::message::write_buffers_t &write_buffers,
00288                                const bool send_final_chunk);
00289     
00291     inline void flushContentStream(void) {
00292         if (! m_stream_is_empty) {
00293             std::string string_to_add(m_content_stream.str());
00294             if (! string_to_add.empty()) {
00295                 m_content_stream.str("");
00296                 m_content_length += string_to_add.size();
00297                 m_text_cache.push_back(string_to_add);
00298                 m_content_buffers.push_back(boost::asio::buffer(m_text_cache.back()));
00299             }
00300             m_stream_is_empty = true;
00301         }
00302     }
00303     
00304     
00306     class binary_cache_t : public std::vector<std::pair<const char *, size_t> > {
00307     public:
00308         ~binary_cache_t() {
00309             for (iterator i=begin(); i!=end(); ++i) {
00310                 delete[] i->first;
00311             }
00312         }
00313         inline boost::asio::const_buffer add(const void *ptr, const size_t size) {
00314             char *data_ptr = new char[size];
00315             memcpy(data_ptr, ptr, size);
00316             push_back( std::make_pair(data_ptr, size) );
00317             return boost::asio::buffer(data_ptr, size);
00318         }
00319     };
00320     
00322     typedef std::list<std::string>          text_cache_t;
00323 
00324     
00326     logger                                  m_logger;
00327 
00329     tcp::connection_ptr                     m_tcp_conn;
00330     
00332     http::message::write_buffers_t          m_content_buffers;
00333     
00335     binary_cache_t                          m_binary_cache;
00336 
00338     text_cache_t                            m_text_cache;
00339     
00341     std::ostringstream                      m_content_stream;
00342     
00344     size_t                                  m_content_length;
00345 
00347     bool                                    m_stream_is_empty;
00348     
00350     bool                                    m_client_supports_chunks;
00351     
00353     bool                                    m_sending_chunks;
00354     
00356     bool                                    m_sent_headers;
00357 
00359     finished_handler_t                      m_finished;
00360 };
00361 
00362 
00364 typedef boost::shared_ptr<writer>   writer_ptr;
00365 
00366 
00368 template <typename T>
00369 inline const writer_ptr& operator<<(const writer_ptr& writer, const T& data) {
00370     writer->write(data);
00371     return writer;
00372 }
00373 
00374 inline const writer_ptr& operator<<(const writer_ptr& writer, std::ostream& (*iomanip)(std::ostream&)) {
00375     writer->write(iomanip);
00376     return writer;
00377 }
00378 
00379 }   
00380 }   
00381 
00382 #endif