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