1 /*
2 * JsonRpc.hpp
3 *
4 * Copyright (C) 2021 by RStudio, PBC
5 *
6 * Unless you have received this program directly from RStudio pursuant
7 * to the terms of a commercial license agreement with RStudio, then
8 * this program is licensed to you under the terms of version 3 of the
9 * GNU Affero General Public License. This program is distributed WITHOUT
10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13 *
14 */
15
16 #ifndef CORE_JSON_RPC_HPP
17 #define CORE_JSON_RPC_HPP
18
19 #include <boost/system/error_code.hpp>
20
21 #include <core/type_traits/TypeTraits.hpp>
22 #include <core/json/JsonRpc.hpp>
23
24 #include <shared_core/json/Json.hpp>
25
26 namespace rstudio {
27 namespace core {
28 namespace system {
29 struct ProcessResult;
30 }
31 namespace json {
32 namespace errc {
33
34 enum errc_t {
35 Success = 0, // request succeeded
36
37 //
38 // Invocation Errors -- All of these errors are guaranteed to have occurred
39 // prior to the execution of the method on the service
40 //
41 ConnectionError = 1, // unable to connect to the service
42 Unavailable = 2, // service is currently unavailable
43 Unauthorized = 3, // client does not have required credentials
44 InvalidClientId = 4, // provided client id is invalid
45 ParseError = 5, // invalid json or an unexpected error during parsing
46 InvalidRequest = 6, // invalid json-rpc request
47 MethodNotFound = 7, // specified method not found on the server
48 ParamMissing = 8, // parameter missing
49 ParamTypeMismatch = 9, // parameter type mismatch
50 ParamInvalid = 10, // parameter invalid
51 MethodUnexpected = 11, // method unexpected for current application state
52 InvalidClientVersion = 12, // client is running an invalid version
53 ServerOffline = 13, // server is offline
54 InvalidSession = 14, // target session is invalid
55 MaxSessionsReached = 15, // license error - max sessions reached
56 MaxUsersReached = 16, // license error - max users reached
57
58 // launcher session parameters not found and should be resent to implicitly resume the session
59 LaunchParametersMissing = 17,
60
61 // profile errors - those imposed by limits in user profiles
62 LimitSessionsReached = 20,
63
64 // Execution errors -- These errors occurred during execution of the method.
65 // Application state is therefore known based on the expected behavior
66 // of the error which occurred. More details are provided within the
67 // optional "error" field of the result
68 ExecutionError = 100,
69
70 // Transmission errors -- These errors leave the application in an unknown
71 // state (it is not known whether the method finished all, some, or none
72 // of its work).
73 TransmissionError = 200
74 };
75
76 } // namespace errc
77 } // namespace json
78 } // namespace core
79 } // namespace rstudio
80
81 namespace RSTUDIO_BOOST_NAMESPACE {
82 namespace system {
83 template <>
84 struct is_error_code_enum<rstudio::core::json::errc::errc_t>
85 {
86 static const bool value = true;
87 };
88
89 } // namespace system
90 } // namespace boost
91
92 #include <string>
93
94 #include <boost/function.hpp>
95 #include <boost/optional.hpp>
96 #include <boost/unordered_map.hpp>
97 #include <boost/bind/bind.hpp>
98
99 #include <shared_core/Error.hpp>
100 #include <shared_core/json/Json.hpp>
101
102 using namespace boost::placeholders;
103
104 namespace rstudio {
105 namespace core {
106 namespace http {
107 class Response;
108 }
109 }
110 }
111
112 namespace rstudio {
113 namespace core {
114 namespace json {
115
116 // constants
117 extern const char * const kRpcResult;
118 extern const char * const kRpcError;
119 extern const char * const kJsonContentType;
120 extern const char * const kRpcAsyncHandle;
121
122 // jsonRpcCategory
123 const boost::system::error_category& jsonRpcCategory();
124
125
126 //
127 // json error codes
128 //
129 namespace errc {
130
make_error_code(errc_t e)131 inline boost::system::error_code make_error_code( errc_t e )
132 {
133 return boost::system::error_code( e, jsonRpcCategory() ); }
134
make_error_condition(errc_t e)135 inline boost::system::error_condition make_error_condition( errc_t e )
136 {
137 return boost::system::error_condition( e, jsonRpcCategory() );
138 }
139
140 } // namespace errc
141 } // namespace json
142 } // namespace core
143 } // namespace rstudio
144
145 namespace rstudio {
146 namespace core {
147 namespace json {
148
149 struct JsonRpcRequest
150 {
JsonRpcRequestrstudio::core::json::JsonRpcRequest151 JsonRpcRequest() : version(0), isBackgroundConnection(false) {}
152
153 std::string method;
154 Array params;
155 Object kwparams;
156 std::string sourceWindow;
157 std::string clientId;
158 double version;
159 std::string clientVersion;
160 bool isBackgroundConnection;
161
emptyrstudio::core::json::JsonRpcRequest162 bool empty() const { return method.empty(); }
163
clearrstudio::core::json::JsonRpcRequest164 void clear()
165 {
166 method.clear();
167 params.clear();
168 kwparams.clear();
169 }
170
toJsonObjectrstudio::core::json::JsonRpcRequest171 Object toJsonObject()
172 {
173 Object obj;
174 obj["method"] = method;
175 obj["params"] = params;
176 obj["kwparams"] = kwparams;
177 obj["sourceWindow"] = sourceWindow;
178 obj["clientId"] = clientId;
179 obj["version"] = version;
180 obj["isBackgroundConnection"] = isBackgroundConnection;
181
182 return obj;
183 }
184 };
185
186 //
187 // json request parsing
188 //
189 Error parseJsonRpcRequest(const std::string& input, JsonRpcRequest* pRequest);
190
191 bool parseJsonRpcRequestForMethod(const std::string& input,
192 const std::string& method,
193 JsonRpcRequest* pRequest,
194 http::Response* pResponse);
195
196
197 //
198 // json parameter reading helpers
199 //
200
201
readParam(const Array & params,unsigned int index,Value * pValue)202 inline core::Error readParam(const Array& params,
203 unsigned int index,
204 Value* pValue)
205 {
206 if (index >= params.getSize())
207 return core::Error(json::errc::ParamMissing, ERROR_LOCATION);
208
209 *pValue = params[index];
210 return Success();
211 }
212
213 template <typename T>
readParam(const Array & params,unsigned int index,T * pValue)214 core::Error readParam(const Array& params, unsigned int index, T* pValue)
215 {
216 if (index >= params.getSize())
217 return core::Error(json::errc::ParamMissing, ERROR_LOCATION);
218
219 if (!isType<T>(params[index]))
220 return core::Error(json::errc::ParamTypeMismatch, ERROR_LOCATION);
221
222 *pValue = params[index].getValue<T>();
223
224 return Success();
225 }
226
227 template <typename T, typename... Args>
readParams(const Array & params,T * pValue,Args...args)228 core::Error readParams(const Array& params, T* pValue, Args... args)
229 {
230 unsigned int index = 0;
231
232 Error error = readParam(params, index++, pValue);
233 if (error)
234 return error;
235
236 error = readParams(params, index, args...);
237 if (error)
238 return error;
239
240 return Success();
241 }
242
243 template <typename T, typename... Args>
readParams(const Array & params,unsigned int index,T * pValue,Args...args)244 core::Error readParams(const Array& params, unsigned int index, T* pValue, Args... args)
245 {
246 Error error = readParam(params, index++, pValue);
247 if (error)
248 return error;
249
250 error = readParams(params, index, args...);
251 if (error)
252 return error;
253
254 return Success();
255 }
256
257 template <typename T>
readParams(const Array & params,unsigned int index,T * pValue)258 core::Error readParams(const Array& params, unsigned int index, T* pValue)
259 {
260 return readParam(params, index, pValue);
261 }
262
263 template <typename T1>
readParams(const json::Array & params,T1 * pValue1)264 core::Error readParams(const json::Array& params, T1* pValue1)
265 {
266 return readParam(params, 0, pValue1);
267 }
268
269 namespace errors {
270
paramMissing(const std::string & name,const ErrorLocation & location)271 inline Error paramMissing(const std::string& name,
272 const ErrorLocation& location)
273 {
274 Error error(json::errc::ParamTypeMismatch, location);
275 error.addProperty("description", "no such parameter '" + name + "'");
276 return error;
277 }
278
typeMismatch(const Value & value,Type expectedType,const ErrorLocation & location)279 inline Error typeMismatch(const Value& value,
280 Type expectedType,
281 const ErrorLocation& location)
282 {
283 Error error(json::errc::ParamTypeMismatch, location);
284
285 std::string description = std::string("expected ") +
286 "'" + typeAsString(expectedType) + "'" +
287 "; got " +
288 "'" + typeAsString(value) + "'";
289 error.addProperty("description", description);
290 return error;
291 }
292
293 } // namespace errors
294
295 template <typename T, typename... Args>
readObjectParam(const Array & params,unsigned int index,const std::string & name,T * pValue,Args...args)296 core::Error readObjectParam(const Array& params,
297 unsigned int index,
298 const std::string& name,
299 T* pValue,
300 Args... args)
301 {
302 Error error = readObjectParam(params, index, name, pValue);
303 if (error)
304 return error;
305
306 error = readObjectParam(params, index, args...);
307 if (error)
308 return error;
309
310 return Success();
311 }
312
313 template <typename T>
readObjectParam(const Array & params,unsigned int index,const std::string & name,T * pValue)314 core::Error readObjectParam(const Array& params,
315 unsigned int index,
316 const std::string& name,
317 T* pValue)
318 {
319 Object object;
320 Error error = readParam(params, index, &object);
321 if (error)
322 return error;
323
324 return readObject(object, name, *pValue);
325 }
326
327 template <typename T>
getOptionalParam(const Object & json,const std::string & param,const T & defaultValue,T * outParam)328 core::Error getOptionalParam(const Object& json, const std::string& param,
329 const T& defaultValue, T* outParam)
330 {
331 boost::optional<T> paramVal;
332 Error error = readObject(json, param, paramVal);
333 if (error)
334 return error;
335
336 *outParam = paramVal.get_value_or(defaultValue);
337
338 return Success();
339 }
340
341 template <typename T>
getOptionalParam(const Object & json,const std::string & param,boost::optional<T> * pOutParam)342 core::Error getOptionalParam(const Object& json,
343 const std::string& param,
344 boost::optional<T>* pOutParam)
345 {
346 return readObject(json, param, *pOutParam);
347 }
348
349 // json rpc response
350
351 class JsonRpcResponse
352 {
353 public:
JsonRpcResponse()354 JsonRpcResponse() : suppressDetectChanges_(false)
355 {
356 setResult(Value());
357 }
358
359 // COPYING: via compiler (copyable members)
360
361 public:
362
363 template <typename T>
setResult(const T & result)364 void setResult(const T& result)
365 {
366 setField(json::kRpcResult, result);
367 }
368
result()369 Value result()
370 {
371 return response_[json::kRpcResult];
372 }
373
error()374 Value error()
375 {
376 return response_[json::kRpcError];
377 }
378
379 void setError(const core::Error& error,
380 bool includeErrorProperties = false);
381
382 void setError(const core::Error& error,
383 const char* message,
384 bool includeErrorProperties = false);
385
386 void setError(const core::Error& error,
387 const std::string& message,
388 bool includeErrorProperties = false);
389
390 void setError(const core::Error& error,
391 const Value& clientInfo,
392 bool includeErrorProperties = false);
393
394 void setError(const boost::system::error_code& ec,
395 const Value& clientInfo = Value());
396
397 void setRedirectError(const core::Error& error,
398 const std::string& redirectUrl);
399
400 void setAsyncHandle(const std::string& handle);
401
setField(const std::string & name,const Value & value)402 void setField(const std::string& name, const Value& value)
403 {
404 response_[name] = value;
405 }
406
hasField(const std::string & name)407 bool hasField(const std::string& name)
408 {
409 return response_.hasMember(name);
410 }
411
412 template <typename T>
setField(const std::string & name,const T & value)413 void setField(const std::string& name, const T& value)
414 {
415 setField(name, Value(value));
416 }
417
418 template <typename T>
getField(const std::string & name,T * pValue)419 Error getField(const std::string& name, T* pValue)
420 {
421 return readObject(response_, name, *pValue);
422 }
423
424 // low level hook to set the full response
setResponse(const Object & response)425 void setResponse(const Object& response)
426 {
427 response_ = response;
428 }
429
430 // specify a function to run after the response
431 void setAfterResponse(const boost::function<void()>& afterResponse);
432 bool hasAfterResponse() const;
433 void runAfterResponse();
434
suppressDetectChanges()435 bool suppressDetectChanges() { return suppressDetectChanges_; }
setSuppressDetectChanges(bool suppress)436 void setSuppressDetectChanges(bool suppress)
437 {
438 suppressDetectChanges_ = suppress;
439 }
440
441 Object getRawResponse();
442
443 void write(std::ostream& os) const;
444
445 static bool parse(const std::string& input,
446 JsonRpcResponse* pResponse);
447
448 static bool parse(const Value& value,
449 JsonRpcResponse* pResponse);
450
451 private:
452 Object response_;
453 boost::function<void()> afterResponse_;
454 bool suppressDetectChanges_;
455 };
456
457
458 // convenience functions for sending json-rpc responses
459
460 void setJsonRpcResponse(const JsonRpcResponse& jsonRpcResponse,
461 http::Response* pResponse);
462
463
setVoidJsonRpcResult(http::Response * pResponse)464 inline void setVoidJsonRpcResult(http::Response* pResponse)
465 {
466 JsonRpcResponse jsonRpcResponse;
467 setJsonRpcResponse(jsonRpcResponse, pResponse);
468 }
469
470
471 template <typename T>
setJsonRpcResult(const T & result,http::Response * pResponse)472 void setJsonRpcResult(const T& result, http::Response* pResponse)
473 {
474 JsonRpcResponse jsonRpcResponse;
475 jsonRpcResponse.setResult(result);
476 setJsonRpcResponse(jsonRpcResponse, pResponse);
477 }
478
479 template <typename T>
setJsonRpcError(const T & error,core::http::Response * pResponse,bool includeErrorProperties=false)480 void setJsonRpcError(const T& error, core::http::Response* pResponse, bool includeErrorProperties = false)
481 {
482 JsonRpcResponse jsonRpcResponse;
483 jsonRpcResponse.setError(error, includeErrorProperties);
484 setJsonRpcResponse(jsonRpcResponse, pResponse);
485 }
486
487 template <typename T>
setJsonRpcRedirectError(const T & error,const std::string & redirectUrl,core::http::Response * pResponse)488 void setJsonRpcRedirectError(const T& error,
489 const std::string& redirectUrl,
490 core::http::Response* pResponse)
491 {
492 JsonRpcResponse jsonRpcResponse;
493 jsonRpcResponse.setRedirectError(error, redirectUrl);
494 setJsonRpcResponse(jsonRpcResponse, pResponse);
495 }
496
497 // helpers for populating an existing response object with errors
498 void setErrorResponse(const core::Error& error, core::json::JsonRpcResponse* pResponse);
499 void setProcessErrorResponse(const core::system::ProcessResult& result,
500 const core::ErrorLocation& location,
501 core::json::JsonRpcResponse* pResponse);
502
503 // helper for reading a json value (and setting an error response if it
504 // doesn't parse or is of the wrong type)
505 template <typename T>
parseJsonForResponse(const std::string & output,T * pVal,json::JsonRpcResponse * pResponse)506 bool parseJsonForResponse(const std::string& output, T* pVal, json::JsonRpcResponse* pResponse)
507 {
508 using namespace json;
509 T jsonValue;
510 Error error = jsonValue.parse(output);
511 if (error)
512 {
513 Error parseError(boost::system::errc::state_not_recoverable,
514 errorMessage(error),
515 ERROR_LOCATION);
516 json::setErrorResponse(parseError, pResponse);
517 return false;
518 }
519 else if (!isType<T>(jsonValue))
520 {
521 Error outputError(boost::system::errc::state_not_recoverable,
522 "Unexpected JSON output from pandoc",
523 ERROR_LOCATION);
524 json::setErrorResponse(outputError, pResponse);
525 return false;
526 }
527 else
528 {
529 *pVal = jsonValue;
530 return true;
531 }
532 }
533
534
535
536 // convenience typedefs for managing a map of json rpc functions
537 typedef boost::function<core::Error(const JsonRpcRequest&, JsonRpcResponse*)>
538 JsonRpcFunction;
539 typedef std::pair<std::string,JsonRpcFunction>
540 JsonRpcMethod;
541 typedef boost::unordered_map<std::string,JsonRpcFunction>
542 JsonRpcMethods;
543
544 /*
545 Async method support -- JsonRpcAsyncFunction is intended for potentially
546 long running operations that need to keep the HTTP connection open until
547 their work is done. (See registerRpcAsyncCoupleMethod for a different
548 mechanism that provides similar functionality, but closes the HTTP
549 connection and uses an event to simulate returning a result to the client.)
550 */
551
552 // JsonRpcFunctionContinuation is what a JsonRpcAsyncFunction needs to call
553 // when its work is complete
554 typedef boost::function<void(const core::Error&, JsonRpcResponse*)>
555 JsonRpcFunctionContinuation;
556 typedef boost::function<void(const JsonRpcRequest&, const JsonRpcFunctionContinuation&)>
557 JsonRpcAsyncFunction;
558 // The bool in the next two typedefs specifies whether the function wants the
559 // HTTP connection to stay open until the method finishes executing (direct return),
560 // or for the HTTP connection to immediate return with an "asyncHandle" value that
561 // can be used to look in the event stream later when the method completes (indirect
562 // return). Direct return provides lower latency for short operations, and indirect
563 // return must be used for longer-running operations to prevent the browser from
564 // being starved of available HTTP connections to the server.
565 typedef std::pair<std::string,std::pair<bool, JsonRpcAsyncFunction> >
566 JsonRpcAsyncMethod;
567 typedef boost::unordered_map<std::string,std::pair<bool, JsonRpcAsyncFunction> >
568 JsonRpcAsyncMethods;
569
570 JsonRpcAsyncFunction adaptToAsync(JsonRpcFunction synchronousFunction);
571 JsonRpcAsyncMethod adaptMethodToAsync(JsonRpcMethod synchronousMethod);
572
573 } // namespace json
574 } // namespace core
575 } // namespace rstudio
576
577
578
579 #endif // CORE_JSON_RPC_HPP
580
581