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