1 /**
2  * Реализация Rpc протокола JsonRpc 2.0
3  *
4  * Copyright: (c) 2015-2017, Milofon Project.
5  * License: Subject to the terms of the BSD license, as written in the included LICENSE.txt file.
6  * Author: <m.galanin@milofon.org> Maksim Galanin
7  * Date: 2018-01-28
8  */
9 
10 module dango.service.protocol.jsonrpc;
11 
12 private
13 {
14     import std..string : strip;
15 
16     import vibe.core.log;
17 
18     import dango.service.protocol.core;
19     import dango.service.serializer.core;
20     import dango.service.transport.core;
21 }
22 
23 
24 
25 class JsonRpcServerProtocol : RpcServerProtocol
26 {
27     private
28     {
29         Dispatcher _dispatcher;
30         Serializer _serializer;
31     }
32 
33 
34     void initialize(Dispatcher dispatcher, Serializer serializer, Properties config)
35     {
36         _dispatcher = dispatcher;
37         _serializer = serializer;
38     }
39 
40 
41     ubyte[] handle(ubyte[] data)
42     {
43         UniNode uniReq;
44         try
45             uniReq = _serializer.deserialize(data);
46         catch (Exception e)
47         {
48             logInfo("Error deserialize: (%s)", e.msg);
49             return createErrorBody(createErrorByCode(
50                     ErrorCode.PARSE_ERROR, e.msg));
51         }
52 
53         string method;
54         UniNode* id;
55         UniNode params;
56 
57         if (uniReq.type != UniNode.Type.object)
58             return createErrorBody(createEmptyErrorByCode(
59                     ErrorCode.PARSE_ERROR));
60 
61         UniNode[string] uniReqMap = uniReq.via.map;
62         try
63         {
64             auto vMethod = "method" in uniReqMap;
65             if (!vMethod || !(vMethod.type == UniNode.Type.text
66                         || vMethod.type == UniNode.Type.raw))
67             {
68                 logInfo("Not found method");
69                 return createErrorBody(createErrorByCode!string(
70                         ErrorCode.INVALID_REQUEST,
71                         "Parameter method is invalid"));
72             }
73 
74             method = (*vMethod).get!string.strip;
75             id = "id" in uniReqMap;
76             params = UniNode.emptyObject();
77             if (auto pv = "params" in uniReqMap)
78                 params = *pv;
79         }
80         catch (Exception e)
81         {
82             logInfo("Error extract meta info: (%s)", e.msg);
83             return createErrorBody(createErrorByCode(
84                     ErrorCode.SERVER_ERROR, e.msg));
85         }
86 
87         if (_dispatcher.existst(method))
88         {
89             try
90             {
91                 UniNode uniRes = _dispatcher.handler(method, params);
92                 return createResultBody(id, uniRes);
93             }
94             catch (RpcException e)
95                 return createErrorBody(e.error);
96             catch (Exception e)
97             {
98                 logInfo("Error execute handler: (%s)", e.msg);
99                 return createErrorBody(createErrorByCode(
100                         ErrorCode.SERVER_ERROR, e.msg));
101             }
102         }
103         else
104             return createErrorBody(createEmptyErrorByCode(
105                 ErrorCode.METHOD_NOT_FOUND));
106     }
107 
108 private:
109 
110     ubyte[] createErrorBody(T)(RpcError!T error)
111     {
112         RpcError!UniNode uniError;
113         uniError.code = error.code;
114         uniError.message = error.message;
115         if (!error.data.isNull)
116             uniError.data = marshalObject!T(error.data);
117         return createErrorBody(uniError);
118     }
119 
120 
121     ubyte[] createErrorBody(RpcError!UniNode error)
122     {
123         UniNode[string] response;
124         response["jsonrpc"] = UniNode("2.0");
125         response["id"] = UniNode();
126         UniNode[string] err;
127         err["code"] = UniNode(error.code);
128         err["message"] = UniNode(error.message);
129         if (!error.data.isNull)
130             err["data"] = error.data.get;
131         response["error"] = UniNode(err);
132         return _serializer.serialize(UniNode(response));
133     }
134 
135 
136     ubyte[] createResultBody(UniNode* id, UniNode result)
137     {
138         if (id is null)
139             return [];
140 
141         UniNode[string] response;
142         response["jsonrpc"] = UniNode("2.0");
143         response["id"] = *id;
144         response["result"] = result;
145         return _serializer.serialize(UniNode(response));
146     }
147 }
148 
149 
150 
151 class JsonRpcClientProtocol : RpcClientProtocol
152 {
153     private
154     {
155         Serializer _serializer;
156         ClientTransport _transport;
157     }
158 
159 
160     this() {}
161 
162 
163     this(Serializer serializer, ClientTransport transport)
164     {
165         initialize(serializer, transport);
166     }
167 
168 
169     void initialize(Serializer serializer, ClientTransport transport)
170     {
171         _serializer = serializer;
172         _transport = transport;
173     }
174 
175 
176     UniNode request(string cmd, UniNode params)
177     {
178         UniNode[string] request;
179         request["jsonrpc"] = UniNode("2.0");
180         request["id"] = UniNode(2.0);
181         request["method"] = UniNode(cmd);
182         request["params"] = params;
183 
184         ubyte[] resData;
185         try
186         {
187             auto reqData = _serializer.serialize(UniNode(request));
188             resData = _transport.request(reqData);
189         }
190         catch (Exception e)
191             throw new RpcException(ErrorCode.SERVER_ERROR, e.msg);
192 
193         if (resData.length <= 0)
194             throw new RpcException(ErrorCode.INTERNAL_ERROR, "Empty response");
195 
196         auto response = _serializer.deserialize(resData);
197         if (response.type != UniNode.Type.object)
198             throw new RpcException(ErrorCode.INTERNAL_ERROR, "Error response");
199 
200         auto responseMap = response.via.map;
201         if (auto error = "error" in responseMap)
202         {
203             int errorCode;
204             string errorMsg;
205             auto errorMap = (*error).via.map;
206 
207             if (auto codePtr = "code" in errorMap)
208                 errorCode = (*codePtr).get!int;
209 
210             if (auto msgPtr = "message" in errorMap)
211                 errorMsg = (*msgPtr).get!string;
212 
213             if (auto dataPtr = "data" in errorMap)
214                 throw new RpcException(errorCode, errorMsg, *dataPtr);
215             else
216                 throw new RpcException(errorCode, errorMsg);
217         }
218         else if (auto result = "result" in responseMap)
219             return *result;
220 
221         throw new RpcException(ErrorCode.INTERNAL_ERROR, "Not found result");
222     }
223 }