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 }