kore

Kore is a web application platform for writing scalable, concurrent web based processes in C or Python.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

lua.c (12893B)



      1 /*
      2  * Copyright (c) 2023 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/types.h>
     18 
     19 #include "kore.h"
     20 
     21 #if !defined(KORE_NO_HTTP)
     22 #include "http.h"
     23 #endif
     24 
     25 #include "lua_api.h"
     26 #include "lua_methods.h"
     27 
     28 struct lua_http_request {
     29 	struct http_request	*req;
     30 };
     31 
     32 struct lua_symbol {
     33 	lua_State		*L;
     34 	int			ref;
     35 	LIST_ENTRY(lua_symbol)	list;
     36 };
     37 
     38 struct lua_module {
     39 	lua_State			*L;
     40 	LIST_HEAD(, lua_symbol)		symbols;
     41 };
     42 
     43 static int	lua_runtime_resolve(const char *, const struct stat *);
     44 static int	lua_runtime_http_request(void *, struct http_request *);
     45 static void	lua_runtime_http_request_free(void *, struct http_request *);
     46 static int	lua_runtime_http_body_chunk(void *, struct http_request *,
     47 		    const void *, size_t);
     48 static int	lua_runtime_validator(void *, struct http_request *,
     49 		    const void *);
     50 static void	lua_runtime_wsmessage(void *, struct connection *,
     51 		    u_int8_t, const void *, size_t);
     52 static void	lua_runtime_execute(void *);
     53 static int	lua_runtime_onload(void *, int);
     54 static void	lua_runtime_signal(void *, int);
     55 static void	lua_runtime_configure(void *, int, char **);
     56 static void	lua_runtime_connect(void *, struct connection *);
     57 
     58 static void	lua_module_load(struct kore_module *);
     59 static void	lua_module_free(struct kore_module *);
     60 static void	lua_module_reload(struct kore_module *);
     61 static void	*lua_module_getsym(struct kore_module *, const char *);
     62 
     63 static void	*lua_mem_alloc(void *, void *, size_t, size_t);
     64 
     65 static int	lua_kore_module_init(lua_State *);
     66 static void	lua_symbol_resolve(struct lua_symbol *, lua_State **);
     67 
     68 static int		lua_argument_get_bool(lua_State *, const char *);
     69 static const char	*lua_argument_get_string(lua_State *, const char *);
     70 
     71 struct kore_module_functions kore_lua_module = {
     72 	.free = lua_module_free,
     73 	.load = lua_module_load,
     74 	.getsym = lua_module_getsym,
     75 	.reload = lua_module_reload
     76 };
     77 
     78 struct kore_runtime kore_lua_runtime = {
     79 	KORE_RUNTIME_LUA,
     80 	.resolve = lua_runtime_resolve,
     81 	.http_request = lua_runtime_http_request,
     82 	.http_body_chunk = lua_runtime_http_body_chunk,
     83 	.http_request_free = lua_runtime_http_request_free,
     84 	.validator = lua_runtime_validator,
     85 	.wsconnect = lua_runtime_connect,
     86 	.wsmessage = lua_runtime_wsmessage,
     87 	.wsdisconnect = lua_runtime_connect,
     88 	.onload = lua_runtime_onload,
     89 	.signal = lua_runtime_signal,
     90 	.connect = lua_runtime_connect,
     91 	.execute = lua_runtime_execute,
     92 	.configure = lua_runtime_configure,
     93 };
     94 
     95 #define LUA_CONSTANT(x)		{ #x, x }
     96 
     97 static struct {
     98 	const char		*symbol;
     99 	int			value;
    100 } lua_integers[] = {
    101 	LUA_CONSTANT(LOG_ERR),
    102 	LUA_CONSTANT(LOG_INFO),
    103 	LUA_CONSTANT(LOG_NOTICE),
    104 	LUA_CONSTANT(HTTP_METHOD_GET),
    105 	LUA_CONSTANT(HTTP_METHOD_PUT),
    106 	LUA_CONSTANT(HTTP_METHOD_POST),
    107 	LUA_CONSTANT(HTTP_METHOD_HEAD),
    108 	LUA_CONSTANT(HTTP_METHOD_PATCH),
    109 	LUA_CONSTANT(HTTP_METHOD_DELETE),
    110 	LUA_CONSTANT(HTTP_METHOD_OPTIONS),
    111 	{ NULL, -1 },
    112 };
    113 
    114 void
    115 kore_lua_init(void)
    116 {
    117 	if (!kore_configure_setting("deployment", "dev"))
    118 		fatal("failed to set initial deployment to dev");
    119 }
    120 
    121 void
    122 kore_lua_cleanup(void)
    123 {
    124 }
    125 
    126 static void *
    127 lua_mem_alloc(void *uptr, void *ptr, size_t osize, size_t nsize)
    128 {
    129 	if (nsize == 0) {
    130 		kore_free(ptr);
    131 		return (NULL);
    132 	}
    133 
    134 	return (kore_realloc(ptr, nsize));
    135 }
    136 
    137 static void
    138 lua_symbol_resolve(struct lua_symbol *sym, lua_State **L)
    139 {
    140 	lua_rawgeti(sym->L, LUA_REGISTRYINDEX, sym->ref);
    141 	*L = sym->L;
    142 }
    143 
    144 static int
    145 lua_argument_get_bool(lua_State *L, const char *field)
    146 {
    147 	int		ret;
    148 
    149 	lua_pushstring(L, field);
    150 	ret = lua_gettable(L, -2);
    151 
    152 	if (ret == LUA_TNIL) {
    153 		lua_pop(L, 1);
    154 		return (0);
    155 	}
    156 
    157 	luaL_argcheck(L, ret == LUA_TBOOLEAN, 0, field);
    158 
    159 	ret = lua_toboolean(L, -1);
    160 	lua_pop(L, 1);
    161 
    162 	return (ret);
    163 }
    164 
    165 static const char *
    166 lua_argument_get_string(lua_State *L, const char *field)
    167 {
    168 	const char	*v;
    169 	int		type;
    170 
    171 	lua_pushstring(L, field);
    172 	type = lua_gettable(L, -2);
    173 
    174 	if (type == LUA_TNIL) {
    175 		lua_pop(L, 1);
    176 		return (NULL);
    177 	}
    178 
    179 	luaL_argcheck(L, type == LUA_TSTRING, 0, field);
    180 
    181 	v = lua_tostring(L, -1);
    182 	lua_pop(L, 1);
    183 
    184 	return (v);
    185 }
    186 
    187 static int
    188 lua_kore_module_init(lua_State *L)
    189 {
    190 	int		i;
    191 
    192 	luaL_newlib(L, lua_kore_functions);
    193 
    194 	for (i = 0; lua_integers[i].symbol != NULL; i++) {
    195 		lua_pushstring(L, lua_integers[i].symbol);
    196 		lua_pushnumber(L, lua_integers[i].value);
    197 		lua_settable(L, -3);
    198 	}
    199 
    200 	return (1);
    201 }
    202 
    203 static void
    204 lua_module_free(struct kore_module *module)
    205 {
    206 	struct lua_symbol	*sym;
    207 	struct lua_module	*lua;
    208 
    209 	lua = module->handle;
    210 
    211 	while ((sym = LIST_FIRST(&lua->symbols)) != NULL) {
    212 		LIST_REMOVE(sym, list);
    213 		kore_free(sym);
    214 	}
    215 
    216 	kore_free(lua);
    217 }
    218 
    219 static void
    220 lua_module_reload(struct kore_module *module)
    221 {
    222 	lua_module_free(module);
    223 	lua_module_load(module);
    224 }
    225 
    226 static void
    227 lua_module_load(struct kore_module *module)
    228 {
    229 	struct lua_module	*lua;
    230 
    231 	lua = kore_calloc(1, sizeof(*lua));
    232 	LIST_INIT(&lua->symbols);
    233 
    234 	if ((lua->L = lua_newstate(lua_mem_alloc, NULL)) == NULL)
    235 		fatal("luaL_newstate");
    236 
    237 	luaL_openlibs(lua->L);
    238 
    239 	luaL_requiref(lua->L, "kore", lua_kore_module_init, 1);
    240 	lua_pop(lua->L, 1);
    241 
    242 	luaL_newmetatable(lua->L, "http_request");
    243 	luaL_setfuncs(lua->L, lua_http_request_meta, 0);
    244 	lua_pop(lua->L, 1);
    245 
    246 	lua_pushliteral(lua->L, "http_request_methods");
    247 	luaL_newlib(lua->L, lua_http_request_methods);
    248 	lua_settable(lua->L, LUA_REGISTRYINDEX);
    249 
    250 	luaL_newlib(lua->L, lua_http_request_methods);
    251 	lua_pop(lua->L, 1);
    252 
    253 	if (luaL_loadfile(lua->L, module->path) != LUA_OK) {
    254 		fatal("%s: failed to import module (%s)", module->path,
    255 		    lua_tostring(lua->L, -1));
    256 	}
    257 
    258 	if (lua_pcall(lua->L, 0, 0, 0) != LUA_OK) {
    259 		fatal("%s: failed to import module (%s)", module->path,
    260 		    lua_tostring(lua->L, -1));
    261 	}
    262 
    263 	module->handle = lua;
    264 }
    265 
    266 static void *
    267 lua_module_getsym(struct kore_module *module, const char *symbol)
    268 {
    269 	int			ref;
    270 	struct lua_module	*lua;
    271 	struct lua_symbol	*sym;
    272 
    273 	lua = module->handle;
    274 
    275 	if (lua_getglobal(lua->L, symbol) != LUA_TFUNCTION)
    276 		return (NULL);
    277 
    278 	if ((ref = luaL_ref(lua->L, LUA_REGISTRYINDEX)) == LUA_REFNIL)
    279 		return (NULL);
    280 
    281 	sym = kore_calloc(1, sizeof(*sym));
    282 
    283 	sym->ref = ref;
    284 	sym->L = lua->L;
    285 
    286 	LIST_INSERT_HEAD(&lua->symbols, sym, list);
    287 
    288 	return (sym);
    289 }
    290 
    291 static int
    292 lua_runtime_resolve(const char *module, const struct stat *st)
    293 {
    294 	const char	*ext;
    295 
    296 	if (!S_ISREG(st->st_mode))
    297 		return (KORE_RESULT_ERROR);
    298 
    299 	ext = strrchr(module, '.');
    300 
    301 	if (ext == NULL || strcasecmp(ext, ".lua"))
    302 		return (KORE_RESULT_ERROR);
    303 
    304 	kore_module_load(module, NULL, KORE_MODULE_LUA);
    305 
    306 	return (KORE_RESULT_OK);
    307 }
    308 
    309 static int
    310 lua_runtime_http_request(void *addr, struct http_request *req)
    311 {
    312 	lua_State			*L;
    313 	struct lua_http_request		*lreq;
    314 
    315 	lua_symbol_resolve(addr, &L);
    316 
    317 	lreq = lua_newuserdata(L, sizeof(*lreq));
    318 	luaL_setmetatable(L, "http_request");
    319 
    320 	lreq->req = req;
    321 
    322 	if (lua_pcall(L, 1, 0, 0)) {
    323 		kore_log(LOG_NOTICE, "%s: failed to call handler: %s", __func__,
    324 		    lua_tostring(L, -1));
    325 		http_response(req, 500, NULL, 0);
    326 		return (KORE_RESULT_OK);
    327 	}
    328 
    329 	return (KORE_RESULT_OK);
    330 }
    331 
    332 static void
    333 lua_runtime_http_request_free(void *addr, struct http_request *req)
    334 {
    335 	fatal("%s: not yet implemented", __func__);
    336 }
    337 
    338 static int
    339 lua_runtime_http_body_chunk(void *addr, struct http_request *req,
    340     const void *data, size_t len)
    341 {
    342 	fatal("%s: not yet implemented", __func__);
    343 
    344 	return (KORE_RESULT_ERROR);
    345 }
    346 
    347 static int
    348 lua_runtime_validator(void *addr, struct http_request *req, const void *data)
    349 {
    350 	fatal("%s: not yet implemented", __func__);
    351 
    352 	return (KORE_RESULT_ERROR);
    353 }
    354 
    355 static void
    356 lua_runtime_wsmessage(void *addr, struct connection *c, u_int8_t op,
    357     const void *data, size_t len)
    358 {
    359 	fatal("%s: not yet implemented", __func__);
    360 }
    361 
    362 static void
    363 lua_runtime_execute(void *addr)
    364 {
    365 	lua_State	*L;
    366 
    367 	lua_symbol_resolve(addr, &L);
    368 
    369 	if (lua_pcall(L, 0, 0, 0)) {
    370 		fatal("failed to execute function: %s",
    371 		    lua_tostring(L, -1));
    372 	}
    373 }
    374 
    375 static void
    376 lua_runtime_configure(void *addr, int argc, char **argv)
    377 {
    378 	lua_State	*L;
    379 	int		idx;
    380 
    381 	lua_symbol_resolve(addr, &L);
    382 
    383 	lua_pushinteger(L, argc);
    384 	lua_newtable(L);
    385 
    386 	for (idx = 0; idx < argc; idx++) {
    387 		lua_pushstring(L, argv[idx]);
    388 		lua_rawseti(L, -2, idx);
    389 	}
    390 
    391 	if (lua_pcall(L, 2, 0, 0)) {
    392 		fatal("failed to configure your application (%s)",
    393 		    lua_tostring(L, -1));
    394 	}
    395 }
    396 
    397 static int
    398 lua_runtime_onload(void *addr, int action)
    399 {
    400 	fatal("%s: not yet implemented", __func__);
    401 
    402 	return (KORE_RESULT_ERROR);
    403 }
    404 
    405 static void
    406 lua_runtime_connect(void *addr, struct connection *c)
    407 {
    408 	fatal("%s: not yet implemented", __func__);
    409 }
    410 
    411 static void
    412 lua_runtime_signal(void *addr, int sig)
    413 {
    414 	fatal("%s: not yet implemented", __func__);
    415 }
    416 
    417 static int
    418 lua_kore_config(lua_State *L)
    419 {
    420 	char		*v;
    421 	const char	*opt, *val;
    422 
    423 	lua_pushnil(L);
    424 
    425 	while (lua_next(L, -2) != 0) {
    426 		if (!lua_isstring(L, -2))
    427 			fatal("kore.config: keyword not a string");
    428 
    429 		opt = lua_tostring(L, -2);
    430 
    431 		if (lua_isinteger(L, -1)) {
    432 			lua_pushvalue(L, -1);
    433 			val = lua_tostring(L, -1);
    434 			lua_pop(L, 1);
    435 		} else if (lua_isstring(L, -1)) {
    436 			val = lua_tostring(L, -1);
    437 		} else {
    438 			fatal("kore.config: value not a string or integer");
    439 		}
    440 
    441 		v = kore_strdup(val);
    442 
    443 		if (!kore_configure_setting(opt, v)) {
    444 			kore_free(v);
    445 			luaL_error(L, "kore.config: cannot be set at runtime");
    446 			lua_pop(L, 1);
    447 			return (0);
    448 		}
    449 
    450 		kore_free(v);
    451 		lua_pop(L, 1);
    452 	}
    453 
    454 	return (0);
    455 }
    456 
    457 static int
    458 lua_kore_server(lua_State *L)
    459 {
    460 	struct kore_server	*srv;
    461 	const char		*name, *ip, *port;
    462 
    463 	if ((name = lua_argument_get_string(L, "name")) == NULL)
    464 		name = "default";
    465 
    466 	if ((ip = lua_argument_get_string(L, "ip")) == NULL) {
    467 		luaL_error(L, "kore.server: missing ip keyword");
    468 		return (0);
    469 	}
    470 
    471 	if ((port = lua_argument_get_string(L, "port")) == NULL) {
    472 		luaL_error(L, "kore.server: missing port keyword");
    473 		return (0);
    474 	}
    475 
    476 	if ((srv = kore_server_lookup(name)) != NULL) {
    477 		luaL_error(L, "kore.server: server '%s' exists", name);
    478 		return (0);
    479 	}
    480 
    481 	srv = kore_server_create(name);
    482 	srv->tls = lua_argument_get_bool(L, "tls");
    483 
    484 	if (srv->tls && !kore_tls_supported()) {
    485 		kore_server_free(srv);
    486 		luaL_error(L, "kore.server: TLS not supported");
    487 		return (0);
    488 	}
    489 
    490 	if (!kore_server_bind(srv, ip, port, NULL)) {
    491 		kore_server_free(srv);
    492 		luaL_error(L, "kore.server: failed to bind %s:%s", ip, port);
    493 		return (0);
    494 	}
    495 
    496 	kore_server_finalize(srv);
    497 
    498 	return (0);
    499 }
    500 
    501 static int
    502 lua_http_request_gc(lua_State *L)
    503 {
    504 	struct lua_http_request		*lreq;
    505 
    506 	lreq = luaL_checkudata(L, 1, "http_request");
    507 	kore_free(lreq);
    508 
    509 	return (0);
    510 }
    511 
    512 static int
    513 lua_http_request_index(lua_State *L)
    514 {
    515 	struct lua_http_request		*lreq;
    516 	const char			*field;
    517 
    518 	lreq = luaL_checkudata(L, 1, "http_request");
    519 	field = luaL_checkstring(L, 2);
    520 
    521 	lua_getfield(L, LUA_REGISTRYINDEX, "http_request_methods");
    522 	lua_getfield(L, -1, field);
    523 
    524 	if (!lua_isnil(L, -1))
    525 		return (1);
    526 
    527 	lua_pop(L, 2);
    528 
    529 	if (!strcmp(field, "path")) {
    530 		lua_pushstring(L, lreq->req->path);
    531 		return (1);
    532 	} else if (!strcmp(field, "host")) {
    533 		lua_pushstring(L, lreq->req->host);
    534 		return (1);
    535 	} else if (!strcmp(field, "agent")) {
    536 		lua_pushstring(L, lreq->req->agent);
    537 		return (1);
    538 	} else if (!strcmp(field, "referer")) {
    539 		lua_pushstring(L, lreq->req->referer);
    540 		return (1);
    541 	} else if (!strcmp(field, "method")) {
    542 		lua_pushinteger(L, lreq->req->method);
    543 		return (1);
    544 	}
    545 
    546 	return (0);
    547 }
    548 
    549 static int
    550 lua_http_response_header(lua_State *L)
    551 {
    552 	struct lua_http_request		*lreq;
    553 	const char			*header, *value;
    554 
    555 	lreq = luaL_checkudata(L, 1, "http_request");
    556 	header = luaL_checkstring(L, 2);
    557 	value = luaL_checkstring(L, 3);
    558 
    559 	http_response_header(lreq->req, header, value);
    560 
    561 	return (0);
    562 }
    563 
    564 static int
    565 lua_http_request_header(lua_State *L)
    566 {
    567 	struct lua_http_request		*lreq;
    568 	const char			*header, *value;
    569 
    570 	lreq = luaL_checkudata(L, 1, "http_request");
    571 	header = luaL_checkstring(L, 2);
    572 
    573 	if (!http_request_header(lreq->req, header, &value)) {
    574 		lua_pushnil(L);
    575 	} else {
    576 		lua_pushstring(L, value);
    577 	}
    578 
    579 	return (1);
    580 }
    581 
    582 static int
    583 lua_http_response(lua_State *L)
    584 {
    585 	size_t				len;
    586 	struct lua_http_request		*lreq;
    587 	const void			*data;
    588 	int				status;
    589 
    590 	lreq = luaL_checkudata(L, 1, "http_request");
    591 	status = luaL_checkinteger(L, 2);
    592 
    593 	if (lua_isnil(L, 3)) {
    594 		len = 0;
    595 		data = NULL;
    596 	} else {
    597 		data = luaL_checklstring(L, 3, &len);
    598 	}
    599 
    600 	http_response(lreq->req, status, data, len);
    601 
    602 	return (0);
    603 }