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/client.d * 13 * * 14 * hprose client library for D. * 15 * * 16 * LastModified: Jan 13, 2016 * 17 * Author: Ma Bingyao <andot@hprose.com> * 18 * * 19 \**********************************************************/ 20 21 module hprose.rpc.client; 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 private { 35 pure string generateMethods(alias methods, T, string namespace)(string code) { 36 static if (methods.length > 0) { 37 alias STC = ParameterStorageClass; 38 enum m = methods[0]; 39 foreach(mm; __traits(getVirtualMethods, T, m)) { 40 string name = m; 41 ResultMode mode = ResultMode.Normal; 42 bool simple = false; 43 code ~= " override "; 44 enum attrs = __traits(getAttributes, mm); 45 foreach (attr; attrs) { 46 static if (is(typeof(attr) == MethodName)) { 47 name = attr.value; 48 } 49 else static if (is(typeof(attr) == ResultMode)) { 50 mode = attr; 51 } 52 else static if (is(typeof(attr) == Simple)) { 53 simple = attr.value; 54 } 55 } 56 alias paramTypes = ParameterTypeTuple!(mm); 57 alias paramStors = ParameterStorageClassTuple!(mm); 58 alias paramIds = ParameterIdentifierTuple!(mm); 59 alias paramValues = ParameterDefaultValueTuple!(mm); 60 alias returntype = ReturnType!(mm); 61 alias variadic = variadicFunctionStyle!(mm); 62 code ~= returntype.stringof ~ " " ~ m ~ "("; 63 bool byref = false; 64 foreach(i, p; paramTypes) { 65 static if (i > 0) { 66 code ~= ", "; 67 } 68 static if (paramStors[i] == STC.out_ || paramStors[i] == STC.ref_ || paramStors[i] == STC.return_) { 69 byref = true; 70 } 71 final switch (paramStors[i]) { 72 case STC.none: break; 73 case STC.scope_: code ~= "scope "; break; 74 case STC.out_: code ~= "out "; break; 75 case STC.ref_: code ~= "ref "; break; 76 case STC.lazy_: code ~= "lazy "; break; 77 case STC.return_: code ~= "return ref "; break; 78 } 79 static if (paramIds[i] != "") { 80 code ~= p.stringof ~ " " ~ paramIds[i]; 81 } 82 else { 83 code ~= p.stringof ~ " arg" ~ to!string(i); 84 } 85 static if (!is(paramValues[i] == void)) { 86 code ~= " = " ~ paramValues[i].stringof; 87 } 88 } 89 static if (variadic == Variadic.typesafe) { 90 code ~= "..."; 91 } 92 code ~= ") {\n"; 93 static if (is(returntype == void)) { 94 static if (paramTypes.length > 0 && is(paramTypes[$-1] == return)) { 95 alias Callback = paramTypes[$-1]; 96 alias callbackParams = ParameterTypeTuple!Callback; 97 static if (callbackParams.length == 0) { 98 code ~= " invoke(\""; 99 } 100 else static if (callbackParams.length == 1) { 101 code ~= " invoke!("; 102 } 103 else static if (callbackParams.length > 1) { 104 foreach(s; ParameterStorageClassTuple!Callback) { 105 static if (s == STC.out_ || s == STC.ref_) { 106 byref = true; 107 } 108 } 109 code ~= " invoke!(" ~ to!string(byref) ~ ", "; 110 } 111 else { 112 static assert(0, "can't support this callback type: " ~ Callback.stringof); 113 } 114 static if (callbackParams.length > 0) { 115 code ~= "ResultMode." ~ to!string(mode) ~ ", " ~ 116 to!string(simple) ~ ")(\""; 117 } 118 } 119 else { 120 code ~= " invoke!(" ~ returntype.stringof ~ ", " ~ 121 to!string(byref) ~ ", " ~ 122 "ResultMode." ~ to!string(mode) ~ ", " ~ 123 to!string(simple) ~ ")(\""; 124 } 125 } 126 else { 127 code ~= " return invoke!(" ~ returntype.stringof ~ ", " ~ 128 to!string(byref) ~ ", " ~ 129 "ResultMode." ~ to!string(mode) ~ ", " ~ 130 to!string(simple) ~ ")(\""; 131 } 132 static if (namespace != "") { 133 code ~= namespace ~ "_"; 134 } 135 code ~= name ~ "\"" ; 136 foreach(i, id; paramIds) { 137 static if (id != "") { 138 code ~= ", " ~ id; 139 } 140 else { 141 code ~= ", arg" ~ to!string(i); 142 } 143 } 144 code ~= ");\n"; 145 code ~= " }\n"; 146 } 147 static if (methods.length > 1) { 148 code = generateMethods!(tuple(methods[1..$]), T, namespace)(code); 149 } 150 } 151 return code; 152 } 153 154 pure string generate(T, string namespace)() { 155 return generateMethods!(getAbstractMethods!(T), T, namespace)("new class T {\n") ~ "}\n"; 156 } 157 158 pure string asyncInvoke(bool byref, bool hasresult, bool hasargs)() { 159 string code = "foreach(T; Args) static assert(isSerializable!T);\n"; 160 code ~= "try {\n"; 161 code ~= " auto context = new Context();\n"; 162 code ~= " auto request = doOutput!(" ~ to!string(byref) ~ ", simple)(name, context, args);\n"; 163 code ~= " sendAndReceive(request, delegate(ubyte[] response) {\n"; 164 code ~= " try {\n"; 165 code ~= " auto result = doInput!(Result, mode)(response, context, args);\n"; 166 code ~= " if (callback !is null) {\n"; 167 code ~= " callback(" ~ (hasresult ? "result" ~ (hasargs ? ", args" : "") : "") ~ ");\n"; 168 code ~= " }\n"; 169 code ~= " }\n"; 170 code ~= " catch(Exception e) {\n"; 171 code ~= " if (onError !is null) onError(name, e);\n"; 172 code ~= " }\n"; 173 code ~= " });\n"; 174 code ~= "}\n"; 175 code ~= "catch(Exception e) {\n"; 176 code ~= " if (onError !is null) onError(name, e);\n"; 177 code ~= "}\n"; 178 return code; 179 } 180 } 181 182 abstract class Client { 183 private { 184 Filter[] _filters; 185 ubyte[] doOutput(bool byref, bool simple, Args...)(string name, Context context, ref Args args) { 186 auto bytes = new BytesIO(); 187 auto writer = new Writer(bytes, simple); 188 bytes.write(TagCall); 189 writer.writeString(name); 190 if (args.length > 0 || byref) { 191 writer.reset(); 192 writer.writeTuple(args); 193 static if (byref) { 194 writer.writeBool(true); 195 } 196 } 197 bytes.write(TagEnd); 198 auto request = cast(ubyte[])(bytes.buffer); 199 bytes.close(); 200 foreach(filter; _filters) { 201 request = filter.outputFilter(request, context); 202 } 203 return request; 204 } 205 Result doInput(Result, ResultMode mode, Args...)(ubyte[]response, Context context, ref Args args) if (mode == ResultMode.Normal || is(Result == ubyte[])) { 206 foreach_reverse(filter; _filters) { 207 response = filter.inputFilter(response, context); 208 } 209 static if (mode == ResultMode.RawWithEndTag) { 210 return response; 211 } 212 else static if (mode == ResultMode.Raw) { 213 return response[0..$-1]; 214 } 215 else { 216 auto bytes = new BytesIO(response); 217 auto reader = new Reader(bytes); 218 Result result; 219 char tag; 220 while((tag = bytes.read()) != TagEnd) { 221 switch(tag) { 222 case TagResult: { 223 static if (mode == ResultMode.Serialized) { 224 result = cast(ubyte[])(reader.readRaw().buffer); 225 } 226 else { 227 reader.reset(); 228 result = reader.unserialize!Result(); 229 } 230 break; 231 } 232 case TagArgument: { 233 reader.reset(); 234 reader.readTuple(args); 235 break; 236 } 237 case TagError: { 238 reader.reset(); 239 throw new Exception(reader.unserialize!string()); 240 } 241 default: { 242 throw new Exception("Wrong Response: \r\n" ~ cast(string)response); 243 } 244 } 245 } 246 bytes.close(); 247 return result; 248 } 249 } 250 } 251 protected { 252 string uri; 253 abstract ubyte[] sendAndReceive(ubyte[] request); 254 abstract void sendAndReceive(ubyte[] request, void delegate(ubyte[]) callback); 255 } 256 257 void delegate(string name, Exception e) onError = null; 258 259 this(string uri = "") { 260 this.uri = uri; 261 this._filters = []; 262 } 263 void useService(string uri = "") { 264 if (uri != "") { 265 this.uri = uri; 266 } 267 } 268 T useService(T, string namespace = "")(string uri = "") if (is(T == interface) || is(T == class)) { 269 useService(uri); 270 return mixin(generate!(T, namespace)); 271 } 272 Result invoke(Result, bool byref = false, ResultMode mode = ResultMode.Normal, bool simple = false, Args...) 273 (string name, Args args) if (args.length > 0 && byref == false && !is(typeof(args[$-1]) == return) && 274 (mode == ResultMode.Normal || is(Result == ubyte[]))) { 275 static if (is(Result == void)) { 276 invoke!(Result, byref, mode, simple)(name, args); 277 } 278 else { 279 return invoke!(Result, byref, mode, simple)(name, args); 280 } 281 } 282 Result invoke(Result, bool byref = false, ResultMode mode = ResultMode.Normal, bool simple = false, Args...) 283 (string name, ref Args args) if (((args.length == 0) || !is(typeof(args[$-1]) == return)) && 284 (mode == ResultMode.Normal || is(Result == ubyte[]))) { 285 foreach(T; Args) static assert(isSerializable!(T)); 286 auto context = new Context(); 287 auto request = doOutput!(byref, simple)(name, context, args); 288 static if (is(Result == void)) { 289 doInput!(Variant, mode)(sendAndReceive(request), context, args); 290 } 291 else { 292 return doInput!(Result, mode)(sendAndReceive(request), context, args); 293 } 294 } 295 void invoke(Args...) 296 (string name, Args args, void delegate() callback) { 297 alias Result = Variant; 298 enum mode = ResultMode.Normal; 299 enum simple = false; 300 mixin(asyncInvoke!(false, false, false)); 301 } 302 void invoke(ResultMode mode = ResultMode.Normal, bool simple = false, Callback, Args...) 303 (string name, Args args, Callback callback) if (is(Callback R == void delegate(R)) && (mode == ResultMode.Normal || is(R == ubyte[]))) { 304 alias Result = ParameterTypeTuple!callback[0]; 305 mixin(asyncInvoke!(false, true, false)); 306 } 307 void invoke(bool byref = false, ResultMode mode = ResultMode.Normal, bool simple = false, Result, Args...) 308 (string name, Args args, void delegate(Result result, Args args) callback) if (args.length > 0 && (mode == ResultMode.Normal || is(Result == ubyte[]))) { 309 mixin(asyncInvoke!(byref, true, true)); 310 } 311 void invoke(bool byref = true, ResultMode mode = ResultMode.Normal, bool simple = false, Result, Args...) 312 (string name, ref Args args, void delegate(Result result, ref Args args) callback) if (args.length > 0 && (mode == ResultMode.Normal || is(Result == ubyte[]))) { 313 mixin(asyncInvoke!(byref, true, true)); 314 } 315 void invoke(Args...) 316 (string name, Args args, void function() callback) { 317 alias Result = Variant; 318 enum mode = ResultMode.Normal; 319 enum simple = false; 320 mixin(asyncInvoke!(false, false, false)); 321 } 322 void invoke(ResultMode mode = ResultMode.Normal, bool simple = false, Callback, Args...) 323 (string name, Args args, Callback callback) if (is(Callback R == void function(R)) && (mode == ResultMode.Normal || is(R == ubyte[]))) { 324 alias Result = ParameterTypeTuple!callback[0]; 325 mixin(asyncInvoke!(false, true, false)); 326 } 327 void invoke(bool byref = false, ResultMode mode = ResultMode.Normal, bool simple = false, Result, Args...) 328 (string name, Args args, void function(Result result, Args args) callback) if (args.length > 0 && (mode == ResultMode.Normal || is(Result == ubyte[]))) { 329 mixin(asyncInvoke!(byref, true, true)); 330 } 331 void invoke(bool byref = true, ResultMode mode = ResultMode.Normal, bool simple = false, Result, Args...) 332 (string name, ref Args args, void function(Result result, ref Args args) callback) if (args.length > 0 && (mode == ResultMode.Normal || is(Result == ubyte[]))) { 333 mixin(asyncInvoke!(byref, true, true)); 334 } 335 @property ref filters() { 336 return this._filters; 337 } 338 }