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.
This commit is contained in:
Joris Vink 2021-09-15 11:09:52 +02:00
parent 2576427dc0
commit e98a4ddab5
41 changed files with 631 additions and 769 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,8 +6,6 @@ server tls {
load ./generic.so example_load
tls_dhparam dh2048.pem
http_body_max 1024000000
http_body_disk_offload 1024000

View File

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

View File

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

View File

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

View File

@ -1,5 +0,0 @@
*.o
.objs
json_yajl.so
assets.h
cert

View File

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

View File

@ -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.
#}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,8 +4,6 @@ server tls {
bind 127.0.0.1 8888 connection_setup
}
tls_dhparam dh2048.pem
domain * {
attach tls

View File

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

View File

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

View File

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

View File

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

View File

@ -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. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

138
src/route.c Normal file
View File

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

View File

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