1 /*
2 * JsonRpc.cpp
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 #include <core/json/JsonRpc.hpp>
17
18 #include <sstream>
19
20 #include <core/Log.hpp>
21 #include <core/system/Process.hpp>
22 #include <core/http/Response.hpp>
23
24 namespace rstudio {
25 namespace core {
26 namespace json {
27
28 const char * const kRpcResult = "result";
29 const char * const kRpcAsyncHandle = "asyncHandle";
30 const char * const kRpcError = "error";
31 const char * const kJsonContentType = "application/json";
32
parseJsonRpcRequest(const std::string & input,JsonRpcRequest * pRequest)33 Error parseJsonRpcRequest(const std::string& input, JsonRpcRequest* pRequest)
34 {
35 try
36 {
37 // parse data and verify it contains an object
38 Value var;
39 if ( var.parse(input) || !var.isObject() )
40 {
41 return Error(json::errc::InvalidRequest, ERROR_LOCATION);
42 }
43
44 // extract the fields
45 const Object& requestObject = var.getObject();
46 for (Object::Iterator it =
47 requestObject.begin(); it != requestObject.end(); ++it)
48 {
49 std::string fieldName = (*it).getName();
50 Value fieldValue = (*it).getValue();
51
52 if ( fieldName == "method" )
53 {
54 if (!fieldValue.isString())
55 return Error(json::errc::InvalidRequest, ERROR_LOCATION);
56
57 pRequest->method = fieldValue.getString();
58 }
59 else if ( fieldName == "params" )
60 {
61 if (!fieldValue.isArray())
62 return Error(json::errc::ParamTypeMismatch, ERROR_LOCATION);
63
64 pRequest->params = fieldValue.getValue<json::Array>();
65 }
66 else if ( fieldName == "kwparams" )
67 {
68 if (!fieldValue.isObject())
69 return Error(json::errc::ParamTypeMismatch, ERROR_LOCATION);
70
71 pRequest->kwparams = fieldValue.getValue<json::Object>();
72 }
73 else if (fieldName == "sourceWnd")
74 {
75 if (!fieldValue.isString())
76 return Error(json::errc::InvalidRequest, ERROR_LOCATION);
77
78 pRequest->sourceWindow = fieldValue.getString();
79 }
80 else if (fieldName == "clientId" )
81 {
82 if (!fieldValue.isString())
83 return Error(json::errc::InvalidRequest, ERROR_LOCATION);
84
85 pRequest->clientId = fieldValue.getString();
86 }
87 // legacy version field
88 else if (fieldName == "version" )
89 {
90 if (isType<double>(fieldValue))
91 pRequest->version = fieldValue.getDouble();
92 else
93 pRequest->version = 0;
94 }
95 // new version field
96 else if (fieldName == "clientVersion")
97 {
98 if (!fieldValue.isString())
99 pRequest->clientVersion = fieldValue.getString();
100 else
101 pRequest->clientVersion = std::string();
102 }
103 }
104
105 // method is required
106 if (pRequest->method.empty() )
107 return Error(json::errc::InvalidRequest, ERROR_LOCATION);
108
109 return Success();
110 }
111 catch(const std::exception& e)
112 {
113 Error error = Error(json::errc::ParseError, ERROR_LOCATION);
114 error.addProperty("exception", e.what());
115 return error;
116 }
117 }
118
setErrorResponse(const core::Error & error,core::json::JsonRpcResponse * pResponse)119 void setErrorResponse(const core::Error& error, core::json::JsonRpcResponse* pResponse)
120 {
121 pResponse->setError(error, core::errorMessage(error));
122 }
123
setProcessErrorResponse(const core::system::ProcessResult & result,const core::ErrorLocation & location,core::json::JsonRpcResponse * pResponse)124 void setProcessErrorResponse(const core::system::ProcessResult& result,
125 const core::ErrorLocation& location,
126 core::json::JsonRpcResponse* pResponse)
127 {
128 Error error = systemError(boost::system::errc::state_not_recoverable, result.stdErr, location);
129 pResponse->setError(error, json::Value(result.stdErr), true);
130 }
131
parseJsonRpcRequestForMethod(const std::string & input,const std::string & method,JsonRpcRequest * pRequest,http::Response * pResponse)132 bool parseJsonRpcRequestForMethod(const std::string& input,
133 const std::string& method,
134 JsonRpcRequest* pRequest,
135 http::Response* pResponse)
136 {
137 // parse request
138 Error parseError = parseJsonRpcRequest(input, pRequest);
139 if (parseError)
140 {
141 LOG_ERROR(parseError);
142 setJsonRpcError(parseError, pResponse);
143 return false;
144 }
145
146 // check for method
147 if (pRequest->method != method)
148 {
149 Error methodError = Error(json::errc::MethodNotFound, ERROR_LOCATION);
150 methodError.addProperty("method", pRequest->method);
151 LOG_ERROR(methodError);
152 setJsonRpcError(methodError, pResponse);
153 return false;
154 }
155
156 // success
157 return true;
158 }
159
160 namespace {
161
copyErrorCodeToJsonError(const boost::system::error_code & code,Object * pError)162 void copyErrorCodeToJsonError(const boost::system::error_code& code,
163 Object* pError)
164 {
165 pError->operator[]("code") = code.value();
166 pError->operator[]("message") = code.message();
167 }
168
setErrorProperties(Object & jsonError,const Error & error)169 void setErrorProperties(Object& jsonError,
170 const Error& error)
171 {
172 if (error.getProperties().empty())
173 return;
174
175 Object properties;
176 for (const std::pair<std::string, std::string>& property : error.getProperties())
177 {
178 properties[property.first] = property.second;
179 }
180
181 jsonError["properties"] = properties;
182 }
183
184 }
185
setAfterResponse(const boost::function<void ()> & afterResponse)186 void JsonRpcResponse::setAfterResponse(
187 const boost::function<void()>& afterResponse)
188 {
189 afterResponse_ = afterResponse;
190 }
191
hasAfterResponse() const192 bool JsonRpcResponse::hasAfterResponse() const
193 {
194 return afterResponse_;
195 }
196
197
runAfterResponse()198 void JsonRpcResponse::runAfterResponse()
199 {
200 if (afterResponse_)
201 afterResponse_();
202 }
203
getRawResponse()204 Object JsonRpcResponse::getRawResponse()
205 {
206 return response_;
207 }
208
write(std::ostream & os) const209 void JsonRpcResponse::write(std::ostream& os) const
210 {
211 response_.write(os);
212 }
213
setError(const Error & error,const Value & clientInfo,bool includeErrorProperties)214 void JsonRpcResponse::setError(const Error& error,
215 const Value& clientInfo,
216 bool includeErrorProperties)
217 {
218 // remove result
219 response_.erase(json::kRpcResult);
220 response_.erase(json::kRpcAsyncHandle);
221
222 if (error.getName() == json::jsonRpcCategory().name())
223 {
224 setError(boost::system::error_code(error.getCode(), json::jsonRpcCategory()),
225 json::Value());
226 }
227 else
228 {
229 // execution error
230 Object jsonError;
231 copyErrorCodeToJsonError(json::errc::ExecutionError, &jsonError);
232
233 // populate sub-error field with error details
234 Object executionError;
235 executionError["code"] = error.getCode();
236
237 std::string errorCategoryName = error.getName();
238 executionError["category"] = errorCategoryName;
239
240 std::string errorMessage = error.getMessage();
241 executionError["message"] = errorMessage;
242
243 if (error.getLocation().hasLocation())
244 {
245 std::string errorLocation = error.getLocation().asString();
246 executionError["location"] = errorLocation;
247 }
248
249 jsonError["error"] = executionError;
250 if (!clientInfo.isNull())
251 {
252 jsonError["client_info"] = clientInfo;
253 }
254
255 // add error properties if requested
256 if (includeErrorProperties)
257 setErrorProperties(jsonError, error);
258
259 // set error
260 setField(json::kRpcError, jsonError);
261 }
262 }
263
setError(const Error & error,bool includeErrorProperties)264 void JsonRpcResponse::setError(const Error& error,
265 bool includeErrorProperties)
266 {
267 setError(error, Value(), includeErrorProperties);
268 }
269
setError(const Error & error,const char * message,bool includeErrorProperties)270 void JsonRpcResponse::setError(const Error& error,
271 const char* message,
272 bool includeErrorProperties)
273 {
274 setError(error, Value(message), includeErrorProperties);
275 }
276
setError(const Error & error,const std::string & message,bool includeErrorProperties)277 void JsonRpcResponse::setError(const Error& error,
278 const std::string& message,
279 bool includeErrorProperties)
280 {
281 setError(error, Value(message), includeErrorProperties);
282 }
283
284
setError(const boost::system::error_code & ec,const Value & clientInfo)285 void JsonRpcResponse::setError(const boost::system::error_code& ec,
286 const Value& clientInfo)
287 {
288 // remove result
289 response_.erase(json::kRpcResult);
290 response_.erase(json::kRpcAsyncHandle);
291
292 // error from error code
293 Object error;
294 copyErrorCodeToJsonError(ec, &error);
295
296 // client info if provided
297 if (!clientInfo.isNull())
298 {
299 error["client_info"] = clientInfo;
300 }
301
302 // sub-error is null
303 error["error"] = Value();
304
305 // set error
306 setField(json::kRpcError, error);
307 }
308
setRedirectError(const Error & error,const std::string & redirectUrl)309 void JsonRpcResponse::setRedirectError(const Error& error,
310 const std::string& redirectUrl)
311 {
312 // set a standard error
313 setError(error);
314
315 Object errorObj;
316 getField(json::kRpcError, &errorObj);
317
318 // extend the error with redirect information
319 errorObj["redirect_url"] = redirectUrl;
320 setField(json::kRpcError, errorObj);
321 }
322
setAsyncHandle(const std::string & handle)323 void JsonRpcResponse::setAsyncHandle(const std::string& handle)
324 {
325 response_.erase(json::kRpcResult);
326 response_.erase(json::kRpcError);
327
328 setField(json::kRpcAsyncHandle, handle);
329 }
330
setJsonRpcResponse(const JsonRpcResponse & jsonRpcResponse,core::http::Response * pResponse)331 void setJsonRpcResponse(const JsonRpcResponse& jsonRpcResponse,
332 core::http::Response* pResponse)
333 {
334 // no cache!
335 pResponse->setNoCacheHeaders();
336
337 // set content type if necessary (allows callers to override the
338 // default application/json content-type, which is necessary in some
339 // circumstances such as returning results to the GWT FileUpload widget
340 // (which expects text/html)
341 if (pResponse->contentType().empty())
342 pResponse->setContentType(json::kJsonContentType);
343
344 // set body
345 std::stringstream responseStream;
346 jsonRpcResponse.write(responseStream);
347 Error error = pResponse->setBody(responseStream);
348
349 // report error to client if one occurred
350 if (error)
351 {
352 LOG_ERROR(error);
353 pResponse->setError(http::status::InternalServerError,
354 error.getMessage());
355 }
356 }
357
parse(const std::string & input,JsonRpcResponse * pResponse)358 bool JsonRpcResponse::parse(const std::string& input,
359 JsonRpcResponse* pResponse)
360 {
361 Value value;
362 if (value.parse(input))
363 return false;
364
365 return parse(value, pResponse);
366 }
367
parse(const Value & value,JsonRpcResponse * pResponse)368 bool JsonRpcResponse::parse(const Value& value,
369 JsonRpcResponse* pResponse)
370 {
371 if (!value.isObject())
372 return false;
373
374 pResponse->response_ = value.getValue<json::Object>();
375 return true;
376 }
377
378 class JsonRpcErrorCategory : public boost::system::error_category
379 {
380 public:
381 virtual const char * name() const BOOST_NOEXCEPT;
382 virtual std::string message( int ev ) const;
383 };
384
jsonRpcCategory()385 const boost::system::error_category& jsonRpcCategory()
386 {
387 static JsonRpcErrorCategory jsonRpcErrorCategoryConst;
388 return jsonRpcErrorCategoryConst;
389 }
390
name() const391 const char * JsonRpcErrorCategory::name() const BOOST_NOEXCEPT
392 {
393 return "jsonrpc";
394 }
395
message(int ev) const396 std::string JsonRpcErrorCategory::message( int ev ) const
397 {
398 switch(ev)
399 {
400 case errc::Success:
401 return "Method call succeeded";
402
403 case errc::ConnectionError:
404 return "Unable to connect to service";
405
406 case errc::Unavailable:
407 return "Service currently unavailable";
408
409 case errc::Unauthorized:
410 return "Client unauthorized";
411
412 case errc::InvalidClientId:
413 return "Invalid client id";
414
415 case errc::ParseError:
416 return "Invalid json or unexpected error occurred while parsing";
417
418 case errc::InvalidRequest:
419 return "Invalid json-rpc request";
420
421 case errc::MethodNotFound:
422 return "Method not found";
423
424 case errc::ParamMissing:
425 return "Parameter missing";
426
427 case errc::ParamTypeMismatch:
428 return "Parameter type mismatch";
429
430 case errc::ParamInvalid:
431 return "Parameter value invalid";
432
433 case errc::MethodUnexpected:
434 return "Unexpected call to method";
435
436 case errc::ExecutionError:
437 return "Error occurred while executing method";
438
439 case errc::TransmissionError:
440 return "Error occurred during transmission";
441
442 case errc::InvalidClientVersion:
443 return "Invalid client version";
444
445 case errc::ServerOffline:
446 return "Server is offline";
447
448 case errc::InvalidSession:
449 return "Invalid session";
450
451 case errc::MaxSessionsReached:
452 return "The maximum amount of concurrent sessions for this license has been reached";
453
454 case errc::MaxUsersReached:
455 return "The maximum amount of concurrent users for this license has been reached";
456
457 case errc::LaunchParametersMissing:
458 return "Launch parameters for launcher session missing and should be resent";
459
460 case errc::LimitSessionsReached:
461 return "The maximum amount of concurrent session allowed for the user profile has been reached";
462
463 default:
464 BOOST_ASSERT(false);
465 return "Unknown error type";
466 }
467 }
468
469 namespace {
470
runSynchronousFunction(const JsonRpcFunction & func,const JsonRpcRequest & request,const JsonRpcFunctionContinuation & continuation)471 void runSynchronousFunction(const JsonRpcFunction& func,
472 const JsonRpcRequest& request,
473 const JsonRpcFunctionContinuation& continuation)
474 {
475 JsonRpcResponse response;
476 if (request.isBackgroundConnection)
477 response.setSuppressDetectChanges(true);
478 core::Error error = func(request, &response);
479 continuation(error, &response);
480 }
481
482 } // anonymous namespace
483
adaptToAsync(JsonRpcFunction synchronousFunction)484 JsonRpcAsyncFunction adaptToAsync(JsonRpcFunction synchronousFunction)
485 {
486 return boost::bind(runSynchronousFunction, synchronousFunction, _1, _2);
487 }
488
adaptMethodToAsync(JsonRpcMethod synchronousMethod)489 JsonRpcAsyncMethod adaptMethodToAsync(JsonRpcMethod synchronousMethod)
490 {
491 return JsonRpcAsyncMethod(
492 synchronousMethod.first,
493 std::make_pair(true, adaptToAsync(synchronousMethod.second)));
494 }
495
496 } // namespace json
497 } // namespace core
498 } // namespace rstudio
499
500
501
502