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