PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/modules/socket/Socket.h

https://github.com/wyrover/TideSDK
C Header | 355 lines | 287 code | 47 blank | 21 comment | 19 complexity | a1bccf388c01c7751c935c944dcd46c1 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**
  2. * This file has been modified from its orginal sources.
  3. *
  4. * Copyright (c) 2012 Software in the Public Interest Inc (SPI)
  5. * Copyright (c) 2012 Mital Vora
  6. * Copyright (c) 2012 Steven Verbeek
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. *
  20. */
  21. #ifndef _SOCKET_UTILS_H_
  22. #define _SOCKET_UTILS_H_
  23. #include "SocketService.h"
  24. #include "SocketExceptions.h"
  25. #include <boost/system/error_code.hpp>
  26. #include <string>
  27. #include <deque>
  28. #define BUFFER_SIZE 1024 // choose a reasonable size to send back to JS
  29. namespace ti
  30. {
  31. template <class T>
  32. class Socket
  33. : public StaticBoundObject
  34. {
  35. public:
  36. Socket<T>(Host *host, const std::string & name);
  37. virtual ~Socket();
  38. protected:
  39. Host* ti_host;
  40. T *socket;
  41. boost::asio::detail::mutex write_mutex;
  42. std::deque<std::string> write_buffer;
  43. char read_data_buffer[BUFFER_SIZE + 1];
  44. bool non_blocking;
  45. enum SOCK_STATE_en { SOCK_CLOSED,
  46. SOCK_CONNECTING,
  47. SOCK_CONNECTED,
  48. SOCK_HANDSHAKE_IN_PROGRESS,
  49. SOCK_CLOSING
  50. } sock_state;
  51. void on_read(char * data, int size);
  52. void on_error(const std::string& error_text);
  53. void on_close();
  54. inline static tide::Logger* GetLogger()
  55. {
  56. return tide::Logger::Get("Socket.TCPSocket");
  57. }
  58. void registerHandleRead();
  59. virtual bool CompleteClose()=0;
  60. template<typename> friend class Socket;
  61. template <class T1>
  62. void copyHandlers(Socket<T1> *b)
  63. {
  64. this->onRead = b->onRead;
  65. this->onError = b->onError;
  66. this->onClose = b->onClose;
  67. }
  68. private:
  69. TiMethodRef onRead;
  70. TiMethodRef onError;
  71. TiMethodRef onClose;
  72. void SetOnRead(const ValueList& args, ValueRef result)
  73. {
  74. this->onRead = args.at(0)->ToMethod();
  75. }
  76. void SetOnError(const ValueList& args, ValueRef result)
  77. {
  78. this->onError = args.at(0)->ToMethod();
  79. }
  80. void SetOnClose(const ValueList& args, ValueRef result)
  81. {
  82. this->onClose = args.at(0)->ToMethod();
  83. }
  84. void Write(const ValueList& args, ValueRef result);
  85. void Read(const ValueList& args, ValueRef result);
  86. void Close(const ValueList& args, ValueRef result);
  87. void IsClosed(const ValueList& args, ValueRef result);
  88. void registerHandleWrite();
  89. void handleWrite(const boost::system::error_code& error, std::size_t bytes_transferred);
  90. void writeAsync(const std::string &data);
  91. bool writeSync(const std::string &data);
  92. bool write(const std::string &data);
  93. std::string read();
  94. void handleRead(const boost::system::error_code& error, std::size_t bytes_transferred);
  95. };
  96. template <class T>
  97. Socket<T>::Socket(Host *host, const std::string & name)
  98. : StaticBoundObject(name.c_str()),
  99. ti_host(host),
  100. socket(NULL),
  101. non_blocking(false),
  102. sock_state(SOCK_CLOSED)
  103. {
  104. this->SetMethod("onRead",&Socket::SetOnRead);
  105. this->SetMethod("onError",&Socket::SetOnError);
  106. this->SetMethod("onClose",&Socket::SetOnClose);
  107. this->SetMethod("read",&Socket::Read);
  108. this->SetMethod("write",&Socket::Write);
  109. this->SetMethod("isClosed",&Socket::IsClosed);
  110. this->SetMethod("close",&Socket::Close);
  111. }
  112. template <class T>
  113. Socket<T>::~Socket()
  114. {
  115. if (socket)
  116. {
  117. delete socket;
  118. socket = NULL;
  119. }
  120. }
  121. template <class T>
  122. void Socket<T>::on_read(char * data, int size)
  123. {
  124. if(!this->onRead.isNull())
  125. {
  126. BytesRef bytes(new Bytes(data, size));
  127. ValueList args (Value::NewObject(bytes));
  128. RunOnMainThread(this->onRead, args, false);
  129. return;
  130. }
  131. GetLogger()->Warn("Socket::onRead: not read subscriber registered: " + string(data));
  132. }
  133. template <class T>
  134. void Socket<T>::on_error(const std::string& error_text)
  135. {
  136. if(!this->onError.isNull())
  137. {
  138. ValueList args (Value::NewString(error_text.c_str()));
  139. RunOnMainThread(this->onError, args, false);
  140. }
  141. }
  142. template <class T>
  143. void Socket<T>::on_close()
  144. {
  145. if(!this->onClose.isNull())
  146. {
  147. ValueList args;
  148. RunOnMainThread(this->onClose, args, false);
  149. }
  150. }
  151. template <class T>
  152. void Socket<T>::Write(const ValueList& args, ValueRef result)
  153. {
  154. try
  155. {
  156. std::string data = args.at(0)->ToString();
  157. result->SetBool(this->write(data));
  158. }
  159. catch(SocketException &e)
  160. {
  161. throw ValueException::FromString(e.what());
  162. }
  163. }
  164. template <class T>
  165. void Socket<T>::Read(const ValueList& args, ValueRef result)
  166. {
  167. try
  168. {
  169. std::string data = this->read();
  170. BytesRef bytes(new Bytes(data.c_str(), data.size()));
  171. result->SetValue(Value::NewObject(bytes));
  172. }
  173. catch(SocketException &e)
  174. {
  175. throw ValueException::FromString(e.what());
  176. }
  177. }
  178. template <class T>
  179. void Socket<T>::Close(const ValueList& args, ValueRef result)
  180. {
  181. result->SetBool(this->CompleteClose());
  182. }
  183. template <class T>
  184. void Socket<T>::IsClosed(const ValueList& args, ValueRef result)
  185. {
  186. return result->SetBool(this->sock_state == SOCK_CLOSED);
  187. }
  188. template <class T>
  189. void Socket<T>::registerHandleWrite()
  190. {
  191. boost::asio::async_write(*socket,
  192. boost::asio::buffer(write_buffer.front().c_str(), write_buffer.front().size()),
  193. boost::bind(&Socket::handleWrite, this,
  194. boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
  195. }
  196. template <class T>
  197. void Socket<T>::handleWrite(const boost::system::error_code& error, std::size_t bytes_transferred)
  198. {
  199. if (error)
  200. {
  201. if (error == boost::asio::error::operation_aborted)
  202. {
  203. GetLogger()->Warn("Socket::handleWrite: operation aborted.");
  204. return;
  205. }
  206. this->on_error(error.message());
  207. return;
  208. }
  209. boost::asio::detail::mutex::scoped_lock lock(write_mutex);
  210. write_buffer.pop_front();
  211. if (!write_buffer.empty())
  212. {
  213. this->registerHandleWrite();
  214. }
  215. }
  216. template <class T>
  217. void Socket<T>::writeAsync(const std::string &data)
  218. {
  219. boost::asio::detail::mutex::scoped_lock lock(write_mutex);
  220. bool write_in_progress = !write_buffer.empty();
  221. write_buffer.push_back(data);
  222. if (!write_in_progress)
  223. {
  224. this->registerHandleWrite();
  225. }
  226. }
  227. template <class T>
  228. bool Socket<T>::writeSync(const std::string &data)
  229. {
  230. try
  231. {
  232. boost::asio::write(*socket, boost::asio::buffer(data.c_str(), data.size()));
  233. }
  234. catch(boost::system::system_error & e)
  235. {
  236. this->CompleteClose();
  237. this->on_error(e.what());
  238. return false;
  239. }
  240. return true;
  241. }
  242. template <class T>
  243. bool Socket<T>::write(const std::string &data)
  244. {
  245. if (this->sock_state != SOCK_CONNECTED)
  246. {
  247. throw TCPSocketWriteException();
  248. }
  249. if(non_blocking)
  250. {
  251. writeAsync(data);
  252. return true;
  253. }
  254. return writeSync(data);
  255. }
  256. template <class T>
  257. void Socket<T>::handleRead(const boost::system::error_code& error, std::size_t bytes_transferred)
  258. {
  259. if (error)
  260. {
  261. if (error == boost::asio::error::operation_aborted)
  262. {
  263. GetLogger()->Warn("Socket::handleRead: operation aborted.");
  264. return;
  265. }
  266. this->on_error(error.message());
  267. return;
  268. }
  269. this->on_read(read_data_buffer, bytes_transferred);
  270. this->registerHandleRead();
  271. }
  272. template <class T>
  273. void Socket<T>::registerHandleRead()
  274. {
  275. boost::asio::async_read(*socket,
  276. boost::asio::buffer(read_data_buffer, BUFFER_SIZE),
  277. boost::asio::transfer_at_least(1),
  278. boost::bind(&Socket::handleRead, this,
  279. boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
  280. }
  281. template <class T>
  282. std::string Socket<T>::read()
  283. {
  284. if (this->sock_state != SOCK_CONNECTED)
  285. {
  286. throw TCPSocketReadNotOpenException();
  287. }
  288. // TODO: implement sync read
  289. size_t size = 0;
  290. try
  291. {
  292. size = boost::asio::read(*socket, boost::asio::buffer(read_data_buffer, BUFFER_SIZE),
  293. boost::asio::transfer_at_least(1));
  294. }
  295. catch(boost::system::system_error & e)
  296. {
  297. this->CompleteClose();
  298. this->on_error(e.what());
  299. throw TCPSocketReadException();
  300. }
  301. if (size > 0)
  302. {
  303. return std::string(read_data_buffer, size);
  304. }
  305. return std::string("");
  306. }
  307. }
  308. #endif