PageRenderTime 102ms CodeModel.GetById 77ms app.highlight 21ms RepoModel.GetById 1ms 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
  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
 22#ifndef _SOCKET_UTILS_H_
 23#define _SOCKET_UTILS_H_
 24
 25#include "SocketService.h"
 26#include "SocketExceptions.h"
 27
 28
 29#include <boost/system/error_code.hpp>
 30
 31#include <string>
 32#include <deque>
 33
 34#define BUFFER_SIZE 1024   // choose a reasonable size to send back to JS
 35
 36namespace ti
 37{
 38	template <class T>
 39	class Socket
 40		: public StaticBoundObject
 41	{
 42	public:
 43		Socket<T>(Host *host, const std::string & name);
 44		virtual ~Socket();
 45
 46	protected:
 47		Host* ti_host;
 48		T *socket;
 49
 50    boost::asio::detail::mutex write_mutex;
 51		std::deque<std::string> write_buffer;
 52		char read_data_buffer[BUFFER_SIZE + 1];
 53		bool non_blocking;
 54		enum SOCK_STATE_en { SOCK_CLOSED,
 55			SOCK_CONNECTING,
 56			SOCK_CONNECTED,
 57			SOCK_HANDSHAKE_IN_PROGRESS,
 58			SOCK_CLOSING
 59		} sock_state;
 60
 61		void on_read(char * data, int size);
 62		void on_error(const std::string& error_text);
 63		void on_close();
 64
 65		inline static tide::Logger* GetLogger()
 66		{
 67			return tide::Logger::Get("Socket.TCPSocket");
 68		}
 69
 70		void registerHandleRead();
 71		virtual bool CompleteClose()=0;
 72
 73		template<typename> friend class Socket;
 74		template <class T1>
 75		void copyHandlers(Socket<T1> *b)
 76		{
 77			this->onRead = b->onRead;
 78			this->onError = b->onError;
 79			this->onClose = b->onClose;
 80		}
 81
 82	private:
 83
 84		TiMethodRef onRead;
 85		TiMethodRef onError;
 86		TiMethodRef onClose;
 87
 88		void SetOnRead(const ValueList& args, ValueRef result)
 89		{
 90			this->onRead = args.at(0)->ToMethod();
 91		}
 92
 93		void SetOnError(const ValueList& args, ValueRef result)
 94		{
 95			this->onError = args.at(0)->ToMethod();
 96		}
 97
 98		void SetOnClose(const ValueList& args, ValueRef result)
 99		{
100			this->onClose = args.at(0)->ToMethod();
101		}
102		void Write(const ValueList& args, ValueRef result);
103		void Read(const ValueList& args, ValueRef result);
104		void Close(const ValueList& args, ValueRef result);
105		void IsClosed(const ValueList& args, ValueRef result);
106
107		void registerHandleWrite();
108		void handleWrite(const boost::system::error_code& error, std::size_t bytes_transferred);
109		void writeAsync(const std::string &data);
110
111		bool writeSync(const std::string &data);
112		bool write(const std::string &data);
113		std::string read();
114
115		void handleRead(const boost::system::error_code& error, std::size_t bytes_transferred);
116	};
117
118
119	template <class T>
120	Socket<T>::Socket(Host *host, const std::string & name)
121		: StaticBoundObject(name.c_str()),
122	ti_host(host),
123	socket(NULL),
124	non_blocking(false),
125	sock_state(SOCK_CLOSED)
126	{
127		this->SetMethod("onRead",&Socket::SetOnRead);
128		this->SetMethod("onError",&Socket::SetOnError);
129		this->SetMethod("onClose",&Socket::SetOnClose);
130
131		this->SetMethod("read",&Socket::Read);
132		this->SetMethod("write",&Socket::Write);
133
134		this->SetMethod("isClosed",&Socket::IsClosed);
135		this->SetMethod("close",&Socket::Close);
136	}
137
138	template <class T>
139	Socket<T>::~Socket()
140	{
141		if (socket)
142		{
143			delete socket;
144			socket = NULL;
145		}
146	}
147
148
149	template <class T>
150	void Socket<T>::on_read(char * data, int size)
151	{
152		if(!this->onRead.isNull()) 
153		{
154			BytesRef bytes(new Bytes(data, size));
155			ValueList args (Value::NewObject(bytes));
156			RunOnMainThread(this->onRead, args, false);
157			return;
158		}
159		GetLogger()->Warn("Socket::onRead: not read subscriber registered:  " + string(data));
160	}
161
162	template <class T>
163	void Socket<T>::on_error(const std::string& error_text)
164	{
165		if(!this->onError.isNull()) 
166		{
167			ValueList args (Value::NewString(error_text.c_str()));
168			RunOnMainThread(this->onError, args, false);
169		}
170	}
171
172	template <class T>
173	void Socket<T>::on_close()
174	{
175		if(!this->onClose.isNull()) 
176		{
177			ValueList args;
178			RunOnMainThread(this->onClose, args, false);
179		}
180	}
181
182	template <class T>
183	void Socket<T>::Write(const ValueList& args, ValueRef result)
184	{
185		try
186		{
187			std::string data = args.at(0)->ToString();
188			result->SetBool(this->write(data));
189		}
190		catch(SocketException &e)
191		{
192			throw ValueException::FromString(e.what());
193		}
194	}
195
196	template <class T>
197	void Socket<T>::Read(const ValueList& args, ValueRef result)
198	{
199		try
200		{
201			std::string data = this->read();
202			BytesRef bytes(new Bytes(data.c_str(), data.size()));
203			result->SetValue(Value::NewObject(bytes));
204		}
205		catch(SocketException &e)
206		{
207			throw ValueException::FromString(e.what());
208		}
209	}
210
211	template <class T>
212	void Socket<T>::Close(const ValueList& args, ValueRef result)
213	{
214		result->SetBool(this->CompleteClose());
215	}
216
217	template <class T>
218	void Socket<T>::IsClosed(const ValueList& args, ValueRef result)
219	{
220		return result->SetBool(this->sock_state == SOCK_CLOSED);
221	}
222
223	
224
225	template <class T>
226	void Socket<T>::registerHandleWrite()
227	{
228    boost::asio::async_write(*socket,
229			boost::asio::buffer(write_buffer.front().c_str(), write_buffer.front().size()),
230			boost::bind(&Socket::handleWrite, this,
231			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
232
233	}
234
235	template <class T>
236	void Socket<T>::handleWrite(const boost::system::error_code& error, std::size_t bytes_transferred)
237	{
238		if (error)
239		{
240			if (error == boost::asio::error::operation_aborted)
241			{
242				GetLogger()->Warn("Socket::handleWrite: operation aborted.");
243				return;
244			}
245			this->on_error(error.message());
246			return;
247		}
248    boost::asio::detail::mutex::scoped_lock lock(write_mutex);
249		write_buffer.pop_front();
250		if (!write_buffer.empty())
251		{
252			this->registerHandleWrite();
253		}
254	}
255
256	template <class T>
257	void Socket<T>::writeAsync(const std::string &data)
258	{
259    boost::asio::detail::mutex::scoped_lock lock(write_mutex);
260		bool write_in_progress = !write_buffer.empty();
261		write_buffer.push_back(data);
262		if (!write_in_progress)
263		{
264			this->registerHandleWrite();
265		}
266	}
267
268	template <class T>
269	bool Socket<T>::writeSync(const std::string &data)
270	{
271		try
272		{
273      boost::asio::write(*socket, boost::asio::buffer(data.c_str(), data.size()));
274		}
275		catch(boost::system::system_error & e)
276		{
277			this->CompleteClose();
278			this->on_error(e.what());
279			return false;
280		}
281		return true;
282	}
283
284	template <class T>
285	bool Socket<T>::write(const std::string &data)
286	{
287		if (this->sock_state != SOCK_CONNECTED)
288		{
289			throw TCPSocketWriteException();
290		}
291		if(non_blocking)
292		{
293			writeAsync(data);
294			return true;
295		}
296		return writeSync(data);
297	}
298
299	template <class T>
300	void Socket<T>::handleRead(const boost::system::error_code& error, std::size_t bytes_transferred)
301	{
302		if (error)
303		{
304			if (error == boost::asio::error::operation_aborted)
305			{
306				GetLogger()->Warn("Socket::handleRead: operation aborted.");
307				return;
308			}
309			this->on_error(error.message());
310			return;
311		}
312		this->on_read(read_data_buffer, bytes_transferred);
313		this->registerHandleRead();
314	}
315
316
317	template <class T>
318	void Socket<T>::registerHandleRead()
319	{
320    boost::asio::async_read(*socket,
321			boost::asio::buffer(read_data_buffer, BUFFER_SIZE),
322			boost::asio::transfer_at_least(1),
323			boost::bind(&Socket::handleRead, this,
324			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
325	}
326
327	template <class T>
328	std::string Socket<T>::read()
329	{
330		if (this->sock_state != SOCK_CONNECTED)
331		{
332			throw TCPSocketReadNotOpenException();
333		}
334		// TODO: implement sync read
335		size_t size = 0;
336		try
337		{
338			size = boost::asio::read(*socket, boost::asio::buffer(read_data_buffer, BUFFER_SIZE),
339				boost::asio::transfer_at_least(1));
340		}
341		catch(boost::system::system_error & e)
342		{
343			this->CompleteClose();
344			this->on_error(e.what());
345			throw TCPSocketReadException();
346		}
347		if (size > 0)
348		{
349			return std::string(read_data_buffer, size);
350		}
351		return std::string("");
352	}
353
354}
355#endif