pion-net
4.0.9
|
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