1 // Written in the D programming language. 2 3 /** 4 * MessagePack RPC Server 5 */ 6 module msgpackrpc.server; 7 8 public import msgpackrpc.common; 9 10 import msgpack; 11 import vibe.vibe; 12 13 import std.traits; 14 15 16 /** 17 * MessagePack RPC Server serves Object or module based dispacher 18 */ 19 class Server(alias T, alias Protocol) 20 { 21 private: 22 enum ModuleDispatcher = T.stringof.startsWith("module "); 23 24 alias Protocol.ServerSocket!(typeof(this)) Socket; 25 alias Protocol.ServerTransport!(typeof(this)) Transport; 26 27 Transport[] _transports; 28 29 public: 30 static if (!ModuleDispatcher) 31 { 32 private T _dispatcher; 33 34 this(T)(T dispatcher) 35 { 36 _dispatcher = dispatcher; 37 } 38 } 39 40 void listen(Endpoint endpoint) 41 { 42 auto transport = new Transport(endpoint); 43 transport.listen(this); 44 _transports ~= transport; 45 } 46 47 void start() 48 { 49 runEventLoop(); 50 } 51 52 void close() 53 { 54 foreach (transport; _transports) 55 transport.close(); 56 57 version( noExitEventloop) {} else 58 { exitEventLoop(); } 59 } 60 61 void onRequest(Sender)(Sender socket, size_t id, string method, ref Value[] params) 62 { 63 try { 64 Value result = dispatch(method, params); 65 socket.sendResponse(id, null, result); 66 } catch (Exception e) { 67 socket.sendResponse(id, e.msg, null); 68 //socket.sendMessage(MessageType.response, id, e, null); // can't be compileds... 69 } 70 } 71 72 void onNotify(string method, ref Value[] params) 73 { 74 try { 75 dispatch(method, params); 76 } catch (Exception e) { } // Notify doesn't return the error; 77 } 78 79 private: 80 Value dispatch(string method, ref Value[] params) 81 { 82 static if (ModuleDispatcher) 83 { 84 mixin("static import " ~ moduleName!T ~ ";"); 85 } 86 87 Value result; 88 89 switch (method) { 90 mixin(generateDispatchCases!T()); 91 default: 92 throw new NoMethodException("'%s' method not found".format(method)); 93 } 94 95 return result; 96 } 97 } 98 99 100 template TCPServer(alias T) 101 { 102 import msgpackrpc.transport.tcp; 103 104 alias Server!(T, msgpackrpc.transport.tcp) TCPServer; 105 } 106 107 template UDPServer(alias T) 108 { 109 import msgpackrpc.transport.udp; 110 111 alias Server!(T, msgpackrpc.transport.udp) UDPServer; 112 } 113 114 private: 115 116 string generateDispatchCases(alias T)() 117 { 118 static if (T.stringof.startsWith("module ")) 119 { 120 return generateDispatchCasesForModule!(T)(); 121 } 122 else 123 { 124 return generateDispatchCasesForObject!(T)(); 125 } 126 } 127 128 string generateDispatchCasesForModule(alias T)() 129 { 130 static assert(T.stringof.startsWith("module "), "T must be module"); 131 132 alias moduleName!T fullyModuleName; 133 mixin("import " ~ moduleName!T ~ ";"); 134 135 string result; 136 137 foreach (method; __traits(allMembers, T)) { 138 static if (!__traits(compiles, __traits(getMember, T, method).stringof.startsWith("template "))) 139 { 140 enum name = fullyModuleName ~ "." ~ method; 141 142 //static assert(__traits(getOverloads, T, method).length != 1, "The function of RPC dispatcher doesn't allow the overloads: function = " ~ name); 143 144 static if (__traits(compiles, ParameterTypeTuple!(mixin(name)))) 145 { 146 alias ParameterTypeTuple!(mixin(name)) ParameterTypes; 147 alias ReturnType!(mixin(name)) RT; 148 149 result ~= genetateCaseBody!(fullyModuleName, method, RT, ParameterTypes)(); 150 } 151 } 152 } 153 154 return result; 155 } 156 157 string generateDispatchCasesForObject(alias T)() 158 { 159 import std.algorithm : canFind; 160 import std.string; 161 162 static immutable string[] builtinFunctions = ["__ctor", "__dtor", "opEquals", "opCmp", "toHash", "toString", "Monitor", "factory"]; 163 164 string result; 165 166 foreach (method; __traits(allMembers, T)) { 167 alias MemberFunctionsTuple!(T, method) funcs; 168 169 // The result of member variables is "funcs.length == 0" 170 static if (funcs.length > 0 && !canFind(builtinFunctions, method)) 171 { 172 static assert(funcs.length == 1, "The function of RPC dispatcher doesn't allow the overloads: function = " ~ method); 173 174 alias typeof(funcs[0]) func; 175 alias ParameterTypeTuple!func ParameterTypes; 176 alias ReturnType!func RT; 177 178 result ~= genetateCaseBody!("_dispatcher", method, RT, ParameterTypes)(); 179 } 180 } 181 182 return result; 183 } 184 185 string genetateCaseBody(string prefix, string name, RT, ParameterTypes...)() 186 { 187 string result; 188 189 static if (ParameterTypes.length > 0) 190 { 191 result ~= q"CASE 192 case "%s": 193 if (params.length != %s) 194 throw new ArgumentException("the number of '%s' parameters is mismatched"); 195 CASE".format(name, ParameterTypes.length, name); 196 } 197 else 198 { 199 result ~= "case \"%s\":\n".format(name); 200 } 201 202 static if (is(RT == void)) 203 { 204 result ~= q"CASE 205 %s.%s(%s); 206 CASE".format(prefix, name, generateParameters!(ParameterTypes)()); 207 } 208 else static if (isArray!(RT) && !isSomeString!(RT)) 209 { 210 alias ForeachType!(RT) ET; 211 212 static if (isIntegral!(ET)) 213 { 214 // TODO: Add this conversion in msgpack-d 215 result ~= q"CASE 216 auto arr = %s.%s(%s); 217 Value[] vals; vals.length = arr.length; 218 foreach (i, e; arr) { 219 vals[i] = Value(cast(%s)e); 220 } 221 result = Value(vals); 222 CASE".format(prefix, name, generateParameters!(ParameterTypes)(), generateIntegerType!(ET)); 223 } 224 else 225 { 226 result ~= q"CASE 227 auto arr = %s.%s(%s); 228 Value[] vals; vals.length = arr.length; 229 foreach (i, e; arr) { 230 vals[i] = Value(e); 231 } 232 result = Value(vals); 233 CASE".format(prefix, name, generateParameters!(ParameterTypes)()); 234 } 235 } 236 else 237 { 238 // TODO: Support struct and class return type. 239 result ~= q"CASE 240 result = Value(%s.%s(%s)); 241 CASE".format(prefix, name, generateParameters!(ParameterTypes)()); 242 } 243 244 result ~= "break;\n"; 245 246 return result; 247 } 248 249 string generateParameters(Types...)() 250 { 251 string result; 252 253 foreach (i, Type; Types) { 254 if (i > 0) 255 result ~= ", "; 256 result ~= "params[%s].as!(%s)".format(i, Type.stringof); 257 } 258 259 return result; 260 } 261 262 template generateIntegerType(Type) 263 { 264 static if (isSigned!Type) 265 enum generateIntegerType = "long"; 266 else 267 enum generateIntegerType = "ulong"; 268 }