1 /*
2  * Collie - An asynchronous event-driven network framework using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd 
5  *
6  * Developer: putao's Dlang team
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 module collie.bootstrap.client;
12 
13 import collie.channel;
14 import collie.net;
15 import collie.utils.memory;
16 
17 import collie.bootstrap.exception;
18 import collie.net.client.linklogInfo;
19 public import kiss.net.TcpStream;
20 import kiss.event.task;
21 
22 final class ClientBootstrap(PipeLine) : PipelineManager
23 {
24 	alias ConnCallBack = void delegate(PipeLine);
25 	alias LinklogInfo = TLinklogInfo!ConnCallBack;
26 	alias ClientCreatorCallBack = void delegate(TcpStream);
27 
28 	this(EventLoop loop)
29 	{
30 		_loop = loop;
31 	}
32 	
33 	~this()
34 	{
35 		if (_timer)
36 			_timer.destroy;
37 		if(_logInfo.client)
38 			_logInfo.client.destroy;
39 	}
40 
41 	void setClientCreatorCallBack(ClientCreatorCallBack cback)
42 	{
43 		_oncreator = cback;
44 	}
45 	
46 	auto pipelineFactory(shared PipelineFactory!PipeLine pipeFactory)
47 	{
48 		_pipelineFactory = pipeFactory;
49 		return this;
50 	}
51 	
52 	/// time is s
53 	auto heartbeatTimeOut(uint second)
54 	{
55 		_timeOut = second * 1000;
56 		return this;
57 	}
58 
59 	void connect(Address to, ConnCallBack cback = null)
60 	{
61 		if (_pipelineFactory is null)
62 			throw new NeedPipeFactoryException(
63 				"Pipeline must be not null! Please set Pipeline frist!");
64 		if (_logInfo.client)
65 			throw new ConnectedException("This Socket is Connected! Please close before connect!");
66 		_logInfo.addr = to;
67 		_logInfo.tryCount = 0;
68 		_logInfo.cback = cback;
69 		_loop.postTask(newTask(&doConnect));
70 	}
71 	
72 	void close()
73 	{
74 		if (_logInfo.client is null)
75 			return;
76 		_logInfo.client.close();
77 	}
78 	
79 	@property EventLoop eventLoop()
80 	{
81 		return _loop;
82 	}
83 	
84 	@property auto pipeLine()
85 	{
86 		if(_logInfo.client is null)
87 			return null;
88 		return _pipe;
89 	}
90 
91 	@property tryCount(){return _tryCount;}
92 	@property tryCount(uint count){_tryCount = count;}
93 
94 protected:
95 	void doConnect()
96 	{
97 		_logInfo.client = new TcpStream(_loop,_logInfo.addr.addressFamily);
98 		if(_oncreator)
99 			_oncreator(_logInfo.client);
100 		_logInfo.client.setCloseHandle(&closeCallBack);
101 		_logInfo.client.setConnectHandle(&connectCallBack);
102 		_logInfo.client.setReadHandle(&readCallBack);
103 		_logInfo.client.connect(_logInfo.addr);
104 	}
105 
106 	void closeCallBack() nothrow @trusted
107 	{
108 		catchAndLogException((){
109 				if (_timer)
110 					_timer.stop();
111 				if(_pipe)
112 					_pipe.transportInactive();
113 			}());
114 	}
115 	
116 	void connectCallBack(bool isconnect) nothrow @trusted
117 	{
118 		catchAndLogException((){
119 			if (isconnect)
120 			{
121 				if (_timeOut > 0)
122 				{
123 					if (_timer is null)
124 					{
125 						logDebug("new timer!");
126 						_timer = new Timer(_loop);
127 						_timer.setTimerHandle(&onTimeOut);
128 					}
129 					if(!_timer.watched) {
130 
131 						bool rv = _timer.start(_timeOut);
132 						logDebug("start timer!   : ", rv);
133 					}
134 				}
135 				_logInfo.tryCount = 0;
136 				_pipe = _pipelineFactory.newPipeline(_logInfo.client);
137 				if(_logInfo.cback)
138 					_logInfo.cback(_pipe);
139 				_pipe.finalize();
140 				_pipe.pipelineManager(this);
141 				_pipe.transportActive();
142 			}else if(_logInfo.tryCount < _tryCount){
143 				_logInfo.client = null;
144 				_logInfo.tryCount ++;
145 				doConnect();
146 			} else {
147 				if(_logInfo.cback)
148 					_logInfo.cback(null);
149 				_logInfo.client = null;
150 				_logInfo.cback = null;
151 				_logInfo.addr = null;
152 				_pipe = null;
153 			}
154 		}());
155 	}
156 	
157 	void readCallBack(in ubyte[] buffer) nothrow @trusted
158 	{
159 		catchAndLogException(_pipe.read(cast(ubyte[])buffer));
160 	}
161 	/// Client Time out is not refresh!
162 	void onTimeOut() nothrow @trusted
163 	{
164 		catchAndLogException((){
165 		if(_pipe)
166 			_pipe.timeOut();
167 			}());
168 	}
169 	
170 	override void deletePipeline(PipelineBase pipeline)
171 	{
172 		if (_timer)
173 			_timer.stop();
174 		gcFree(_logInfo.client);
175 		_logInfo.client = null;
176 		pipeline.pipelineManager(null);
177 		_pipe = null;
178 	}
179 	
180 	override void refreshTimeout()
181 	{
182 	}
183 	
184 private:
185 	EventLoop _loop;
186 	PipeLine _pipe;
187 	shared PipelineFactory!PipeLine _pipelineFactory;
188 	Timer _timer = null;
189 	uint _timeOut = 0;
190 	uint _tryCount;
191 
192 	LinklogInfo _logInfo;
193 	ClientCreatorCallBack _oncreator;
194 }