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 }