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;
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)) {

View File

@ -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)) {

View File

@ -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

View File

@ -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 *);

View File

@ -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 *,

View File

@ -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 },
@ -1203,6 +1205,24 @@ configure_route_on_body_chunk(char *name)
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
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_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;

View File

@ -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)

View File

@ -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,
@ -108,6 +110,13 @@ kore_runtime_http_request(struct kore_runtime_call *rcall,
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
kore_runtime_http_body_chunk(struct kore_runtime_call *rcall,
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));
}
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)