PageRenderTime 227ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/experimental/web/socket.cpp

https://bitbucket.org/ewfiseli/elib
C++ | 365 lines | 283 code | 48 blank | 34 comment | 58 complexity | e60dc44dc693829b0f12eb87443c5cde MD5 | raw file
  1. #include <elib/experimental/web/socket.hpp>
  2. #include <elib/config.hpp>
  3. #include <elib/assert.hpp>
  4. #include <elib/aux/move.hpp>
  5. #define ELIB_WEB_HANDLE_OR_THROW_ERROR(...) \
  6. ELIB_CATCH_AND_RETHROW(detail::handle_or_throw_error(__VA_ARGS__);)
  7. namespace elib { namespace web
  8. {
  9. namespace detail
  10. {
  11. // Forward declarations are provided to satify clang's "-Wmissing-prototype"
  12. bool handle_or_throw_bad_sock(std::string msg, socket const & s, std::error_code *ec);
  13. void handle_error(std::error_code & ec) noexcept;
  14. void handle_or_throw_error(std::string msg, std::error_code *ec);
  15. ////////////////////////////////////////////////////////////////////////
  16. //
  17. bool handle_or_throw_bad_sock(std::string msg, socket const & s
  18. , std::error_code *ec)
  19. {
  20. std::error_code m_ec{};
  21. if (s.fail())
  22. m_ec = s.error_code();
  23. else if (!s.is_open())
  24. m_ec = std::error_code{EBADF, std::system_category()};
  25. else
  26. return false;
  27. if (!ec)
  28. {
  29. ELIB_THROW_EXCEPTION(
  30. socket_error(elib::move(msg), elib::move(m_ec))
  31. );
  32. }
  33. // else
  34. *ec = elib::move(m_ec);
  35. return true;
  36. }
  37. ////////////////////////////////////////////////////////////////////////
  38. //
  39. void handle_error(std::error_code & ec) noexcept
  40. {
  41. ELIB_ASSERT(errno != 0);
  42. ec = std::error_code{errno, std::system_category()};
  43. }
  44. ////////////////////////////////////////////////////////////////////////
  45. //
  46. void handle_or_throw_error(std::string msg
  47. , std::error_code *ec)
  48. {
  49. ELIB_ASSERT(errno != 0);
  50. std::error_code m_ec{errno, std::system_category()};
  51. if (!ec)
  52. {
  53. ELIB_THROW_EXCEPTION(socket_error(
  54. elib::move(msg), elib::move(m_ec)
  55. ));
  56. }
  57. // else
  58. *ec = elib::move(m_ec);
  59. }
  60. } // namespace detail
  61. ////////////////////////////////////////////////////////////////////////////
  62. //
  63. void socket::open(sock_domain d, sock_type t, int protocol) noexcept
  64. {
  65. reset();
  66. m_fd = ::socket(static_cast<int>(d), static_cast<int>(t), protocol);
  67. if (m_fd < 0)
  68. {
  69. detail::handle_error(m_ec);
  70. m_fd = -1;
  71. }
  72. }
  73. ////////////////////////////////////////////////////////////////////////////
  74. //
  75. void socket::shutdown(sock_shut s) noexcept
  76. {
  77. if (! is_open()) return;
  78. if (-1 == ::shutdown(m_fd, static_cast<int>(s)))
  79. detail::handle_error(m_ec);
  80. }
  81. ////////////////////////////////////////////////////////////////////////////
  82. //
  83. void socket::close() noexcept
  84. {
  85. if (! is_open()) return;
  86. if (-1 == ::close(m_fd))
  87. detail::handle_error(m_ec);
  88. m_fd = -1;
  89. }
  90. ////////////////////////////////////////////////////////////////////////////
  91. //
  92. bool socket::listen(int backlog) noexcept
  93. {
  94. if (!good()) return false;
  95. if (-1 == ::listen(m_fd, backlog))
  96. {
  97. detail::handle_error(m_ec);
  98. return false;
  99. }
  100. return true;
  101. }
  102. ////////////////////////////////////////////////////////////////////////////
  103. //
  104. bool socket::m_bind(const sockaddr *addr, socklen_t len) noexcept
  105. {
  106. if (!good()) return false;
  107. if (-1 == ::bind(m_fd, addr, len))
  108. {
  109. detail::handle_error(m_ec);
  110. return false;
  111. }
  112. return true;
  113. }
  114. ////////////////////////////////////////////////////////////////////////////
  115. //
  116. bool socket::m_connect(const sockaddr *addr, socklen_t len) noexcept
  117. {
  118. if (!good()) return false;
  119. if (-1 == ::connect(m_fd, addr, len))
  120. {
  121. detail::handle_error(m_ec);
  122. return false;
  123. }
  124. return true;
  125. }
  126. namespace detail
  127. {
  128. ////////////////////////////////////////////////////////////////////////
  129. //
  130. socket accept_impl(socket const & s, sockaddr *addr, socklen_t *len
  131. , std::error_code *ec)
  132. {
  133. if (ec) ec->clear();
  134. socket m_sock{};
  135. ELIB_RETHROW_BLOCK_BEGIN()
  136. {
  137. if (detail::handle_or_throw_bad_sock(
  138. "bad socket passed to accept", s, ec))
  139. return m_sock;
  140. } ELIB_RETHROW_BLOCK_END()
  141. int fd = ::accept(s.raw_socket(), addr, len);
  142. if (fd >= 0)
  143. m_sock.m_fd = fd;
  144. else
  145. ELIB_WEB_HANDLE_OR_THROW_ERROR("accept failed", ec);
  146. return m_sock;
  147. }
  148. ////////////////////////////////////////////////////////////////////////
  149. //
  150. ssize_t receive_impl(socket const & s, std::vector<char> & v
  151. , msg_flags f, std::error_code *ec)
  152. {
  153. if (ec) ec->clear();
  154. ELIB_RETHROW_BLOCK_BEGIN()
  155. {
  156. if (detail::handle_or_throw_bad_sock(
  157. "bad socket passed to receive", s, ec))
  158. return -1;
  159. }
  160. ELIB_RETHROW_BLOCK_END()
  161. ssize_t ret = ::recv(s.raw_socket(), static_cast<void*>(&v[0])
  162. , v.size(), static_cast<int>(f));
  163. if (-1 == ret)
  164. ELIB_WEB_HANDLE_OR_THROW_ERROR("receive failed", ec);
  165. return ret;
  166. }
  167. ////////////////////////////////////////////////////////////////////////
  168. //
  169. ssize_t receive_msg_impl(socket const & s, message_t & m, msg_flags f
  170. , std::error_code *ec)
  171. {
  172. if (ec) ec->clear();
  173. ELIB_RETHROW_BLOCK_BEGIN()
  174. {
  175. if (detail::handle_or_throw_bad_sock(
  176. "bad socket passed to receive_msg", s, ec))
  177. return -1;
  178. }
  179. ELIB_RETHROW_BLOCK_END()
  180. ssize_t ret = ::recvmsg(s.raw_socket(), &m, static_cast<int>(f));
  181. if (-1 == ret)
  182. ELIB_WEB_HANDLE_OR_THROW_ERROR("receive_msg failed", ec);
  183. return ret;
  184. }
  185. ////////////////////////////////////////////////////////////////////////
  186. //
  187. ssize_t send_impl(socket const & s, std::vector<char> const & v
  188. , msg_flags f, std::error_code *ec)
  189. {
  190. if (ec) ec->clear();
  191. ELIB_RETHROW_BLOCK_BEGIN()
  192. {
  193. if (detail::handle_or_throw_bad_sock(
  194. "bad socket passed to send", s, ec))
  195. return -1;
  196. }
  197. ELIB_RETHROW_BLOCK_END()
  198. ssize_t ret = ::send(s.raw_socket(), static_cast<const void*>(&v[0])
  199. , v.size(), static_cast<int>(f));
  200. if (-1 == ret)
  201. {
  202. ELIB_WEB_HANDLE_OR_THROW_ERROR("send failed", ec);
  203. }
  204. return ret;
  205. }
  206. ////////////////////////////////////////////////////////////////////////
  207. //
  208. ssize_t send_msg_impl(socket const & s, message_t const & m
  209. , msg_flags f, std::error_code *ec)
  210. {
  211. ELIB_ASSERT(!errno);
  212. if (ec) ec->clear();
  213. ELIB_RETHROW_BLOCK_BEGIN()
  214. {
  215. if (detail::handle_or_throw_bad_sock(
  216. "bad socket passed to send_msg", s, ec))
  217. return -1;
  218. }
  219. ELIB_RETHROW_BLOCK_END()
  220. ssize_t ret = ::sendmsg(s.raw_socket(), &m, static_cast<int>(f));
  221. if (-1 == ret)
  222. {
  223. ELIB_WEB_HANDLE_OR_THROW_ERROR("send_msg failed", ec);
  224. }
  225. return ret;
  226. }
  227. ////////////////////////////////////////////////////////////////////////
  228. //
  229. ssize_t send_to_impl(socket const & s, std::vector<char> const & v
  230. , msg_flags f
  231. , const sockaddr *dest_addr, socklen_t len
  232. , std::error_code *ec)
  233. {
  234. if (ec) ec->clear();
  235. ELIB_RETHROW_BLOCK_BEGIN()
  236. {
  237. if (detail::handle_or_throw_bad_sock(
  238. "bad socket passed to send_to", s, ec))
  239. return -1;
  240. }
  241. ELIB_RETHROW_BLOCK_END()
  242. ssize_t ret = ::sendto(s.raw_socket(), static_cast<const void*>(&v[0])
  243. , v.size(), static_cast<int>(f)
  244. , dest_addr, len);
  245. if (-1 == ret)
  246. {
  247. ELIB_WEB_HANDLE_OR_THROW_ERROR("send_to failed", ec);
  248. }
  249. return ret;
  250. }
  251. #if defined(__GNUC__) && !defined(__clang__)
  252. # pragma GCC diagnostic push
  253. # pragma GCC diagnostic ignored "-Wstrict-aliasing"
  254. #endif
  255. ::sockaddr_in get_peer_name_impl(socket const & s, std::error_code *ec)
  256. {
  257. if (ec) ec->clear();
  258. ::sockaddr_in in;
  259. ELIB_RETHROW_BLOCK_BEGIN()
  260. {
  261. if (detail::handle_or_throw_bad_sock(
  262. "bad socket passed to get_peer_name", s, ec))
  263. return in;
  264. }
  265. ELIB_RETHROW_BLOCK_END()
  266. ::socklen_t len = sizeof(::sockaddr_in);
  267. int ret = ::getpeername(
  268. s.raw_socket()
  269. , reinterpret_cast<::sockaddr*>(&in)
  270. , &len
  271. );
  272. if (ret == -1)
  273. {
  274. ELIB_WEB_HANDLE_OR_THROW_ERROR("get_peer_name failed", ec);
  275. return in;
  276. }
  277. // TODO this is wrong
  278. ELIB_ASSERT(len <= sizeof(::sockaddr_in));
  279. return in;
  280. }
  281. #if defined(__GNUC__) && !defined(__clang__)
  282. # pragma GCC diagnostic pop
  283. #endif
  284. #if defined(__GNUC__) && !defined(__clang__)
  285. # pragma GCC diagnostic push
  286. # pragma GCC diagnostic ignored "-Wstrict-aliasing"
  287. #endif
  288. ::sockaddr_in get_sock_name_impl(socket const & s, std::error_code *ec)
  289. {
  290. if (ec) ec->clear();
  291. ::sockaddr_in in;
  292. ELIB_RETHROW_BLOCK_BEGIN()
  293. {
  294. if (detail::handle_or_throw_bad_sock(
  295. "bad socket passed to get_sock_name", s, ec))
  296. return in;
  297. }
  298. ELIB_RETHROW_BLOCK_END()
  299. ::socklen_t len = sizeof(::sockaddr_in);
  300. int ret = ::getsockname(
  301. s.raw_socket()
  302. , reinterpret_cast<::sockaddr*>(&in)
  303. , &len
  304. );
  305. if (ret == -1)
  306. {
  307. ELIB_WEB_HANDLE_OR_THROW_ERROR("get_sock_name failed", ec);
  308. return in;
  309. }
  310. ELIB_ASSERT(len <= sizeof(::sockaddr_in));
  311. return in;
  312. }
  313. #if defined(__GNUC__) && !defined(__clang__)
  314. # pragma GCC diagnostic pop
  315. #endif
  316. } // namespace detail
  317. }} // namespace elib::web