00001
00002
00003
00004
00005
00006
00007
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 {
00028 namespace net {
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
00193
00194 m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
00195 }
00196
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
00264 if (! m_tcp_conn->is_open())
00265 finishedWriting(boost::asio::error::connection_reset);
00266
00267 flushContentStream();
00268
00269 HTTPMessage::WriteBuffers write_buffers;
00270 prepareWriteBuffers(write_buffers, send_final_chunk);
00271
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 }
00370 }
00371
00372 #endif