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.
This commit is contained in:
Joris Vink 2021-12-14 23:15:21 +01:00
parent 97ef486d22
commit e8e01980fc
9 changed files with 153 additions and 11 deletions

View File

@ -44,7 +44,7 @@ state_setup(struct http_request *req)
{ {
struct kore_curl *client; struct kore_curl *client;
client = http_state_create(req, sizeof(*client), NULL); client = http_state_create(req, sizeof(*client));
if (!kore_curl_init(client, if (!kore_curl_init(client,
"http://ftp.eu.openbsd.org/pub/OpenBSD/README", KORE_CURL_ASYNC)) { "http://ftp.eu.openbsd.org/pub/OpenBSD/README", KORE_CURL_ASYNC)) {

View File

@ -59,7 +59,7 @@ state_setup(struct http_request *req)
{ {
struct kore_curl *client; struct kore_curl *client;
client = http_state_create(req, sizeof(*client), NULL); client = http_state_create(req, sizeof(*client));
/* Initialize curl. */ /* Initialize curl. */
if (!kore_curl_init(client, "https://kore.io", KORE_CURL_ASYNC)) { if (!kore_curl_init(client, "https://kore.io", KORE_CURL_ASYNC)) {

View File

@ -85,7 +85,7 @@ request_perform_init(struct http_request *req)
/* Setup our state context (if not yet set). */ /* Setup our state context (if not yet set). */
if (!http_state_exists(req)) { 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 * Initialize the kore_pgsql data structure and bind it

View File

@ -413,8 +413,7 @@ const char *http_media_type(const char *);
void *http_state_get(struct http_request *); void *http_state_get(struct http_request *);
int http_state_exists(struct http_request *); int http_state_exists(struct http_request *);
void http_state_cleanup(struct http_request *); void http_state_cleanup(struct http_request *);
void *http_state_create(struct http_request *, size_t, void *http_state_create(struct http_request *, size_t);
void (*onfree)(struct http_request *));
int http_argument_urldecode(char *); int http_argument_urldecode(char *);
int http_header_recv(struct netbuf *); int http_header_recv(struct netbuf *);

View File

@ -287,6 +287,7 @@ struct kore_runtime {
int type; int type;
#if !defined(KORE_NO_HTTP) #if !defined(KORE_NO_HTTP)
int (*http_request)(void *, struct http_request *); int (*http_request)(void *, struct http_request *);
void (*http_request_free)(void *, struct http_request *);
void (*http_body_chunk)(void *, void (*http_body_chunk)(void *,
struct http_request *, const void *, size_t); struct http_request *, const void *, size_t);
int (*validator)(void *, struct http_request *, const void *); int (*validator)(void *, struct http_request *, const void *);
@ -327,6 +328,7 @@ struct kore_route {
struct kore_domain *dom; struct kore_domain *dom;
struct kore_auth *auth; struct kore_auth *auth;
struct kore_runtime_call *rcall; struct kore_runtime_call *rcall;
struct kore_runtime_call *on_free;
struct kore_runtime_call *on_headers; struct kore_runtime_call *on_headers;
struct kore_runtime_call *on_body_chunk; 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) #if !defined(KORE_NO_HTTP)
int kore_runtime_http_request(struct kore_runtime_call *, int kore_runtime_http_request(struct kore_runtime_call *,
struct http_request *); 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 *, void kore_runtime_http_body_chunk(struct kore_runtime_call *,
struct http_request *, const void *, size_t); struct http_request *, const void *, size_t);
int kore_runtime_validator(struct kore_runtime_call *, int kore_runtime_validator(struct kore_runtime_call *,

View File

@ -111,6 +111,7 @@ static int configure_client_verify_depth(char *);
static int configure_route(char *); static int configure_route(char *);
static int configure_route_methods(char *); static int configure_route_methods(char *);
static int configure_route_handler(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_headers(char *);
static int configure_route_on_body_chunk(char *); static int configure_route_on_body_chunk(char *);
static int configure_filemap(char *); static int configure_filemap(char *);
@ -201,6 +202,7 @@ static struct {
{ "handler", configure_route_handler }, { "handler", configure_route_handler },
{ "on_headers", configure_route_on_headers }, { "on_headers", configure_route_on_headers },
{ "on_body_chunk", configure_route_on_body_chunk }, { "on_body_chunk", configure_route_on_body_chunk },
{ "on_free", configure_route_on_free },
{ "methods", configure_route_methods }, { "methods", configure_route_methods },
{ "filemap", configure_filemap }, { "filemap", configure_filemap },
{ "redirect", configure_redirect }, { "redirect", configure_redirect },
@ -1203,6 +1205,24 @@ configure_route_on_body_chunk(char *name)
return (KORE_RESULT_OK); return (KORE_RESULT_OK);
} }
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 static int
configure_route_methods(char *options) configure_route_methods(char *options)
{ {

View File

@ -442,8 +442,8 @@ http_request_free(struct http_request *req)
struct http_header *hdr, *next; struct http_header *hdr, *next;
struct http_cookie *ck, *cknext; struct http_cookie *ck, *cknext;
if (req->onfree != NULL) if (req->rt->on_free != NULL)
req->onfree(req); kore_runtime_http_request_free(req->rt->on_free, req);
if (req->runlock != NULL) { if (req->runlock != NULL) {
LIST_REMOVE(req->runlock, list); LIST_REMOVE(req->runlock, list);
@ -1454,14 +1454,12 @@ http_state_exists(struct http_request *req)
} }
void * void *
http_state_create(struct http_request *req, size_t len, http_state_create(struct http_request *req, size_t len)
void (*onfree)(struct http_request *))
{ {
if (req->hdlr_extra != NULL) if (req->hdlr_extra != NULL)
fatal("http_state_create: state already exists"); fatal("http_state_create: state already exists");
req->state_len = len; req->state_len = len;
req->onfree = onfree;
req->hdlr_extra = kore_calloc(1, len); req->hdlr_extra = kore_calloc(1, len);
return (req->hdlr_extra); return (req->hdlr_extra);
@ -2035,7 +2033,6 @@ http_request_new(struct connection *c, const char *host,
req->status = 0; req->status = 0;
req->method = m; req->method = m;
req->agent = NULL; req->agent = NULL;
req->onfree = NULL;
req->referer = NULL; req->referer = NULL;
req->runlock = NULL; req->runlock = NULL;
req->flags = flags; req->flags = flags;

View File

@ -88,6 +88,9 @@ static int python_route_params(PyObject *, struct kore_route *,
static int python_route_methods(PyObject *, PyObject *, static int python_route_methods(PyObject *, PyObject *,
struct kore_route *); struct kore_route *);
static int python_route_auth(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 int python_coro_run(struct python_coro *);
static void python_coro_wakeup(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_validator_check(PyObject *);
static int python_runtime_http_request(void *, struct http_request *); 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 *, static int python_runtime_validator(void *, struct http_request *,
const void *); const void *);
static void python_runtime_wsmessage(void *, struct connection *, 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 = { struct kore_runtime kore_python_runtime = {
KORE_RUNTIME_PYTHON, KORE_RUNTIME_PYTHON,
.http_request = python_runtime_http_request, .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, .validator = python_runtime_validator,
.wsconnect = python_runtime_connect, .wsconnect = python_runtime_connect,
.wsmessage = python_runtime_wsmessage, .wsmessage = python_runtime_wsmessage,
@ -1318,6 +1326,49 @@ python_runtime_http_request(void *addr, struct http_request *req)
return (KORE_RESULT_OK); 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 static int
python_runtime_validator(void *addr, struct http_request *req, const void *data) 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); 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] == '/') { if (rt->path[0] == '/') {
@ -5605,6 +5664,51 @@ python_route_auth(PyObject *dict, struct kore_route *rt)
return (KORE_RESULT_OK); 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) #if defined(KORE_USE_PGSQL)
static PyObject * static PyObject *
python_kore_pgsql_query(PyObject *self, PyObject *args, PyObject *kwargs) python_kore_pgsql_query(PyObject *self, PyObject *args, PyObject *kwargs)

View File

@ -33,6 +33,7 @@ static void native_runtime_connect(void *, struct connection *);
static void native_runtime_configure(void *, int, char **); static void native_runtime_configure(void *, int, char **);
#if !defined(KORE_NO_HTTP) #if !defined(KORE_NO_HTTP)
static int native_runtime_http_request(void *, struct http_request *); 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 *, static void native_runtime_http_body_chunk(void *, struct http_request *,
const void *, size_t); const void *, size_t);
static int native_runtime_validator(void *, struct http_request *, static int native_runtime_validator(void *, struct http_request *,
@ -46,6 +47,7 @@ struct kore_runtime kore_native_runtime = {
KORE_RUNTIME_NATIVE, KORE_RUNTIME_NATIVE,
#if !defined(KORE_NO_HTTP) #if !defined(KORE_NO_HTTP)
.http_request = native_runtime_http_request, .http_request = native_runtime_http_request,
.http_request_free = native_runtime_http_request_free,
.http_body_chunk = native_runtime_http_body_chunk, .http_body_chunk = native_runtime_http_body_chunk,
.validator = native_runtime_validator, .validator = native_runtime_validator,
.wsconnect = native_runtime_connect, .wsconnect = native_runtime_connect,
@ -108,6 +110,13 @@ kore_runtime_http_request(struct kore_runtime_call *rcall,
return (rcall->runtime->http_request(rcall->addr, req)); return (rcall->runtime->http_request(rcall->addr, req));
} }
void
kore_runtime_http_request_free(struct kore_runtime_call *rcall,
struct http_request *req)
{
rcall->runtime->http_request_free(rcall->addr, req);
}
void void
kore_runtime_http_body_chunk(struct kore_runtime_call *rcall, kore_runtime_http_body_chunk(struct kore_runtime_call *rcall,
struct http_request *req, const void *data, size_t len) struct http_request *req, const void *data, size_t len)
@ -188,6 +197,15 @@ native_runtime_http_request(void *addr, struct http_request *req)
return (cb(req)); return (cb(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 static void
native_runtime_http_body_chunk(void *addr, struct http_request *req, native_runtime_http_body_chunk(void *addr, struct http_request *req,
const void *data, size_t len) const void *data, size_t len)