LCOV - code coverage report
Current view: top level - lib - lwan-lua.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 94 209 45.0 %
Date: 2023-04-18 16:19:03 Functions: 13 36 36.1 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2014 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 <errno.h>
      23             : #include <ctype.h>
      24             : #include <lauxlib.h>
      25             : #include <lualib.h>
      26             : #include <pthread.h>
      27             : #include <stdlib.h>
      28             : #include <string.h>
      29             : 
      30             : #include "lwan-private.h"
      31             : 
      32             : #include "lwan-lua.h"
      33             : 
      34             : #if defined(LWAN_HAVE_LUA_JIT)
      35             : #define luaL_reg luaL_Reg
      36             : #endif
      37             : 
      38             : static const char *request_metatable_name = "Lwan.Request";
      39             : 
      40           0 : ALWAYS_INLINE struct lwan_request *lwan_lua_get_request_from_userdata(lua_State *L)
      41             : {
      42           0 :     struct lwan_request **r = luaL_checkudata(L, 1, request_metatable_name);
      43             : 
      44          11 :     return *r;
      45             : }
      46             : 
      47           2 : LWAN_LUA_METHOD(say)
      48             : {
      49             :     size_t response_str_len;
      50           1 :     const char *response_str = lua_tolstring(L, -1, &response_str_len);
      51             : 
      52           1 :     lwan_strbuf_set_static(request->response.buffer, response_str,
      53             :                            response_str_len);
      54           1 :     lwan_response_send_chunk(request);
      55             : 
      56           1 :     return 0;
      57             : }
      58             : 
      59           0 : LWAN_LUA_METHOD(send_event)
      60             : {
      61             :     size_t event_str_len;
      62           0 :     const char *event_str = lua_tolstring(L, -1, &event_str_len);
      63           0 :     const char *event_name = lua_tostring(L, -2);
      64             : 
      65           0 :     lwan_strbuf_set_static(request->response.buffer, event_str, event_str_len);
      66           0 :     lwan_response_send_event(request, event_name);
      67             : 
      68           0 :     return 0;
      69             : }
      70             : 
      71          10 : LWAN_LUA_METHOD(set_response)
      72             : {
      73             :     size_t response_str_len;
      74           5 :     const char *response_str = lua_tolstring(L, -1, &response_str_len);
      75             : 
      76           5 :     lwan_strbuf_set(request->response.buffer, response_str, response_str_len);
      77             : 
      78           5 :     return 0;
      79             : }
      80             : 
      81           3 : static int request_param_getter(lua_State *L,
      82             :                                 struct lwan_request *request,
      83             :                                 const char *(*getter)(struct lwan_request *req,
      84             :                                                       const char *key))
      85             : {
      86           3 :     const char *key_str = lua_tostring(L, -1);
      87           3 :     const char *value = getter(request, key_str);
      88             : 
      89           3 :     if (!value)
      90           1 :         lua_pushnil(L);
      91             :     else
      92           2 :         lua_pushstring(L, value);
      93             : 
      94           3 :     return 1;
      95             : }
      96             : 
      97           0 : LWAN_LUA_METHOD(remote_address)
      98             : {
      99             :     char ip_buffer[INET6_ADDRSTRLEN];
     100           0 :     lua_pushstring(L, lwan_request_get_remote_address(request, ip_buffer));
     101           0 :     return 1;
     102             : }
     103             : 
     104           0 : LWAN_LUA_METHOD(header)
     105             : {
     106           0 :     return request_param_getter(L, request, lwan_request_get_header);
     107             : }
     108             : 
     109           0 : LWAN_LUA_METHOD(is_https)
     110             : {
     111           0 :     lua_pushboolean(L, !!(request->conn->flags & CONN_TLS));
     112           0 :     return 1;
     113             : }
     114             : 
     115           0 : LWAN_LUA_METHOD(path)
     116             : {
     117           0 :     lua_pushlstring(L, request->url.value, request->url.len);
     118           0 :     return 1;
     119             : }
     120             : 
     121           0 : LWAN_LUA_METHOD(host)
     122             : {
     123           0 :     const char *host = lwan_request_get_host(request);
     124             : 
     125           0 :     if (host)
     126           0 :         lua_pushstring(L, host);
     127             :     else
     128           0 :         lua_pushnil(L);
     129             : 
     130           0 :     return 1;
     131             : }
     132             : 
     133           0 : LWAN_LUA_METHOD(query_string)
     134             : {
     135           0 :     if (request->helper->query_string.len) {
     136           0 :         lua_pushlstring(L, request->helper->query_string.value, request->helper->query_string.len);
     137             :     } else {
     138           0 :         lua_pushlstring(L, "", 0);
     139             :     }
     140           0 :     return 1;
     141             : }
     142             : 
     143           4 : LWAN_LUA_METHOD(query_param)
     144             : {
     145           2 :     return request_param_getter(L, request, lwan_request_get_query_param);
     146             : }
     147             : 
     148           0 : LWAN_LUA_METHOD(post_param)
     149             : {
     150           0 :     return request_param_getter(L, request, lwan_request_get_post_param);
     151             : }
     152             : 
     153           2 : LWAN_LUA_METHOD(cookie)
     154             : {
     155           1 :     return request_param_getter(L, request, lwan_request_get_cookie);
     156             : }
     157             : 
     158           0 : LWAN_LUA_METHOD(body)
     159             : {
     160           0 :     if (request->helper->body_data.len) {
     161           0 :         lua_pushlstring(L, request->helper->body_data.value, request->helper->body_data.len);
     162             :     } else {
     163           0 :         lua_pushlstring(L, "", 0);
     164             :     }
     165           0 :     return 1;
     166             : }
     167             : 
     168           0 : LWAN_LUA_METHOD(ws_upgrade)
     169             : {
     170           0 :     enum lwan_http_status status = lwan_request_websocket_upgrade(request);
     171             : 
     172           0 :     lua_pushinteger(L, status);
     173             : 
     174           0 :     return 1;
     175             : }
     176             : 
     177           0 : LWAN_LUA_METHOD(ws_write_text)
     178             : {
     179             :     size_t data_len;
     180           0 :     const char *data_str = lua_tolstring(L, -1, &data_len);
     181             : 
     182           0 :     lwan_strbuf_set_static(request->response.buffer, data_str, data_len);
     183           0 :     lwan_response_websocket_write_text(request);
     184             : 
     185           0 :     return 0;
     186             : }
     187             : 
     188           0 : LWAN_LUA_METHOD(ws_write_binary)
     189             : {
     190             :     size_t data_len;
     191           0 :     const char *data_str = lua_tolstring(L, -1, &data_len);
     192             : 
     193           0 :     lwan_strbuf_set_static(request->response.buffer, data_str, data_len);
     194           0 :     lwan_response_websocket_write_binary(request);
     195             : 
     196           0 :     return 0;
     197             : }
     198             : 
     199           0 : LWAN_LUA_METHOD(ws_write)
     200             : {
     201             :     size_t data_len;
     202           0 :     const char *data_str = lua_tolstring(L, -1, &data_len);
     203             : 
     204           0 :     lwan_strbuf_set_static(request->response.buffer, data_str, data_len);
     205             : 
     206           0 :     for (size_t i = 0; i < data_len; i++) {
     207           0 :         if ((signed char)data_str[i] < 0) {
     208           0 :             lwan_response_websocket_write_binary(request);
     209           0 :             return 0;
     210             :         }
     211             :     }
     212             : 
     213           0 :     lwan_response_websocket_write_text(request);
     214           0 :     return 0;
     215             : }
     216             : 
     217           0 : LWAN_LUA_METHOD(ws_read)
     218             : {
     219             :     int r;
     220             : 
     221             :     /* FIXME: maybe return a table {status=r, content=buf}? */
     222             : 
     223           0 :     r = lwan_response_websocket_read(request);
     224           0 :     switch (r) {
     225           0 :     case 0:
     226           0 :         lua_pushlstring(L, lwan_strbuf_get_buffer(request->response.buffer),
     227           0 :                         lwan_strbuf_get_length(request->response.buffer));
     228           0 :         break;
     229           0 :     case ENOTCONN:
     230             :     case EAGAIN:
     231           0 :         lua_pushinteger(L, r);
     232           0 :         break;
     233           0 :     default:
     234           0 :         lua_pushinteger(L, ENOMSG);
     235           0 :         break;
     236             :     }
     237             : 
     238           0 :     return 1;
     239             : }
     240             : 
     241           5 : static bool append_key_value(struct lwan_request *request,
     242             :                              lua_State *L,
     243             :                              struct coro *coro,
     244             :                              struct lwan_key_value_array *arr,
     245             :                              char *key,
     246             :                              int value_index)
     247             : {
     248             :     size_t len;
     249           5 :     const char *lua_value = lua_tolstring(L, value_index, &len);
     250           5 :     char *value = coro_memdup(coro, lua_value, len + 1);
     251             : 
     252           5 :     if (strcaseequal_neutral(key, "Content-Type")) {
     253           1 :         request->response.mime_type = value;
     254             :     } else {
     255             :         struct lwan_key_value *kv;
     256             : 
     257           4 :         kv = lwan_key_value_array_append(arr);
     258           4 :         if (!kv)
     259           0 :             return false;
     260             : 
     261           4 :         kv->key = key;
     262           4 :         kv->value = value;
     263             :     }
     264             : 
     265           5 :     return value != NULL;
     266             : }
     267             : 
     268           4 : LWAN_LUA_METHOD(set_headers)
     269             : {
     270           2 :     const int table_index = 2;
     271           2 :     const int key_index = -2;
     272           2 :     const int value_index = -1;
     273             :     struct lwan_key_value_array *headers;
     274           2 :     struct coro *coro = request->conn->coro;
     275             :     struct lwan_key_value *kv;
     276             : 
     277           2 :     if (request->flags & RESPONSE_SENT_HEADERS)
     278           0 :         goto out;
     279             : 
     280           2 :     if (!lua_istable(L, table_index))
     281           0 :         goto out;
     282             : 
     283           2 :     headers = coro_lwan_key_value_array_new(request->conn->coro);
     284           2 :     if (!headers)
     285           0 :         goto out;
     286             : 
     287           6 :     for (lua_pushnil(L); lua_next(L, table_index) != 0; lua_pop(L, 1)) {
     288             :         char *key;
     289             : 
     290           4 :         if (lua_type(L, key_index) != LUA_TSTRING)
     291           0 :             continue;
     292             : 
     293           4 :         key = coro_strdup(request->conn->coro, lua_tostring(L, key_index));
     294           4 :         if (!key)
     295           0 :             goto out;
     296             : 
     297           4 :         switch (lua_type(L, value_index)) {
     298           3 :         case LUA_TSTRING:
     299           3 :             if (!append_key_value(request, L, coro, headers, key, value_index))
     300           0 :                 goto out;
     301           3 :             break;
     302           1 :         case LUA_TTABLE:
     303           3 :             for (lua_pushnil(L); lua_next(L, value_index - 1) != 0; lua_pop(L, 1)) {
     304           2 :                 if (!lua_isstring(L, value_index))
     305           0 :                     continue;
     306           2 :                 if (!append_key_value(request, L, coro, headers, key,
     307             :                                       value_index))
     308           0 :                     goto out;
     309             :             }
     310           1 :             break;
     311             :         }
     312             :     }
     313             : 
     314           2 :     kv = lwan_key_value_array_append(headers);
     315           2 :     if (!kv)
     316           0 :         goto out;
     317           2 :     kv->key = kv->value = NULL;
     318             : 
     319           2 :     request->response.headers = lwan_key_value_array_get_array(headers);
     320           2 :     lua_pushinteger(L, (lua_Integer)headers->base.elements);
     321           2 :     return 1;
     322             : 
     323           0 : out:
     324           0 :     lua_pushnil(L);
     325           0 :     return 1;
     326             : }
     327             : 
     328           0 : LWAN_LUA_METHOD(sleep)
     329             : {
     330           0 :     lua_Integer ms = lua_tointeger(L, -1);
     331             : 
     332           0 :     lwan_request_sleep(request, (uint64_t)ms);
     333             : 
     334           0 :     return 0;
     335             : }
     336             : 
     337           0 : LWAN_LUA_METHOD(request_id)
     338             : {
     339           0 :     lua_pushfstring(L, "%016lx", lwan_request_get_id(request));
     340           0 :     return 1;
     341             : }
     342             : 
     343           0 : LWAN_LUA_METHOD(request_date)
     344             : {
     345           0 :     lua_pushstring(L, request->conn->thread->date.date);
     346           0 :     return 1;
     347             : }
     348             : 
     349             : #define IMPLEMENT_LOG_FUNCTION(name)                                           \
     350             :     static int lwan_lua_log_##name(lua_State *L)                               \
     351             :     {                                                                          \
     352             :         size_t log_str_len = 0;                                                \
     353             :         const char *log_str = lua_tolstring(L, -1, &log_str_len);              \
     354             :         if (log_str_len)                                                       \
     355             :             lwan_status_##name("%.*s", (int)log_str_len, log_str);             \
     356             :         return 0;                                                              \
     357             :     }
     358             : 
     359           0 : IMPLEMENT_LOG_FUNCTION(info)
     360           0 : IMPLEMENT_LOG_FUNCTION(warning)
     361           0 : IMPLEMENT_LOG_FUNCTION(error)
     362           0 : IMPLEMENT_LOG_FUNCTION(critical)
     363             : 
     364             : #undef IMPLEMENT_LOG_FUNCTION
     365             : 
     366          10 : static int luaopen_log(lua_State *L)
     367             : {
     368             :     static const char *metatable_name = "Lwan.log";
     369             :     static const struct luaL_Reg functions[] = {
     370             : #define LOG_FUNCTION(name) {#name, lwan_lua_log_##name}
     371             :         LOG_FUNCTION(info),
     372             :         LOG_FUNCTION(warning),
     373             :         LOG_FUNCTION(error),
     374             :         LOG_FUNCTION(critical),
     375             :         {},
     376             : #undef LOG_FUNCTION
     377             :     };
     378             : 
     379          10 :     luaL_newmetatable(L, metatable_name);
     380          10 :     luaL_register(L, metatable_name, functions);
     381             : 
     382          10 :     return 0;
     383             : }
     384             : 
     385        2126 : DEFINE_ARRAY_TYPE(lwan_lua_method_array, luaL_reg)
     386             : static struct lwan_lua_method_array lua_methods;
     387             : 
     388             : __attribute__((constructor))
     389             : __attribute__((no_sanitize_address))
     390          92 : static void register_lua_methods(void)
     391             : {
     392             :     const struct lwan_lua_method_info *info;
     393             :     luaL_reg *r;
     394             : 
     395        2116 :     LWAN_SECTION_FOREACH(lwan_lua_method, info) {
     396        2024 :         r = lwan_lua_method_array_append(&lua_methods);
     397        2024 :         if (!r) {
     398           0 :             lwan_status_critical("Could not register Lua method `%s`",
     399             :                                  info->name);
     400             :         }
     401             : 
     402        2024 :         r->name = info->name;
     403        2024 :         r->func = info->func;
     404             :     }
     405             : 
     406          92 :     r = lwan_lua_method_array_append(&lua_methods);
     407          92 :     if (!r)
     408           0 :         lwan_status_critical("Could not add Lua method sentinel");
     409             : 
     410          92 :     r->name = NULL;
     411          92 :     r->func = NULL;
     412          92 : }
     413             : 
     414           0 : const char *lwan_lua_state_last_error(lua_State *L)
     415             : {
     416           0 :     return lua_tostring(L, -1);
     417             : }
     418             : 
     419          10 : lua_State *lwan_lua_create_state(const char *script_file, const char *script)
     420             : {
     421             :     lua_State *L;
     422             : 
     423          10 :     L = luaL_newstate();
     424          10 :     if (UNLIKELY(!L))
     425           0 :         return NULL;
     426             : 
     427          10 :     luaL_openlibs(L);
     428          10 :     luaopen_log(L);
     429             : 
     430          10 :     luaL_newmetatable(L, request_metatable_name);
     431          10 :     luaL_register(L, NULL, lwan_lua_method_array_get_array(&lua_methods));
     432          10 :     lua_setfield(L, -1, "__index");
     433             : 
     434          10 :     if (script_file) {
     435           5 :         if (UNLIKELY(luaL_dofile(L, script_file) != 0)) {
     436           0 :             lwan_status_error("Error opening Lua script %s: %s", script_file,
     437             :                               lua_tostring(L, -1));
     438           0 :             goto close_lua_state;
     439             :         }
     440           5 :     } else if (script) {
     441           5 :         if (UNLIKELY(luaL_dostring(L, script) != 0)) {
     442           0 :             lwan_status_error("Error evaluating Lua script %s",
     443             :                               lua_tostring(L, -1));
     444           0 :             goto close_lua_state;
     445             :         }
     446             :     } else {
     447           0 :         lwan_status_error("Either file or inline script has to be provided");
     448           0 :         goto close_lua_state;
     449             :     }
     450             : 
     451          10 :     return L;
     452             : 
     453           0 : close_lua_state:
     454           0 :     lua_close(L);
     455           0 :     return NULL;
     456             : }
     457             : 
     458          10 : void lwan_lua_state_push_request(lua_State *L, struct lwan_request *request)
     459             : {
     460             :     struct lwan_request **userdata =
     461          10 :         lua_newuserdata(L, sizeof(struct lwan_request *));
     462             : 
     463          10 :     *userdata = request;
     464          10 :     luaL_getmetatable(L, request_metatable_name);
     465          10 :     lua_setmetatable(L, -2);
     466          10 : }

Generated by: LCOV version 1.15-2-gb9d6727