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 }