Mercurial > hg > orthanc-python
annotate Sources/RestCallbacks.cpp @ 92:c814297de620
sync
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 31 Aug 2021 13:46:02 +0200 |
parents | 0b3ef425db31 |
children | 62f2731094ae |
rev | line source |
---|---|
0 | 1 /** |
2 * Python plugin for Orthanc | |
56
23f3099bed47
upgrade to year 2021
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
53
diff
changeset
|
3 * Copyright (C) 2020-2021 Osimis S.A., Belgium |
0 | 4 * |
5 * This program is free software: you can redistribute it and/or | |
6 * modify it under the terms of the GNU Affero General Public License | |
7 * as published by the Free Software Foundation, either version 3 of | |
8 * the License, or (at your option) any later version. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, but | |
11 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Affero General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Affero General Public License | |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 **/ | |
18 | |
19 | |
20 #include "RestCallbacks.h" | |
21 | |
68
0b3ef425db31
refactoring using ICallbackRegistration::Apply()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
63
diff
changeset
|
22 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" |
0 | 23 #include "Autogenerated/sdk.h" |
68
0b3ef425db31
refactoring using ICallbackRegistration::Apply()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
63
diff
changeset
|
24 #include "PythonString.h" |
0 | 25 |
26 #include <boost/regex.hpp> | |
27 | |
28 | |
29 class RestCallback : public boost::noncopyable | |
30 { | |
31 private: | |
32 boost::regex regex_; | |
33 PyObject* callback_; | |
34 | |
35 public: | |
36 RestCallback(const std::string& uri, | |
37 PyObject* callback) : | |
38 regex_(uri), | |
39 callback_(callback) | |
40 { | |
41 Py_XINCREF(callback_); | |
42 } | |
43 | |
44 ~RestCallback() | |
45 { | |
46 Py_XDECREF(callback_); | |
47 } | |
48 | |
49 bool IsMatch(const std::string& uri) const | |
50 { | |
51 return boost::regex_match(uri, regex_); | |
52 } | |
53 | |
54 PyObject* GetCallback() | |
55 { | |
56 return callback_; | |
57 } | |
58 }; | |
59 | |
60 | |
61 // Concurrent accesses to the callbacks are protected by the | |
62 // "PythonLock" (GIL mutex) | |
63 static std::list<RestCallback*> restCallbacks_; | |
64 | |
65 | |
66 static void RestCallbackHandler(OrthancPluginRestOutput* output, | |
67 const char* uri, | |
68 const OrthancPluginHttpRequest* request) | |
69 { | |
70 PythonLock lock; | |
71 | |
72 for (std::list<RestCallback*>::const_iterator it = restCallbacks_.begin(); | |
73 it != restCallbacks_.end(); ++it) | |
74 { | |
75 assert(*it != NULL); | |
76 if ((*it)->IsMatch(uri)) | |
77 { | |
78 /** | |
79 * Construct an instance object of the "orthanc.RestOutput" | |
80 * class. This is done by calling the constructor function | |
81 * "sdk_OrthancPluginRestOutput_Type". | |
82 **/ | |
83 PythonObject args(lock, PyTuple_New(2)); | |
84 PyTuple_SetItem(args.GetPyObject(), 0, PyLong_FromSsize_t((intptr_t) output)); | |
85 PyTuple_SetItem(args.GetPyObject(), 1, PyBool_FromLong(true /* borrowed, don't destruct */)); | |
63
32de70a1e4c7
New functions from the SDK wrapped in Python: CreateDicom, RegisterFindCallback, RegisterMoveCallback
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
56
diff
changeset
|
86 PyObject *pInst = PyObject_CallObject((PyObject*) GetOrthancPluginRestOutputType(), args.GetPyObject()); |
0 | 87 |
88 | |
89 /** | |
90 * Construct the arguments tuple (output, uri) | |
91 **/ | |
45
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
92 PythonString str(lock, uri); |
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
93 |
0 | 94 PythonObject args2(lock, PyTuple_New(2)); |
95 PyTuple_SetItem(args2.GetPyObject(), 0, pInst); | |
45
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
96 PyTuple_SetItem(args2.GetPyObject(), 1, str.Release()); |
0 | 97 // No need to decrement refcount with "PyTuple_SetItem()" |
98 | |
99 /** | |
100 * Construct the named arguments from the "request" argument | |
101 **/ | |
102 const char* method; | |
103 switch (request->method) | |
104 { | |
105 case OrthancPluginHttpMethod_Get: | |
106 method = "GET"; | |
107 break; | |
108 | |
109 case OrthancPluginHttpMethod_Post: | |
110 method = "POST"; | |
111 break; | |
112 | |
113 case OrthancPluginHttpMethod_Put: | |
114 method = "PUT"; | |
115 break; | |
116 | |
117 case OrthancPluginHttpMethod_Delete: | |
118 method = "DELETE"; | |
119 break; | |
120 | |
121 default: | |
122 ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange); | |
123 } | |
45
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
124 |
0 | 125 PythonObject kw(lock, PyDict_New()); |
45
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
126 |
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
127 { |
53 | 128 PythonString tmp(lock, method); |
129 PyDict_SetItemString(kw.GetPyObject(), "method", tmp.Release()); | |
45
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
130 } |
0 | 131 |
132 { | |
133 PythonObject groups(lock, PyTuple_New(request->groupsCount)); | |
134 | |
135 for (uint32_t i = 0; i < request->groupsCount; i++) | |
136 { | |
53 | 137 PythonString tmp(lock, request->groups[i]); |
138 PyTuple_SetItem(groups.GetPyObject(), i, tmp.Release()); | |
0 | 139 } |
140 | |
141 PyDict_SetItemString(kw.GetPyObject(), "groups", groups.Release()); | |
142 } | |
143 | |
144 if (request->method == OrthancPluginHttpMethod_Get) | |
145 { | |
146 PythonObject get(lock, PyDict_New()); | |
147 | |
148 for (uint32_t i = 0; i < request->getCount; i++) | |
149 { | |
45
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
150 PythonString value(lock, request->getValues[i]); |
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
151 PyDict_SetItemString(get.GetPyObject(), request->getKeys[i], value.Release()); |
0 | 152 } |
153 | |
154 PyDict_SetItemString(kw.GetPyObject(), "get", get.Release()); | |
155 } | |
156 | |
157 { | |
158 PythonObject headers(lock, PyDict_New()); | |
159 | |
160 for (uint32_t i = 0; i < request->headersCount; i++) | |
161 { | |
45
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
162 PythonString value(lock, request->headersValues[i]); |
ee76cced46a5
Fix issue #185 (segfaults on non-UTF8 special characters in request URI)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
36
diff
changeset
|
163 PyDict_SetItemString(headers.GetPyObject(), request->headersKeys[i], value.Release()); |
0 | 164 } |
165 | |
166 PyDict_SetItemString(kw.GetPyObject(), "headers", headers.Release()); | |
167 } | |
168 | |
169 if (request->method == OrthancPluginHttpMethod_Post || | |
170 request->method == OrthancPluginHttpMethod_Put) | |
171 { | |
172 PyDict_SetItemString(kw.GetPyObject(), "body", PyBytes_FromStringAndSize( | |
173 reinterpret_cast<const char*>(request->body), request->bodySize)); | |
174 } | |
175 | |
176 /** | |
177 * Call the user-defined function | |
178 **/ | |
179 PythonObject result(lock, PyObject_Call( | |
180 (*it)->GetCallback(), args2.GetPyObject(), kw.GetPyObject())); | |
3
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
181 |
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
182 std::string traceback; |
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
183 if (lock.HasErrorOccurred(traceback)) |
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
184 { |
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
185 OrthancPlugins::LogError("Error in the REST callback, traceback:\n" + traceback); |
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
186 ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin); |
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
187 } |
26762eb9d704
reporting of exceptions
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
188 |
0 | 189 return; |
190 } | |
191 } | |
192 | |
193 // Should never happen | |
194 OrthancPlugins::LogError("Unable to find the Python handler for URI: " + std::string(uri)); | |
195 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); | |
196 } | |
197 | |
198 | |
199 | |
200 PyObject* RegisterRestCallback(PyObject* module, PyObject* args) | |
201 { | |
202 // The GIL is locked at this point (no need to create "PythonLock") | |
203 | |
204 // https://docs.python.org/3/extending/extending.html#calling-python-functions-from-c | |
205 const char* uri = NULL; | |
206 PyObject* callback = NULL; | |
207 | |
208 if (!PyArg_ParseTuple(args, "sO", &uri, &callback) || | |
209 uri == NULL || | |
210 callback == NULL) | |
211 { | |
212 PyErr_SetString(PyExc_ValueError, "Expected a string (URI) and a callback function"); | |
213 return NULL; | |
214 } | |
215 | |
216 OrthancPlugins::LogInfo("Registering a Python REST callback on URI: " + std::string(uri)); | |
217 OrthancPlugins::RegisterRestCallback<RestCallbackHandler>(uri, true /* thread safe */); | |
218 | |
219 restCallbacks_.push_back(new RestCallback(uri, callback)); | |
220 | |
221 Py_INCREF(Py_None); | |
222 return Py_None; | |
223 } | |
224 | |
225 | |
226 | |
227 void FinalizeRestCallbacks() | |
228 { | |
229 PythonLock lock; | |
230 | |
231 for (std::list<RestCallback*>::iterator it = restCallbacks_.begin(); | |
232 it != restCallbacks_.end(); ++it) | |
233 { | |
234 assert(*it != NULL); | |
235 delete *it; | |
236 } | |
237 | |
238 restCallbacks_.clear(); | |
239 } |