LCOV - code coverage report
Current view: top level - lib - lwan-mod-lua.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 89 144 61.8 %
Date: 2023-04-18 16:19:03 Functions: 8 10 80.0 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2017 L. A. F. Pereira <l@tia.mat.br>
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU General Public License
       7             :  * as published by the Free Software Foundation; either version 2
       8             :  * of the License, or any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
      18             :  * USA.
      19             :  */
      20             : 
      21             : #define _GNU_SOURCE
      22             : #include <ctype.h>
      23             : #include <lauxlib.h>
      24             : #include <lualib.h>
      25             : #include <pthread.h>
      26             : #include <stdlib.h>
      27             : #include <string.h>
      28             : 
      29             : #include "lwan-private.h"
      30             : 
      31             : #include "lwan-array.h"
      32             : #include "lwan-cache.h"
      33             : #include "lwan-config.h"
      34             : #include "lwan-lua.h"
      35             : #include "lwan-mod-lua.h"
      36             : 
      37             : struct lwan_lua_priv {
      38             :     char *default_type;
      39             :     char *script_file;
      40             :     char *script;
      41             :     pthread_key_t cache_key;
      42             :     unsigned cache_period;
      43             : };
      44             : 
      45             : struct lwan_lua_state {
      46             :     struct cache_entry base;
      47             :     lua_State *L;
      48             : };
      49             : 
      50           8 : static struct cache_entry *state_create(const void *key __attribute__((unused)),
      51             :                                         void *context)
      52             : {
      53           8 :     struct lwan_lua_priv *priv = context;
      54           8 :     struct lwan_lua_state *state = malloc(sizeof(*state));
      55             : 
      56           8 :     if (UNLIKELY(!state))
      57           0 :         return NULL;
      58             : 
      59           8 :     state->L = lwan_lua_create_state(priv->script_file, priv->script);
      60           8 :     if (LIKELY(state->L))
      61           8 :         return (struct cache_entry *)state;
      62             : 
      63           0 :     free(state);
      64           0 :     return NULL;
      65             : }
      66             : 
      67           0 : static void state_destroy(struct cache_entry *entry,
      68             :                           void *context __attribute__((unused)))
      69             : {
      70           0 :     struct lwan_lua_state *state = (struct lwan_lua_state *)entry;
      71             : 
      72           0 :     lua_close(state->L);
      73           0 :     free(state);
      74           0 : }
      75             : 
      76           8 : static struct cache *get_or_create_cache(struct lwan_lua_priv *priv)
      77             : {
      78           8 :     struct cache *cache = pthread_getspecific(priv->cache_key);
      79             : 
      80           8 :     if (UNLIKELY(!cache)) {
      81           8 :         lwan_status_debug("Creating cache for this thread");
      82             :         cache =
      83           8 :             cache_create(state_create, state_destroy, priv, priv->cache_period);
      84           8 :         if (UNLIKELY(!cache))
      85           0 :             lwan_status_error("Could not create cache");
      86             :         /* FIXME: This cache instance leaks: store it somewhere and
      87             :          * free it on module shutdown */
      88           8 :         pthread_setspecific(priv->cache_key, cache);
      89             :     }
      90             : 
      91           8 :     return cache;
      92             : }
      93             : 
      94           8 : static void unref_thread(void *data1, void *data2)
      95             : {
      96           8 :     lua_State *L = data1;
      97           8 :     int thread_ref = (int)(intptr_t)data2;
      98             : 
      99           8 :     luaL_unref(L, LUA_REGISTRYINDEX, thread_ref);
     100           8 : }
     101             : 
     102             : static ALWAYS_INLINE struct lwan_value
     103             : get_handle_prefix(struct lwan_request *request)
     104             : {
     105             : #define ENTRY(s) {.value = s, .len = sizeof(s) - 1}
     106             : #define GEN_TABLE_ENTRY(upper, lower, mask, constant, probability)             \
     107             :     [REQUEST_METHOD_##upper] = ENTRY("handle_" #lower "_"),
     108             : 
     109             :     static const struct lwan_value method2name[REQUEST_METHOD_MASK] = {
     110             :         FOR_EACH_REQUEST_METHOD(GEN_TABLE_ENTRY)
     111             :     };
     112             : 
     113             : #undef GEN_TABLE_ENTRY
     114             : #undef ENTRY
     115             : 
     116           8 :     return method2name[lwan_request_get_method(request)];
     117             : }
     118             : 
     119           8 : static bool get_handler_function(lua_State *L, struct lwan_request *request)
     120             : {
     121             :     char handler_name[128];
     122             :     struct lwan_value handle_prefix = get_handle_prefix(request);
     123             : 
     124           8 :     if (UNLIKELY(!handle_prefix.len))
     125           0 :         return false;
     126           8 :     if (UNLIKELY(request->url.len >= sizeof(handler_name) - handle_prefix.len))
     127           0 :         return false;
     128             : 
     129             :     char *url;
     130             :     size_t url_len;
     131           8 :     if (request->url.len) {
     132           7 :         url = strndupa(request->url.value, request->url.len);
     133             : 
     134          59 :         for (char *c = url; *c; c++) {
     135          52 :             if (*c == '/') {
     136           0 :                 *c = '\0';
     137           0 :                 break;
     138             :             }
     139             : 
     140          52 :             if (UNLIKELY(!isalnum(*c) && *c != '_'))
     141           0 :                 return false;
     142             :         }
     143             : 
     144           7 :         url_len = strlen(url);
     145             :     } else {
     146           1 :         url = "root";
     147           1 :         url_len = 4;
     148             :     }
     149             : 
     150             :     size_t total_len;
     151           8 :     if (UNLIKELY(__builtin_add_overflow(handle_prefix.len, url_len, &total_len)))
     152           0 :         return false;
     153           8 :     if (UNLIKELY(total_len > sizeof(handler_name) - 1))
     154           0 :         return false;
     155             : 
     156           8 :     char *method_name = mempcpy(handler_name, handle_prefix.value, handle_prefix.len);
     157           8 :     memcpy(method_name, url, url_len + 1);
     158             : 
     159           8 :     lua_getglobal(L, handler_name);
     160           8 :     return lua_isfunction(L, -1);
     161             : }
     162             : 
     163           8 : static lua_State *push_newthread(lua_State *L, struct coro *coro)
     164             : {
     165           8 :     lua_State *L1 = lua_newthread(L);
     166             : 
     167           8 :     if (UNLIKELY(!L1))
     168           0 :         return NULL;
     169             : 
     170           8 :     int thread_ref = luaL_ref(L, LUA_REGISTRYINDEX);
     171           8 :     coro_defer2(coro, unref_thread, L, (void *)(intptr_t)thread_ref);
     172             : 
     173           8 :     return L1;
     174             : }
     175             : 
     176           8 : static enum lwan_http_status lua_handle_request(struct lwan_request *request,
     177             :                                                 struct lwan_response *response,
     178             :                                                 void *instance)
     179             : {
     180           8 :     struct lwan_lua_priv *priv = instance;
     181             : 
     182           8 :     struct cache *cache = get_or_create_cache(priv);
     183           8 :     if (UNLIKELY(!cache))
     184           0 :         return HTTP_INTERNAL_ERROR;
     185             : 
     186             :     struct lwan_lua_state *state =
     187           8 :         (struct lwan_lua_state *)cache_coro_get_and_ref_entry(
     188           8 :             cache, request->conn->coro, "");
     189           8 :     if (UNLIKELY(!state))
     190           0 :         return HTTP_NOT_FOUND;
     191             : 
     192           8 :     lua_State *L = push_newthread(state->L, request->conn->coro);
     193           8 :     if (UNLIKELY(!L))
     194           0 :         return HTTP_INTERNAL_ERROR;
     195             : 
     196           8 :     if (UNLIKELY(!get_handler_function(L, request)))
     197           0 :         return HTTP_NOT_FOUND;
     198             : 
     199           8 :     int n_arguments = 1;
     200           8 :     lwan_lua_state_push_request(L, request);
     201           8 :     response->mime_type = priv->default_type;
     202             :     while (true) {
     203           8 :         switch (lua_resume(L, n_arguments)) {
     204           0 :         case LUA_YIELD:
     205           0 :             coro_yield(request->conn->coro, CONN_CORO_YIELD);
     206           0 :             n_arguments = 0;
     207           0 :             break;
     208           8 :         case 0:
     209           8 :             if (lua_isnil(L, -1))
     210           6 :                 return HTTP_OK;
     211             : 
     212           2 :             if (lua_isnumber(L, -1)) {
     213           2 :                 lua_Integer code = lua_tointeger(L, -1);
     214             : 
     215           2 :                 if (code >= 100 && code <= 999)
     216           1 :                     return (enum lwan_http_status)code;
     217             :             }
     218             : 
     219           1 :             return HTTP_INTERNAL_ERROR;
     220           0 :         default:
     221           0 :             lwan_status_error("Error from Lua script: %s", lua_tostring(L, -1));
     222           0 :             return HTTP_INTERNAL_ERROR;
     223             :         }
     224             :     }
     225             : }
     226             : 
     227         266 : static void *lua_create(const char *prefix __attribute__((unused)), void *data)
     228             : {
     229         266 :     struct lwan_lua_settings *settings = data;
     230             :     struct lwan_lua_priv *priv;
     231             : 
     232         266 :     priv = calloc(1, sizeof(*priv));
     233         266 :     if (!priv) {
     234           0 :         lwan_status_error("Could not allocate memory for private Lua struct");
     235           0 :         return NULL;
     236             :     }
     237             : 
     238         266 :     priv->default_type =
     239         266 :         strdup(settings->default_type ? settings->default_type : "text/plain");
     240         266 :     if (!priv->default_type) {
     241           0 :         lwan_status_perror("strdup");
     242           0 :         goto error;
     243             :     }
     244             : 
     245         266 :     if (settings->script) {
     246         179 :         priv->script = strdup(settings->script);
     247         179 :         if (!priv->script) {
     248           0 :             lwan_status_perror("strdup");
     249           0 :             goto error;
     250             :         }
     251          87 :     } else if (settings->script_file) {
     252          87 :         priv->script_file = strdup(settings->script_file);
     253          87 :         if (!priv->script_file) {
     254           0 :             lwan_status_perror("strdup");
     255           0 :             goto error;
     256             :         }
     257             :     } else {
     258           0 :         lwan_status_error("No Lua script_file or script provided");
     259           0 :         goto error;
     260             :     }
     261             : 
     262         266 :     if (pthread_key_create(&priv->cache_key, NULL)) {
     263           0 :         lwan_status_perror("pthread_key_create");
     264           0 :         goto error;
     265             :     }
     266             : 
     267         266 :     priv->cache_period = settings->cache_period;
     268             : 
     269         266 :     return priv;
     270             : 
     271           0 : error:
     272           0 :     free(priv->script_file);
     273           0 :     free(priv->default_type);
     274           0 :     free(priv->script);
     275           0 :     free(priv);
     276             : 
     277           0 :     return NULL;
     278             : }
     279             : 
     280           0 : static void lua_destroy(void *instance)
     281             : {
     282           0 :     struct lwan_lua_priv *priv = instance;
     283             : 
     284           0 :     if (priv) {
     285           0 :         pthread_key_delete(priv->cache_key);
     286           0 :         free(priv->default_type);
     287           0 :         free(priv->script_file);
     288           0 :         free(priv->script);
     289           0 :         free(priv);
     290             :     }
     291           0 : }
     292             : 
     293         266 : static void *lua_create_from_hash(const char *prefix, const struct hash *hash)
     294             : {
     295        1064 :     struct lwan_lua_settings settings = {
     296         266 :         .default_type = hash_find(hash, "default_type"),
     297         266 :         .script_file = hash_find(hash, "script_file"),
     298         266 :         .cache_period = parse_time_period(hash_find(hash, "cache_period"), 15),
     299         266 :         .script = hash_find(hash, "script")
     300             :     };
     301             : 
     302         266 :     return lua_create(prefix, &settings);
     303             : }
     304             : 
     305             : static const struct lwan_module module = {
     306             :     .create = lua_create,
     307             :     .create_from_hash = lua_create_from_hash,
     308             :     .destroy = lua_destroy,
     309             :     .handle_request = lua_handle_request,
     310             :     .flags = HANDLER_EXPECTS_BODY_DATA,
     311             : };
     312             : 
     313             : LWAN_REGISTER_MODULE(lua, &module);

Generated by: LCOV version 1.15-2-gb9d6727