PageRenderTime 51ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/suckerserv-v4/src/fungu/include/fungu/net/http/connection.hpp

http://suckerserv.googlecode.com/
C++ Header | 358 lines | 293 code | 57 blank | 8 comment | 19 complexity | c4213730ab7401b7fd1be9f46fbc3519 MD5 | raw file
Possible License(s): Cube, LGPL-2.1
  1. /*
  2. * The Fungu Network Library
  3. *
  4. * Copyright (c) 2009 Graham Daws.
  5. *
  6. * Distributed under a BSD style license (see accompanying file LICENSE.txt)
  7. */
  8. #ifndef FUNGU_NET_HTTP_CONNECTION_HPP
  9. #define FUNGU_NET_HTTP_CONNECTION_HPP
  10. #include "../../streams.hpp"
  11. #include <boost/asio.hpp>
  12. #include <boost/bind.hpp>
  13. #include <boost/bind/protect.hpp>
  14. #include <boost/bind/make_adaptable.hpp>
  15. #include <queue>
  16. #include <cstdio>
  17. namespace fungu{
  18. namespace http{
  19. class connection
  20. {
  21. public:
  22. typedef unsigned short port_type;
  23. class error
  24. {
  25. public:
  26. enum error_type
  27. {
  28. NONE = 0,
  29. BUFFER,
  30. NETWORK,
  31. PROTOCOL
  32. };
  33. error():m_value(NONE){}
  34. error(error_type value):m_value(value){}
  35. error(error_type value, const std::string & message):m_value(value), m_message(message){}
  36. error_type type()const{return m_value;}
  37. operator bool()const{return m_value != NONE;}
  38. const std::string & message()const{return m_message;}
  39. private:
  40. error_type m_value;
  41. std::string m_message;
  42. };
  43. connection(boost::asio::ip::tcp::socket &);
  44. template<typename ReadHandler>
  45. void async_read_header(ReadHandler handler)
  46. {
  47. boost::asio::socket_base::receive_buffer_size option;
  48. m_socket.get_option(option);
  49. m_receive_buffer_size = option.value();
  50. boost::asio::async_read_until(m_socket, m_read_buffer, "\r\n\r\n", bind_io_handler(&connection::read_header_complete<ReadHandler>, handler));
  51. }
  52. template<typename CompletionHandler>
  53. void async_read_body(std::size_t content_length, stream::sink & output, CompletionHandler handler)
  54. {
  55. consume_header();
  56. boost::system::error_code noerror;
  57. read_body_complete(0, content_length, output, boost::make_adaptable<void, const error &>(handler), noerror, 0);
  58. }
  59. template<typename CompletionHandler>
  60. void async_read_chunked_body(stream::sink & output, CompletionHandler handler)
  61. {
  62. consume_header();
  63. _async_read_chunked_body(output, boost::make_adaptable<void, const error &>(handler));
  64. }
  65. template<typename CompletionHandler>
  66. void async_send(const std::string & data, CompletionHandler handler)
  67. {
  68. boost::asio::async_write(m_socket, boost::asio::buffer(data), boost::asio::transfer_at_least(data.length()), bind_io_handler(&connection::send_complete<CompletionHandler>, handler));
  69. }
  70. template<typename CompletionHandler>
  71. void async_send(const void * data, std::size_t datalen, CompletionHandler handler)
  72. {
  73. boost::asio::async_write(m_socket, boost::asio::buffer(data, datalen), boost::asio::transfer_at_least(datalen), bind_io_handler(&connection::send_complete<CompletionHandler>, handler));
  74. }
  75. template<typename CompletionHandler>
  76. void async_send_chunk(const void * data, std::size_t datalen, CompletionHandler handler)
  77. {
  78. char buffer[32];
  79. std::sprintf(buffer, "%x\r\n", static_cast<unsigned int>(datalen));
  80. m_sent_chunk_headers.push(buffer);
  81. std::vector<boost::asio::const_buffer> chunk;
  82. chunk.push_back(boost::asio::buffer(m_sent_chunk_headers.back()));
  83. if(datalen) chunk.push_back(boost::asio::buffer(data, datalen));
  84. chunk.push_back(boost::asio::buffer("\r\n", 2));
  85. m_socket.async_send(chunk, bind_io_handler(&connection::send_chunk_complete<CompletionHandler>, handler));
  86. }
  87. void close();
  88. port_type remote_port()const;
  89. std::string remote_ip_string()const;
  90. unsigned long remote_ip_v4_ulong()const;
  91. boost::asio::io_service & io_service()
  92. {
  93. return m_socket.get_io_service();
  94. }
  95. bool has_connection_error()const
  96. {
  97. return m_read_error || m_send_error;
  98. }
  99. bool is_open()const
  100. {
  101. return m_socket.is_open();
  102. }
  103. private:
  104. template<typename MemberFunction, typename CompletionHandler>
  105. class io_handler_binder
  106. {
  107. public:
  108. io_handler_binder(MemberFunction ih, connection * c, CompletionHandler eh)
  109. :m_this(c), m_internal_handler(ih), m_external_handler(eh){}
  110. void operator()(const boost::system::error_code & error_code, const std::size_t bytes_transferred)
  111. {
  112. (m_this->*m_internal_handler)(m_external_handler, error_code, bytes_transferred);
  113. }
  114. private:
  115. connection * m_this;
  116. MemberFunction m_internal_handler;
  117. CompletionHandler m_external_handler;
  118. };
  119. template<typename MemberFunction, typename CompletionHandler>
  120. io_handler_binder<MemberFunction, CompletionHandler> bind_io_handler(MemberFunction internal_handler, CompletionHandler external_handler)
  121. {
  122. return io_handler_binder<MemberFunction, CompletionHandler>(internal_handler, this, external_handler);
  123. }
  124. template<typename ReadHandler>
  125. void read_header_complete(ReadHandler handler, const boost::system::error_code error_code, const std::size_t readSize)
  126. {
  127. m_read_error = error_code;
  128. m_unconsumed_header_size = readSize;
  129. if(error_code)
  130. {
  131. handler(static_cast<const char * >(NULL), 0, error(error::NETWORK, error_code.message()));
  132. }
  133. else
  134. {
  135. handler(boost::asio::buffer_cast<const char *>(*m_read_buffer.data().begin()), readSize, error());
  136. }
  137. consume_header();
  138. }
  139. template<typename CompletionHandler>
  140. void read_body_complete(std::size_t completed, std::size_t contentLength, stream::sink & output, CompletionHandler finishedHandler, const boost::system::error_code error_code, const std::size_t readSize)
  141. {
  142. if(error_code)
  143. {
  144. m_read_error = error_code;
  145. m_socket.get_io_service().post(boost::bind(finishedHandler, error(error::NETWORK, error_code.message())));
  146. return;
  147. }
  148. if(readSize)
  149. {
  150. output.write(boost::asio::buffer_cast<const char *>(*m_read_buffer.data().begin()), readSize);
  151. m_read_buffer.consume(readSize);
  152. }
  153. else if(m_read_buffer.size()) //read what was left over from when the header was read
  154. {
  155. read_body_complete(0, contentLength, output, finishedHandler, error_code, std::min(m_read_buffer.size(), contentLength));
  156. return;
  157. }
  158. completed += readSize;
  159. std::size_t remaining = contentLength - completed;
  160. if(!remaining)
  161. {
  162. m_socket.get_io_service().post(boost::bind(finishedHandler, error()));
  163. return;
  164. }
  165. boost::asio::async_read(m_socket, m_read_buffer,
  166. boost::asio::transfer_at_least(std::min(remaining, m_receive_buffer_size)),
  167. boost::bind(&connection::read_body_complete<CompletionHandler>, this, completed, contentLength, boost::ref(output), finishedHandler, _1, _2));
  168. }
  169. static bool parse_chunk_size(const char *, const char *, std::size_t *);
  170. template<typename CompletionHandler>
  171. void _async_read_chunked_body(stream::sink & output, CompletionHandler handler)
  172. {
  173. boost::asio::async_read_until(m_socket, m_read_buffer, "\r\n", boost::bind(&connection::read_chunk_header<CompletionHandler>, this, &output, handler, _1, _2));
  174. }
  175. template<typename CompletionHandler>
  176. void read_chunk_header(stream::sink * output, CompletionHandler handler, const boost::system::error_code & error_code, const std::size_t readSize)
  177. {
  178. if(error_code)
  179. {
  180. m_read_error = error_code;
  181. handler(error(error::NETWORK, error_code.message()));
  182. return;
  183. }
  184. const char * line = boost::asio::buffer_cast<const char *>(*m_read_buffer.data().begin());
  185. std::size_t chunk_size;
  186. if(!parse_chunk_size(line, line + readSize, &chunk_size))
  187. {
  188. handler(error(error::PROTOCOL));
  189. return;
  190. }
  191. m_read_buffer.consume(readSize);
  192. if(chunk_size)
  193. {
  194. boost::system::error_code noerror;
  195. read_chunk(output, handler, chunk_size, noerror, 0);
  196. }
  197. else
  198. {
  199. boost::asio::async_read_until(m_socket, m_read_buffer, "\r\n\r\n",
  200. boost::bind(&connection::read_trailer<CompletionHandler>, this, handler, _1, _2));
  201. }
  202. }
  203. template<typename CompletionHandler>
  204. void read_chunk(stream::sink * output, CompletionHandler handler, std::size_t remaining, const boost::system::error_code & error_code, const std::size_t readSize)
  205. {
  206. if(error_code)
  207. {
  208. m_read_error = error_code;
  209. handler(error(error::NETWORK, error_code.message()));
  210. return;
  211. }
  212. if(readSize)
  213. {
  214. const char * data = boost::asio::buffer_cast<const char *>(*m_read_buffer.data().begin());
  215. output->write(data, readSize);
  216. m_read_buffer.consume(readSize);
  217. }
  218. remaining -= readSize;
  219. if(remaining)
  220. {
  221. boost::asio::async_read(m_socket, m_read_buffer,
  222. boost::asio::transfer_at_least(std::min(remaining, m_receive_buffer_size)),
  223. boost::bind(&connection::read_chunk<CompletionHandler>, this, output, handler, remaining, _1, _2));
  224. }
  225. else
  226. {
  227. boost::asio::async_read_until(m_socket, m_read_buffer, "\r\n",
  228. boost::bind(&connection::read_end_chunk_data<CompletionHandler>, this, output, handler, _1, _2));
  229. }
  230. }
  231. template<typename CompletionHandler>
  232. void read_end_chunk_data(stream::sink * output, CompletionHandler handler, const boost::system::error_code & error_code, const std::size_t readSize)
  233. {
  234. if(error_code)
  235. {
  236. m_read_error = error_code;
  237. handler(error(error::NETWORK, error_code.message()));
  238. return;
  239. }
  240. _async_read_chunked_body(*output, handler);
  241. }
  242. template<typename CompletionHandler>
  243. void read_trailer(CompletionHandler handler, const boost::system::error_code & error_code, const std::size_t readSize)
  244. {
  245. if(error_code)
  246. {
  247. m_read_error = error_code;
  248. handler(error(error::NETWORK, error_code.message()));
  249. return;
  250. }
  251. //ignore headers
  252. handler(error(error::NONE));
  253. }
  254. template<typename CompletionHandler>
  255. void send_complete(CompletionHandler handler, const boost::system::error_code & error_code, std::size_t bytes_sent)
  256. {
  257. if(error_code)
  258. {
  259. m_send_error = error_code;
  260. handler(error(error::NETWORK, error_code.message()));
  261. return;
  262. }
  263. handler(error(error::NONE));
  264. }
  265. template<typename CompletionHandler>
  266. void send_chunk_complete(CompletionHandler handler, const boost::system::error_code & error_code, std::size_t bytes_sent)
  267. {
  268. m_sent_chunk_headers.pop();
  269. if(error_code)
  270. {
  271. m_send_error = error_code;
  272. handler(error(error::NETWORK, error_code.message()));
  273. return;
  274. }
  275. handler(error(error::NONE));
  276. }
  277. void consume_header()
  278. {
  279. m_read_buffer.consume(m_unconsumed_header_size);
  280. m_unconsumed_header_size = 0;
  281. }
  282. boost::asio::streambuf m_read_buffer;
  283. std::size_t m_unconsumed_header_size;
  284. boost::system::error_code m_read_error;
  285. boost::system::error_code m_send_error;
  286. boost::asio::ip::tcp::socket & m_socket;
  287. std::size_t m_receive_buffer_size;
  288. std::size_t m_send_buffer_size;
  289. std::queue<std::string> m_sent_chunk_headers;
  290. };
  291. namespace server{
  292. class client_connection:public boost::asio::ip::tcp::socket, public connection
  293. {
  294. public:
  295. client_connection(boost::asio::io_service & service)
  296. :boost::asio::ip::tcp::socket(service), connection(*static_cast<boost::asio::ip::tcp::socket *>(this)){}
  297. };
  298. } //namespace server
  299. } //namespace http
  300. } //namespace fungu
  301. #endif