diff --git a/Makefile b/Makefile index 6201d4c..d5ebd8c 100644 --- a/Makefile +++ b/Makefile @@ -134,6 +134,8 @@ endif ifeq ("$(OSNAME)", "darwin") CFLAGS+=-I/opt/local/include/ -I/usr/local/opt/openssl/include LDFLAGS+=-L/opt/local/lib -L/usr/local/opt/openssl/lib + CFLAGS+=-I/opt/homebrew/opt/openssl/include + LDFLAGS+=-L/opt/homebrew/opt/openssl/lib S_SRC+=src/bsd.c else ifeq ("$(OSNAME)", "linux") CFLAGS+=-D_GNU_SOURCE=1 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 diff --git a/include/kore/kore.h b/include/kore/kore.h index c26553f..5554d10 100644 --- a/include/kore/kore.h +++ b/include/kore/kore.h @@ -952,8 +952,8 @@ void kore_domain_crl_add(struct kore_domain *, const void *, size_t); int kore_module_handler_new(struct kore_domain *, const char *, const char *, const char *, int); void kore_module_handler_free(struct kore_module_handle *); -struct kore_module_handle *kore_module_handler_find(struct http_request *, - struct kore_domain *); +int kore_module_handler_find(struct http_request *, + struct kore_domain *, int, struct kore_module_handle **); #endif struct kore_runtime_call *kore_runtime_getcall(const char *); diff --git a/include/kore/python_api.h b/include/kore/python_api.h index 21ce053..d8052aa 100644 --- a/include/kore/python_api.h +++ b/include/kore/python_api.h @@ -34,6 +34,7 @@ void kore_python_proc_reap(void); int kore_python_coro_pending(void); void kore_python_path(const char *); void kore_python_coro_delete(void *); +void kore_python_routes_resolve(void); void kore_python_log_error(const char *); PyObject *kore_python_callable(PyObject *, const char *); diff --git a/include/kore/python_methods.h b/include/kore/python_methods.h index e4ad14d..65895f7 100644 --- a/include/kore/python_methods.h +++ b/include/kore/python_methods.h @@ -54,6 +54,7 @@ static PyObject *python_kore_task_kill(PyObject *, PyObject *); static PyObject *python_kore_prerequest(PyObject *, PyObject *); static PyObject *python_kore_task_create(PyObject *, PyObject *); static PyObject *python_kore_socket_wrap(PyObject *, PyObject *); +static PyObject *python_kore_route(PyObject *, PyObject *, PyObject *); static PyObject *python_kore_timer(PyObject *, PyObject *, PyObject *); static PyObject *python_kore_domain(PyObject *, PyObject *, PyObject *); static PyObject *python_kore_gather(PyObject *, PyObject *, PyObject *); @@ -62,6 +63,7 @@ static PyObject *python_kore_sendobj(PyObject *, PyObject *, static PyObject *python_kore_server(PyObject *, PyObject *, PyObject *); + #if defined(KORE_USE_PGSQL) static PyObject *python_kore_pgsql_query(PyObject *, PyObject *, PyObject *); @@ -101,10 +103,11 @@ static struct PyMethodDef pykore_methods[] = { METHOD("prerequest", python_kore_prerequest, METH_VARARGS), METHOD("task_create", python_kore_task_create, METH_VARARGS), METHOD("socket_wrap", python_kore_socket_wrap, METH_VARARGS), + METHOD("route", python_kore_route, METH_VARARGS | METH_KEYWORDS), METHOD("timer", python_kore_timer, METH_VARARGS | METH_KEYWORDS), + METHOD("domain", python_kore_domain, METH_VARARGS | METH_KEYWORDS), METHOD("server", python_kore_server, METH_VARARGS | METH_KEYWORDS), METHOD("gather", python_kore_gather, METH_VARARGS | METH_KEYWORDS), - METHOD("domain", python_kore_domain, METH_VARARGS | METH_KEYWORDS), METHOD("sendobj", python_kore_sendobj, METH_VARARGS | METH_KEYWORDS), METHOD("websocket_broadcast", python_websocket_broadcast, METH_VARARGS), #if defined(KORE_USE_PGSQL) @@ -186,9 +189,38 @@ static PyTypeObject pyseccomp_type = { }; #endif +struct pyroute { + PyObject_HEAD + char *path; + PyObject *func; + PyObject *kwargs; + struct kore_domain *domain; + TAILQ_ENTRY(pyroute) list; +}; + +static PyObject *pyroute_inner(struct pyroute *, PyObject *); +static void pyroute_dealloc(struct pyroute *); + +static PyMethodDef pyroute_methods[] = { + METHOD("inner", pyroute_inner, METH_VARARGS), + METHOD(NULL, NULL, -1) +}; + +static PyTypeObject pyroute_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "kore.route", + .tp_doc = "kore route function", + .tp_methods = pyroute_methods, + .tp_basicsize = sizeof(struct pyroute), + .tp_dealloc = (destructor)pyroute_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, +}; + struct pydomain { PyObject_HEAD - struct kore_domain *config; + struct kore_domain *config; + struct kore_module_handle *next; + PyObject *kwargs; }; static PyObject *pydomain_filemaps(struct pydomain *, PyObject *); diff --git a/kodev/Makefile b/kodev/Makefile index c204d05..b21a6d6 100644 --- a/kodev/Makefile +++ b/kodev/Makefile @@ -30,6 +30,8 @@ OSNAME=$(shell uname -s | sed -e 's/[-_].*//g' | tr A-Z a-z) ifeq ("$(OSNAME)", "darwin") CFLAGS+=-I/opt/local/include/ -I/usr/local/opt/openssl/include LDFLAGS+=-L/opt/local/lib -L/usr/local/opt/openssl/lib + CFLAGS+=-I/opt/homebrew/opt/openssl/include + LDFLAGS+=-L/opt/homebrew/opt/openssl/lib else ifeq ("$(OSNAME)", "linux") CFLAGS+=-D_GNU_SOURCE=1 endif diff --git a/src/cli.c b/src/cli.c index fbd50ac..f102146 100644 --- a/src/cli.c +++ b/src/cli.c @@ -1901,6 +1901,7 @@ cli_build_flags_common(struct buildopt *bopt, struct cli_buf *buf) /* Add default openssl include path from homebrew / ports under OSX. */ cli_buf_appendf(buf, "-I/opt/local/include "); cli_buf_appendf(buf, "-I/usr/local/opt/openssl/include "); + cli_buf_appendf(buf, "-I/opt/homebrew/opt/openssl/include "); #endif if (bopt->single_binary == 0) { cli_kore_features(bopt, &data, &len); diff --git a/src/config.c b/src/config.c index f7dc090..7f3466e 100644 --- a/src/config.c +++ b/src/config.c @@ -1876,13 +1876,10 @@ configure_deployment(char *value) skip_chroot = 0; } else { kore_log(LOG_NOTICE, - "pyko.config.deployment: bad value '%s'", value); + "kore.config.deployment: bad value '%s'", value); return (KORE_RESULT_ERROR); } - if (!kore_quiet) - kore_log(LOG_NOTICE, "deployment set to %s", value); - return (KORE_RESULT_OK); } diff --git a/src/http.c b/src/http.c index 9f9b214..fd46591 100644 --- a/src/http.c +++ b/src/http.c @@ -1594,7 +1594,7 @@ http_request_new(struct connection *c, const char *host, struct kore_domain *dom; struct http_request *req; char *p, *hp; - int m, flags; + int m, flags, exists; size_t hostlen, pathlen, qsoff; if (http_request_count >= http_request_limit) { @@ -1748,7 +1748,7 @@ http_request_new(struct connection *c, const char *host, } /* Checked further down below if we need to 404. */ - req->hdlr = kore_module_handler_find(req, dom); + exists = kore_module_handler_find(req, dom, m, &req->hdlr); TAILQ_INIT(&(req->resp_headers)); TAILQ_INIT(&(req->req_headers)); @@ -1774,13 +1774,13 @@ http_request_new(struct connection *c, const char *host, return (NULL); } - if (req->hdlr == NULL) { + if (exists == 0) { http_request_free(req); http_error_response(c, HTTP_STATUS_NOT_FOUND); return (NULL); } - if (!(req->hdlr->methods & m)) { + if (req->hdlr == NULL) { http_request_free(req); http_error_response(c, HTTP_STATUS_METHOD_NOT_ALLOWED); return (NULL); diff --git a/src/kore.c b/src/kore.c index 9963140..a500fbc 100644 --- a/src/kore.c +++ b/src/kore.c @@ -909,6 +909,10 @@ kore_server_start(int argc, char *argv[]) kore_call_parent_configure(argc, argv); #endif +#if defined(KORE_USE_PYTHON) + kore_python_routes_resolve(); +#endif + /* Check if keymgr will be active. */ LIST_FOREACH(srv, &kore_servers, list) { if (srv->tls) { diff --git a/src/module.c b/src/module.c index 6da2ad4..ff57a35 100644 --- a/src/module.c +++ b/src/module.c @@ -283,23 +283,38 @@ kore_module_handler_free(struct kore_module_handle *hdlr) kore_free(hdlr); } -struct kore_module_handle * -kore_module_handler_find(struct http_request *req, struct kore_domain *dom) +int +kore_module_handler_find(struct http_request *req, struct kore_domain *dom, + int method, struct kore_module_handle **out) { struct kore_module_handle *hdlr; + int exists; + + exists = 0; + *out = NULL; TAILQ_FOREACH(hdlr, &(dom->handlers), list) { if (hdlr->type == HANDLER_TYPE_STATIC) { - if (!strcmp(hdlr->path, req->path)) - return (hdlr); + if (!strcmp(hdlr->path, req->path)) { + if (hdlr->methods & method) { + *out = hdlr; + return (1); + } + exists++; + } } else { if (!regexec(&(hdlr->rctx), req->path, - HTTP_CAPTURE_GROUPS, req->cgroups, 0)) - return (hdlr); + HTTP_CAPTURE_GROUPS, req->cgroups, 0)) { + if (hdlr->methods & method) { + *out = hdlr; + return (1); + } + exists++; + } } } - return (NULL); + return (exists); } #endif /* !KORE_NO_HTTP */ diff --git a/src/python.c b/src/python.c index 8cafd27..8ae91f0 100644 --- a/src/python.c +++ b/src/python.c @@ -77,6 +77,15 @@ static PyObject *pyhttp_request_alloc(const struct http_request *); static struct python_coro *python_coro_create(PyObject *, struct http_request *); +static struct kore_domain *python_route_domain_resolve(struct pyroute *); + +static int python_route_install(struct pyroute *); +static int python_route_params(PyObject *, + struct kore_module_handle *, const char *, int); +static int python_route_methods(PyObject *, PyObject *, + struct kore_module_handle *); +static int python_route_auth(PyObject *, + struct kore_module_handle *); static int python_coro_run(struct python_coro *); static void python_coro_wakeup(struct python_coro *); @@ -108,10 +117,6 @@ static int pyhttp_iterobj_chunk_sent(struct netbuf *); static int pyhttp_iterobj_next(struct pyhttp_iterobj *); static void pyhttp_iterobj_disconnect(struct connection *); -static int pydomain_params(PyObject *, - struct kore_module_handle *, const char *, int); -static int pydomain_auth(PyObject *, struct kore_module_handle *); - #if defined(KORE_USE_PGSQL) static int pykore_pgsql_result(struct pykore_pgsql *); static void pykore_pgsql_callback(struct kore_pgsql *, void *); @@ -259,6 +264,7 @@ static struct pyseccomp *py_seccomp = NULL; #endif static TAILQ_HEAD(, pyproc) procs; +static TAILQ_HEAD(, pyroute) routes; static struct reqcall_list prereq; static struct kore_pool coro_pool; @@ -301,6 +307,7 @@ kore_python_init(void) TAILQ_INIT(&prereq); TAILQ_INIT(&procs); + TAILQ_INIT(&routes); TAILQ_INIT(&coro_runnable); TAILQ_INIT(&coro_suspended); @@ -351,6 +358,9 @@ kore_python_init(void) kore_seccomp_filter("python", filter_python, KORE_FILTER_LEN(filter_python)); #endif + + if (!kore_configure_setting("deployment", "dev")) + fatal("failed to set initial deployment"); } void @@ -449,6 +459,19 @@ kore_python_coro_pending(void) return (!TAILQ_EMPTY(&coro_runnable)); } +void +kore_python_routes_resolve(void) +{ + struct pyroute *route; + + while ((route = TAILQ_FIRST(&routes)) != NULL) { + TAILQ_REMOVE(&routes, route, list); + if (!python_route_install(route)) + fatalx("failed to install route for %s", route->path); + Py_DECREF((PyObject *)route); + } +} + void kore_python_log_error(const char *function) { @@ -1570,6 +1593,7 @@ python_module_init(void) python_push_type("pylock", pykore, &pylock_type); python_push_type("pytimer", pykore, &pytimer_type); python_push_type("pyqueue", pykore, &pyqueue_type); + python_push_type("pyroute", pykore, &pyroute_type); python_push_type("pysocket", pykore, &pysocket_type); python_push_type("pydomain", pykore, &pydomain_type); python_push_type("pyconnection", pykore, &pyconnection_type); @@ -1756,9 +1780,6 @@ python_kore_server(PyObject *self, PyObject *args, PyObject *kwargs) struct kore_server *srv; const char *name, *ip, *port, *path; - if (!PyArg_ParseTuple(args, "s", &name)) - return (NULL); - if (kwargs == NULL) { PyErr_SetString(PyExc_RuntimeError, "missing keyword args"); return (NULL); @@ -1778,6 +1799,16 @@ python_kore_server(PyObject *self, PyObject *args, PyObject *kwargs) return (NULL); } + name = python_string_from_dict(kwargs, "name"); + if (name == NULL) + name = "default"; + + if ((srv = kore_server_lookup(name)) != NULL) { + PyErr_Format(PyExc_RuntimeError, + "server '%s' already exist", name); + return (NULL); + } + srv = kore_server_create(name); python_bool_from_dict(kwargs, "tls", &srv->tls); @@ -2011,16 +2042,11 @@ python_kore_domain(PyObject *self, PyObject *args, PyObject *kwargs) if (!PyArg_ParseTuple(args, "s", &name)) return (NULL); - if (kwargs == NULL) { - PyErr_SetString(PyExc_RuntimeError, "missing keyword args"); - return (NULL); - } + if (kwargs != NULL) + attach = python_string_from_dict(kwargs, "attach"); - if ((attach = python_string_from_dict(kwargs, "attach")) == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "missing or invalid 'attach' keyword"); - return (NULL); - } + if (attach == NULL) + attach = "default"; if ((srv = kore_server_lookup(attach)) == NULL) { PyErr_Format(PyExc_RuntimeError, @@ -2029,6 +2055,11 @@ python_kore_domain(PyObject *self, PyObject *args, PyObject *kwargs) } if (srv->tls) { + if (kwargs == NULL) { + PyErr_Format(PyExc_RuntimeError, + "no keywords for TLS enabled domain %s", name); + return (NULL); + } key = python_string_from_dict(kwargs, "key"); cert = python_string_from_dict(kwargs, "cert"); @@ -2069,6 +2100,9 @@ python_kore_domain(PyObject *self, PyObject *args, PyObject *kwargs) if ((domain = PyObject_New(struct pydomain, &pydomain_type)) == NULL) return (NULL); + domain->next = NULL; + domain->kwargs = NULL; + if ((domain->config = kore_domain_new(name)) == NULL) fatal("failed to create new domain configuration"); @@ -2096,6 +2130,35 @@ python_kore_domain(PyObject *self, PyObject *args, PyObject *kwargs) return ((PyObject *)domain); } +static PyObject * +python_kore_route(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *path; + PyObject *inner; + struct pyroute *route; + + if ((route = PyObject_New(struct pyroute, &pyroute_type)) == NULL) + return (NULL); + + if (!PyArg_ParseTuple(args, "s", &path)) + return (NULL); + + route->domain = NULL; + route->kwargs = kwargs; + route->path = kore_strdup(path); + + Py_XINCREF(route->kwargs); + + inner = PyObject_GetAttrString((PyObject *)route, "inner"); + if (inner == NULL) { + Py_DECREF((PyObject *)route); + PyErr_SetString(PyExc_RuntimeError, "failed to find inner"); + return (NULL); + } + + return (inner); +} + static PyObject * python_kore_gather(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -4931,6 +4994,36 @@ pyhttp_file_get_filename(struct pyhttp_file *pyfile, void *closure) return (name); } +void +pyroute_dealloc(struct pyroute *route) +{ + kore_free(route->path); + + Py_XDECREF(route->func); + Py_XDECREF(route->kwargs); + + PyObject_Del((PyObject *)route); +} + +static PyObject * +pyroute_inner(struct pyroute *route, PyObject *args) +{ + PyObject *obj; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return (NULL); + + if (!PyCallable_Check(obj)) + return (NULL); + + route->func = obj; + Py_INCREF(route->func); + + TAILQ_INSERT_TAIL(&routes, route, list); + + return (route->func); +} + void pydomain_dealloc(struct pydomain *domain) { @@ -5004,114 +5097,179 @@ pydomain_filemaps(struct pydomain *domain, PyObject *args) static PyObject * pydomain_route(struct pydomain *domain, PyObject *args, PyObject *kwargs) { - struct kore_module_handle *hdlr; - int method; - const char *path, *val; - Py_ssize_t list_len, idx; - PyObject *callable, *repr, *obj, *item; + PyObject *obj; + const char *path; + struct pyroute *route; - if (!PyArg_ParseTuple(args, "sO", &path, &callable)) + if (!PyArg_ParseTuple(args, "sO", &path, &obj)) return (NULL); - if (!PyCallable_Check(callable)) + if (!PyCallable_Check(obj)) return (NULL); - TAILQ_FOREACH(hdlr, &domain->config->handlers, list) { - if (!strcmp(hdlr->path, path)) { - PyErr_Format(PyExc_RuntimeError, - "route '%s' exists", path); - return (NULL); - } - } - - if ((repr = PyObject_Repr(callable)) == NULL) + if ((route = PyObject_New(struct pyroute, &pyroute_type)) == NULL) return (NULL); - val = PyUnicode_AsUTF8(repr); + route->kwargs = kwargs; + route->domain = domain->config; + route->path = kore_strdup(path); - hdlr = kore_calloc(1, sizeof(*hdlr)); - hdlr->dom = domain->config; - hdlr->func = kore_strdup(val); - hdlr->path = kore_strdup(path); - hdlr->methods = HTTP_METHOD_ALL; - TAILQ_INIT(&hdlr->params); + Py_XINCREF(route->kwargs); - Py_DECREF(repr); + route->func = obj; + Py_INCREF(route->func); - hdlr->rcall = kore_calloc(1, sizeof(struct kore_runtime_call)); - hdlr->rcall->addr = callable; - hdlr->rcall->runtime = &kore_python_runtime; - - if (kwargs != NULL) { - if ((obj = PyDict_GetItemString(kwargs, "methods")) != NULL) { - if (!PyList_CheckExact(obj)) { - kore_module_handler_free(hdlr); - return (NULL); - } - - hdlr->methods = 0; - list_len = PyList_Size(obj); - - for (idx = 0; idx < list_len; idx++) { - if ((item = PyList_GetItem(obj, idx)) == NULL) { - kore_module_handler_free(hdlr); - return (NULL); - } - - if ((val = PyUnicode_AsUTF8(item)) == NULL) { - kore_module_handler_free(hdlr); - return (NULL); - } - - method = http_method_value(val); - if (method == 0) { - PyErr_Format(PyExc_RuntimeError, - "unknown method '%s'", val); - kore_module_handler_free(hdlr); - return (NULL); - } - - hdlr->methods |= method; - if (method == HTTP_METHOD_GET) - hdlr->methods |= HTTP_METHOD_HEAD; - - if (!pydomain_params(kwargs, - hdlr, val, method)) { - kore_module_handler_free(hdlr); - return (NULL); - } - } - } - - if ((obj = PyDict_GetItemString(kwargs, "auth")) != NULL) { - if (!pydomain_auth(obj, hdlr)) { - kore_module_handler_free(hdlr); - return (NULL); - } - } - } - - if (path[0] == '/') { - hdlr->type = HANDLER_TYPE_STATIC; - } else { - hdlr->type = HANDLER_TYPE_DYNAMIC; - - if (regcomp(&hdlr->rctx, hdlr->path, REG_EXTENDED)) { - PyErr_SetString(PyExc_RuntimeError, - "failed to compile regex for path"); - kore_module_handler_free(hdlr); - return (NULL); - } - } - - Py_INCREF(callable); - TAILQ_INSERT_TAIL(&domain->config->handlers, hdlr, list); + TAILQ_INSERT_TAIL(&routes, route, list); Py_RETURN_NONE; } static int -pydomain_params(PyObject *kwargs, struct kore_module_handle *hdlr, +python_route_install(struct pyroute *route) +{ + const char *val; + struct kore_domain *domain; + struct kore_module_handle *hdlr, *entry; + PyObject *kwargs, *repr, *obj; + + if ((repr = PyObject_Repr(route->func)) == NULL) { + kore_python_log_error("python_route_install"); + return (KORE_RESULT_ERROR); + } + + domain = python_route_domain_resolve(route); + + hdlr = kore_calloc(1, sizeof(*hdlr)); + hdlr->dom = domain; + hdlr->methods = HTTP_METHOD_ALL; + hdlr->path = kore_strdup(route->path); + + TAILQ_INIT(&hdlr->params); + + val = PyUnicode_AsUTF8(repr); + hdlr->func = kore_strdup(val); + + kwargs = route->kwargs; + + hdlr->rcall = kore_calloc(1, sizeof(struct kore_runtime_call)); + hdlr->rcall->addr = route->func; + hdlr->rcall->runtime = &kore_python_runtime; + Py_INCREF(hdlr->rcall->addr); + + if (kwargs != NULL) { + if ((obj = PyDict_GetItemString(kwargs, "methods")) != NULL) { + if (!python_route_methods(obj, kwargs, hdlr)) { + kore_python_log_error("python_route_install"); + kore_module_handler_free(hdlr); + return (KORE_RESULT_ERROR); + } + } + + if ((obj = PyDict_GetItemString(kwargs, "auth")) != NULL) { + if (!python_route_auth(obj, hdlr)) { + kore_python_log_error("python_route_install"); + kore_module_handler_free(hdlr); + return (KORE_RESULT_ERROR); + } + } + } + + if (hdlr->path[0] == '/') { + hdlr->type = HANDLER_TYPE_STATIC; + } else { + hdlr->type = HANDLER_TYPE_DYNAMIC; + if (regcomp(&hdlr->rctx, hdlr->path, REG_EXTENDED)) + fatal("failed to compile regex for '%s'", hdlr->path); + } + + TAILQ_FOREACH(entry, &domain->handlers, list) { + if (!strcmp(entry->path, hdlr->path) && + (entry->methods & hdlr->methods)) + fatal("duplicate route for '%s'", route->path); + } + + TAILQ_INSERT_TAIL(&domain->handlers, hdlr, list); + + return (KORE_RESULT_OK); +} + +static struct kore_domain * +python_route_domain_resolve(struct pyroute *route) +{ + struct kore_server *srv; + const char *name; + struct kore_domain *domain; + + if (route->domain != NULL) + return (route->domain); + + if (route->kwargs != NULL) + name = python_string_from_dict(route->kwargs, "domain"); + else + name = NULL; + + if (name != NULL) { + domain = NULL; + LIST_FOREACH(srv, &kore_servers, list) { + TAILQ_FOREACH(domain, &srv->domains, list) { + if (!strcmp(domain->domain, name)) + break; + } + } + + if (domain == NULL) + fatal("domain '%s' does not exist", name); + } else { + if ((domain = kore_domain_byid(1)) != NULL) + fatal("ambiguous domain on route, please specify one"); + if ((domain = kore_domain_byid(0)) == NULL) + fatal("no domains configured, please configure one"); + } + + return (domain); +} + +static int +python_route_methods(PyObject *obj, PyObject *kwargs, + struct kore_module_handle *hdlr) +{ + const char *val; + PyObject *item; + int method; + Py_ssize_t list_len, idx; + + if (!PyList_CheckExact(obj)) + return (KORE_RESULT_ERROR); + + hdlr->methods = 0; + list_len = PyList_Size(obj); + + for (idx = 0; idx < list_len; idx++) { + if ((item = PyList_GetItem(obj, idx)) == NULL) + return (KORE_RESULT_ERROR); + + if ((val = PyUnicode_AsUTF8(item)) == NULL) + return (KORE_RESULT_ERROR); + + if ((method = http_method_value(val)) == 0) { + PyErr_Format(PyExc_RuntimeError, + "unknown HTTP method: %s", val); + return (KORE_RESULT_ERROR); + } + + hdlr->methods |= method; + if (method == HTTP_METHOD_GET) + hdlr->methods |= HTTP_METHOD_HEAD; + + if (!python_route_params(kwargs, hdlr, val, method)) + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + +static int +python_route_params(PyObject *kwargs, struct kore_module_handle *hdlr, const char *method, int type) { Py_ssize_t idx; @@ -5182,7 +5340,7 @@ pydomain_params(PyObject *kwargs, struct kore_module_handle *hdlr, } static int -pydomain_auth(PyObject *dict, struct kore_module_handle *hdlr) +python_route_auth(PyObject *dict, struct kore_module_handle *hdlr) { int type; struct kore_auth *auth;