changeset 50:70abe3ebbbfc

New Python function: "orthanc.RegisterIncomingHttpRequestFilter()"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 08 Dec 2020 19:02:30 +0100
parents 9e466631660a
children 3dc37a5af1b1
files CMakeLists.txt NEWS Sources/IncomingHttpRequestFilter.cpp Sources/IncomingHttpRequestFilter.h Sources/Plugin.cpp
diffstat 5 files changed, 201 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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)
 
--- /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 <http://www.gnu.org/licenses/>.
+ **/
+
+
+#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_);
+  }
+}
--- /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 <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "PythonHeaderWrapper.h"
+
+PyObject* RegisterIncomingHttpRequestFilter(PyObject* module, PyObject* args);
+
+void FinalizeIncomingHttpRequestFilter();
--- 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();
     }