1 /*
2 * SessionPostback.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 /* Postback Handlers
17
18 Postback handlers are generally used for situations where R options call for
19 an external executable rather than a function. For example, the R action
20 for viewing PDFs, options("pdfviewer"), is handle using a postback handler
21 named 'pdfviewer'.
22
23 To create a new postback handler for an action 'foo' do the following:
24
25 1) Create a shell script named 'rpostback-foo' (based on rpostback-pdfviewer)
26
27 2) Ensure that the shell script is included in the installation
28
29 3) Call module_context::registerPostbackHandler with 'foo' as the name param
30 and the function you want called during the postback. The function will
31 be passed a single parameter corresponding to the first command line
32 argument passed by R to the shell script.
33
34 4) The registration function uses the pShellCommand out param to provide you
35 with the shell command which you in turn provide to R.
36
37 */
38
39 #include <string>
40 #include <map>
41
42 #include <boost/function.hpp>
43
44 #include <shared_core/Error.hpp>
45 #include <shared_core/SafeConvert.hpp>
46
47 #include <core/http/Request.hpp>
48 #include <core/http/Response.hpp>
49
50 #include <session/SessionModuleContext.hpp>
51
52 #include <session/SessionOptions.hpp>
53
54 using namespace rstudio::core;
55
56 namespace rstudio {
57 namespace session {
58 namespace module_context {
59
60 namespace {
61
62 std::map<std::string,
63 module_context::PostbackHandlerFunction> s_postbackHandlers;
64
endHandlePostback(const http::UriHandlerFunctionContinuation & cont,int exitCode,const std::string & output)65 void endHandlePostback(const http::UriHandlerFunctionContinuation& cont,
66 int exitCode,
67 const std::string& output)
68 {
69 http::Response response;
70 // send basic response
71 response.setStatusCode(http::status::Ok);
72 response.setContentType("text/plain");
73 response.setHeader(kPostbackExitCodeHeader,
74 safe_convert::numberToString(exitCode));
75 response.setBody(output);
76 cont(&response);
77 }
78
79 // UriHandlerFunction wrapper for simple postbacks
handlePostback(const PostbackHandlerFunction & handlerFunction,const http::Request & request,const http::UriHandlerFunctionContinuation & cont)80 void handlePostback(const PostbackHandlerFunction& handlerFunction,
81 const http::Request& request,
82 const http::UriHandlerFunctionContinuation& cont)
83 {
84 // pass the body to the postback function
85 handlerFunction(request.body(), boost::bind(endHandlePostback, cont, _1, _2));
86 }
87
88 } // anonymous namespace
89
90
registerPostbackHandler(const std::string & name,const PostbackHandlerFunction & handlerFunction,std::string * pShellCommand)91 Error registerPostbackHandler(const std::string& name,
92 const PostbackHandlerFunction& handlerFunction,
93 std::string* pShellCommand)
94 {
95 // form postback uri fragment
96 std::string postback = kPostbackUriScope + name;
97
98 // register a uri handler for this prefix
99 Error error = module_context::registerAsyncLocalUriHandler(
100 postback,
101 boost::bind(handlePostback, handlerFunction, _1, _2));
102 if (error)
103 return error;
104
105 // compute the shell command required to invoke this handler and return it
106 Options& options = session::options();
107 *pShellCommand = options.rpostbackPath().getAbsolutePath() + "-" + name;
108
109 // return success
110 return Success();
111 }
112
113
114
115 } // namespace module_context
116 } // namespace session
117 } // namespace rstudio
118