- Main Page
- Classes
- Files

- File List

# include/pion/http/writer.hpp

    00001 // ---------------------------------------------------------------------
    00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
    00003 // ---------------------------------------------------------------------
    00004 // Copyright (C) 2007-2012 Cloudmeter, Inc.  (http://www.cloudmeter.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_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             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             // 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             finished_writing(boost::asio::error::connection_reset);
    00272         // make sure that the content-length is up-to-date
    00273         flushContentStream();
    00274         // prepare the write buffers to be sent
    00275         http::message::write_buffers_t write_buffers;
    00276         prepare_write_buffers(write_buffers, send_final_chunk);
    00277         // send data in the write buffers
    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 }   // end namespace http
    00380 }   // end namespace pion
    00381
    00382 #endif