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