commit 9caa45a05024ef3593133bde6e08098b8963a704
parent c3ab570f56a0a6d9810e3d2caf7053f7a4093ddc
Author: Joris Vink <joris@coders.se>
Date: Mon, 18 Mar 2019 09:34:31 +0100
Allow python validator methods to be async.
Diffstat:
2 files changed, 89 insertions(+), 16 deletions(-)
diff --git a/include/kore/python_methods.h b/include/kore/python_methods.h
@@ -21,6 +21,7 @@ struct python_coro {
u_int64_t id;
int state;
PyObject *obj;
+ PyObject *result;
struct pysocket_op *sockop;
struct pygather_op *gatherop;
struct http_request *request;
diff --git a/src/python.c b/src/python.c
@@ -80,6 +80,7 @@ static void python_append_path(const char *);
static void python_push_integer(PyObject *, const char *, long);
static void python_push_type(const char *, PyObject *, PyTypeObject *);
+static int python_validator_check(PyObject *);
static int python_runtime_http_request(void *, struct http_request *);
static int python_runtime_validator(void *, struct http_request *,
const void *);
@@ -286,6 +287,7 @@ kore_python_coro_delete(void *obj)
else
TAILQ_REMOVE(&coro_suspended, coro, list);
+ Py_XDECREF(coro->result);
kore_pool_put(&coro_pool, coro);
}
@@ -505,6 +507,7 @@ python_coro_create(PyObject *obj, struct http_request *req)
coro = kore_pool_get(&coro_pool);
coro_count++;
+ coro->result = NULL;
coro->sockop = NULL;
coro->gatherop = NULL;
coro->exception = NULL;
@@ -527,6 +530,7 @@ static int
python_coro_run(struct python_coro *coro)
{
PyObject *item;
+ PyObject *type, *traceback;
if (coro->state != CORO_STATE_RUNNABLE)
fatal("non-runnable coro attempted to run");
@@ -538,7 +542,15 @@ python_coro_run(struct python_coro *coro)
item = _PyGen_Send((PyGenObject *)coro->obj, NULL);
if (item == NULL) {
- kore_python_log_error("coroutine");
+ if (PyErr_Occurred() &&
+ PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ PyErr_Fetch(&type, &coro->result, &traceback);
+ Py_DECREF(type);
+ Py_XDECREF(traceback);
+ } else {
+ kore_python_log_error("coroutine");
+ }
+
coro_running = NULL;
return (KORE_RESULT_OK);
}
@@ -651,28 +663,48 @@ python_runtime_http_request(void *addr, struct http_request *req)
static int
python_runtime_validator(void *addr, struct http_request *req, const void *data)
{
- int ret;
- PyObject *pyret, *pyreq, *args, *callable, *arg;
+ int ret;
+ struct python_coro *coro;
+ PyObject *pyret, *pyreq, *args, *callable, *arg;
- callable = (PyObject *)addr;
+ if (req->py_coro != NULL) {
+ coro = req->py_coro;
+ python_coro_wakeup(coro);
+ if (python_coro_run(coro) == KORE_RESULT_OK) {
+ ret = python_validator_check(coro->result);
+ kore_python_coro_delete(coro);
+ req->py_coro = NULL;
+ return (ret);
+ }
- if ((pyreq = pyhttp_request_alloc(req)) == NULL)
- fatal("python_runtime_validator: pyreq alloc failed");
+ return (KORE_RESULT_RETRY);
+ }
+
+ callable = (PyObject *)addr;
if (req->flags & HTTP_VALIDATOR_IS_REQUEST) {
if ((arg = pyhttp_request_alloc(data)) == NULL)
- fatal("python_runtime_validator: pyreq failed");
+ fatal("%s: pyreq failed", __func__);
+
+ if ((args = PyTuple_New(1)) == NULL)
+ fatal("%s: PyTuple_New failed", __func__);
+
+ if (PyTuple_SetItem(args, 0, arg) != 0)
+ fatal("%s: PyTuple_SetItem failed", __func__);
} else {
+ if ((pyreq = pyhttp_request_alloc(req)) == NULL)
+ fatal("%s: pyreq alloc failed", __func__);
+
if ((arg = PyUnicode_FromString(data)) == NULL)
fatal("python_runtime_validator: PyUnicode failed");
- }
- if ((args = PyTuple_New(2)) == NULL)
- fatal("python_runtime_validator: PyTuple_New failed");
+ if ((args = PyTuple_New(2)) == NULL)
+ fatal("%s: PyTuple_New failed", __func__);
- if (PyTuple_SetItem(args, 0, pyreq) != 0 ||
- PyTuple_SetItem(args, 1, arg) != 0)
- fatal("python_runtime_vaildator: PyTuple_SetItem failed");
+ if (PyTuple_SetItem(args, 0, pyreq) != 0 ||
+ PyTuple_SetItem(args, 1, arg) != 0)
+ fatal("%s: PyTuple_SetItem failed", __func__);
+ }
PyErr_Clear();
pyret = PyObject_Call(callable, args, NULL);
@@ -683,15 +715,55 @@ python_runtime_validator(void *addr, struct http_request *req, const void *data)
fatal("failed to execute python call");
}
- if (!PyLong_Check(pyret))
- fatal("python_runtime_validator: unexpected return type");
+ if (PyCoro_CheckExact(pyret)) {
+ coro = python_coro_create(pyret, req);
+ req->py_coro = coro;
+ if (python_coro_run(coro) == KORE_RESULT_OK) {
+ ret = python_validator_check(coro->result);
+ kore_python_coro_delete(coro);
+ req->py_coro = NULL;
+ return (ret);
+ }
+ http_request_sleep(req);
+ return (KORE_RESULT_RETRY);
+ }
- ret = (int)PyLong_AsLong(pyret);
+ ret = python_validator_check(pyret);
Py_DECREF(pyret);
return (ret);
}
+static int
+python_validator_check(PyObject *obj)
+{
+ int ret;
+
+ if (obj == NULL)
+ return (KORE_RESULT_ERROR);
+
+ if (!PyLong_Check(obj)) {
+ kore_log(LOG_WARNING,
+ "invalid return value from authenticator (not an int)");
+ ret = KORE_RESULT_ERROR;
+ } else {
+ ret = (int)PyLong_AsLong(obj);
+ }
+
+ switch (ret) {
+ case KORE_RESULT_OK:
+ case KORE_RESULT_ERROR:
+ break;
+ default:
+ kore_log(LOG_WARNING,
+ "unsupported authenticator return value '%d'", ret);
+ ret = KORE_RESULT_ERROR;
+ break;
+ }
+
+ return (ret);
+}
+
static void
python_runtime_wsmessage(void *addr, struct connection *c, u_int8_t op,
const void *data, size_t len)