1 module app.http; 2 3 4 5 import hunt.net; 6 import hunt.raft; 7 8 import app.raft; 9 import std.string; 10 import std.conv; 11 import std.format; 12 13 14 15 enum RequestMethod 16 { 17 METHOD_GET = 0, 18 METHOD_SET = 1 19 }; 20 21 struct RequestCommand 22 { 23 RequestMethod Method; 24 string Key; 25 string Value; 26 size_t Hash; 27 }; 28 29 30 enum MAX_HTTP_REQUEST_BUFF = 4096; 31 32 alias Raft = app.raft.Raft; 33 34 class HttpBase 35 { 36 this(NetSocket sock, Raft raft ) 37 { 38 this.sock = sock; 39 this.raft = raft; 40 sock.handler((in ubyte[] data){ 41 onRead(data); 42 }); 43 sock.closeHandler((){ 44 onClose(); 45 }); 46 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 72 strlength = cast(string)buffer[cast(size_t)(pos + strlength.length) .. cast(size_t)left]; 73 intlength = to!int(strlength); 74 } 75 76 77 if(header_pos + 4 + intlength == buffer.length) 78 { 79 finish = true; 80 } 81 else 82 { 83 finish = false; 84 return true; 85 } 86 87 long pos_url = indexOf(str , "\r\n"); 88 if(pos_url == -1) 89 return false; 90 91 auto strs = split(cast(string)buffer[0 .. cast(size_t)pos_url]); 92 if(strs.length < 3) 93 return false; 94 95 url = strs[1]; 96 strbody = cast(string)buffer[cast(size_t)(header_pos + 4) .. $]; 97 98 return true; 99 } 100 101 bool do_response(string strbody) 102 { 103 auto res = format("HTTP/1.1 200 OK\r\nServer: kiss\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s" 104 , strbody.length , strbody); 105 sock.write(res); 106 return true; 107 } 108 109 bool process_request(string url , string strbody) 110 { 111 string path; 112 long pos = indexOf(url , "?"); 113 string[string] params; 114 if(pos == -1){ 115 path = url; 116 } 117 else{ 118 path = url[0 .. pos]; 119 auto keyvalues = split(url[pos + 1 .. $] , "&"); 120 foreach( k ; keyvalues) 121 { 122 auto kv = split(k , "="); 123 if(kv.length == 2) 124 params[kv[0]] = kv[1]; 125 } 126 } 127 128 if(path == "/get") 129 { 130 auto key = "key" in params; 131 if(key == null || key.length == 0) 132 return do_response("params key must not empty"); 133 134 RequestCommand command = { Method:RequestMethod.METHOD_GET , Key: *key , Hash:this.toHash()}; 135 raft.readIndex(command , this); 136 return true; 137 } 138 else if(path == "/set") 139 { 140 141 auto key = "key" in params; 142 auto value = "value" in params; 143 if(key == null || value == null || key.length == 0) 144 return do_response("params key must not empty or value not exist"); 145 146 RequestCommand command = { Method:RequestMethod.METHOD_SET , Key: *key ,Value : *value , Hash:this.toHash()}; 147 raft.propose(command , this); 148 return true; 149 } 150 else if(path == "/add") 151 { 152 153 154 auto nodeID = "ID" in params; 155 auto Context = "Context" in params; 156 if(nodeID == null || nodeID.length == 0 || Context.length == 0 || Context == null) 157 return do_response("ID or Context must not empty"); 158 159 ConfChange cc = { NodeID : to!ulong(*nodeID) , Type : ConfChangeType.ConfChangeAddNode ,Context:*Context }; 160 raft.proposeConfChange(cc); 161 return do_response("have request this add conf"); 162 163 } 164 else if(path == "/del") 165 { 166 auto nodeID = "ID" in params; 167 if(nodeID == null || nodeID.length == 0) 168 return do_response("ID must not empty"); 169 ConfChange cc = { NodeID : to!ulong(*nodeID) , Type : ConfChangeType.ConfChangeRemoveNode }; 170 raft.proposeConfChange(cc); 171 return do_response("have request this remove conf"); 172 } 173 else 174 { 175 return do_response("can not sovle " ~ path); 176 } 177 178 } 179 180 181 182 void onRead(in ubyte[] data) 183 { 184 buffer ~= data; 185 bool finish; 186 string strurl; 187 string strbody; 188 if(!is_request_finish(finish ,strurl,strbody )) 189 return ; 190 191 if(finish) 192 { 193 process_request(strurl , strbody); 194 } 195 else if(buffer.length >= MAX_HTTP_REQUEST_BUFF) 196 { 197 return ; 198 } 199 200 } 201 202 void close() 203 { 204 sock.close(); 205 } 206 207 void onClose() { 208 raft.delPropose(this); 209 } 210 211 ubyte[] buffer; 212 NetSocket sock; 213 Raft raft; 214 } 215