1 /**
2  * Реализация упрощенного Rpc протокола
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.simple;
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 SimpleRpcServerProtocol : 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["id"] = UniNode();
125         UniNode[string] err;
126         err["code"] = UniNode(error.code);
127         err["message"] = UniNode(error.message);
128         if (!error.data.isNull)
129             err["data"] = error.data.get;
130         response["error"] = UniNode(err);
131         return _serializer.serialize(UniNode(response));
132     }
133 
134 
135     ubyte[] createResultBody(UniNode* id, UniNode result)
136     {
137         if (id is null)
138             return [];
139 
140         UniNode[string] response;
141         response["id"] = *id;
142         response["result"] = result;
143         return _serializer.serialize(UniNode(response));
144     }
145 }
146 
147 
148 class SimpleRpcClientProtocol : RpcClientProtocol
149 {
150     private
151     {
152         Serializer _serializer;
153         ClientTransport _transport;
154     }
155 
156 
157     this() {}
158 
159 
160     this(Serializer serializer, ClientTransport transport)
161     {
162         initialize(serializer, transport);
163     }
164 
165 
166     void initialize(Serializer serializer, ClientTransport transport)
167     {
168         _serializer = serializer;
169         _transport = transport;
170     }
171 
172 
173     UniNode request(string cmd, UniNode params)
174     {
175         UniNode[string] request;
176         request["id"] = UniNode(1);
177         request["method"] = UniNode(cmd);
178         request["params"] = params;
179 
180         ubyte[] resData;
181         try
182         {
183             auto reqData = _serializer.serialize(UniNode(request));
184             resData = _transport.request(reqData);
185         }
186         catch (Exception e)
187             throw new RpcException(ErrorCode.SERVER_ERROR, e.msg);
188 
189         if (resData.length <= 0)
190             throw new RpcException(ErrorCode.INTERNAL_ERROR, "Empty response");
191 
192         auto response = _serializer.deserialize(resData);
193         if (response.type != UniNode.Type.object)
194             throw new RpcException(ErrorCode.INTERNAL_ERROR, "Error response");
195 
196         auto responseMap = response.via.map;
197         if (auto error = "error" in responseMap)
198         {
199             int errorCode;
200             string errorMsg;
201             auto errorMap = (*error).via.map;
202 
203             if (auto codePtr = "code" in errorMap)
204                 errorCode = (*codePtr).get!int;
205 
206             if (auto msgPtr = "message" in errorMap)
207                 errorMsg = (*msgPtr).get!string;
208 
209             if (auto dataPtr = "data" in errorMap)
210                 throw new RpcException(errorCode, errorMsg, *dataPtr);
211             else
212                 throw new RpcException(errorCode, errorMsg);
213         }
214         else if (auto result = "result" in responseMap)
215             return *result;
216 
217         throw new RpcException(ErrorCode.INTERNAL_ERROR, "Not found result");
218     }
219 }