pion
5.0.6
|
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