# HG changeset patch # User Sebastien Jodogne # Date 1607450550 -3600 # Node ID 70abe3ebbbfc76f4976b2f7f8f303a6e7e1547a9 # Parent 9e466631660ae3a5ffece33384f4fcd0f342e599 New Python function: "orthanc.RegisterIncomingHttpRequestFilter()" diff -r 9e466631660a -r 70abe3ebbbfc CMakeLists.txt --- a/CMakeLists.txt Tue Dec 08 16:30:25 2020 +0100 +++ b/CMakeLists.txt Tue Dec 08 19:02:30 2020 +0100 @@ -159,6 +159,7 @@ add_library(OrthancPython SHARED Sources/Autogenerated/sdk.cpp + Sources/IncomingHttpRequestFilter.cpp Sources/OnChangeCallback.cpp Sources/OnStoredInstanceCallback.cpp Sources/Plugin.cpp diff -r 9e466631660a -r 70abe3ebbbfc NEWS --- a/NEWS Tue Dec 08 16:30:25 2020 +0100 +++ b/NEWS Tue Dec 08 19:02:30 2020 +0100 @@ -3,6 +3,7 @@ => Minimum SDK version: 1.8.1 <= +* New Python function: "orthanc.RegisterIncomingHttpRequestFilter()" * Support of Apple OS X * Fix issue #185 (segfaults on non-UTF8 special characters in request URI) diff -r 9e466631660a -r 70abe3ebbbfc Sources/IncomingHttpRequestFilter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/IncomingHttpRequestFilter.cpp Tue Dec 08 19:02:30 2020 +0100 @@ -0,0 +1,163 @@ +/** + * Python plugin for Orthanc + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "IncomingHttpRequestFilter.h" + +#include "PythonString.h" +#include "Autogenerated/sdk.h" + +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" + + +static PyObject* incomingHttpRequestFilter_ = NULL; + + +static int32_t IncomingHttpRequestFilter(OrthancPluginHttpMethod method, + const char *uri, + const char *ip, + uint32_t headersCount, + const char *const *headersKeys, + const char *const *headersValues, + uint32_t getArgumentsCount, + const char *const *getArgumentsKeys, + const char *const *getArgumentsValues) +{ + try + { + PythonLock lock; + /** + * Construct an instance object of the "orthanc.RestOutput" + * class. This is done by calling the constructor function + * "sdk_OrthancPluginRestOutput_Type". + **/ + PythonObject args(lock, PyTuple_New(1)); + + { + PythonString str(lock, uri); + PyTuple_SetItem(args.GetPyObject(), 0, str.Release()); + } + + PythonObject kw(lock, PyDict_New()); + PyDict_SetItemString(kw.GetPyObject(), "method", PyLong_FromLong(method)); + + { + PythonString str(lock, ip); + PyDict_SetItemString(kw.GetPyObject(), "ip", str.Release()); + } + + { + PythonObject headers(lock, PyDict_New()); + + for (uint32_t i = 0; i < headersCount; i++) + { + PythonString str(lock, headersValues[i]); + PyDict_SetItemString(headers.GetPyObject(), headersKeys[i], str.Release()); + } + + PyDict_SetItemString(kw.GetPyObject(), "headers", headers.Release()); + } + + if (method == OrthancPluginHttpMethod_Get) + { + PythonObject getArguments(lock, PyDict_New()); + + for (uint32_t i = 0; i < getArgumentsCount; i++) + { + PythonString str(lock, getArgumentsValues[i]); + PyDict_SetItemString(getArguments.GetPyObject(), getArgumentsKeys[i], str.Release()); + } + + PyDict_SetItemString(kw.GetPyObject(), "get", getArguments.Release()); + } + + + PythonObject result(lock, PyObject_Call(incomingHttpRequestFilter_, + args.GetPyObject(), kw.GetPyObject())); + + std::string traceback; + if (lock.HasErrorOccurred(traceback)) + { + OrthancPlugins::LogError("Error in the Python incoming-http-request filter, " + "traceback:\n" + traceback); + return -1; + } + else + { + if (PyBool_Check(result.GetPyObject())) + { + return (PyObject_IsTrue(result.GetPyObject()) ? 1 /* allowed */ : 0 /* forbidden */); + } + else + { + OrthancPlugins::LogError("The Python incoming-http-request filter has not returned a Boolean"); + return -1; + } + } + } + catch (OrthancPlugins::PluginException& e) + { + OrthancPlugins::LogError("Exception in the Python incoming-http-request filter: " + + std::string(e.What(OrthancPlugins::GetGlobalContext()))); + return e.GetErrorCode(); + } +} + + +PyObject* RegisterIncomingHttpRequestFilter(PyObject* module, PyObject* args) +{ + // The GIL is locked at this point (no need to create "PythonLock") + + // https://docs.python.org/3/extending/extending.html#calling-python-functions-from-c + PyObject* callback = NULL; + + if (!PyArg_ParseTuple(args, "O", &callback) || + callback == NULL) + { + PyErr_SetString(PyExc_ValueError, "Expected a callback function"); + return NULL; + } + + if (incomingHttpRequestFilter_ != NULL) + { + PyErr_SetString(PyExc_RuntimeError, "Can only register one Python incoming-http-request filter"); + return NULL; + } + + OrthancPlugins::LogInfo("Registering a Python incoming-http-request filter"); + + OrthancPluginRegisterIncomingHttpRequestFilter2( + OrthancPlugins::GetGlobalContext(), IncomingHttpRequestFilter); + + incomingHttpRequestFilter_ = callback; + Py_XINCREF(incomingHttpRequestFilter_); + + Py_INCREF(Py_None); + return Py_None; +} + + +void FinalizeIncomingHttpRequestFilter() +{ + PythonLock lock; + + if (incomingHttpRequestFilter_ != NULL) + { + Py_XDECREF(incomingHttpRequestFilter_); + } +} diff -r 9e466631660a -r 70abe3ebbbfc Sources/IncomingHttpRequestFilter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/IncomingHttpRequestFilter.h Tue Dec 08 19:02:30 2020 +0100 @@ -0,0 +1,26 @@ +/** + * Python plugin for Orthanc + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "PythonHeaderWrapper.h" + +PyObject* RegisterIncomingHttpRequestFilter(PyObject* module, PyObject* args); + +void FinalizeIncomingHttpRequestFilter(); diff -r 9e466631660a -r 70abe3ebbbfc Sources/Plugin.cpp --- a/Sources/Plugin.cpp Tue Dec 08 16:30:25 2020 +0100 +++ b/Sources/Plugin.cpp Tue Dec 08 19:02:30 2020 +0100 @@ -24,8 +24,10 @@ // https://fr.slideshare.net/YiLungTsai/embed-python +#include "IncomingHttpRequestFilter.h" +#include "OnChangeCallback.h" #include "OnStoredInstanceCallback.h" -#include "OnChangeCallback.h" + #include "RestCallbacks.h" #include "PythonModule.h" @@ -88,6 +90,12 @@ METH_VARARGS, "" }; functions.push_back(f); } + + { + // New in release 3.0 + PyMethodDef f = { "RegisterIncomingHttpRequestFilter", RegisterIncomingHttpRequestFilter, METH_VARARGS, "" }; + functions.push_back(f); + } /** * Append all the global functions that were automatically generated @@ -289,6 +297,7 @@ FinalizeOnChangeCallback(); FinalizeRestCallbacks(); FinalizeOnStoredInstanceCallback(); + FinalizeIncomingHttpRequestFilter(); PythonLock::GlobalFinalize(); }