PageRenderTime 37ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/boost/cgi/fcgi/client.hpp

http://github.com/darrengarvey/cgi
C++ Header | 262 lines | 172 code | 28 blank | 62 comment | 17 complexity | b4445930d3799be5858f48287b4fe801 MD5 | raw file
  1. // -- fcgi/client.hpp --
  2. //
  3. // Copyright (c) Darren Garvey 2007.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. ////////////////////////////////////////////////////////////////
  9. #ifndef CGI_FCGI_CLIENT_HPP_INCLUDED__
  10. #define CGI_FCGI_CLIENT_HPP_INCLUDED__
  11. #include <vector>
  12. ///////////////////////////////////////////////////////////
  13. #include <boost/shared_ptr.hpp>
  14. #include <boost/logic/tribool.hpp>
  15. #include <boost/asio/buffer.hpp>
  16. ///////////////////////////////////////////////////////////
  17. #include "boost/cgi/basic_client.hpp"
  18. #include "boost/cgi/common/map.hpp"
  19. #include "boost/cgi/fcgi/traits.hpp"
  20. #include "boost/cgi/connections/shareable_tcp_socket.hpp"
  21. #include "boost/cgi/detail/throw_error.hpp"
  22. #include "boost/cgi/fcgi/specification.hpp"
  23. #include "boost/cgi/fwd/basic_request_fwd.hpp"
  24. #include "boost/cgi/error.hpp"
  25. #include "boost/cgi/import/read.hpp"
  26. #include "boost/cgi/import/buffer.hpp"
  27. #include "boost/cgi/import/io_service.hpp"
  28. #undef min
  29. #undef max
  30. #include <algorithm>
  31. #if !defined(BOOST_CGI_NO_LOGGING) && !defined(NDEBUG)
  32. # include <iostream>
  33. #endif
  34. BOOST_CGI_NAMESPACE_BEGIN
  35. namespace common {
  36. /// A client that uses a TCP socket that owned by it.
  37. /// Construct
  38. template<>
  39. basic_client<
  40. ::BOOST_CGI_NAMESPACE::common::tags::fcgi
  41. >::basic_client()
  42. : request_id_(-1)
  43. , status_(none_)
  44. , total_sent_bytes_(0)
  45. , total_sent_packets_(0)
  46. , header_()
  47. , outbuf_()
  48. , keep_connection_(false)
  49. {
  50. }
  51. /// Override basic_client::close().
  52. /**
  53. * Closing a FastCGI means sending an END_REQUEST header
  54. * to the HTTP server and potentially closing the connection.
  55. *
  56. * Note that in general the HTTP server is responsible for the
  57. * lifetime of the connection, but can hand that control over
  58. * to the library (eg. if the server is set up to recycle
  59. * connections after N requests).
  60. */
  61. template<>
  62. boost::system::error_code
  63. basic_client<
  64. ::BOOST_CGI_NAMESPACE::common::tags::fcgi
  65. >::close(boost::uint64_t app_status, boost::system::error_code& ec)
  66. {
  67. // Note that the request may already be closed if the client aborts
  68. // the connection.
  69. if (!is_open())
  70. ec = error::already_closed;
  71. else
  72. {
  73. if ((constructed < status_) && (status_ < end_request_sent))
  74. {
  75. status_ = closed_;
  76. // Write an EndRequest packet to the server.
  77. outbuf_.clear();
  78. header_.reset(fcgi::spec_detail::END_REQUEST, request_id_, 8);
  79. fcgi::spec::end_request_body body(app_status, fcgi::spec_detail::REQUEST_COMPLETE);
  80. outbuf_.push_back(header_.data());
  81. outbuf_.push_back(body.data());
  82. write(*connection_, outbuf_, boost::asio::transfer_all(), ec);
  83. }
  84. status_ = closed_;
  85. if (!ec && !keep_connection_)
  86. connection_->close();
  87. }
  88. return ec;
  89. }
  90. /// Prepare a buffer by wrapping it into a FastCGI packet.
  91. /**
  92. * FastCGI dictates that data is sent in packets which identify
  93. * which request the data relates to, along with the size of
  94. * the packet.
  95. *
  96. * This function takes a buffer of data and creates another
  97. * buffer which includes the packet header information. As the
  98. * buffers themselves only contain pointers to the data the
  99. * overhead is negligible as the data is not copied.
  100. *
  101. * The lifetime of the header is guaranteed as it is kept in
  102. * the client object itself.
  103. */
  104. template<>
  105. template<typename ConstBufferSequence>
  106. void
  107. basic_client<
  108. ::BOOST_CGI_NAMESPACE::common::tags::fcgi
  109. >::prepare_buffer(const ConstBufferSequence& buf)
  110. {
  111. typename ConstBufferSequence::const_iterator iter = buf.begin();
  112. typename ConstBufferSequence::const_iterator end = buf.end();
  113. outbuf_.clear();
  114. outbuf_.push_back(boost::asio::buffer(header_.data()));
  115. std::size_t total_buffer_size(0);
  116. for(; iter != end; ++iter)
  117. {
  118. boost::asio::const_buffer buffer(*iter);
  119. std::size_t new_buf_size( boost::asio::buffer_size(*iter) );
  120. if (total_buffer_size + new_buf_size
  121. > static_cast<std::size_t>(fcgi::spec::max_packet_size::value))
  122. {
  123. // If the send buffer is empty, extract a chunk of the
  124. // new buffer to send. If there is already some data
  125. // ready to send, don't add any more data to the pack.
  126. if (total_buffer_size == 0)
  127. {
  128. total_buffer_size
  129. = std::min<std::size_t>(new_buf_size,65500);
  130. /*
  131. std::cerr<< "Oversized buffer: " << total_buffer_size
  132. << " / " << new_buf_size << " bytes sent\n";
  133. */
  134. outbuf_.push_back(
  135. boost::asio::buffer(*iter, total_buffer_size));
  136. break;
  137. }
  138. else
  139. break;
  140. }
  141. else
  142. {
  143. total_buffer_size += new_buf_size;
  144. outbuf_.push_back(*iter);
  145. }
  146. }
  147. header_.reset(fcgi::spec_detail::STDOUT, request_id_, total_buffer_size);
  148. }
  149. template<>
  150. void
  151. basic_client<
  152. ::BOOST_CGI_NAMESPACE::common::tags::fcgi
  153. >::handle_write(std::size_t bytes_transferred, boost::system::error_code& ec)
  154. {
  155. total_sent_bytes_ += bytes_transferred;
  156. total_sent_packets_ += 1;
  157. std::size_t total_buffer_size = static_cast<std::size_t>(header_.content_length());
  158. #if !defined(BOOST_CGI_NO_LOGGING) && !defined(NDEBUG)
  159. std::ofstream log("../logs/fcgi_client.log", std::ios::out | std::ios::app);
  160. if (ec)
  161. std::cerr<< "Error " << ec << ": " << ec.message() << '\n';
  162. else
  163. //std::cerr
  164. log
  165. << "Transferred " << total_buffer_size
  166. << " (+" << (bytes_transferred - total_buffer_size)
  167. << " protocol) bytes (running total: "
  168. << total_sent_bytes_ << " bytes; "
  169. << total_sent_packets_ << " packets).\n";
  170. #endif // !defined(BOOST_CGI_NO_LOGGING) && !defined(NDEBUG)
  171. // Now remove the protocol overhead for the caller, who
  172. // doesn't want to count it.
  173. bytes_transferred -= fcgi::spec::header_length::value;
  174. // Check everything was written ok.
  175. if (!ec && bytes_transferred != total_buffer_size)
  176. ec = ::BOOST_CGI_NAMESPACE::fcgi::error::couldnt_write_complete_packet;
  177. }
  178. /// Write some data to the client.
  179. /**
  180. * Currently this actually writes an entire packet - which may be up
  181. * to 65,535 bytes in size - as the connection may potentially be
  182. * multiplexed and shared between requests.
  183. */
  184. template<>
  185. template<typename ConstBufferSequence>
  186. std::size_t
  187. basic_client<
  188. ::BOOST_CGI_NAMESPACE::common::tags::fcgi
  189. >::write_some(
  190. const ConstBufferSequence& buf
  191. , boost::system::error_code& ec
  192. )
  193. {
  194. prepare_buffer(buf);
  195. std::size_t bytes_transferred
  196. = boost::asio::write(*connection_, outbuf_
  197. , boost::asio::transfer_all(), ec);
  198. handle_write(bytes_transferred, ec);
  199. return bytes_transferred;
  200. }
  201. /// Write some data to the client.
  202. /**
  203. * Currently this actually writes an entire packet - which may be up
  204. * to 65,535 bytes in size - as the connection may potentially be
  205. * multiplexed and shared between requests.
  206. */
  207. template<>
  208. template<typename ConstBufferSequence, typename Handler>
  209. void
  210. basic_client<
  211. ::BOOST_CGI_NAMESPACE::common::tags::fcgi
  212. >::async_write_some(
  213. const ConstBufferSequence& buf
  214. , Handler handler
  215. )
  216. {
  217. prepare_buffer(buf);
  218. boost::system::error_code ec;
  219. std::size_t bytes_transferred
  220. = boost::asio::write(*connection_, outbuf_
  221. , boost::asio::transfer_all(), ec);
  222. handle_write(bytes_transferred, ec);
  223. handler(bytes_transferred, ec);
  224. }
  225. } // namespace common
  226. namespace fcgi {
  227. typedef
  228. common::basic_client<
  229. ::BOOST_CGI_NAMESPACE::common::tags::fcgi
  230. >
  231. client;
  232. } // namespace fcgi
  233. BOOST_CGI_NAMESPACE_END
  234. #endif // CGI_FCGI_CLIENT_HPP_INCLUDED__