commit e8e01980fc45cddf62f6eca3fffc6d2c9e9ae2ae
parent 97ef486d2264f17d50e916c09458da5fbe12371c
Author: Joris Vink <joris@coders.se>
Date: Tue, 14 Dec 2021 23:15:21 +0100
Python: allow route hooks via kore.route().
Adding the hooks keyword with a dictionary attached to specify
the relevant hooks will hook them for the given route.
Eg:
domain.route("/", self.index, methods=["get"],
hooks={
"on_free": self.request_free
}
)
These are the same hooks available via a normal Kore route configuration.
Diffstat:
9 files changed, 153 insertions(+), 11 deletions(-)
diff --git a/examples/async-curl/src/ftp.c b/examples/async-curl/src/ftp.c
@@ -44,7 +44,7 @@ state_setup(struct http_request *req)
{
struct kore_curl *client;
- client = http_state_create(req, sizeof(*client), NULL);
+ client = http_state_create(req, sizeof(*client));
if (!kore_curl_init(client,
"http://ftp.eu.openbsd.org/pub/OpenBSD/README", KORE_CURL_ASYNC)) {
diff --git a/examples/async-curl/src/http.c b/examples/async-curl/src/http.c
@@ -59,7 +59,7 @@ state_setup(struct http_request *req)
{
struct kore_curl *client;
- client = http_state_create(req, sizeof(*client), NULL);
+ client = http_state_create(req, sizeof(*client));
/* Initialize curl. */
if (!kore_curl_init(client, "https://kore.io", KORE_CURL_ASYNC)) {
diff --git a/examples/pgsql/src/pgsql.c b/examples/pgsql/src/pgsql.c
@@ -85,7 +85,7 @@ request_perform_init(struct http_request *req)
/* Setup our state context (if not yet set). */
if (!http_state_exists(req)) {
- state = http_state_create(req, sizeof(*state), NULL);
+ state = http_state_create(req, sizeof(*state));
/*
* Initialize the kore_pgsql data structure and bind it
diff --git a/include/kore/http.h b/include/kore/http.h
@@ -413,8 +413,7 @@ const char *http_media_type(const char *);
void *http_state_get(struct http_request *);
int http_state_exists(struct http_request *);
void http_state_cleanup(struct http_request *);
-void *http_state_create(struct http_request *, size_t,
- void (*onfree)(struct http_request *));
+void *http_state_create(struct http_request *, size_t);
int http_argument_urldecode(char *);
int http_header_recv(struct netbuf *);
diff --git a/include/kore/kore.h b/include/kore/kore.h
@@ -287,6 +287,7 @@ struct kore_runtime {
int type;
#if !defined(KORE_NO_HTTP)
int (*http_request)(void *, struct http_request *);
+ void (*http_request_free)(void *, struct http_request *);
void (*http_body_chunk)(void *,
struct http_request *, const void *, size_t);
int (*validator)(void *, struct http_request *, const void *);
@@ -327,6 +328,7 @@ struct kore_route {
struct kore_domain *dom;
struct kore_auth *auth;
struct kore_runtime_call *rcall;
+ struct kore_runtime_call *on_free;
struct kore_runtime_call *on_headers;
struct kore_runtime_call *on_body_chunk;
@@ -993,6 +995,8 @@ void kore_runtime_connect(struct kore_runtime_call *, struct connection *);
#if !defined(KORE_NO_HTTP)
int kore_runtime_http_request(struct kore_runtime_call *,
struct http_request *);
+void kore_runtime_http_request_free(struct kore_runtime_call *,
+ struct http_request *);
void kore_runtime_http_body_chunk(struct kore_runtime_call *,
struct http_request *, const void *, size_t);
int kore_runtime_validator(struct kore_runtime_call *,
diff --git a/src/config.c b/src/config.c
@@ -111,6 +111,7 @@ static int configure_client_verify_depth(char *);
static int configure_route(char *);
static int configure_route_methods(char *);
static int configure_route_handler(char *);
+static int configure_route_on_free(char *);
static int configure_route_on_headers(char *);
static int configure_route_on_body_chunk(char *);
static int configure_filemap(char *);
@@ -201,6 +202,7 @@ static struct {
{ "handler", configure_route_handler },
{ "on_headers", configure_route_on_headers },
{ "on_body_chunk", configure_route_on_body_chunk },
+ { "on_free", configure_route_on_free },
{ "methods", configure_route_methods },
{ "filemap", configure_filemap },
{ "redirect", configure_redirect },
@@ -1204,6 +1206,24 @@ configure_route_on_body_chunk(char *name)
}
static int
+configure_route_on_free(char *name)
+{
+ if (current_route == NULL) {
+ kore_log(LOG_ERR,
+ "on_free keyword not inside of route context");
+ return (KORE_RESULT_ERROR);
+ }
+
+ if ((current_route->on_free = kore_runtime_getcall(name)) == NULL) {
+ kore_log(LOG_ERR, "on_free callback '%s' for '%s' not found",
+ name, current_route->path);
+ return (KORE_RESULT_ERROR);
+ }
+
+ return (KORE_RESULT_OK);
+}
+
+static int
configure_route_methods(char *options)
{
int i, cnt;
diff --git a/src/http.c b/src/http.c
@@ -442,8 +442,8 @@ http_request_free(struct http_request *req)
struct http_header *hdr, *next;
struct http_cookie *ck, *cknext;
- if (req->onfree != NULL)
- req->onfree(req);
+ if (req->rt->on_free != NULL)
+ kore_runtime_http_request_free(req->rt->on_free, req);
if (req->runlock != NULL) {
LIST_REMOVE(req->runlock, list);
@@ -1454,14 +1454,12 @@ http_state_exists(struct http_request *req)
}
void *
-http_state_create(struct http_request *req, size_t len,
- void (*onfree)(struct http_request *))
+http_state_create(struct http_request *req, size_t len)
{
if (req->hdlr_extra != NULL)
fatal("http_state_create: state already exists");
req->state_len = len;
- req->onfree = onfree;
req->hdlr_extra = kore_calloc(1, len);
return (req->hdlr_extra);
@@ -2035,7 +2033,6 @@ http_request_new(struct connection *c, const char *host,
req->status = 0;
req->method = m;
req->agent = NULL;
- req->onfree = NULL;
req->referer = NULL;
req->runlock = NULL;
req->flags = flags;
diff --git a/src/python.c b/src/python.c
@@ -88,6 +88,9 @@ static int python_route_params(PyObject *, struct kore_route *,
static int python_route_methods(PyObject *, PyObject *,
struct kore_route *);
static int python_route_auth(PyObject *, struct kore_route *);
+static int python_route_hooks(PyObject *, struct kore_route *);
+static int python_route_hook_set(PyObject *, const char *,
+ struct kore_runtime_call **);
static int python_coro_run(struct python_coro *);
static void python_coro_wakeup(struct python_coro *);
@@ -146,6 +149,9 @@ 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 void python_runtime_http_request_free(void *, struct http_request *);
+static void python_runtime_http_body_chunk(void *, struct http_request *,
+ const void *, size_t);
static int python_runtime_validator(void *, struct http_request *,
const void *);
static void python_runtime_wsmessage(void *, struct connection *,
@@ -175,6 +181,8 @@ struct kore_module_functions kore_python_module = {
struct kore_runtime kore_python_runtime = {
KORE_RUNTIME_PYTHON,
.http_request = python_runtime_http_request,
+ .http_body_chunk = python_runtime_http_body_chunk,
+ .http_request_free = python_runtime_http_request_free,
.validator = python_runtime_validator,
.wsconnect = python_runtime_connect,
.wsmessage = python_runtime_wsmessage,
@@ -1318,6 +1326,49 @@ python_runtime_http_request(void *addr, struct http_request *req)
return (KORE_RESULT_OK);
}
+static void
+python_runtime_http_request_free(void *addr, struct http_request *req)
+{
+ PyObject *ret;
+
+ if (req->py_req == NULL)
+ fatal("%s: py_req is NULL", __func__);
+
+ PyErr_Clear();
+ ret = PyObject_CallFunctionObjArgs(addr, req->py_req, NULL);
+
+ if (ret == NULL)
+ kore_python_log_error("python_runtime_http_request_free");
+
+ Py_XDECREF(ret);
+}
+
+static void
+python_runtime_http_body_chunk(void *addr, struct http_request *req,
+ const void *data, size_t len)
+{
+ PyObject *args, *ret;
+
+ if (req->py_req == NULL) {
+ if ((req->py_req = pyhttp_request_alloc(req)) == NULL)
+ fatal("%s: pyreq alloc failed", __func__);
+ }
+
+ if ((args = Py_BuildValue("(Oy#)", req->py_req, data, len)) == NULL) {
+ kore_python_log_error("python_runtime_http_body_chunk");
+ return;
+ }
+
+ PyErr_Clear();
+ ret = PyObject_Call(addr, args, NULL);
+
+ if (ret == NULL)
+ kore_python_log_error("python_runtime_http_body_chunk");
+
+ Py_XDECREF(ret);
+ Py_DECREF(args);
+}
+
static int
python_runtime_validator(void *addr, struct http_request *req, const void *data)
{
@@ -5358,6 +5409,14 @@ python_route_install(struct pyroute *route)
return (KORE_RESULT_ERROR);
}
}
+
+ if ((obj = PyDict_GetItemString(kwargs, "hooks")) != NULL) {
+ if (!python_route_hooks(obj, rt)) {
+ kore_python_log_error("python_route_install");
+ kore_route_free(rt);
+ return (KORE_RESULT_ERROR);
+ }
+ }
}
if (rt->path[0] == '/') {
@@ -5605,6 +5664,51 @@ python_route_auth(PyObject *dict, struct kore_route *rt)
return (KORE_RESULT_OK);
}
+static int
+python_route_hooks(PyObject *dict, struct kore_route *rt)
+{
+ if (!PyDict_CheckExact(dict))
+ return (KORE_RESULT_ERROR);
+
+ if (!python_route_hook_set(dict, "on_free", &rt->on_free))
+ return (KORE_RESULT_ERROR);
+
+ if (!python_route_hook_set(dict, "on_headers", &rt->on_headers))
+ return (KORE_RESULT_ERROR);
+
+ if (!python_route_hook_set(dict, "on_body_chunk", &rt->on_body_chunk))
+ return (KORE_RESULT_ERROR);
+
+ return (KORE_RESULT_OK);
+}
+
+static int
+python_route_hook_set(PyObject *dict, const char *name,
+ struct kore_runtime_call **out)
+{
+ PyObject *obj;
+ struct kore_runtime_call *rcall;
+
+ if ((obj = PyDict_GetItemString(dict, name)) == NULL)
+ return (KORE_RESULT_OK);
+
+ if (!PyCallable_Check(obj)) {
+ PyErr_Format(PyExc_RuntimeError,
+ "%s for a route not callable", name);
+ Py_DECREF(obj);
+ return (KORE_RESULT_ERROR);
+ }
+
+ rcall = kore_calloc(1, sizeof(struct kore_runtime_call));
+ rcall->addr = obj;
+ rcall->runtime = &kore_python_runtime;
+
+ Py_INCREF(rcall->addr);
+ *out = rcall;
+
+ return (KORE_RESULT_OK);
+}
+
#if defined(KORE_USE_PGSQL)
static PyObject *
python_kore_pgsql_query(PyObject *self, PyObject *args, PyObject *kwargs)
diff --git a/src/runtime.c b/src/runtime.c
@@ -33,6 +33,7 @@ static void native_runtime_connect(void *, struct connection *);
static void native_runtime_configure(void *, int, char **);
#if !defined(KORE_NO_HTTP)
static int native_runtime_http_request(void *, struct http_request *);
+static void native_runtime_http_request_free(void *, struct http_request *);
static void native_runtime_http_body_chunk(void *, struct http_request *,
const void *, size_t);
static int native_runtime_validator(void *, struct http_request *,
@@ -46,6 +47,7 @@ struct kore_runtime kore_native_runtime = {
KORE_RUNTIME_NATIVE,
#if !defined(KORE_NO_HTTP)
.http_request = native_runtime_http_request,
+ .http_request_free = native_runtime_http_request_free,
.http_body_chunk = native_runtime_http_body_chunk,
.validator = native_runtime_validator,
.wsconnect = native_runtime_connect,
@@ -109,6 +111,13 @@ kore_runtime_http_request(struct kore_runtime_call *rcall,
}
void
+kore_runtime_http_request_free(struct kore_runtime_call *rcall,
+ struct http_request *req)
+{
+ rcall->runtime->http_request_free(rcall->addr, req);
+}
+
+void
kore_runtime_http_body_chunk(struct kore_runtime_call *rcall,
struct http_request *req, const void *data, size_t len)
{
@@ -189,6 +198,15 @@ native_runtime_http_request(void *addr, struct http_request *req)
}
static void
+native_runtime_http_request_free(void *addr, struct http_request *req)
+{
+ int (*cb)(struct http_request *);
+
+ *(void **)&(cb) = addr;
+ cb(req);
+}
+
+static void
native_runtime_http_body_chunk(void *addr, struct http_request *req,
const void *data, size_t len)
{