kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit e98a4ddab58d4e8dce21a4dc512f05cc278685d8
parent 2576427dc07321bd80c9ccbf18d5f5bb3221f942
Author: Joris Vink <joris@coders.se>
Date:   Wed, 15 Sep 2021 11:09:52 +0200

Change how routes are configured in Kore.

Routes are now configured in a context per route:

route /path {
	handler handler_name
	methods get post head
	validate qs:get id v_id
}

All route related configurations are per-route, allowing multiple
routes for the same path (for different methods).

The param context is removed and merged into the route context now
so that you use the validate keyword to specify what needs validating.

Diffstat:
Makefile | 2+-
examples/async-curl/conf/async-curl.conf | 11+++++++----
examples/cookies/conf/cookies.conf | 16+++++++++++-----
examples/cpp/conf/cpp.conf | 6++++--
examples/cpp/dh2048.pem | 9---------
examples/generic/conf/generic.conf | 2--
examples/integers/README.md | 6+++---
examples/integers/conf/integers.conf | 12++++++------
examples/json/conf/json.conf | 8++++----
examples/json_yajl/.gitignore | 5-----
examples/json_yajl/README.md | 21---------------------
examples/json_yajl/conf/build.conf | 19-------------------
examples/json_yajl/conf/json_yajl.conf | 18------------------
examples/json_yajl/src/json_yajl.c | 98-------------------------------------------------------------------------------
examples/jsonrpc/conf/jsonrpc.conf | 11+++++++----
examples/memtag/conf/memtag.conf | 6+++---
examples/messaging/conf/messaging.conf | 12++++++++----
examples/nohttp/conf/nohttp.conf | 2--
examples/parameters/conf/parameters.conf | 15++++-----------
examples/pgsql-sync/conf/pgsql-sync.conf | 8+++++---
examples/pgsql/conf/pgsql.conf | 11+++++++----
examples/pipe_task/conf/pipe_task.conf | 12++++++++----
examples/pipe_task/src/pipe_task.c | 14++++----------
examples/sse/conf/sse.conf | 15+++++++++------
examples/tasks/conf/tasks.conf | 16+++++++---------
examples/tls-proxy/conf/tls-proxy.conf | 1-
examples/upload/conf/upload.conf | 6+++---
examples/video_stream/conf/video_stream.conf | 11+++++++----
examples/websocket/conf/websocket.conf | 11+++++++----
include/kore/http.h | 2+-
include/kore/kore.h | 71++++++++++++++++++++++++++++++++++++++---------------------------------
src/accesslog.c | 2+-
src/cli.c | 5++++-
src/config.c | 503++++++++++++++++++++++++++++++++++++++++---------------------------------------
src/domain.c | 25+++++++++++--------------
src/filemap.c | 19+++++--------------
src/http.c | 18+++++++++---------
src/module.c | 128+------------------------------------------------------------------------------
src/python.c | 87++++++++++++++++++++++++++++++++++++++-----------------------------------------
src/route.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/worker.c | 19++++++++++---------
41 files changed, 631 insertions(+), 770 deletions(-)

diff --git a/Makefile b/Makefile @@ -66,7 +66,7 @@ ifneq ("$(NOHTTP)", "") FEATURES+=-DKORE_NO_HTTP else S_SRC+= src/auth.c src/accesslog.c src/http.c \ - src/validator.c src/websocket.c + src/route.c src/validator.c src/websocket.c endif ifneq ("$(PGSQL)", "") diff --git a/examples/async-curl/conf/async-curl.conf b/examples/async-curl/conf/async-curl.conf @@ -5,8 +5,6 @@ server tls { } workers 1 -tls_dhparam dh2048.pem - pledge dns domain * { @@ -15,6 +13,11 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / http - route /ftp ftp + route / { + handler http + } + + route /ftp { + handler ftp + } } diff --git a/examples/cookies/conf/cookies.conf b/examples/cookies/conf/cookies.conf @@ -6,15 +6,21 @@ server tls { load ./cookies.so -tls_dhparam dh2048.pem - domain * { attach tls certfile cert/server.pem certkey cert/key.pem - route / serve_cookies - route /secure serve_cookies - route /vault serve_cookies + route / { + handler serve_cookies + } + + route /secure { + handler serve_cookies + } + + route /vault { + handler serve_cookies + } } diff --git a/examples/cpp/conf/cpp.conf b/examples/cpp/conf/cpp.conf @@ -5,12 +5,14 @@ server tls { } load ./cpp.so -tls_dhparam dh2048.pem domain * { attach tls certfile cert/server.pem certkey cert/key.pem - route / page + + route / { + handler page + } } diff --git a/examples/cpp/dh2048.pem b/examples/cpp/dh2048.pem @@ -1,8 +0,0 @@ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEAn4f4Qn5SudFjEYPWTbUaOTLUH85YWmmPFW1+b5bRa9ygr+1wfamv -VKVT7jO8c4msSNikUf6eEfoH0H4VTCaj+Habwu+Sj+I416r3mliMD4SjNsUJrBrY -Y0QV3ZUgZz4A8ARk/WwQcRl8+ZXJz34IaLwAcpyNhoV46iHVxW0ty8ND0U4DIku/ -PNayKimu4BXWXk4RfwNVP59t8DQKqjshZ4fDnbotskmSZ+e+FHrd+Kvrq/WButvV -Bzy9fYgnUlJ82g/bziCI83R2xAdtH014fR63MpElkqdNeChb94pPbEdFlNUvYIBN -xx2vTUQMqRbB4UdG2zuzzr5j98HDdblQ+wIBAg== ------END DH PARAMETERS------ \ No newline at end of file diff --git a/examples/generic/conf/generic.conf b/examples/generic/conf/generic.conf @@ -6,8 +6,6 @@ server tls { load ./generic.so example_load -tls_dhparam dh2048.pem - http_body_max 1024000000 http_body_disk_offload 1024000 diff --git a/examples/integers/README.md b/examples/integers/README.md @@ -2,13 +2,13 @@ Test parameter to integer conversions. Run: ``` - # kodev run + $ kodev run ``` Test: ``` - # curl -i -k https://127.0.0.1:8888/?id=123123 - # curl -i -k https://127.0.0.1:8888/?id=-123123 + $ curl -i -k https://127.0.0.1:8888/?id=123123 + $ curl -i -k https://127.0.0.1:8888/?id=-123123 ``` The correct integer types should only be represented in the output. diff --git a/examples/integers/conf/integers.conf b/examples/integers/conf/integers.conf @@ -9,8 +9,6 @@ load ./integers.so workers 2 worker_max_connections 5000 -tls_dhparam dh2048.pem - validator v_id regex ^-?[0-9]*.?[0-9]+$ domain * { @@ -18,10 +16,12 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / page - # allowed parameters in the query string for GETs - params qs:get / { - validate id v_id + route / { + handler page + methods get + + # allowed parameters in the query string for GETs + validate get id v_id } } diff --git a/examples/json/conf/json.conf b/examples/json/conf/json.conf @@ -6,14 +6,14 @@ server tls { load ./json.so -tls_dhparam dh2048.pem - domain 127.0.0.1 { attach tls certfile cert/server.pem certkey cert/key.pem - route / page - restrict / post + route / { + handler page + methods post + } } diff --git a/examples/json_yajl/.gitignore b/examples/json_yajl/.gitignore @@ -1,5 +0,0 @@ -*.o -.objs -json_yajl.so -assets.h -cert diff --git a/examples/json_yajl/README.md b/examples/json_yajl/README.md @@ -1,21 +0,0 @@ -This example demonstrates how you can use external libs in your application. - -In this case we link against yajl (Yet Another JSON library) in order to -parse a JSON string that was POSTed to the server. - -Take a peek at conf/build.conf for different build flavors and how to -link to other libraries. - -Run: -``` - $ kodev run -``` - -Test: -``` - $ curl -i -k -d '{"foo":{"bar": "Hello world"}}' https://127.0.0.1:8888 -``` - -The result should echo back the foo.bar JSON path value: Hello world. - -The yajl repo is available @ https://github.com/lloyd/yajl diff --git a/examples/json_yajl/conf/build.conf b/examples/json_yajl/conf/build.conf @@ -1,19 +0,0 @@ -# json_yajl build config -# You can switch flavors using: kodev flavor [newflavor] - -# The cflags below are shared between flavors -cflags=-Wall -Wmissing-declarations -Wshadow -cflags=-Wstrict-prototypes -Wmissing-prototypes -cflags=-Wpointer-arith -Wcast-qual -Wsign-compare - -dev { - # These cflags are added to the shared ones when - # you build the "dev" flavor. - cflags=-g - ldflags=-lyajl -} - -#prod { -# You can specify additional CFLAGS here which are only -# included if you build with the "prod" flavor. -#} diff --git a/examples/json_yajl/conf/json_yajl.conf b/examples/json_yajl/conf/json_yajl.conf @@ -1,18 +0,0 @@ -# Placeholder configuration - -server tls { - bind 127.0.0.1 8888 -} - -load ./json_yajl.so - -tls_dhparam dh2048.pem - -domain 127.0.0.1 { - attach tls - - certfile cert/server.pem - certkey cert/key.pem - - route / page -} diff --git a/examples/json_yajl/src/json_yajl.c b/examples/json_yajl/src/json_yajl.c @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2013-2018 Joris Vink <joris@coders.se> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <kore/kore.h> -#include <kore/http.h> - -#include <yajl/yajl_tree.h> - -int page(struct http_request *); - -int -page(struct http_request *req) -{ - ssize_t ret; - struct kore_buf *buf; - char *body; - yajl_val node, v; - char eb[1024]; - u_int8_t data[BUFSIZ]; - const char *path[] = { "foo", "bar", NULL }; - - /* We only allow POST/PUT methods. */ - if (req->method != HTTP_METHOD_POST && - req->method != HTTP_METHOD_PUT) { - http_response_header(req, "allow", "POST, PUT"); - http_response(req, HTTP_STATUS_METHOD_NOT_ALLOWED, NULL, 0); - return (KORE_RESULT_OK); - } - - /* - * Read the entire received body into a memory buffer. - */ - buf = kore_buf_alloc(128); - for (;;) { - ret = http_body_read(req, data, sizeof(data)); - if (ret == -1) { - kore_buf_free(buf); - kore_log(LOG_NOTICE, "error reading body"); - http_response(req, 500, NULL, 0); - return (KORE_RESULT_OK); - } - - if (ret == 0) - break; - - kore_buf_append(buf, data, ret); - } - - /* Grab our body data as a NUL-terminated string. */ - body = kore_buf_stringify(buf, NULL); - - /* Parse the body via yajl now. */ - node = yajl_tree_parse(body, eb, sizeof(eb)); - if (node == NULL) { - if (strlen(eb)) { - kore_log(LOG_NOTICE, "parse error: %s", eb); - } else { - kore_log(LOG_NOTICE, "parse error: unknown"); - } - - kore_buf_free(buf); - http_response(req, 400, NULL, 0); - return (KORE_RESULT_OK); - } - - /* Reuse old buffer, don't need it anymore for body. */ - kore_buf_reset(buf); - - /* Attempt to grab foo.bar from the JSON tree. */ - v = yajl_tree_get(node, path, yajl_t_string); - if (v == NULL) { - kore_buf_appendf(buf, "no such path: foo.bar\n"); - } else { - kore_buf_appendf(buf, "foo.bar = '%s'\n", YAJL_GET_STRING(v)); - } - - /* Release the JSON tree now. */ - yajl_tree_free(node); - - /* Respond to the client. */ - http_response(req, 200, buf->data, buf->offset); - kore_buf_free(buf); - - return (KORE_RESULT_OK); -} diff --git a/examples/jsonrpc/conf/jsonrpc.conf b/examples/jsonrpc/conf/jsonrpc.conf @@ -6,14 +6,17 @@ server tls { load ./jsonrpc.so -tls_dhparam dh2048.pem - domain * { attach tls certfile cert/server.pem certkey cert/key.pem - route / homepage - route /v1 v1 + route / { + handler homepage + } + + route /v1 { + handler v1 + } } diff --git a/examples/memtag/conf/memtag.conf b/examples/memtag/conf/memtag.conf @@ -6,13 +6,13 @@ server tls { load ./memtag.so init -tls_dhparam dh2048.pem - domain * { attach tls certfile cert/server.pem certkey cert/key.pem - route / page + route / { + handler page + } } diff --git a/examples/messaging/conf/messaging.conf b/examples/messaging/conf/messaging.conf @@ -5,14 +5,18 @@ server tls { } load ./messaging.so init -tls_dhparam dh2048.pem -workers 4 domain * { attach tls certfile cert/server.pem certkey cert/key.pem - route / page - route /shutdown page_shutdown + + route / { + handler page + } + + route /shutdown { + handler page_shutdown + } } diff --git a/examples/nohttp/conf/nohttp.conf b/examples/nohttp/conf/nohttp.conf @@ -4,8 +4,6 @@ server tls { bind 127.0.0.1 8888 connection_setup } -tls_dhparam dh2048.pem - domain * { attach tls diff --git a/examples/parameters/conf/parameters.conf b/examples/parameters/conf/parameters.conf @@ -6,8 +6,6 @@ server tls { load ./parameters.so -tls_dhparam dh2048.pem - # The validator used to validate the 'id' parameter # defined below. We'll use a simple regex to make sure # it only matches positive numbers. @@ -19,15 +17,10 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / page + route / { + handler page + methods get - # The parameters allowed for "/" (GET method). - # - # If you would want to declare parameters available - # to the page handler for POST, swap the 'get' setting - # to 'post' instead, Kore takes care of the rest. - params qs:get / { - # Validate the id parameter with the v_id validator. - validate id v_id + validate qs:get id v_id } } diff --git a/examples/pgsql-sync/conf/pgsql-sync.conf b/examples/pgsql-sync/conf/pgsql-sync.conf @@ -4,13 +4,15 @@ server tls { bind 127.0.0.1 8888 } -load ./pgsql-sync.so init -tls_dhparam dh2048.pem +load ./pgsql-sync.so init domain * { attach tls certfile cert/server.pem certkey cert/key.pem - route / page + + route / { + handler page + } } diff --git a/examples/pgsql/conf/pgsql.conf b/examples/pgsql/conf/pgsql.conf @@ -10,8 +10,6 @@ server other { bind 127.0.0.1 8889 connection_new } -tls_dhparam dh2048.pem - http_keepalive_time 0 domain * { @@ -20,6 +18,11 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / page - route /hello hello + route / { + handler page + } + + route /hello { + handler hello + } } diff --git a/examples/pipe_task/conf/pipe_task.conf b/examples/pipe_task/conf/pipe_task.conf @@ -4,8 +4,6 @@ server tls { bind 127.0.0.1 8888 } -tls_dhparam dh2048.pem - websocket_maxframe 65536 websocket_timeout 10000 @@ -15,6 +13,12 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / page - route /connect page_ws_connect + route / { + handler page + } + + route /connect { + handler page_ws_connect + methods get + } } diff --git a/examples/pipe_task/src/pipe_task.c b/examples/pipe_task/src/pipe_task.c @@ -28,6 +28,7 @@ #include <kore/kore.h> #include <kore/http.h> #include <kore/tasks.h> +#include <kore/hooks.h> #include <fcntl.h> #include <unistd.h> @@ -49,17 +50,12 @@ void pipe_data_available(struct kore_task *); /* Our pipe reader. */ struct kore_task pipe_task; -/* Module init function (see config). */ -int -init(int state) +void +kore_worker_configure(void) { - /* Do not allow reload. */ - if (state == KORE_MODULE_UNLOAD) - return (KORE_RESULT_ERROR); - /* Only do this on a dedicated worker. */ if (worker->id != 1) - return (KORE_RESULT_OK); + return; /* Create our task. */ kore_task_create(&pipe_task, pipe_reader); @@ -69,8 +65,6 @@ init(int state) /* Start the task. */ kore_task_run(&pipe_task); - - return (KORE_RESULT_OK); } /* Called whenever we get a new websocket connection. */ diff --git a/examples/sse/conf/sse.conf b/examples/sse/conf/sse.conf @@ -4,10 +4,8 @@ server tls { bind 127.0.0.1 8888 } -load ./sse.so -tls_dhparam dh2048.pem - -http_keepalive_time 600 +load ./sse.so +http_keepalive_time 600 domain * { attach tls @@ -15,6 +13,11 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / page - route /subscribe subscribe + route / { + handler page + } + + route /subscribe { + handler subscribe + } } diff --git a/examples/tasks/conf/tasks.conf b/examples/tasks/conf/tasks.conf @@ -4,8 +4,6 @@ server tls { bind 127.0.0.1 8888 } -tls_dhparam dh2048.pem - task_threads 4 worker_max_connections 1000 http_keepalive_time 0 @@ -19,14 +17,14 @@ domain * { certkey cert/key.pem accesslog kore_access.log - route / page_handler - route /post_back post_back - - params qs:get / { - validate user v_user + route / { + handler page_handler + validate qs:get user v_user } - params post /post_back { - validate user v_user + route /post_back { + handler post_back + methods post + validate post user v_user } } diff --git a/examples/tls-proxy/conf/tls-proxy.conf b/examples/tls-proxy/conf/tls-proxy.conf @@ -1,7 +1,6 @@ # Kore as a TLS proxy configuration. load ./tls-proxy.so -tls_dhparam dh2048.pem # # Bind the proxy to a given IP and port. For every diff --git a/examples/upload/conf/upload.conf b/examples/upload/conf/upload.conf @@ -6,8 +6,6 @@ server tls { load ./upload.so -tls_dhparam dh2048.pem - http_body_max 1024000000 http_body_disk_offload 4096 @@ -17,5 +15,7 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / page + route / { + handler page + } } diff --git a/examples/video_stream/conf/video_stream.conf b/examples/video_stream/conf/video_stream.conf @@ -6,8 +6,6 @@ server tls { load ./video_stream.so init -tls_dhparam dh2048.pem - http_keepalive_time 600 domain * { @@ -17,6 +15,11 @@ domain * { certkey cert/key.pem accesslog access.log - route / asset_serve_video_html - route ^/[a-z]*.[a-z0-9]{3}$ video_stream + route / { + handler asset_serve_video_html + } + + route ^/[a-z]*.[a-z0-9]{3}$ { + handler video_stream + } } diff --git a/examples/websocket/conf/websocket.conf b/examples/websocket/conf/websocket.conf @@ -6,8 +6,6 @@ server tls { load ./websocket.so -tls_dhparam dh2048.pem - # Increase workers so connections are spread # across them to demonstrate WEBSOCKET_BROADCAST_GLOBAL. workers 4 @@ -21,6 +19,11 @@ domain * { certfile cert/server.pem certkey cert/key.pem - route / page - route /connect page_ws_connect + route / { + handler page + } + + route /connect { + handler page_ws_connect + } } diff --git a/include/kore/http.h b/include/kore/http.h @@ -268,7 +268,7 @@ struct http_request { void *hdlr_extra; size_t state_len; char *query_string; - struct kore_module_handle *hdlr; + struct kore_route *rt; struct http_runlock_queue *runlock; void (*onfree)(struct http_request *); diff --git a/include/kore/kore.h b/include/kore/kore.h @@ -304,6 +304,33 @@ struct kore_runtime_call { struct kore_runtime *runtime; }; +#if !defined(KORE_NO_HTTP) + +struct kore_route_params { + char *name; + int flags; + u_int8_t method; + struct kore_validator *validator; + + TAILQ_ENTRY(kore_route_params) list; +}; + +struct kore_route { + char *path; + char *func; + int type; + int errors; + regex_t rctx; + struct kore_domain *dom; + struct kore_runtime_call *rcall; + struct kore_auth *auth; + int methods; + TAILQ_HEAD(, kore_route_params) params; + TAILQ_ENTRY(kore_route) list; +}; + +#endif + struct kore_domain { u_int16_t id; int logerr; @@ -327,7 +354,7 @@ struct kore_domain { SSL_CTX *ssl_ctx; int x509_verify_depth; #if !defined(KORE_NO_HTTP) - TAILQ_HEAD(, kore_module_handle) handlers; + TAILQ_HEAD(, kore_route) routes; TAILQ_HEAD(, http_redirect) redirects; #endif TAILQ_ENTRY(kore_domain) list; @@ -363,15 +390,6 @@ LIST_HEAD(kore_server_list, kore_server); #define KORE_PARAMS_QUERY_STRING 0x0001 -struct kore_handler_params { - char *name; - int flags; - u_int8_t method; - struct kore_validator *validator; - - TAILQ_ENTRY(kore_handler_params) list; -}; - #define KORE_AUTH_TYPE_COOKIE 1 #define KORE_AUTH_TYPE_HEADER 2 #define KORE_AUTH_TYPE_REQUEST 3 @@ -420,23 +438,6 @@ struct kore_module { TAILQ_ENTRY(kore_module) list; }; -#if !defined(KORE_NO_HTTP) - -struct kore_module_handle { - char *path; - char *func; - int type; - int errors; - regex_t rctx; - struct kore_domain *dom; - struct kore_runtime_call *rcall; - struct kore_auth *auth; - int methods; - TAILQ_HEAD(, kore_handler_params) params; - TAILQ_ENTRY(kore_module_handle) list; -}; -#endif - /* * The workers get a 128KB log buffer per worker, and parent will fetch their * logs when it reached at least 75% of that or if its been > 1 second since @@ -471,7 +472,7 @@ struct kore_worker { u_int8_t has_lock; int restarted; u_int64_t time_locked; - struct kore_module_handle *active_hdlr; + struct kore_route *active_route; struct kore_privsep *ps; /* Used by the workers to store accesslogs. */ @@ -959,12 +960,16 @@ int kore_domain_attach(struct kore_domain *, struct kore_server *); void kore_domain_tlsinit(struct kore_domain *, int, const void *, size_t); void kore_domain_crl_add(struct kore_domain *, const void *, size_t); + #if !defined(KORE_NO_HTTP) -int kore_module_handler_new(struct kore_domain *, const char *, - const char *, const char *, int); -void kore_module_handler_free(struct kore_module_handle *); -int kore_module_handler_find(struct http_request *, - struct kore_domain *, int, struct kore_module_handle **); +void kore_route_reload(void); +void kore_route_free(struct kore_route *); +void kore_route_callback(struct kore_route *, const char *); + +struct kore_route *kore_route_create(struct kore_domain *, + const char *, int); +int kore_route_lookup(struct http_request *, + struct kore_domain *, int, struct kore_route **); #endif struct kore_runtime_call *kore_runtime_getcall(const char *); diff --git a/src/accesslog.c b/src/accesslog.c @@ -195,7 +195,7 @@ kore_accesslog(struct http_request *req) } hdr->loglen = len; - hdr->domain = req->hdlr->dom->id; + hdr->domain = req->rt->dom->id; worker->lb.offset += (size_t)len; break; diff --git a/src/cli.c b/src/cli.c @@ -293,7 +293,10 @@ static const char *config_data = "\tcertfile\tcert/server.pem\n" "\tcertkey\t\tcert/key.pem\n" "\n" - "\troute\t/\tpage\n" + "\troute / {\n" + "\t\thandler page\n" + "\t}\n" + "\n" "}\n"; static const char *build_data = diff --git a/src/config.c b/src/config.c @@ -109,12 +109,13 @@ static int configure_client_verify_depth(char *); #if !defined(KORE_NO_HTTP) static int configure_route(char *); +static int configure_route_handler(char *); +static int configure_route_methods(char *); static int configure_filemap(char *); static int configure_return(char *); static int configure_redirect(char *); static int configure_static_handler(char *); static int configure_dynamic_handler(char *); -static int configure_restrict(char *); static int configure_accesslog(char *); static int configure_http_header_max(char *); static int configure_http_header_timeout(char *); @@ -132,7 +133,6 @@ static int configure_http_body_disk_path(char *); static int configure_http_server_version(char *); static int configure_http_pretty_error(char *); static int configure_validator(char *); -static int configure_params(char *); static int configure_validate(char *); static int configure_authentication(char *); static int configure_authentication_uri(char *); @@ -189,16 +189,16 @@ static struct { { "client_verify", configure_client_verify }, { "client_verify_depth", configure_client_verify_depth }, #if !defined(KORE_NO_HTTP) - { "route", configure_route}, + { "route", configure_route }, + { "handler", configure_route_handler }, + { "methods", configure_route_methods }, { "filemap", configure_filemap }, { "redirect", configure_redirect }, { "return", configure_return }, { "static", configure_static_handler }, { "dynamic", configure_dynamic_handler }, { "accesslog", configure_accesslog }, - { "restrict", configure_restrict }, { "validator", configure_validator }, - { "params", configure_params }, { "validate", configure_validate }, { "authentication", configure_authentication }, { "authentication_uri", configure_authentication_uri }, @@ -282,10 +282,8 @@ char *config_file = NULL; #endif #if !defined(KORE_NO_HTTP) -static u_int8_t current_method = 0; -static int current_flags = 0; static struct kore_auth *current_auth = NULL; -static struct kore_module_handle *current_handler = NULL; +static struct kore_route *current_route = NULL; #endif extern const char *__progname; @@ -341,10 +339,8 @@ kore_parse_config(void) fatal("getcwd: %s", errno_s); worker_privsep.root = kore_strdup(path); - if (!kore_quiet) { - kore_log(LOG_NOTICE, "privsep: no root path set, " - "using working directory"); - } + if (!kore_quiet) + kore_log(LOG_NOTICE, "privsep: no root path set"); } if (worker_privsep.runas == NULL) { @@ -352,26 +348,24 @@ kore_parse_config(void) fatal("getpwuid: %s", errno_s); worker_privsep.runas = kore_strdup(pwd->pw_name); - if (!kore_quiet) { - kore_log(LOG_NOTICE, "privsep: no runas user set, " - "using current user %s", worker_privsep.runas); - } + if (!kore_quiet) + kore_log(LOG_NOTICE, "privsep: no runas user set"); endpwent(); } configure_check_var(&keymgr_privsep.runas, worker_privsep.runas, - "privsep: no keymgr runas set, using 'privsep.worker.runas`"); + "privsep: no keymgr runas set"); #if defined(KORE_USE_ACME) configure_check_var(&acme_privsep.runas, worker_privsep.runas, - "privsep: no acme runas set, using 'privsep.worker.runas`"); + "privsep: no acme runas set"); #endif configure_check_var(&keymgr_privsep.root, worker_privsep.root, - "privsep: no keymgr root set, using 'privsep.worker.root`"); + "privsep: no keymgr root set"); #if defined(KORE_USE_ACME) configure_check_var(&acme_privsep.root, worker_privsep.root, - "privsep: no acme root set, using 'privsep.worker.root`"); + "privsep: no acme root set"); #endif if (skip_chroot) { @@ -426,11 +420,9 @@ kore_parse_config_file(FILE *fp) } #if !defined(KORE_NO_HTTP) - if (!strcmp(p, "}") && current_handler != NULL) { + if (!strcmp(p, "}") && current_route != NULL) { lineno++; - current_flags = 0; - current_method = 0; - current_handler = NULL; + current_route = NULL; continue; } @@ -476,7 +468,8 @@ kore_parse_config_file(FILE *fp) } if ((t = strchr(p, ' ')) == NULL) { - printf("ignoring \"%s\" on line %d\n", p, lineno++); + kore_log(LOG_NOTICE, + "ignoring \"%s\" on line %d", p, lineno++); continue; } @@ -486,7 +479,8 @@ kore_parse_config_file(FILE *fp) t = kore_text_trim(t, strlen(t)); if (strlen(p) == 0 || strlen(t) == 0) { - printf("ignoring \"%s\" on line %d\n", p, lineno++); + kore_log(LOG_NOTICE, + "ignoring \"%s\" on line %d", p, lineno++); continue; } @@ -517,8 +511,10 @@ kore_parse_config_file(FILE *fp) } } - if (config_settings[i].name == NULL) - printf("ignoring \"%s\" on line %d\n", p, lineno); + if (config_settings[i].name == NULL) { + kore_log(LOG_NOTICE, + "ignoring \"%s\" on line %d", p, lineno); + } lineno++; } @@ -592,24 +588,24 @@ configure_server(char *options) char *argv[3]; if (current_server != NULL) { - printf("nested server contexts are not allowed\n"); + kore_log(LOG_ERR, "nested server contexts are not allowed"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 3); if (argv[0] == NULL || argv[1] == NULL) { - printf("invalid server context\n"); + kore_log(LOG_ERR, "server context invalid"); return (KORE_RESULT_ERROR); } if (strcmp(argv[1], "{")) { - printf("server context not opened correctly\n"); + kore_log(LOG_ERR, "server context not opened correctly"); return (KORE_RESULT_ERROR); } if ((srv = kore_server_lookup(argv[0])) != NULL) { - printf("a server with name '%s' already exists\n", srv->name); + kore_log(LOG_ERR, "server with name '%s' exists", srv->name); return (KORE_RESULT_ERROR); } @@ -622,7 +618,7 @@ static int configure_tls(char *yesno) { if (current_server == NULL) { - printf("bind directive not inside a server context\n"); + kore_log(LOG_ERR, "bind keyword not inside a server context"); return (KORE_RESULT_ERROR); } @@ -631,7 +627,7 @@ configure_tls(char *yesno) } else if (!strcmp(yesno, "yes")) { current_server->tls = 1; } else { - printf("invalid '%s' for yes|no tls option\n", yesno); + kore_log(LOG_ERR, "invalid '%s' for yes|no tls option", yesno); return (KORE_RESULT_ERROR); } @@ -643,12 +639,13 @@ static int configure_acme(char *yesno) { if (current_domain == NULL) { - printf("acme directive not inside a domain context\n"); + kore_log(LOG_ERR, "acme keyword not inside a domain context"); return (KORE_RESULT_ERROR); } if (strchr(current_domain->domain, '*')) { - printf("wildcards not supported due to lack of dns-01\n"); + kore_log(LOG_ERR, + "wildcards not supported due to lack of dns-01"); return (KORE_RESULT_ERROR); } @@ -665,7 +662,7 @@ configure_acme(char *yesno) &current_domain->certkey, &current_domain->certfile); acme_domains++; } else { - printf("invalid '%s' for yes|no acme option\n", yesno); + kore_log(LOG_ERR, "invalid '%s' for yes|no acme option", yesno); return (KORE_RESULT_ERROR); } @@ -698,7 +695,7 @@ configure_bind(char *options) char *argv[4]; if (current_server == NULL) { - printf("bind directive not inside a server context\n"); + kore_log(LOG_ERR, "bind keyword not inside a server context"); return (KORE_RESULT_ERROR); } @@ -715,7 +712,8 @@ configure_bind_unix(char *options) char *argv[3]; if (current_server == NULL) { - printf("bind_unix directive not inside a server context\n"); + kore_log(LOG_ERR, + "bind_unix keyword not inside a server context"); return (KORE_RESULT_ERROR); } @@ -800,7 +798,9 @@ configure_tls_version(char *version) } else if (!strcmp(version, "both")) { tls_version = KORE_TLS_VERSION_BOTH; } else { - printf("unknown value for tls_version: %s\n", version); + kore_log(LOG_ERR, + "unknown value for tls_version: %s (use 1.3, 1.2, both)", + version); return (KORE_RESULT_ERROR); } @@ -811,7 +811,7 @@ static int configure_tls_cipher(char *cipherlist) { if (strcmp(kore_tls_cipher_list, KORE_DEFAULT_CIPHER_LIST)) { - printf("tls_cipher specified twice\n"); + kore_log(LOG_ERR, "tls_cipher specified twice"); return (KORE_RESULT_ERROR); } @@ -825,12 +825,12 @@ configure_tls_dhparam(char *path) BIO *bio; if (tls_dhparam != NULL) { - printf("tls_dhparam specified twice\n"); + kore_log(LOG_ERR, "tls_dhparam specified twice"); return (KORE_RESULT_ERROR); } if ((bio = BIO_new_file(path, "r")) == NULL) { - printf("%s did not exist\n", path); + kore_log(LOG_ERR, "tls_dhparam file '%s' not accessible", path); return (KORE_RESULT_ERROR); } @@ -838,7 +838,7 @@ configure_tls_dhparam(char *path) BIO_free(bio); if (tls_dhparam == NULL) { - printf("PEM_read_bio_DHparams(): %s\n", ssl_errno_s); + kore_log(LOG_ERR, "PEM_read_bio_DHparams(): %s", ssl_errno_s); return (KORE_RESULT_ERROR); } @@ -851,13 +851,14 @@ configure_client_verify_depth(char *value) int err, depth; if (current_domain == NULL) { - printf("client_verify_depth not specified in domain context\n"); + kore_log(LOG_ERR, + "client_verify_depth keyword not in domain context"); return (KORE_RESULT_ERROR); } depth = kore_strtonum(value, 10, 0, INT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad client_verify_depth value: %s\n", value); + kore_log(LOG_ERR, "bad client_verify_depth value: %s", value); return (KORE_RESULT_ERROR); } @@ -872,18 +873,19 @@ configure_client_verify(char *options) char *argv[3]; if (current_domain == NULL) { - printf("client_verify not specified in domain context\n"); + kore_log(LOG_ERR, + "client_verify keyword not in domain context"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 3); if (argv[0] == NULL) { - printf("client_verify is missing a parameter\n"); + kore_log(LOG_ERR, "client_verify is missing a parameter"); return (KORE_RESULT_ERROR); } if (current_domain->cafile != NULL) { - printf("client_verify already set for %s\n", + kore_log(LOG_ERR, "client_verify already set for '%s'", current_domain->domain); return (KORE_RESULT_ERROR); } @@ -910,7 +912,8 @@ static int configure_certfile(char *path) { if (current_domain == NULL) { - printf("certfile not specified in domain context\n"); + kore_log(LOG_ERR, + "certfile keyword not specified in domain context"); return (KORE_RESULT_ERROR); } @@ -923,7 +926,8 @@ static int configure_certkey(char *path) { if (current_domain == NULL) { - printf("certkey not specified in domain text\n"); + kore_log(LOG_ERR, + "certkey keyword not specified in domain context"); return (KORE_RESULT_ERROR); } @@ -938,19 +942,19 @@ configure_privsep(char *options) char *argv[3]; if (current_privsep != NULL) { - printf("nested privsep contexts are not allowed\n"); + kore_log(LOG_ERR, "nested privsep contexts are not allowed"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 3); if (argv[0] == NULL || argv[1] == NULL) { - printf("invalid privsep context\n"); + kore_log(LOG_ERR, "invalid privsep context"); return (KORE_RESULT_ERROR); } if (strcmp(argv[1], "{")) { - printf("privsep context not opened correctly\n"); + kore_log(LOG_ERR, "privsep context not opened correctly"); return (KORE_RESULT_ERROR); } @@ -963,7 +967,7 @@ configure_privsep(char *options) current_privsep = &acme_privsep; #endif } else { - printf("unknown privsep context: %s\n", argv[0]); + kore_log(LOG_ERR, "unknown privsep context: %s", argv[0]); return (KORE_RESULT_ERROR); } @@ -974,7 +978,7 @@ static int configure_privsep_runas(char *user) { if (current_privsep == NULL) { - printf("runas not specified in privsep context\n"); + kore_log(LOG_ERR, "runas keyword not in privsep context"); return (KORE_RESULT_ERROR); } @@ -990,7 +994,7 @@ static int configure_privsep_root(char *root) { if (current_privsep == NULL) { - printf("root not specified in privsep context\n"); + kore_log(LOG_ERR, "root keyword not in privsep context"); return (KORE_RESULT_ERROR); } @@ -1006,14 +1010,14 @@ static int configure_privsep_skip(char *option) { if (current_privsep == NULL) { - printf("skip not specified in privsep context\n"); + kore_log(LOG_ERR, "skip keyword not in privsep context"); return (KORE_RESULT_ERROR); } if (!strcmp(option, "chroot")) { current_privsep->skip_chroot = 1; } else { - printf("unknown skip option '%s'\n", option); + kore_log(LOG_ERR, "unknown skip option '%s'", option); return (KORE_RESULT_ERROR); } @@ -1026,24 +1030,24 @@ configure_domain(char *options) char *argv[3]; if (current_domain != NULL) { - printf("nested domain contexts are not allowed\n"); + kore_log(LOG_ERR, "nested domain contexts are not allowed"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 3); if (argv[0] == NULL || argv[1] == NULL) { - printf("invalid domain context\n"); + kore_log(LOG_ERR, "invalid domain context"); return (KORE_RESULT_ERROR); } if (strcmp(argv[1], "{")) { - printf("domain context not opened correctly\n"); + kore_log(LOG_ERR, "domain context not opened correctly"); return (KORE_RESULT_ERROR); } if (strlen(argv[0]) >= KORE_DOMAINNAME_LEN - 1) { - printf("domain name '%s' too long\n", argv[0]); + kore_log(LOG_ERR, "domain name '%s' too long", argv[0]); return (KORE_RESULT_ERROR); } @@ -1058,23 +1062,23 @@ configure_attach(char *name) struct kore_server *srv; if (current_domain == NULL) { - printf("attach not specified in domain context\n"); + kore_log(LOG_ERR, "attach keyword not in domain context"); return (KORE_RESULT_ERROR); } if (current_domain->server != NULL) { - printf("domain '%s' already attached to server\n", + kore_log(LOG_ERR, "domain '%s' already attached to server", current_domain->domain); return (KORE_RESULT_ERROR); } if ((srv = kore_server_lookup(name)) == NULL) { - printf("server '%s' does not exist\n", name); + kore_log(LOG_ERR, "server '%s' does not exist", name); return (KORE_RESULT_ERROR); } if (!kore_domain_attach(current_domain, srv)) { - printf("failed to attach '%s' to '%s'\n", + kore_log(LOG_ERR, "failed to attach '%s' to '%s'", current_domain->domain, name); return (KORE_RESULT_ERROR); } @@ -1100,18 +1104,24 @@ configure_dynamic_handler(char *options) static int configure_route(char *options) { - int type; - char *argv[4]; + struct kore_route *rt; + int type; + char *argv[4]; if (current_domain == NULL) { - printf("route not specified in domain context\n"); + kore_log(LOG_ERR, "route keyword not in domain context"); + return (KORE_RESULT_ERROR); + } + + if (current_route != NULL) { + kore_log(LOG_ERR, "nested route contexts not allowed"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 4); - if (argv[0] == NULL || argv[1] == NULL) { - printf("missing parameters for route \n"); + if (argv[1] == NULL || strcmp(argv[1], "{")) { + kore_log(LOG_ERR, "invalid route context"); return (KORE_RESULT_ERROR); } @@ -1120,12 +1130,72 @@ configure_route(char *options) else type = HANDLER_TYPE_DYNAMIC; - if (!kore_module_handler_new(current_domain, - argv[0], argv[1], argv[2], type)) { - printf("cannot create route for %s\n", argv[0]); + if ((rt = kore_route_create(current_domain, argv[0], type)) == NULL) { + kore_log(LOG_ERR, + "failed to create route handler for '%s'", argv[0]); + return (KORE_RESULT_ERROR); + } + + current_route = rt; + + return (KORE_RESULT_OK); +} + +static int +configure_route_handler(char *name) +{ + if (current_route == NULL) { + kore_log(LOG_ERR, + "handler keyword not inside of route context"); return (KORE_RESULT_ERROR); } + kore_route_callback(current_route, name); + + return (KORE_RESULT_OK); +} + +static int +configure_route_methods(char *options) +{ + int i, cnt; + char *argv[10]; + + if (current_route == NULL) { + kore_log(LOG_ERR, + "methods keyword not inside of route context"); + return (KORE_RESULT_ERROR); + } + + cnt = kore_split_string(options, " ", argv, 10); + if (cnt < 1) { + kore_log(LOG_ERR, + "bad methods option '%s', missing methods", options); + return (KORE_RESULT_ERROR); + } + + current_route->methods = 0; + + for (i = 0; i < cnt; i++) { + if (!strcasecmp(argv[i], "post")) { + current_route->methods |= HTTP_METHOD_POST; + } else if (!strcasecmp(argv[i], "get")) { + current_route->methods |= HTTP_METHOD_GET; + } else if (!strcasecmp(argv[i], "put")) { + current_route->methods |= HTTP_METHOD_PUT; + } else if (!strcasecmp(argv[i], "delete")) { + current_route->methods |= HTTP_METHOD_DELETE; + } else if (!strcasecmp(argv[i], "head")) { + current_route->methods |= HTTP_METHOD_HEAD; + } else if (!strcasecmp(argv[i], "patch")) { + current_route->methods |= HTTP_METHOD_PATCH; + } else { + kore_log(LOG_ERR, "unknown method: %s in method for %s", + argv[i], current_route->path); + return (KORE_RESULT_ERROR); + } + } + return (KORE_RESULT_OK); } @@ -1136,24 +1206,25 @@ configure_return(char *options) int elm, status, err; if (current_domain == NULL) { - printf("return outside of domain context\n"); + kore_log(LOG_ERR, "return keyword not in domain context"); return (KORE_RESULT_ERROR); } elm = kore_split_string(options, " ", argv, 3); if (elm != 2) { - printf("missing parameters for return\n"); + kore_log(LOG_ERR, "missing parameters for return"); return (KORE_RESULT_ERROR); } status = kore_strtonum(argv[1], 10, 400, 600, &err); if (err != KORE_RESULT_OK) { - printf("invalid status code on return (%s)\n", argv[1]); + kore_log(LOG_ERR, + "invalid status code on return (%s)", argv[1]); return (KORE_RESULT_ERROR); } if (!http_redirect_add(current_domain, argv[0], status, NULL)) { - printf("invalid regex on return path\n"); + kore_log(LOG_ERR, "invalid regex on return path"); return (KORE_RESULT_ERROR); } @@ -1167,24 +1238,25 @@ configure_redirect(char *options) int elm, status, err; if (current_domain == NULL) { - printf("redirect outside of domain context\n"); + kore_log(LOG_ERR, "redirect keyword not in domain context"); return (KORE_RESULT_ERROR); } elm = kore_split_string(options, " ", argv, 4); if (elm != 3) { - printf("missing parameters for redirect\n"); + kore_log(LOG_ERR, "missing parameters for redirect"); return (KORE_RESULT_ERROR); } status = kore_strtonum(argv[1], 10, 300, 399, &err); if (err != KORE_RESULT_OK) { - printf("invalid status code on redirect (%s)\n", argv[1]); + kore_log(LOG_ERR, + "invalid status code on redirect (%s)", argv[1]); return (KORE_RESULT_ERROR); } if (!http_redirect_add(current_domain, argv[0], status, argv[2])) { - printf("invalid regex on redirect path\n"); + kore_log(LOG_ERR, "invalid regex on redirect path"); return (KORE_RESULT_ERROR); } @@ -1197,19 +1269,19 @@ configure_filemap(char *options) char *argv[3]; if (current_domain == NULL) { - printf("filemap outside of domain context\n"); + kore_log(LOG_ERR, "filemap keyword not in domain context"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 3); if (argv[0] == NULL || argv[1] == NULL) { - printf("missing parameters for filemap\n"); + kore_log(LOG_ERR, "missing parameters for filemap"); return (KORE_RESULT_ERROR); } if (!kore_filemap_create(current_domain, argv[1], argv[0])) { - printf("cannot create filemap for %s\n", argv[1]); + kore_log(LOG_ERR, "cannot create filemap for %s", argv[1]); return (KORE_RESULT_ERROR); } @@ -1225,7 +1297,7 @@ configure_accesslog(char *path) } if (current_domain->accesslog != -1) { - printf("domain %s already has an open accesslog\n", + kore_log(LOG_ERR, "domain '%s' already has an open accesslog", current_domain->domain); return (KORE_RESULT_ERROR); } @@ -1234,64 +1306,10 @@ configure_accesslog(char *path) O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (current_domain->accesslog == -1) { - printf("accesslog open(%s): %s\n", path, errno_s); - return (KORE_RESULT_ERROR); - } - - return (KORE_RESULT_OK); -} - -static int -configure_restrict(char *options) -{ - struct kore_module_handle *hdlr; - int i, cnt; - char *argv[10]; - - if (current_domain == NULL) { - printf("restrict not used in domain context\n"); - return (KORE_RESULT_ERROR); - } - - cnt = kore_split_string(options, " ", argv, 10); - if (cnt < 2) { - printf("bad restrict option '%s', missing methods\n", options); - return (KORE_RESULT_ERROR); - } - - hdlr = NULL; - TAILQ_FOREACH(hdlr, &(current_domain->handlers), list) { - if (!strcmp(hdlr->path, argv[0])) - break; - } - - if (hdlr == NULL) { - printf("bad restrict option handler '%s' not found\n", argv[0]); + kore_log(LOG_ERR, "accesslog open(%s): %s", path, errno_s); return (KORE_RESULT_ERROR); } - hdlr->methods = 0; - - for (i = 1; i < cnt; i++) { - if (!strcasecmp(argv[i], "post")) { - hdlr->methods |= HTTP_METHOD_POST; - } else if (!strcasecmp(argv[i], "get")) { - hdlr->methods |= HTTP_METHOD_GET; - } else if (!strcasecmp(argv[i], "put")) { - hdlr->methods |= HTTP_METHOD_PUT; - } else if (!strcasecmp(argv[i], "delete")) { - hdlr->methods |= HTTP_METHOD_DELETE; - } else if (!strcasecmp(argv[i], "head")) { - hdlr->methods |= HTTP_METHOD_HEAD; - } else if (!strcasecmp(argv[i], "patch")) { - hdlr->methods |= HTTP_METHOD_PATCH; - } else { - printf("unknown method: %s in restrict for %s\n", - argv[i], argv[0]); - return (KORE_RESULT_ERROR); - } - } - return (KORE_RESULT_OK); } @@ -1321,7 +1339,7 @@ configure_http_media_type(char *type) extensions = strchr(type, ' '); if (extensions == NULL) { - printf("bad http_media_type value: %s\n", type); + kore_log(LOG_ERR, "bad http_media_type value '%s'", type); return (KORE_RESULT_ERROR); } @@ -1330,13 +1348,14 @@ configure_http_media_type(char *type) kore_split_string(extensions, " \t", ext, 10); for (i = 0; ext[i] != NULL; i++) { if (!http_media_register(ext[i], type)) { - printf("duplicate extension found: %s\n", ext[i]); + kore_log(LOG_ERR, + "duplicate extension found '%s'", ext[i]); return (KORE_RESULT_ERROR); } } if (i == 0) { - printf("missing extensions in: %s\n", type); + kore_log(LOG_ERR, "missing extensions in '%s'", type); return (KORE_RESULT_ERROR); } @@ -1350,7 +1369,7 @@ configure_http_header_max(char *option) http_header_max = kore_strtonum(option, 10, 1, 65535, &err); if (err != KORE_RESULT_OK) { - printf("bad http_header_max value: %s\n", option); + kore_log(LOG_ERR, "bad http_header_max value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1364,7 +1383,7 @@ configure_http_header_timeout(char *option) http_header_timeout = kore_strtonum(option, 10, 1, 65535, &err); if (err != KORE_RESULT_OK) { - printf("bad http_header_timeout value: %s\n", option); + kore_log(LOG_ERR, "bad http_header_timeout value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1378,7 +1397,7 @@ configure_http_body_max(char *option) http_body_max = kore_strtonum(option, 10, 0, LONG_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad http_body_max value: %s\n", option); + kore_log(LOG_ERR, "bad http_body_max value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1392,7 +1411,7 @@ configure_http_body_timeout(char *option) http_body_timeout = kore_strtonum(option, 10, 1, 65535, &err); if (err != KORE_RESULT_OK) { - printf("bad http_body_timeout value: %s\n", option); + kore_log(LOG_ERR, "bad http_body_timeout value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1406,7 +1425,8 @@ configure_http_body_disk_offload(char *option) http_body_disk_offload = kore_strtonum(option, 10, 0, LONG_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad http_body_disk_offload value: %s\n", option); + kore_log(LOG_ERR, + "bad http_body_disk_offload value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1439,8 +1459,8 @@ configure_http_pretty_error(char *yesno) } else if (!strcmp(yesno, "yes")) { http_pretty_error = 1; } else { - printf("invalid '%s' for yes|no http_pretty_error option\n", - yesno); + kore_log(LOG_ERR, + "invalid '%s' for yes|no http_pretty_error option", yesno); return (KORE_RESULT_ERROR); } @@ -1454,7 +1474,7 @@ configure_http_hsts_enable(char *option) http_hsts_enable = kore_strtonum(option, 10, 0, LONG_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad http_hsts_enable value: %s\n", option); + kore_log(LOG_ERR, "bad http_hsts_enable value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1468,7 +1488,8 @@ configure_http_keepalive_time(char *option) http_keepalive_time = kore_strtonum(option, 10, 0, USHRT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad http_keepalive_time value: %s\n", option); + kore_log(LOG_ERR, + "bad http_keepalive_time value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1482,7 +1503,7 @@ configure_http_request_ms(char *option) http_request_ms = kore_strtonum(option, 10, 0, UINT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad http_request_ms value: %s\n", option); + kore_log(LOG_ERR, "bad http_request_ms value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1496,7 +1517,7 @@ configure_http_request_limit(char *option) http_request_limit = kore_strtonum(option, 10, 0, UINT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad http_request_limit value: %s\n", option); + kore_log(LOG_ERR, "bad http_request_limit value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1510,14 +1531,14 @@ configure_validator(char *name) char *tname, *value; if ((tname = strchr(name, ' ')) == NULL) { - printf("missing validator name\n"); + kore_log(LOG_ERR, "missing validator name"); return (KORE_RESULT_ERROR); } *(tname)++ = '\0'; tname = kore_text_trim(tname, strlen(tname)); if ((value = strchr(tname, ' ')) == NULL) { - printf("missing validator value\n"); + kore_log(LOG_ERR, "missing validator value"); return (KORE_RESULT_ERROR); } @@ -1529,12 +1550,13 @@ configure_validator(char *name) } else if (!strcmp(tname, "function")) { type = KORE_VALIDATOR_TYPE_FUNCTION; } else { - printf("bad type for validator %s\n", tname); + kore_log(LOG_ERR, + "bad type '%s' for validator '%s'", tname, name); return (KORE_RESULT_ERROR); } if (!kore_validator_add(name, type, value)) { - printf("bad validator specified: %s\n", tname); + kore_log(LOG_ERR, "bad validator specified for '%s'", name); return (KORE_RESULT_ERROR); } @@ -1542,101 +1564,75 @@ configure_validator(char *name) } static int -configure_params(char *options) +configure_validate(char *options) { - struct kore_module_handle *hdlr; - char *method, *argv[3]; - - if (current_domain == NULL) { - printf("params not used in domain context\n"); - return (KORE_RESULT_ERROR); - } + struct kore_validator *val; + struct kore_route_params *param; + char *method, *argv[4]; + int flags, http_method; - if (current_handler != NULL) { - printf("previous params block not closed\n"); + if (kore_split_string(options, " ", argv, 4) != 3) { + kore_log(LOG_ERR, + "validate keyword needs 3 args: method param validator"); return (KORE_RESULT_ERROR); } - kore_split_string(options, " ", argv, 3); - if (argv[1] == NULL) - return (KORE_RESULT_ERROR); + flags = 0; if ((method = strchr(argv[0], ':')) != NULL) { *(method)++ = '\0'; if (!strcasecmp(argv[0], "qs")) { - current_flags = KORE_PARAMS_QUERY_STRING; + flags = KORE_PARAMS_QUERY_STRING; } else { - printf("unknown prefix '%s' for '%s'\n", - argv[0], argv[1]); + kore_log(LOG_ERR, + "unknown validate method prefix '%s' for '%s'", + argv[0], current_route->path); return (KORE_RESULT_ERROR); } } else { method = argv[0]; } + if ((val = kore_validator_lookup(argv[2])) == NULL) { + kore_log(LOG_ERR, "unknown validator '%s'", argv[2]); + return (KORE_RESULT_ERROR); + } + if (!strcasecmp(method, "post")) { - current_method = HTTP_METHOD_POST; + http_method = HTTP_METHOD_POST; } else if (!strcasecmp(method, "get")) { - current_method = HTTP_METHOD_GET; + http_method = HTTP_METHOD_GET; /* Let params get /foo {} imply qs:get automatically. */ - current_flags |= KORE_PARAMS_QUERY_STRING; + flags |= KORE_PARAMS_QUERY_STRING; } else if (!strcasecmp(method, "put")) { - current_method = HTTP_METHOD_PUT; + http_method = HTTP_METHOD_PUT; } else if (!strcasecmp(method, "delete")) { - current_method = HTTP_METHOD_DELETE; + http_method = HTTP_METHOD_DELETE; } else if (!strcasecmp(method, "head")) { - current_method = HTTP_METHOD_HEAD; + http_method = HTTP_METHOD_HEAD; } else if (!strcasecmp(method, "patch")) { - current_method = HTTP_METHOD_PATCH; + http_method = HTTP_METHOD_PATCH; } else { - printf("unknown method: %s in params block for %s\n", - method, argv[1]); + kore_log(LOG_ERR, "unknown method: %s in validator for %s", + method, current_route->path); return (KORE_RESULT_ERROR); } - /* - * Find the handler ourselves, otherwise the regex is applied - * in case of a dynamic page. - */ - TAILQ_FOREACH(hdlr, &(current_domain->handlers), list) { - if (!strcmp(hdlr->path, argv[1])) { - current_handler = hdlr; - return (KORE_RESULT_OK); - } - } - - printf("params for unknown page handler: %s\n", argv[1]); - return (KORE_RESULT_ERROR); -} - -static int -configure_validate(char *options) -{ - struct kore_handler_params *p; - struct kore_validator *val; - char *argv[3]; - - if (current_handler == NULL) { - printf("validate not used in domain context\n"); + if (!(current_route->methods & http_method)) { + kore_log(LOG_ERR, "method '%s' not enabled for route '%s'", + method, current_route->path); return (KORE_RESULT_ERROR); } - kore_split_string(options, " ", argv, 3); - if (argv[1] == NULL) - return (KORE_RESULT_ERROR); + param = kore_calloc(1, sizeof(*param)); - if ((val = kore_validator_lookup(argv[1])) == NULL) { - printf("unknown validator %s for %s\n", argv[1], argv[0]); - return (KORE_RESULT_ERROR); - } + param->flags = flags; + param->validator = val; + param->method = http_method; + param->name = kore_strdup(argv[1]); - p = kore_malloc(sizeof(*p)); - p->validator = val; - p->flags = current_flags; - p->method = current_method; - p->name = kore_strdup(argv[0]); + TAILQ_INSERT_TAIL(&current_route->params, param, list); - TAILQ_INSERT_TAIL(&(current_handler->params), p, list); return (KORE_RESULT_OK); } @@ -1646,18 +1642,18 @@ configure_authentication(char *options) char *argv[3]; if (current_auth != NULL) { - printf("previous authentication block not closed\n"); + kore_log(LOG_ERR, "previous authentication block not closed"); return (KORE_RESULT_ERROR); } kore_split_string(options, " ", argv, 3); if (argv[1] == NULL) { - printf("missing name for authentication block\n"); + kore_log(LOG_ERR, "missing name for authentication block"); return (KORE_RESULT_ERROR); } if (strcmp(argv[1], "{")) { - printf("missing { for authentication block\n"); + kore_log(LOG_ERR, "missing { for authentication block"); return (KORE_RESULT_ERROR); } @@ -1673,7 +1669,8 @@ static int configure_authentication_type(char *option) { if (current_auth == NULL) { - printf("authentication_type outside authentication context\n"); + kore_log(LOG_ERR, + "authentication_type keyword not in correct context"); return (KORE_RESULT_ERROR); } @@ -1684,7 +1681,7 @@ configure_authentication_type(char *option) } else if (!strcmp(option, "request")) { current_auth->type = KORE_AUTH_TYPE_REQUEST; } else { - printf("unknown authentication type '%s'\n", option); + kore_log(LOG_ERR, "unknown authentication type '%s'", option); return (KORE_RESULT_ERROR); } @@ -1695,7 +1692,8 @@ static int configure_authentication_value(char *option) { if (current_auth == NULL) { - printf("authentication_value outside authentication context\n"); + kore_log(LOG_ERR, + "authentication_value keyword not in correct context"); return (KORE_RESULT_ERROR); } @@ -1712,12 +1710,14 @@ configure_authentication_validator(char *validator) struct kore_validator *val; if (current_auth == NULL) { - printf("authentication_validator outside authentication\n"); + kore_log(LOG_ERR, + "authentication_validator not in correct context"); return (KORE_RESULT_ERROR); } if ((val = kore_validator_lookup(validator)) == NULL) { - printf("authentication validator '%s' not found\n", validator); + kore_log(LOG_ERR, + "authentication validator '%s' not found", validator); return (KORE_RESULT_ERROR); } @@ -1730,7 +1730,8 @@ static int configure_authentication_uri(char *uri) { if (current_auth == NULL) { - printf("authentication_uri outside authentication context\n"); + kore_log(LOG_ERR, + "authentication_uri keyword not in correct context"); return (KORE_RESULT_ERROR); } @@ -1748,7 +1749,8 @@ configure_websocket_maxframe(char *option) kore_websocket_maxframe = kore_strtonum64(option, 1, &err); if (err != KORE_RESULT_OK) { - printf("bad kore_websocket_maxframe value: %s\n", option); + kore_log(LOG_ERR, + "bad kore_websocket_maxframe value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1762,7 +1764,8 @@ configure_websocket_timeout(char *option) kore_websocket_timeout = kore_strtonum64(option, 1, &err); if (err != KORE_RESULT_OK) { - printf("bad kore_websocket_timeout value: %s\n", option); + kore_log(LOG_ERR, + "bad kore_websocket_timeout value '%s'", option); return (KORE_RESULT_ERROR); } @@ -1787,7 +1790,7 @@ configure_workers(char *option) worker_count = kore_strtonum(option, 10, 1, KORE_WORKER_MAX, &err); if (err != KORE_RESULT_OK) { - printf("%s is not a valid worker number\n", option); + kore_log(LOG_ERR, "bad value for worker '%s'", option); return (KORE_RESULT_ERROR); } @@ -1811,7 +1814,8 @@ configure_max_connections(char *option) worker_max_connections = kore_strtonum(option, 10, 1, UINT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad value for worker_max_connections: %s\n", option); + kore_log(LOG_ERR, + "bad value for worker_max_connections '%s'", option); return (KORE_RESULT_ERROR); } @@ -1825,7 +1829,8 @@ configure_rlimit_nofiles(char *option) worker_rlimit_nofiles = kore_strtonum(option, 10, 1, UINT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad value for worker_rlimit_nofiles: %s\n", option); + kore_log(LOG_ERR, + "bad value for worker_rlimit_nofiles '%s'", option); return (KORE_RESULT_ERROR); } @@ -1839,7 +1844,8 @@ configure_accept_threshold(char *option) worker_accept_threshold = kore_strtonum(option, 0, 1, UINT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad value for worker_accept_threshold: %s\n", option); + kore_log(LOG_ERR, + "bad value for worker_accept_threshold '%s'\n", option); return (KORE_RESULT_ERROR); } @@ -1854,7 +1860,8 @@ configure_death_policy(char *option) } else if (!strcmp(option, "terminate")) { worker_policy = KORE_WORKER_POLICY_TERMINATE; } else { - printf("bad value for worker_death_policy: %s\n", option); + kore_log(LOG_ERR, + "bad value for worker_death_policy '%s'\n", option); return (KORE_RESULT_ERROR); } @@ -1868,7 +1875,8 @@ configure_set_affinity(char *option) worker_set_affinity = kore_strtonum(option, 10, 0, 1, &err); if (err != KORE_RESULT_OK) { - printf("bad value for worker_set_affinity: %s\n", option); + kore_log(LOG_ERR, + "bad value for worker_set_affinity '%s'", option); return (KORE_RESULT_ERROR); } @@ -1882,7 +1890,7 @@ configure_socket_backlog(char *option) kore_socket_backlog = kore_strtonum(option, 10, 0, UINT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad socket_backlog value: %s\n", option); + kore_log(LOG_ERR, "bad socket_backlog value: '%s'", option); return (KORE_RESULT_ERROR); } @@ -1897,7 +1905,7 @@ configure_pgsql_conn_max(char *option) pgsql_conn_max = kore_strtonum(option, 10, 0, USHRT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad value for pgsql_conn_max: %s\n", option); + kore_log(LOG_ERR, "bad value for pgsql_conn_max '%s'", option); return (KORE_RESULT_ERROR); } @@ -1911,7 +1919,8 @@ configure_pgsql_queue_limit(char *option) pgsql_queue_limit = kore_strtonum(option, 10, 0, UINT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad value for pgsql_queue_limit: %s\n", option); + kore_log(LOG_ERR, + "bad value for pgsql_queue_limit '%s'", option); return (KORE_RESULT_ERROR); } @@ -1927,7 +1936,7 @@ configure_task_threads(char *option) kore_task_threads = kore_strtonum(option, 10, 0, UCHAR_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad value for task_threads: %s\n", option); + kore_log(LOG_ERR, "bad value for task_threads: '%s'", option); return (KORE_RESULT_ERROR); } @@ -1980,7 +1989,7 @@ configure_curl_recv_max(char *option) kore_curl_recv_max = kore_strtonum64(option, 1, &err); if (err != KORE_RESULT_OK) { - printf("bad curl_recv_max value: %s\n", option); + kore_log(LOG_ERR, "bad curl_recv_max value '%s'\n", option); return (KORE_RESULT_ERROR); } @@ -1994,7 +2003,7 @@ configure_curl_timeout(char *option) kore_curl_timeout = kore_strtonum(option, 10, 0, USHRT_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad kore_curl_timeout value: %s\n", option); + kore_log(LOG_ERR, "bad kore_curl_timeout value: '%s'", option); return (KORE_RESULT_ERROR); } @@ -2011,8 +2020,8 @@ configure_seccomp_tracing(char *opt) } else if (!strcmp(opt, "no")) { kore_seccomp_tracing = 0; } else { - printf("bad seccomp_tracing value: %s (expected yes|no)\n", - opt); + kore_log(LOG_ERR, + "bad seccomp_tracing value '%s' (expected yes|no)\n", opt); return (KORE_RESULT_ERROR); } diff --git a/src/domain.c b/src/domain.c @@ -131,8 +131,8 @@ kore_domain_new(const char *domain) dom->domain = kore_strdup(domain); #if !defined(KORE_NO_HTTP) - TAILQ_INIT(&(dom->handlers)); - TAILQ_INIT(&(dom->redirects)); + TAILQ_INIT(&dom->routes); + TAILQ_INIT(&dom->redirects); #endif if (dom->id < KORE_DOMAIN_CACHE) { @@ -174,8 +174,8 @@ void kore_domain_free(struct kore_domain *dom) { #if !defined(KORE_NO_HTTP) + struct kore_route *rt; struct http_redirect *rdr; - struct kore_module_handle *hdlr; #endif if (dom == NULL) return; @@ -190,20 +190,17 @@ kore_domain_free(struct kore_domain *dom) if (dom->ssl_ctx != NULL) SSL_CTX_free(dom->ssl_ctx); - if (dom->cafile != NULL) - kore_free(dom->cafile); - if (dom->certkey != NULL) - kore_free(dom->certkey); - if (dom->certfile != NULL) - kore_free(dom->certfile); - if (dom->crlfile != NULL) - kore_free(dom->crlfile); + + kore_free(dom->cafile); + kore_free(dom->certkey); + kore_free(dom->certfile); + kore_free(dom->crlfile); #if !defined(KORE_NO_HTTP) /* Drop all handlers associated with this domain */ - while ((hdlr = TAILQ_FIRST(&(dom->handlers))) != NULL) { - TAILQ_REMOVE(&(dom->handlers), hdlr, list); - kore_module_handler_free(hdlr); + while ((rt = TAILQ_FIRST(&dom->routes)) != NULL) { + TAILQ_REMOVE(&dom->routes, rt, list); + kore_route_free(rt); } while ((rdr = TAILQ_FIRST(&(dom->redirects))) != NULL) { diff --git a/src/filemap.c b/src/filemap.c @@ -58,7 +58,7 @@ kore_filemap_create(struct kore_domain *dom, const char *path, const char *root) size_t sz; struct stat st; int len; - struct kore_module_handle *hdlr; + struct kore_route *rt; struct filemap_entry *entry; char regex[1024], fpath[PATH_MAX]; @@ -86,20 +86,11 @@ kore_filemap_create(struct kore_domain *dom, const char *path, const char *root) if (len == -1 || (size_t)len >= sizeof(regex)) fatal("kore_filemap_create: buffer too small"); - if (!kore_module_handler_new(dom, regex, "filemap_resolve", - NULL, HANDLER_TYPE_DYNAMIC)) + if ((rt = kore_route_create(dom, regex, HANDLER_TYPE_DYNAMIC)) == NULL) return (KORE_RESULT_ERROR); - hdlr = NULL; - TAILQ_FOREACH(hdlr, &dom->handlers, list) { - if (!strcmp(hdlr->path, regex)) - break; - } - - if (hdlr == NULL) - fatal("couldn't find newly created handler for filemap"); - - hdlr->methods = HTTP_METHOD_GET | HTTP_METHOD_HEAD; + kore_route_callback(rt, "filemap_resolve"); + rt->methods = HTTP_METHOD_GET | HTTP_METHOD_HEAD; entry = kore_calloc(1, sizeof(*entry)); entry->domain = dom; @@ -151,7 +142,7 @@ filemap_resolve(struct http_request *req) best_len = 0; TAILQ_FOREACH(entry, &maps, list) { - if (entry->domain != req->hdlr->dom) + if (entry->domain != req->rt->dom) continue; if (!strncmp(entry->root, req->path, entry->root_len)) { diff --git a/src/http.c b/src/http.c @@ -345,18 +345,18 @@ http_process_request(struct http_request *req) kore_debug("http_process_request: %p->%p (%s)", req->owner, req, req->path); - if (req->flags & HTTP_REQUEST_DELETE || req->hdlr == NULL) + if (req->flags & HTTP_REQUEST_DELETE || req->rt == NULL) return; req->start = kore_time_ms(); - if (req->hdlr->auth != NULL && !(req->flags & HTTP_REQUEST_AUTHED)) - r = kore_auth_run(req, req->hdlr->auth); + if (req->rt->auth != NULL && !(req->flags & HTTP_REQUEST_AUTHED)) + r = kore_auth_run(req, req->rt->auth); else r = KORE_RESULT_OK; switch (r) { case KORE_RESULT_OK: - r = kore_runtime_http_request(req->hdlr->rcall, req); + r = kore_runtime_http_request(req->rt->rcall, req); break; case KORE_RESULT_RETRY: break; @@ -389,7 +389,7 @@ http_process_request(struct http_request *req) fatal("A page handler returned an unknown result: %d", r); } - if (req->hdlr->dom->accesslog) + if (req->rt->dom->accesslog) kore_accesslog(req); req->flags |= HTTP_REQUEST_DELETE; @@ -2103,7 +2103,7 @@ http_request_new(struct connection *c, const char *host, } /* Checked further down below if we need to 404. */ - exists = kore_module_handler_find(req, dom, m, &req->hdlr); + exists = kore_route_lookup(req, dom, m, &req->rt); TAILQ_INIT(&(req->resp_headers)); TAILQ_INIT(&(req->req_headers)); @@ -2135,7 +2135,7 @@ http_request_new(struct connection *c, const char *host, return (NULL); } - if (req->hdlr == NULL) { + if (req->rt == NULL) { http_request_free(req); http_error_response(c, HTTP_STATUS_METHOD_NOT_ALLOWED); return (NULL); @@ -2339,14 +2339,14 @@ http_argument_add(struct http_request *req, char *name, char *value, int qs, int decode) { struct http_arg *q; - struct kore_handler_params *p; + struct kore_route_params *p; if (decode) { if (!http_argument_urldecode(name)) return; } - TAILQ_FOREACH(p, &(req->hdlr->params), list) { + TAILQ_FOREACH(p, &req->rt->params, list) { if (qs == 1 && !(p->flags & KORE_PARAMS_QUERY_STRING)) continue; if (qs == 0 && (p->flags & KORE_PARAMS_QUERY_STRING)) diff --git a/src/module.c b/src/module.c @@ -135,11 +135,6 @@ kore_module_reload(int cbs) { struct stat st; int ret; -#if !defined(KORE_NO_HTTP) - struct kore_server *srv; - struct kore_domain *dom; - struct kore_module_handle *hdlr; -#endif struct kore_module *module; TAILQ_FOREACH(module, &modules, list) { @@ -183,22 +178,7 @@ kore_module_reload(int cbs) } #if !defined(KORE_NO_HTTP) - LIST_FOREACH(srv, &kore_servers, list) { - TAILQ_FOREACH(dom, &srv->domains, list) { - TAILQ_FOREACH(hdlr, &(dom->handlers), list) { - kore_free(hdlr->rcall); - hdlr->rcall = kore_runtime_getcall(hdlr->func); - if (hdlr->rcall == NULL) { - fatal("no function '%s' found", - hdlr->func); - } - hdlr->errors = 0; - } - } - } -#endif - -#if !defined(KORE_NO_HTTP) + kore_route_reload(); kore_validator_reload(); #endif } @@ -212,112 +192,6 @@ kore_module_loaded(void) return (1); } -#if !defined(KORE_NO_HTTP) -int -kore_module_handler_new(struct kore_domain *dom, const char *path, - const char *func, const char *auth, int type) -{ - struct kore_auth *ap; - struct kore_module_handle *hdlr; - - if (auth != NULL) { - if ((ap = kore_auth_lookup(auth)) == NULL) - fatal("no authentication block '%s' found", auth); - } else { - ap = NULL; - } - - hdlr = kore_malloc(sizeof(*hdlr)); - hdlr->auth = ap; - hdlr->dom = dom; - hdlr->errors = 0; - hdlr->type = type; - hdlr->path = kore_strdup(path); - hdlr->func = kore_strdup(func); - hdlr->methods = HTTP_METHOD_ALL; - - TAILQ_INIT(&(hdlr->params)); - - if ((hdlr->rcall = kore_runtime_getcall(func)) == NULL) { - kore_module_handler_free(hdlr); - kore_log(LOG_ERR, "function '%s' not found", func); - return (KORE_RESULT_ERROR); - } - - if (hdlr->type == HANDLER_TYPE_DYNAMIC) { - if (regcomp(&(hdlr->rctx), hdlr->path, - REG_EXTENDED | REG_NOSUB)) { - kore_module_handler_free(hdlr); - kore_debug("regcomp() on %s failed", path); - return (KORE_RESULT_ERROR); - } - } - - TAILQ_INSERT_TAIL(&(dom->handlers), hdlr, list); - return (KORE_RESULT_OK); -} - -void -kore_module_handler_free(struct kore_module_handle *hdlr) -{ - struct kore_handler_params *param; - - if (hdlr == NULL) - return; - - if (hdlr->func != NULL) - kore_free(hdlr->func); - if (hdlr->path != NULL) - kore_free(hdlr->path); - if (hdlr->type == HANDLER_TYPE_DYNAMIC) - regfree(&(hdlr->rctx)); - - /* Drop all validators associated with this handler */ - while ((param = TAILQ_FIRST(&(hdlr->params))) != NULL) { - TAILQ_REMOVE(&(hdlr->params), param, list); - if (param->name != NULL) - kore_free(param->name); - kore_free(param); - } - - kore_free(hdlr); -} - -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)) { - if (hdlr->methods & method) { - *out = hdlr; - return (1); - } - exists++; - } - } else { - if (!regexec(&(hdlr->rctx), req->path, - HTTP_CAPTURE_GROUPS, req->cgroups, 0)) { - if (hdlr->methods & method) { - *out = hdlr; - return (1); - } - exists++; - } - } - } - - return (exists); -} -#endif /* !KORE_NO_HTTP */ - void * kore_module_getsym(const char *symbol, struct kore_runtime **runtime) { diff --git a/src/python.c b/src/python.c @@ -82,13 +82,11 @@ static struct python_coro *python_coro_create(PyObject *, 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, int); +static int python_route_params(PyObject *, struct kore_route *, + const char *, int, int); static int python_route_methods(PyObject *, PyObject *, - struct kore_module_handle *); -static int python_route_auth(PyObject *, - struct kore_module_handle *); + struct kore_route *); +static int python_route_auth(PyObject *, struct kore_route *); static int python_coro_run(struct python_coro *); static void python_coro_wakeup(struct python_coro *); @@ -1250,7 +1248,7 @@ python_runtime_http_request(void *addr, struct http_request *req) callable = (PyObject *)addr; /* starts at 1 to skip the full path. */ - if (req->hdlr->type == HANDLER_TYPE_DYNAMIC) { + if (req->rt->type == HANDLER_TYPE_DYNAMIC) { for (idx = 1; idx < HTTP_CAPTURE_GROUPS - 1; idx++) { if (req->cgroups[idx].rm_so == -1 || req->cgroups[idx].rm_eo == -1) @@ -5211,7 +5209,7 @@ python_route_install(struct pyroute *route) { const char *val; struct kore_domain *domain; - struct kore_module_handle *hdlr, *entry; + struct kore_route *rt, *entry; PyObject *kwargs, *repr, *obj; if ((repr = PyObject_Repr(route->func)) == NULL) { @@ -5221,56 +5219,56 @@ python_route_install(struct pyroute *route) 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); + rt = kore_calloc(1, sizeof(*rt)); + rt->dom = domain; + rt->methods = HTTP_METHOD_ALL; + rt->path = kore_strdup(route->path); - TAILQ_INIT(&hdlr->params); + TAILQ_INIT(&rt->params); val = PyUnicode_AsUTF8(repr); - hdlr->func = kore_strdup(val); + rt->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); + rt->rcall = kore_calloc(1, sizeof(struct kore_runtime_call)); + rt->rcall->addr = route->func; + rt->rcall->runtime = &kore_python_runtime; + Py_INCREF(rt->rcall->addr); if (kwargs != NULL) { if ((obj = PyDict_GetItemString(kwargs, "methods")) != NULL) { - if (!python_route_methods(obj, kwargs, hdlr)) { + if (!python_route_methods(obj, kwargs, rt)) { kore_python_log_error("python_route_install"); - kore_module_handler_free(hdlr); + kore_route_free(rt); return (KORE_RESULT_ERROR); } } if ((obj = PyDict_GetItemString(kwargs, "auth")) != NULL) { - if (!python_route_auth(obj, hdlr)) { + if (!python_route_auth(obj, rt)) { kore_python_log_error("python_route_install"); - kore_module_handler_free(hdlr); + kore_route_free(rt); return (KORE_RESULT_ERROR); } } } - if (hdlr->path[0] == '/') { - hdlr->type = HANDLER_TYPE_STATIC; + if (rt->path[0] == '/') { + rt->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); + rt->type = HANDLER_TYPE_DYNAMIC; + if (regcomp(&rt->rctx, rt->path, REG_EXTENDED)) + fatal("failed to compile regex for '%s'", rt->path); } - TAILQ_FOREACH(entry, &domain->handlers, list) { - if (!strcmp(entry->path, hdlr->path) && - (entry->methods & hdlr->methods)) + TAILQ_FOREACH(entry, &domain->routes, list) { + if (!strcmp(entry->path, rt->path) && + (entry->methods & rt->methods)) fatal("duplicate route for '%s'", route->path); } - TAILQ_INSERT_TAIL(&domain->handlers, hdlr, list); + TAILQ_INSERT_TAIL(&domain->routes, rt, list); return (KORE_RESULT_OK); } @@ -5312,8 +5310,7 @@ python_route_domain_resolve(struct pyroute *route) } static int -python_route_methods(PyObject *obj, PyObject *kwargs, - struct kore_module_handle *hdlr) +python_route_methods(PyObject *obj, PyObject *kwargs, struct kore_route *rt) { const char *val; PyObject *item; @@ -5323,7 +5320,7 @@ python_route_methods(PyObject *obj, PyObject *kwargs, if (!PyList_CheckExact(obj)) return (KORE_RESULT_ERROR); - hdlr->methods = 0; + rt->methods = 0; list_len = PyList_Size(obj); for (idx = 0; idx < list_len; idx++) { @@ -5339,14 +5336,14 @@ python_route_methods(PyObject *obj, PyObject *kwargs, return (KORE_RESULT_ERROR); } - hdlr->methods |= method; + rt->methods |= method; if (method == HTTP_METHOD_GET) - hdlr->methods |= HTTP_METHOD_HEAD; + rt->methods |= HTTP_METHOD_HEAD; - if (!python_route_params(kwargs, hdlr, val, method, 0)) + if (!python_route_params(kwargs, rt, val, method, 0)) return (KORE_RESULT_ERROR); - if (!python_route_params(kwargs, hdlr, "qs", method, 1)) + if (!python_route_params(kwargs, rt, "qs", method, 1)) return (KORE_RESULT_ERROR); } @@ -5354,14 +5351,14 @@ python_route_methods(PyObject *obj, PyObject *kwargs, } static int -python_route_params(PyObject *kwargs, struct kore_module_handle *hdlr, +python_route_params(PyObject *kwargs, struct kore_route *rt, const char *method, int type, int qs) { Py_ssize_t idx; const char *val; int vtype; struct kore_validator *vldr; - struct kore_handler_params *param; + struct kore_route_params *param; PyObject *obj, *key, *item; if ((obj = PyDict_GetItemString(kwargs, method)) == NULL) @@ -5418,14 +5415,14 @@ python_route_params(PyObject *kwargs, struct kore_module_handle *hdlr, if (type == HTTP_METHOD_GET || qs == 1) param->flags = KORE_PARAMS_QUERY_STRING; - TAILQ_INSERT_TAIL(&hdlr->params, param, list); + TAILQ_INSERT_TAIL(&rt->params, param, list); } return (KORE_RESULT_OK); } static int -python_route_auth(PyObject *dict, struct kore_module_handle *hdlr) +python_route_auth(PyObject *dict, struct kore_route *rt) { int type; struct kore_auth *auth; @@ -5449,7 +5446,7 @@ python_route_auth(PyObject *dict, struct kore_module_handle *hdlr) } else { PyErr_Format(PyExc_RuntimeError, "invalid 'type' (%s) in auth dictionary for '%s'", - value, hdlr->path); + value, rt->path); return (KORE_RESULT_ERROR); } @@ -5464,7 +5461,7 @@ python_route_auth(PyObject *dict, struct kore_module_handle *hdlr) if ((obj = PyDict_GetItemString(dict, "verify")) == NULL || !PyCallable_Check(obj)) { PyErr_Format(PyExc_RuntimeError, - "missing 'verify' in auth dictionary for '%s'", hdlr->path); + "missing 'verify' in auth dictionary for '%s'", rt->path); return (KORE_RESULT_ERROR); } @@ -5495,7 +5492,7 @@ python_route_auth(PyObject *dict, struct kore_module_handle *hdlr) Py_DECREF(repr); auth->validator = vldr; - hdlr->auth = auth; + rt->auth = auth; return (KORE_RESULT_OK); } diff --git a/src/route.c b/src/route.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <dlfcn.h> + +#include "kore.h" +#include "http.h" + +struct kore_route * +kore_route_create(struct kore_domain *dom, const char *path, int type) +{ + struct kore_route *rt; + + rt = kore_calloc(1, sizeof(*rt)); + rt->dom = dom; + rt->type = type; + rt->path = kore_strdup(path); + rt->methods = HTTP_METHOD_ALL; + + TAILQ_INIT(&rt->params); + + if (rt->type == HANDLER_TYPE_DYNAMIC) { + if (regcomp(&rt->rctx, rt->path, REG_EXTENDED | REG_NOSUB)) { + kore_route_free(rt); + return (NULL); + } + } + + TAILQ_INSERT_TAIL(&dom->routes, rt, list); + + return (rt); +} + +void +kore_route_free(struct kore_route *rt) +{ + struct kore_route_params *param; + + if (rt == NULL) + return; + + kore_free(rt->func); + kore_free(rt->path); + + if (rt->type == HANDLER_TYPE_DYNAMIC) + regfree(&rt->rctx); + + /* Drop all validators associated with this handler */ + while ((param = TAILQ_FIRST(&rt->params)) != NULL) { + TAILQ_REMOVE(&rt->params, param, list); + kore_free(param->name); + kore_free(param); + } + + kore_free(rt); +} + +void +kore_route_callback(struct kore_route *rt, const char *func) +{ + if ((rt->rcall = kore_runtime_getcall(func)) == NULL) + fatal("callback '%s' for '%s' not found", func, rt->path); + + kore_free(rt->func); + rt->func = kore_strdup(func); +} + +int +kore_route_lookup(struct http_request *req, struct kore_domain *dom, + int method, struct kore_route **out) +{ + struct kore_route *rt; + int exists; + + exists = 0; + *out = NULL; + + TAILQ_FOREACH(rt, &dom->routes, list) { + if (rt->type == HANDLER_TYPE_STATIC) { + if (!strcmp(rt->path, req->path)) { + if (rt->methods & method) { + *out = rt; + return (1); + } + exists++; + } + } else { + if (!regexec(&rt->rctx, req->path, + HTTP_CAPTURE_GROUPS, req->cgroups, 0)) { + if (rt->methods & method) { + *out = rt; + return (1); + } + exists++; + } + } + } + + return (exists); +} + +void +kore_route_reload(void) +{ + struct kore_route *rt; + struct kore_server *srv; + struct kore_domain *dom; + + LIST_FOREACH(srv, &kore_servers, list) { + TAILQ_FOREACH(dom, &srv->domains, list) { + TAILQ_FOREACH(rt, &dom->routes, list) { + kore_free(rt->rcall); + rt->rcall = kore_runtime_getcall(rt->func); + if (rt->rcall == NULL) { + fatal("no function '%s' for route '%s'", + rt->func, rt->path); + } + rt->errors = 0; + } + } + } +} diff --git a/src/worker.c b/src/worker.c @@ -191,10 +191,11 @@ kore_worker_spawn(u_int16_t idx, u_int16_t id, u_int16_t cpu) kw = WORKER(idx); kw->id = id; kw->cpu = cpu; - kw->has_lock = 0; - kw->active_hdlr = NULL; kw->running = 1; + kw->ready = 0; + kw->has_lock = 0; + kw->active_route = NULL; if (socketpair(AF_UNIX, SOCK_STREAM, 0, kw->pipe) == -1) fatal("socketpair(): %s", errno_s); @@ -743,7 +744,7 @@ kore_worker_started(void) if (!kore_quiet) { kore_log(LOG_NOTICE, - "process started (#%d %s=%s%s%s)", + "started (#%d %s=%s%s%s)", getpid(), chroot, worker->ps->root, worker->ps->skip_runas ? "" : " user=", worker->ps->skip_runas ? "" : worker->ps->runas); @@ -797,8 +798,8 @@ worker_reaper(pid_t pid, int status) func = "none"; #if !defined(KORE_NO_HTTP) - if (kw->active_hdlr != NULL) - func = kw->active_hdlr->func; + if (kw->active_route != NULL) + func = kw->active_route->func; #endif kore_log(LOG_NOTICE, "worker %d (pid: %d) (hdlr: %s) gone", @@ -828,12 +829,12 @@ worker_reaper(pid_t pid, int status) worker_unlock(); #if !defined(KORE_NO_HTTP) - if (kw->active_hdlr != NULL) { - kw->active_hdlr->errors++; + if (kw->active_route != NULL) { + kw->active_route->errors++; kore_log(LOG_NOTICE, "hdlr %s has caused %d error(s)", - kw->active_hdlr->func, - kw->active_hdlr->errors); + kw->active_route->func, + kw->active_route->errors); } #endif