1 module neton.network.Http;
2 
3 import hunt.net;
4 import hunt.raft;
5 import hunt.logging;
6 
7 // import app.raft;
8 import std.string;
9 import std.conv;
10 import std.format;
11 import std.json;
12 import neton.server.NetonHttpServer;
13 import neton.store.Util;
14 
15 enum RequestMethod
16 {
17 	METHOD_GET = 0,
18 	METHOD_SET = 1,
19 	METHOD_POST = 2,
20 	METHOD_PUT = 3,
21 	METHOD_DELETE = 4,
22 	METHOD_UPDATESERVICE = 5,
23 	METHOD_UNKNOWN = 6,
24 };
25 
26 struct RequestCommand
27 {
28 	RequestMethod Method;
29 	string Key;
30 	string Value;
31 	size_t Hash;
32 	string Params;
33 };
34 
35 enum MAX_HTTP_REQUEST_BUFF = 4096;
36 
37 // alias NetonHttpServer = server.NetonHttpServer.NetonHttpServer;
38 
39 class HttpBase
40 {
41 	this(Connection connection, NetonHttpServer netonServer)
42 	{
43 		this._sock = connection;
44 		this._netonServer = netonServer;
45 		// sock.handler((in ubyte[] data) { onRead(data); });
46 		// sock.closeHandler(() { onClose(); });
47 
48 	}
49 
50 	bool is_request_finish(ref bool finish, ref string url, ref string strbody)
51 	{
52 		import std.typecons : No;
53 
54 		string str = cast(string) _buffer;
55 		long header_pos = indexOf(str, "\r\n\r\n");
56 
57 		if (header_pos == -1)
58 		{
59 			finish = false;
60 			return true;
61 		}
62 
63 		string strlength = "content-length: ";
64 		int intlength = 0;
65 		long pos = indexOf(str, strlength, 0, No.caseSensitive);
66 		if (pos != -1)
67 		{
68 			long left = indexOf(str, "\r\n", cast(size_t) pos);
69 			if (pos == -1)
70 				return false;
71 			try
72 			{
73 				strlength = cast(string) _buffer[cast(size_t)(
74 						pos + strlength.length) .. cast(size_t) left];
75 				intlength = to!int(strlength);
76 			}
77 			catch (Exception e)
78 			{
79 				logWarning("request format exception : %s", e.msg);
80 				return false;
81 			}
82 		}
83 
84 		if (header_pos + 4 + intlength == _buffer.length)
85 		{
86 			finish = true;
87 		}
88 		else
89 		{
90 			finish = false;
91 			return true;
92 		}
93 
94 		long pos_url = indexOf(str, "\r\n");
95 		if (pos_url == -1)
96 			return false;
97 
98 		auto strs = split(cast(string) _buffer[0 .. cast(size_t) pos_url]);
99 		if (strs.length < 3)
100 			return false;
101 		auto methond = toUpper(strs[0]);
102 		switch (methond)
103 		{
104 		case "GET":
105 			_requestMethod = RequestMethod.METHOD_GET;
106 			break;
107 		case "PUT":
108 			_requestMethod = RequestMethod.METHOD_PUT;
109 			break;
110 		case "POST":
111 			_requestMethod = RequestMethod.METHOD_POST;
112 			break;
113 		case "DELETE":
114 			_requestMethod = RequestMethod.METHOD_DELETE;
115 			break;
116 		default:
117 			_requestMethod = RequestMethod.METHOD_UNKNOWN;
118 			break;
119 		}
120 		url = strs[1];
121 
122 		try
123 		{
124 			strbody = cast(string) _buffer[cast(size_t)(header_pos + 4) .. $];
125 		}
126 		catch (Exception e)
127 		{
128 			logWarning("request format exception : %s", e.msg);
129 			return false;
130 		}
131 		return true;
132 	}
133 
134 	bool do_response(string strbody)
135 	{
136 		auto res = format(
137 				"HTTP/1.1 200 OK\r\nServer: Hunt\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s",
138 				strbody.length, strbody);
139 		_sock.write(res);
140 
141 		return true;
142 	}
143 
144 	bool process_request(string url, string strbody)
145 	{
146 		//if(!NetonHttpServer.instance()._node.isLeader() /*&& _requestMethod != RequestMethod.METHOD_GET*/)
147 		/*{
148 			// auto leader = NetonHttpServer.instance.leader();
149 			// logInfo("leader id : ", leader);
150 			// auto http = HTTP();
151 
152 			// // Put with data senders
153 			// auto msg = strbody;
154 			// http.contentLength = msg.length;
155 			// http.onSend = (void[] data)
156 			// {
157 			// 	auto m = cast(void[])msg;
158 			// 	size_t len = m.length > data.length ? data.length : m.length;
159 			// 	if (len == 0) return len;
160 			// 	data[0..len] = m[0..len];
161 			// 	msg = msg[len..$];
162 			// 	return len;
163 			// };
164 
165 			// // Track progress
166 			// if(_requestMethod == RequestMethod.METHOD_GET)
167 			// 	http.method = HTTP.Method.get;
168 			// else if(_requestMethod == RequestMethod.METHOD_PUT)
169 			// 	http.method = HTTP.Method.put;
170 			// else if(_requestMethod == RequestMethod.METHOD_DELETE)
171 			// 	http.method = HTTP.Method.del;
172 			// string urlpath = "http://";
173 			// foreach(peer;NetonConfig.instance.peersConf)
174 			// {
175 			// 	if(peer.id == leader)
176 			// 	{
177 			// 		urlpath ~= peer.ip;
178 			// 		urlpath ~= ":";
179 			// 		urlpath ~= to!string(peer.apiport);
180 			// 	}
181 			// }
182 			// http.url = urlpath ~ url;
183 			// http.onReceive = &this.onHttpRecive;
184 			// http.onProgress = &this.onHttpProgress;
185 			// http.perform();
186 
187 			// NetonHttpServer.instance.saveHttp(this);
188 			JSONValue  res;
189 			try
190 			{
191 				res["action"] = "not leader";
192 				
193 				JSONValue  leader;
194 				auto id = NetonHttpServer.instance.leader();
195 			    leader["id"] = id;
196 				foreach(peer;NetonConfig.instance.peersConf)
197 				{
198 					if(peer.id == id)
199 					{
200 						leader["ip"] = peer.ip;
201 						leader["apiport"]= peer.apiport;
202 						break;
203 					}
204 				}
205 
206 				res["leader"] = leader;
207 			}
208 			catch (Exception e)
209 			{
210 				logError("catch %s", e.msg);
211 				res["error"] = e.msg;
212 			}
213 			return do_response(res.toString);	
214 		}*/
215 
216 		_params.clear;
217 		if (strbody.length > 0)
218 		{
219 			//logInfo("http request body : ",strbody);
220 			auto keyvalues = split(strbody, "&");
221 			foreach (k; keyvalues)
222 			{
223 				auto kv = split(k, "=");
224 				if (kv.length == 2)
225 					_params[kv[0]] = kv[1];
226 			}
227 		}
228 
229 		string path;
230 		long pos = indexOf(url, "?");
231 		if (pos == -1)
232 		{
233 			path = url;
234 		}
235 		else
236 		{
237 			path = url[0 .. pos];
238 			auto keyvalues = split(url[pos + 1 .. $], "&");
239 			foreach (k; keyvalues)
240 			{
241 				auto kv = split(k, "=");
242 				if (kv.length == 2)
243 					_params[kv[0]] = kv[1];
244 			}
245 			url = path;
246 		}
247 
248 		_hash = this.toHash();
249 
250 		if (startsWith(url, "/keys"))
251 		{
252 			url = url[5 .. $];
253 		}
254 		else if (startsWith(url, "/register"))
255 		{
256 			//url = url[9..$];
257 		}
258 		else if (startsWith(url, "/deregister"))
259 		{
260 			//url = url[11..$];
261 		}
262 		else
263 		{
264 			return do_response("can not sovle " ~ url);
265 		}
266 
267 		url = getSafeKey(url);
268 
269 		JSONValue jparam;
270 		foreach (key, value; _params)
271 			jparam[key] = value;
272 		logInfo("HTTP param : ", jparam);
273 		if (_requestMethod == RequestMethod.METHOD_GET)
274 		{
275 			// auto key = "key" in _params;
276 			if (url.length == 0)
277 				return do_response("params key must not empty");
278 
279 			RequestCommand command = {
280 			Method:
281 				RequestMethod.METHOD_GET, Key : url, Hash : _hash, Params : jparam.toString};
282 				NetonHttpServer.instance().ReadIndex(command, this);
283 				return true;
284 			}
285 		else if (_requestMethod == RequestMethod.METHOD_PUT)
286 			{
287 				if (url.length == 0)
288 					return do_response("params key  must not empty ");
289 
290 				RequestCommand command = {
291 				Method:
292 					RequestMethod.METHOD_PUT, Key : url, Hash : _hash, Params : jparam.toString};
293 					NetonHttpServer.instance().Propose(command, this);
294 					return true;
295 				}
296 			else if (_requestMethod == RequestMethod.METHOD_DELETE)
297 				{
298 					// auto key = "key" in _params;
299 					if (url.length == 0)
300 						return do_response("params key must not empty");
301 
302 					RequestCommand command = {
303 					Method:
304 						RequestMethod.METHOD_DELETE, Key : url, Hash : _hash,
305 					Params : jparam.toString
306 			};
307 				NetonHttpServer.instance().Propose(command, this);
308 				return true;
309 			}
310 			else if (_requestMethod == RequestMethod.METHOD_POST)
311 			{
312 				RequestCommand command = {
313 				Method:
314 					RequestMethod.METHOD_POST, Key : url, Hash : _hash, Params : strbody
315 				};
316 				NetonHttpServer.instance().Propose(command, this);
317 				return true;
318 			}
319 			// else if(path == "/add")
320 			// {
321 			// 	auto nodeID = "ID" in _params;
322 			// 	auto Context = "Context" in _params;
323 			// 	if(nodeID == null || nodeID.length == 0 || Context.length == 0 || Context == null)
324 			// 		return do_response("ID or Context must not empty");
325 
326 			// 	ConfChange cc = { NodeID : to!ulong(*nodeID) , Type : ConfChangeType.ConfChangeAddNode ,Context:*Context };
327 			// 	NetonHttpServer.instance().ProposeConfChange(cc);
328 			// 	return do_response("have request this add conf");
329 
330 			// }
331 			// else if(path == "/del")
332 			// {
333 			// 	auto nodeID = "ID" in _params;
334 			// 	if(nodeID == null || nodeID.length == 0)
335 			// 		return do_response("ID must not empty");
336 			// 	ConfChange cc = { NodeID : to!ulong(*nodeID) , Type : ConfChangeType.ConfChangeRemoveNode };
337 			// 	NetonHttpServer.instance().ProposeConfChange(cc);
338 			// 	return do_response("have request this remove conf");
339 			// }
340 		else
341 			{
342 				return do_response("can not sovle " ~ url);
343 			}
344 		}
345 
346 		void onRead(in ubyte[] data)
347 		{
348 			_buffer ~= data;
349 			bool finish;
350 			string strurl;
351 			string strbody;
352 			if (!is_request_finish(finish, strurl, strbody))
353 				return;
354 
355 			if (finish)
356 			{
357 				process_request(strurl, strbody);
358 			}
359 			else if (_buffer.length >= MAX_HTTP_REQUEST_BUFF)
360 			{
361 				return;
362 			}
363 
364 		}
365 
366 		void close()
367 		{
368 			_sock.close();
369 		}
370 
371 		void onClose()
372 		{
373 			NetonHttpServer.instance.handleHttpClose(_hash);
374 			// super.onClose();
375 		}
376 
377 		string[string] params()
378 		{
379 			return _params;
380 		}
381 
382 		size_t onHttpRecive(ubyte[] data)
383 		{
384 			_httpRecvBuf ~= data;
385 			return data.length;
386 		}
387 
388 		int onHttpProgress(size_t dlTotal, size_t dlNow, size_t ulTotal, size_t ulNow)
389 		{
390 			if (dlTotal == dlNow)
391 			{
392 				logInfo("leader response data : ", cast(string) _httpRecvBuf);
393 				do_response(cast(string) _httpRecvBuf);
394 			}
395 			return 0;
396 		}
397 
398 		private byte[] _buffer;
399 		private string[string] _params;
400 		private size_t _hash;
401 		private RequestMethod _requestMethod;
402 		private ubyte[] _httpRecvBuf;
403 
404 		// ubyte[]         buffer;
405 		private Connection _sock;
406 		private NetonHttpServer _netonServer;
407 	}