1 /**********************************************************\
2 |                                                          |
3 |                          hprose                          |
4 |                                                          |
5 | Official WebSite: http://www.hprose.com/                 |
6 |                   http://www.hprose.org/                 |
7 |                                                          |
8 \**********************************************************/
9 
10 /**********************************************************\
11  *                                                        *
12  * hprose/rpc/service.d                                   *
13  *                                                        *
14  * hprose service library for D.                          *
15  *                                                        *
16  * LastModified: Apr 22, 2016                             *
17  * Author: Ma Bingyao <andot@hprose.com>                  *
18  *                                                        *
19 \**********************************************************/
20 
21 module hprose.rpc.service;
22 
23 import hprose.io;
24 import hprose.rpc.common;
25 import hprose.rpc.context;
26 import hprose.rpc.filter;
27 import std.conv;
28 import std.stdio;
29 import std.string;
30 import std.traits;
31 import std.typecons;
32 import std.variant;
33 
34 alias OnBeforeInvoke = void delegate(string name, Variant[] args, bool byRef, Context context);
35 alias OnAfterInvoke = void delegate(string name, Variant[] args, bool byRef, Variant result, Context context);
36 alias OnSendError = Exception delegate(Exception e, Context context);
37 
38 class Service {
39 
40     private {
41         alias RemoteMethod = char delegate(string, Reader, BytesIO, Context);
42         Filter[] _filters;
43         RemoteMethod[string] _remoteMethods;
44         string[] _allNames;
45     }
46 
47     bool simple = false;
48 
49     bool debugEnabled = false;
50 
51     OnBeforeInvoke onBeforeInvoke = null;
52     OnAfterInvoke onAfterInvoke = null;
53     OnSendError onSendError = null;
54     InvokeHandler[] invokeHandlers = [];
55     FilterHandler[] beforeFilterHandlers = [];
56     FilterHandler[] afterFilterHandlers = [];
57     NextFilterHandler beforeFilterHandler;
58     NextFilterHandler afterFilterHandler;
59 
60     @property ref filters() {
61         return this._filters;
62     }
63 
64     this() {
65         this._filters = [];
66         beforeFilterHandler = &beforeFilter;
67         afterFilterHandler = &afterFilter;
68     }
69 
70     private {
71         ubyte[] inputFilter(ubyte[] data, Context context) {
72             foreach_reverse(filter; _filters) {
73                 data = filter.inputFilter(data, context);
74             }
75             return data;
76         }
77 
78         ubyte[] outputFilter(ubyte[] data, Context context) {
79             foreach(filter; _filters) {
80                 data = filter.outputFilter(data, context);
81             }
82             return data;
83         }
84 
85         ubyte[] sendError(Exception e, Context context) {
86             try {
87                 if (onSendError !is null) {
88                     Exception ex = onSendError(e, context);
89                     if (ex !is null) {
90                         e = ex;
91                     }
92                 }
93             }
94             catch (Exception ex) {
95                 e = ex;
96             }
97             BytesIO stream = new BytesIO();
98             Writer writer = new Writer(stream, true);
99             stream.write(TagError);
100             writer.writeString(debugEnabled ? e.toString() : e.msg);
101             stream.write(TagEnd);
102             return cast(ubyte[])stream.buffer;
103         }
104     }
105 
106     protected {
107         ubyte[] doInvoke(BytesIO input, Context context) {
108             Reader reader = new Reader(input);
109             BytesIO output = new BytesIO();
110             char tag;
111             do {
112                 reader.reset();
113                 string name = reader.readString!string();
114                 string aliasName = toLower(name);
115                 if (aliasName in _remoteMethods) {
116                     tag = _remoteMethods[aliasName](name, reader, output, context);
117                 }
118                 else if ("*" in _remoteMethods) {
119                     tag = _remoteMethods["*"](name, reader, output, context);
120                 }
121                 else {
122                     throw new Exception("Can't find this method " ~ name);
123                 }
124             } while (tag == TagCall);
125             if (tag != TagEnd && tag != 0) {
126                 throw new Exception("Wrong Request: \r\n" ~ input.toString());
127             }
128             if (tag == TagEnd) {
129                 output.write(TagEnd);
130             }
131             return cast(ubyte[])output.buffer;
132         }
133 
134         ubyte[] doFunctionList() {
135             BytesIO output = new BytesIO();
136             Writer writer = new Writer(output, true);
137             output.write(TagFunctions);
138             writer.writeList(_allNames);
139             output.write(TagEnd);
140             return cast(ubyte[])output.buffer;
141         }
142 
143         ubyte[] afterFilter(ubyte[] data, Context context) {
144             try {
145                 BytesIO input = new BytesIO(data);
146                 switch (input.read()) {
147                     case TagCall: return doInvoke(input, context);
148                     case TagEnd: return doFunctionList();
149                     default: throw new Exception("Wrong Request: \r\n" ~ input.toString());
150                 }
151             }
152             catch (Exception e) {
153                 return sendError(e, context);
154             }
155         }
156 
157         ubyte[] beforeFilter(ubyte[] data, Context context) {
158             try {
159                 data = inputFilter(data, context);
160                 data = afterFilterHandler(data, context);
161                 return outputFilter(data, context);
162             }
163             catch (Exception e) {
164                 return outputFilter(sendError(e, context), context);
165             }
166         }
167 
168         ubyte[] handle(ubyte[] data, Context context) {
169             return beforeFilterHandler(data, context);
170         }
171     }
172 
173     void addFunction(string name, ResultMode mode = ResultMode.Normal, bool simple = false, T)(T func) if (isCallable!T && (name != "*" || is(ReturnType!T == Variant) && Parameters!T.length >= 2 && is(Parameters!T[0] == string) && is(Parameters!T[1] == Variant[]))) {
174         _remoteMethods[toLower(name)] = delegate(string aliasName, Reader reader, BytesIO output, Context context) {
175             alias returnType = ReturnType!T;
176             alias paramsType = Parameters!T;
177             alias defaultArgs = ParameterDefaults!T;
178 
179             Tuple!(paramsType) args;
180             foreach (i, ref arg; args) {
181                 static if (!is(defaultArgs[i] == void)) {
182                     arg = defaultArgs[i];
183                 }
184             }
185 
186             static if (is(paramsType[$ - 1] : Context)) {
187                 args[$ - 1] = cast(paramsType[$ - 1])context;
188             }
189 
190             static if (name == "*") {
191                 args[0] = aliasName;
192             }
193 
194             BytesIO input = reader.stream;
195             char tag = input.read();
196             bool byRef = false;
197             if (tag == TagList) {
198                 reader.reset();
199                 static if (name == "*") {
200                     args[1] = reader.readArrayWithoutTag!(Variant[])();
201                 }
202                 else static if (is(paramsType[$ - 1] : Context)) {
203                     reader.readTupleWithoutTag(args[0 .. $ - 1]);
204                 }
205                 else {
206                     reader.readTupleWithoutTag(args.expand);
207                 }
208                 tag = input.read();
209                 if (tag == TagTrue) {
210                     byRef = true;
211                     tag = input.read();
212                 }
213             }
214             if (onBeforeInvoke !is null) {
215                 static if (name == "*") {
216                     onBeforeInvoke(aliasName, args[1], byRef, context);
217                 }
218                 else {
219                     onBeforeInvoke(aliasName, variantArray(args.expand), byRef, context);
220                 }
221             }
222             static if (is(returnType == void)) {
223                 Variant result = null;
224             }
225             else {
226                 returnType result;
227             }
228             if (invokeHandlers.length == 0) {
229                 static if (is(returnType == void)) {
230                     func(args.expand);
231                 }
232                 else {
233                     result = func(args.expand);
234                 }
235             }
236             else {
237                 NextInvokeHandler next = delegate Variant(string name, ref Variant[] args, Context context) {
238                     Tuple!(paramsType) _args;
239                     foreach (i, ref arg; _args) {
240                         arg = args[i].get!(paramsType[i]);
241                     }
242                     Variant result;
243                     static if (is(returnType == void)) {
244                         func(_args.expand);
245                         result = null;
246                     }
247                     else {
248                         result = cast(Variant)(func(_args.expand));
249                     }
250                     foreach (i, ref arg; _args) {
251                         args[i] = cast(Variant)arg;
252                     }
253                     return result;
254                 };
255 
256                 foreach (handler; invokeHandlers) {
257                     next = (delegate(NextInvokeHandler next, InvokeHandler handler) {
258                         return delegate Variant(string name, ref Variant[] args, Context context) {
259                             return handler(name, args, context, next);
260                         };
261                         })(next, handler);
262                 }
263 
264                 Variant[] _args = variantArray(args.expand);
265 
266                 static if (is(returnType == void)) {
267                     next(name, _args, context);
268                 }
269                 else static if (is(returnType == Variant)) {
270                     result = next(name, _args, context);
271                 }
272                 else {
273                     result = next(name, _args, context).get!(returnType);
274                 }
275 
276                 foreach (i, ref arg; args) {
277                     arg = _args[i].get!(paramsType[i]);
278                 }
279             }
280 
281             if (onAfterInvoke !is null) {
282                 static if (name == "*") {
283                     onAfterInvoke(aliasName, args[1], byRef, result, context);
284                 }
285                 else {
286                     onAfterInvoke(aliasName, variantArray(args.expand), byRef, cast(Variant)result, context);
287                 }
288             }
289             static if (mode == ResultMode.RawWithEndTag) {
290                 static if (is(returnType == Variant)) {
291                     output.write(result.get!(ubyte[]));
292                 }
293                 else {
294                     output.write(cast(ubyte[])result);
295                 }
296                 return 0;
297             }
298             else static if (mode == ResultMode.Raw) {
299                 static if (is(returnType == Variant)) {
300                     output.write(result.get!(ubyte[]));
301                 }
302                 else {
303                     output.write(cast(ubyte[])result);
304                 }
305             }
306             else {
307                 output.write(TagResult);
308                 Writer writer = new Writer(output, simple);
309                 static if (mode == ResultMode.Serialized) {
310                     static if (is(returnType == Variant)) {
311                         output.write(result.get!(ubyte[]));
312                     }
313                     else {
314                         output.write(cast(ubyte[])result);
315                     }
316                 }
317                 else {
318                     writer.serialize(result);
319                 }
320                 static if (name == "*") {
321                     if (byRef) {
322                         output.write(TagArgument);
323                         writer.reset();
324                         writer.writeArray(args[1]);
325                     }
326                 }
327                 else static if ((paramsType.length > 1) || (paramsType.length == 1) && is(paramsType[$ - 1] : Context)) {
328                     if (byRef) {
329                         output.write(TagArgument);
330                         writer.reset();
331                         static if (is(paramsType[$ - 1] : Context)) {
332                              writer.writeTuple(args[0 .. $ - 1]);
333                         }
334                         else {
335                             writer.writeTuple(args.expand);
336                         }
337                     }
338                 }
339             }
340             return tag;
341         };
342         _allNames ~= name;
343     }
344 
345     void addFunction(string name, bool simple, T)(T func) if (isCallable!T) {
346         addFunction!(name, ResultMode.Normal, simple)(func);
347     }
348 
349     void addFunctions(string[] names, ResultMode mode = ResultMode.Normal, bool simple = false, T...)(T funcs) if (names.length == T.length && ((T.length > 1) || (T.length == 1) && isCallable!T)) {
350         addFunction!(names[0], mode, simple)(funcs[0]);
351         static if (names.length > 1) {
352             addFunctions!(names[1..$], mode, simple)(funcs[1..$]);
353         }
354     }
355 
356     void addFunctions(string[] names, bool simple, T...)(T funcs) if (names.length == T.length && ((T.length > 1) || (T.length == 1) && isCallable!T)) {
357         addFunctions!(names, ResultMode.Normal, simple)(funcs);
358     }
359 
360     void addMethod(string name, string aliasName = "", ResultMode mode = ResultMode.Normal, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T) {
361         addFunction!((aliasName == "" ? name : aliasName), mode, simple)(mixin("&obj." ~ name));
362     }
363 
364     void addMethod(string name, ResultMode mode, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T) {
365         addMethod!(name, "", mode, simple)(obj);
366     }
367 
368     void addMethod(string name, bool simple, T)(T obj) if (is(T == class) && !isCallable!T) {
369         addMethod!(name, "", ResultMode.Normal, simple)(obj);
370     }
371 
372     void addMethod(string name, T, string aliasName = "", ResultMode mode = ResultMode.Normal, bool simple = false)() if (is(T == class) && !isCallable!T) {
373         addFunction!((aliasName == "" ? name : aliasName), mode, simple)(mixin("&T." ~ name));
374     }
375 
376     void addMethod(string name, T, ResultMode mode, bool simple = false)() if (is(T == class) && !isCallable!T) {
377         addMethod!(name, T, "", mode, simple)();
378     }
379 
380     void addMethod(string name, T, bool simple)() if (is(T == class) && !isCallable!T) {
381         addMethod!(name, T, "", ResultMode.Normal, simple)();
382     }
383 
384     void addMethods(string[] names, string[] aliasNames = null, ResultMode mode = ResultMode.Normal, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T && (names.length > 0) && ((names.length == aliasNames.length) || (aliasNames == null))) {
385         static if (aliasNames == null) {
386             addMethod!(names[0], "", mode, simple)(obj);
387             static if (names.length > 1) {
388                 addMethods!(names[1..$], null, mode, simple)(obj);
389             }
390         }
391         else {
392             addMethod!(names[0], aliasNames[0], mode, simple)(obj);
393             static if (names.length > 1) {
394                 addMethods!(names[1..$], aliasNames[1..$], mode, simple)(obj);
395             }
396         }
397     }
398 
399     void addMethods(string[] names, ResultMode mode, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T && (names.length > 0)) {
400         addMethods!(names, null, mode, simple)(obj);
401     }
402 
403     void addMethods(string[] names, string[] aliasNames, bool simple, T)(T obj) if (is(T == class) && !isCallable!T && (names.length > 0) && ((names.length == aliasNames.length) || (aliasNames == null))) {
404         addMethods!(names, aliasNames, ResultMode.Normal, simple)(obj);
405     }
406 
407     void addMethods(string[] names, bool simple, T)(T obj) if (is(T == class) && !isCallable!T && (names.length > 0)) {
408         addMethods!(names, null, ResultMode.Normal, simple)(obj);
409     }
410 
411     void addMethods(U = void, string prefix = "", ResultMode mode = ResultMode.Normal, bool simple = false, T)(T obj = null) if ((is(T == class) && (is(U == void) || is(T : U)) && !isCallable!T) || (is(T == typeof(null)) && is(U == class))) {
412         static if (is(U == void)) {
413             alias B = Object;
414             alias C = T;
415         }
416         else {
417             alias B = BaseClassesTuple!U[0];
418             alias C = U;
419         }
420         static if (is(T == typeof(null))) {
421             foreach (M; __traits(allMembers, C)) {
422                 static if (__traits(compiles, mixin("typeof(&U." ~ M ~ ")"))) {
423                     static if (!__traits(hasMember, B, M) || !__traits(isSame, __traits(getMember, B, M), __traits(getMember, C, M))) {
424                         mixin("alias MT = typeof(&U." ~ M ~ ");");
425                         static if (isCallable!MT) {
426                             addFunction!((prefix == "" ? M : prefix ~ '_' ~ M), mode, simple)(mixin("&U." ~ M));
427                         }
428                     }
429                 }
430             }
431         }
432         else {
433             foreach (M; __traits(allMembers, C)) {
434                 static if (__traits(compiles, mixin("typeof(&obj." ~ M ~ ")"))) {
435                     static if (!__traits(hasMember, B, M) || !__traits(isSame, __traits(getMember, B, M), __traits(getMember, C, M))) {
436                         mixin("alias MT = typeof(&obj." ~ M ~ ");");
437                         static if (isCallable!MT) {
438                             addFunction!((prefix == "" ? M : prefix ~ '_' ~ M), mode, simple)(mixin("&obj." ~ M));
439                         }
440                     }
441                 }
442             }
443         }
444     }
445 
446     void addMethods(string prefix, ResultMode mode = ResultMode.Normal, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T) {
447         addMethods!(void, prefix, mode, simple)(obj);
448     }
449 
450     void addMethods(ResultMode mode, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T) {
451         addMethods!(void, "", mode, simple)(obj);
452     }
453 
454     void addMethods(string prefix, bool simple, T)(T obj) if (is(T == class) && !isCallable!T) {
455         addMethods!(void, prefix, ResultMode.Normal, simple)(obj);
456     }
457 
458     void addMethods(bool simple, T)(T obj) if (is(T == class) && !isCallable!T) {
459         addMethods!(void, "", ResultMode.Normal, simple)(obj);
460     }
461 
462     void addMethods(U, ResultMode mode, bool simple = false)() if (is(U == class) && !isCallable!U) {
463         addMethods!(U, "", mode, simple)();
464     }
465 
466     void addMethods(U, string prefix, bool simple)() if (is(U == class) && !isCallable!T) {
467         addMethods!(U, prefix, ResultMode.Normal, simple)();
468     }
469 
470     void addMethods(U, bool simple)() if (is(U == class) && !isCallable!U) {
471         addMethods!(U, "", ResultMode.Normal, simple)();
472     }
473 
474     void addStaticMethods(U, string prefix = "", ResultMode mode = ResultMode.Normal, bool simple = false)() if (is(U == class) && !isCallable!U) {
475         addMethods!(U, prefix, mode, simple)();
476     }
477 
478     void addStaticMethods(U, ResultMode mode, bool simple = false)() if (is(U == class) && !isCallable!U) {
479         addMethods!(U, "", mode, simple)();
480     }
481 
482     void addStaticMethods(U, string prefix, bool simple)() if (is(U == class) && !isCallable!T) {
483         addMethods!(U, prefix, ResultMode.Normal, simple)();
484     }
485 
486     void addStaticMethods(U, bool simple)() if (is(U == class) && !isCallable!U) {
487         addMethods!(U, "", ResultMode.Normal, simple)();
488     }
489 
490     void addInstanceMethods(U = void, string prefix = "", ResultMode mode = ResultMode.Normal, bool simple = false, T)(T obj) if (is(T == class) && (is(U == void) || is(T : U)) && !isCallable!T) {
491         static if (is(U == void)) {
492             alias B = Object;
493             alias C = T;
494         }
495         else {
496             alias B = BaseClassesTuple!U[0];
497             alias C = U;
498         }
499         foreach (M; __traits(allMembers, C)) {
500             static if (__traits(compiles, mixin("typeof(&obj." ~ M ~ ")"))) {
501                 static if (!__traits(hasMember, B, M) || !__traits(isSame, __traits(getMember, B, M), __traits(getMember, C, M))) {
502                     mixin("alias MT = typeof(&obj." ~ M ~ ");");
503                     static if (isCallable!MT && !__traits(isStaticFunction, __traits(getMember, C, M))) {
504                         addFunction!((prefix == "" ? M : prefix ~ '_' ~ M), mode, simple)(mixin("&obj." ~ M));
505                     }
506                 }
507             }
508         }
509     }
510 
511     void addInstanceMethods(U, ResultMode mode, bool simple = false, T)(T obj) if (is(T == class) && (is(U == void) || is(T : U)) && !isCallable!T) {
512         addInstanceMethods!(U, "", mode, simple)(obj);
513     }
514 
515     void addInstanceMethods(U, string prefix, bool simple, T)(T obj) if (is(T == class) && (is(U == void) || is(T : U)) && !isCallable!T) {
516         addInstanceMethods!(U, prefix, ResultMode.Normal, simple)(obj);
517     }
518 
519     void addInstanceMethods(U, bool simple, T)(T obj) if (is(T == class) && (is(U == void) || is(T : U)) && !isCallable!T) {
520         addInstanceMethods!(U, "", ResultMode.Normal, simple)(obj);
521     }
522 
523     void addInstanceMethods(ResultMode mode, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T) {
524         addInstanceMethods!(void, "", mode, simple)(obj);
525     }
526 
527     void addInstanceMethods(string prefix, bool simple, T)(T obj) if (is(T == class) && !isCallable!T) {
528         addInstanceMethods!(void, prefix, ResultMode.Normal, simple)(obj);
529     }
530 
531     void addInstanceMethods(bool simple, T)(T obj) if (is(T == class) && !isCallable!T) {
532         addInstanceMethods!(void, "", ResultMode.Normal, simple)(obj);
533     }
534 
535     void addMissingFunction(ResultMode mode = ResultMode.Normal, bool simple = false, T)(T func) if (isCallable!T && is(ReturnType!T == Variant) && Parameters!T.length >=2 && is(Parameters!T[0] == string) && is(Parameters!T[1] == Variant[])) {
536         addFunction!("*", mode, simple)(func);
537     }
538 
539     void addMissingFunction(bool simple, T)(T func) if (isCallable!T && is(ReturnType!T == Variant) && Parameters!T.length >=2 && is(Parameters!T[0] == string) && is(Parameters!T[1] == Variant[])) {
540         addMissingFunction!(ResultMode.Normal, simple)(func);
541     }
542     
543     void addMissingMethod(string name, ResultMode mode = ResultMode.Normal, bool simple = false, T)(T obj) if (is(T == class) && !isCallable!T) {
544         addMissingFunction!(mode, simple)(mixin("&obj." ~ name));
545     }
546 
547     void addMissingMethod(string name, bool simple, T)(T obj) if (is(T == class) && !isCallable!T) {
548         addMissingMethod!(name, ResultMode.Normal, simple)(obj);
549     }
550 
551     void addMissingMethod(string name, T, ResultMode mode = ResultMode.Normal, bool simple = false)() if (is(T == class) && !isCallable!T) {
552         addMissingFunction!(mode, simple)(mixin("&T." ~ name));
553     }
554 
555     void addMissingMethod(string name, T, bool simple)() if (is(T == class) && !isCallable!T) {
556         addMissingMethod!(name, T, ResultMode.Normal, simple)();
557     }
558 
559     alias addFunction add;
560     alias addFunctions add;
561     alias addMethod add;
562     alias addMethods add;
563 
564     void use(InvokeHandler[] handler...) {
565         if (handler !is null) {
566             invokeHandlers ~= handler;
567         }
568     }
569     void use(string when)(FilterHandler[] handler...) if ((when == "beforeFilter") || (when == "afterFilter")) {
570         if (handler !is null) {
571             mixin(
572                 when ~ "Handlers ~= handler;\r\n" ~
573                 when ~ "Handler = &" ~ when ~ ";\r\n" ~
574                 "foreach (h; " ~ when ~ "Handlers) {\r\n" ~
575                 "    " ~ when ~ "Handler = (delegate(NextFilterHandler next, FilterHandler handler) {\r\n" ~
576                 "        return delegate ubyte[](ubyte[] request, Context context) {\r\n" ~
577                 "            return handler(request, context, next);\r\n" ~
578                 "        };\r\n" ~
579                 "    })(" ~ when ~ "Handler, h);\r\n" ~
580                 "}\r\n"
581             );
582         }
583     }
584 }