1 module dast.wsrpc; 2 import dast.ws.server, 3 lmpl4d, 4 std.traits; 5 public import dast.ws.server : PeerID, WSClient; 6 import core.thread, 7 tame.lockfree.queue; 8 9 /// A WebSocket RPC request 10 struct WSRequest { 11 WSClient src; 12 Unpacker!() unpacker; 13 Packer!() packer; 14 ubyte[] buf; 15 alias unpacker this; 16 17 auto send(T...)(auto ref T data) => packer.pack(data); 18 19 auto read(T)() => unpacker.unpack!T; 20 21 auto read(T...)(auto ref T data) => unpacker.unpack(data); 22 23 @property OK() { 24 string err; 25 return read(err) || err == ""; 26 } 27 28 void reverse() nothrow { 29 unpacker = Unpacker!()(packer.buf); 30 packer.buf.length = 0; 31 } 32 33 bool call(alias fn, A...)(auto ref const A args) if (isCallable!fn) { 34 static if (A.length) 35 send(args); 36 reverse(); 37 fn(this); 38 reverse(); 39 return OK; 40 } 41 } 42 43 struct Action { 44 string name; 45 } 46 47 /// get all functions with @Action 48 template getActions(T...) { 49 import std.meta : Seq = AliasSeq; 50 51 alias getActions = Seq!(); 52 static foreach (f; T) 53 getActions = Seq!(getActions, Filter!(isCallable, getSymbolsByUDA!(f, Action))); 54 } 55 56 private alias 57 SReq = shared WSRequest, 58 LFQ = LockFreeQueue!SReq; 59 60 class WSRPCServer(uint pageCount, modules...) : WSServer { 61 public import dast.ws : Request; 62 import core.memory, 63 tame.meta; 64 65 alias AllActions = Filter!(isCallable, getSymbolsWith!(Action, modules)); 66 67 static if (pageCount) { 68 LFQ queue = LFQ(SReq()); 69 70 void initWorkers(uint n) { 71 auto loop = cast(EventExecutor)_loop; 72 while (n--) { 73 loop.queueTask(new Fiber(&mainLoop, pageCount * pageSize)); 74 } 75 } 76 } 77 78 this(AddrFamily family = AddrFamily.IPv4) { 79 static if (pageCount) 80 super(new EventExecutor, family); 81 else 82 super(family); 83 } 84 85 this(EventLoop loop, AddrFamily family = AddrFamily.IPv4) { 86 super(loop, family); 87 } 88 89 override void onBinaryMessage(WSClient src, const(ubyte)[] msg) { 90 auto req = WSRequest(src, unpacker(msg)); 91 static if (pageCount) 92 queue.enqueue(cast(shared)req); 93 else 94 handleRequest(req); 95 } 96 97 static if (pageCount) 98 noreturn mainLoop() { 99 for (;;) { 100 SReq req = void; 101 while (!queue.dequeue(req)) 102 Fiber.yield(); 103 handleRequest(cast()req); 104 } 105 } 106 107 void handleRequest(ref WSRequest req) nothrow { 108 auto unpacker = &req.unpacker; 109 req.packer = packer(req.buf); 110 uint id = void; 111 scope (exit) 112 req.buf.length = 0; 113 try 114 req.send(id = unpacker.unpack!uint, null); 115 catch (Exception e) { 116 try 117 req.src.send(e.toString()); 118 catch (Exception) { 119 } 120 return; 121 } 122 try { 123 // 调用字符串action对应的带@Action的函数 124 auto action = unpacker.unpack!string; 125 s: 126 switch (action) { 127 static foreach (f; AllActions) { 128 static foreach (attr; getUDAs!(f, Action)) { 129 static if (is(attr)) { 130 //pragma(msg, __traits(identifier, f)); 131 case __traits(identifier, f): 132 } else { 133 //pragma(msg, attr.name); 134 case attr.name: 135 } 136 callAction!f(req, unpacker); 137 break s; 138 } 139 } 140 default: 141 throw new Exception(`Unknown action "` ~ action ~ '"'); 142 } 143 } catch (Exception e) { 144 req.buf.length = 0; 145 req.send(id, e.msg); 146 } 147 req.src.send(req.packer[]); 148 } 149 150 pragma(inline, true) 151 private void callAction(alias f)(ref WSRequest req, Unpacker!()* unpacker) { 152 alias P = Parameters!f; 153 static if (P.length) { 154 enum r = is(Unqual!(P[0]) == WSRequest); 155 static assert(!r || (ParameterStorageClassTuple!f[0] & ParameterStorageClass.ref_), 156 `The first parameter of Action "` ~ fullyQualifiedName!f ~ "\" must be `ref`"); 157 P[r .. $] p = void; 158 foreach (i, ref x; p) 159 static if (is(ParameterDefaults!f[i + r] == void)) 160 x = unpacker.unpack!(P[i + r]); 161 else static if (isArray!(P[i + r])) { 162 x = ParameterDefaults!f[i + r]; 163 unpacker.unpack(x); 164 } else 165 x = unpacker.unpack(ParameterDefaults!f[i + r]); 166 static if (r) 167 f(req, p); 168 else 169 f(p); 170 } else 171 f(); 172 } 173 }