mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-05-14 18:53:43 +00:00
692 lines
14 KiB
C
692 lines
14 KiB
C
#include "../../uwsgi.h"
|
|
|
|
#include <lua.h>
|
|
#include <lualib.h>
|
|
#include <lauxlib.h>
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
struct uwsgi_lua {
|
|
struct lua_State **L;
|
|
|
|
char *filename;
|
|
} ulua;
|
|
|
|
#define LONG_ARGS_LUA_BASE 17000 + (6 * 100)
|
|
#define LONG_ARGS_LUA LONG_ARGS_LUA_BASE + 1
|
|
|
|
#define lca(L, n) ulua_check_args(L, __FUNCTION__, n)
|
|
|
|
struct option uwsgi_lua_options[] = {
|
|
|
|
{"lua", required_argument, 0, LONG_ARGS_LUA},
|
|
|
|
{0, 0, 0, 0},
|
|
|
|
};
|
|
|
|
static void ulua_check_args(lua_State *L, const char *func, int n) {
|
|
int args = lua_gettop(L);
|
|
char error[4096];
|
|
if (args != n) {
|
|
if (n == 1) {
|
|
snprintf(error, 4096, "uwsgi.%s takes 1 parameter", func+10);
|
|
}
|
|
else {
|
|
snprintf(error, 4096, "uwsgi.%s takes %d parameters", func+10, n);
|
|
}
|
|
lua_pushstring(L, error);
|
|
lua_error(L);
|
|
}
|
|
}
|
|
|
|
static int uwsgi_api_log(lua_State *L) {
|
|
|
|
time_t tt;
|
|
const char *logline ;
|
|
|
|
lca(L, 1);
|
|
|
|
if (lua_isstring(L, 1)) {
|
|
logline = lua_tolstring(L, 1, NULL);
|
|
tt = time(NULL);
|
|
if (logline[strlen(logline)] != '\n') {
|
|
uwsgi_log( UWSGI_LOGBASE " %.*s] %s\n", 24, ctime(&tt), logline);
|
|
}
|
|
else {
|
|
uwsgi_log( UWSGI_LOGBASE " %.*s] %s", 24, ctime(&tt), logline);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int uwsgi_api_register_rpc(lua_State *L) {
|
|
|
|
uint8_t argc = lua_gettop(L);
|
|
const char *name;
|
|
// a hack for 64bit;
|
|
int func;
|
|
long lfunc;
|
|
|
|
if (argc < 2) {
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
name = lua_tolstring(L, 1, NULL);
|
|
|
|
lua_pushvalue(L, 2);
|
|
func = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
uwsgi_log("registered function %d in global table\n", func);
|
|
lfunc = func;
|
|
|
|
if (uwsgi_register_rpc((char *)name, 6, 0, (void *) lfunc)) {
|
|
lua_pushnil(L);
|
|
}
|
|
else {
|
|
lua_pushboolean(L, 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
static char *encode_lua_table(lua_State *L, int index, uint16_t *size) {
|
|
|
|
char *buf, *ptrbuf;
|
|
char *key;
|
|
char *value;
|
|
size_t keylen;
|
|
size_t vallen;
|
|
|
|
*size = 0;
|
|
|
|
lua_pushnil(L);
|
|
while (lua_next(L, index) != 0) {
|
|
if (lua_isstring(L, -2) && lua_isstring(L, -1)) {
|
|
key = (char *) lua_tolstring(L, -2, &keylen);
|
|
value = (char *) lua_tolstring(L, -1, &vallen);
|
|
if (keylen > 0xffff || vallen > 0xffff) continue;
|
|
*size += (2+keylen+2+vallen);
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
buf = malloc(*size);
|
|
if (!buf) {
|
|
uwsgi_error("malloc()");
|
|
exit(1);
|
|
}
|
|
|
|
ptrbuf = buf;
|
|
lua_pushnil(L);
|
|
while (lua_next(L, index) != 0) {
|
|
if (lua_isstring(L, -2) && lua_isstring(L, -1)) {
|
|
key = (char *) lua_tolstring(L, -2, &keylen);
|
|
value = (char *) lua_tolstring(L, -1, &vallen);
|
|
|
|
if (keylen > 0xffff || vallen > 0xffff) continue;
|
|
|
|
*ptrbuf++ = (uint8_t) (keylen & 0xff);
|
|
*ptrbuf++ = (uint8_t) ((keylen >>8) & 0xff);
|
|
memcpy(ptrbuf, key, keylen); ptrbuf += keylen;
|
|
*ptrbuf++ = (uint8_t) (vallen & 0xff);
|
|
*ptrbuf++ = (uint8_t) ((vallen >>8) & 0xff);
|
|
memcpy(ptrbuf, value, vallen); ptrbuf += vallen;
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int uwsgi_api_cache_set(lua_State *L) {
|
|
|
|
int args = lua_gettop(L);
|
|
const char *key ;
|
|
const char *value ;
|
|
uint64_t expires = 0;
|
|
size_t vallen;
|
|
|
|
|
|
if (args > 1) {
|
|
|
|
key = lua_tolstring(L, 1, NULL);
|
|
value = lua_tolstring(L, 2, &vallen);
|
|
if (args > 2) {
|
|
expires = lua_tonumber(L, 3);
|
|
}
|
|
|
|
uwsgi_cache_set((char *)key, strlen(key), (char *)value, (uint16_t) vallen, expires);
|
|
|
|
}
|
|
|
|
lua_pushnil(L);
|
|
return 1;
|
|
|
|
}
|
|
|
|
static int uwsgi_api_register_signal(lua_State *L) {
|
|
|
|
int args = lua_gettop(L);
|
|
uint8_t sig, kind;
|
|
long lhandler;
|
|
const char *payload;
|
|
size_t payload_size;
|
|
|
|
if (args >= 3) {
|
|
|
|
sig = lua_tonumber(L, 1);
|
|
kind = lua_tonumber(L, 2);
|
|
lua_pushvalue(L, 3);
|
|
lhandler = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
if (args > 3) {
|
|
payload = lua_tolstring(L, 4, &payload_size);
|
|
uwsgi_register_signal(sig, kind, (void *) lhandler, 6, (char *) payload, payload_size);
|
|
}
|
|
else {
|
|
uwsgi_register_signal(sig, kind, (void *) lhandler, 6, NULL, 0);
|
|
}
|
|
}
|
|
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int uwsgi_api_cache_get(lua_State *L) {
|
|
|
|
char *value ;
|
|
uint16_t valsize;
|
|
const char *key ;
|
|
|
|
lca(L, 1);
|
|
|
|
if (lua_isstring(L, 1)) {
|
|
|
|
key = lua_tolstring(L, 1, NULL);
|
|
value = uwsgi_cache_get((char *)key, strlen(key), &valsize);
|
|
|
|
if (value) {
|
|
lua_pushlstring(L, value, valsize);
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
static int uwsgi_api_send_message(lua_State *L) {
|
|
|
|
int args = lua_gettop(L);
|
|
const char *host;
|
|
int uwsgi_fd;
|
|
uint8_t modifier1, modifier2;
|
|
char *pkt = NULL;
|
|
uint16_t pktsize = 0 ;
|
|
char buf[4096];
|
|
int rlen;
|
|
int items = 0;
|
|
int input_fd = -1, timeout = -1, input_size = 0;
|
|
|
|
// is this an fd ?
|
|
if (lua_isnumber(L, 1)) {
|
|
args = 1;
|
|
}
|
|
else if (lua_isstring(L, 1)) {
|
|
host = lua_tolstring(L, 1, NULL);
|
|
uwsgi_fd = uwsgi_connect((char *)host, timeout, 0);
|
|
modifier1 = lua_tonumber(L, 2);
|
|
modifier2 = lua_tonumber(L, 3);
|
|
if (args > 4) {
|
|
timeout = lua_tonumber(L, 5);
|
|
if (args == 7) {
|
|
input_fd = lua_tonumber(L, 6);
|
|
input_size = lua_tonumber(L, 7);
|
|
}
|
|
}
|
|
if (lua_istable(L,4)) {
|
|
// passed a table
|
|
pkt = encode_lua_table(L, 4, &pktsize);
|
|
}
|
|
if (uwsgi_send_message(uwsgi_fd, modifier1, modifier2, pkt, pktsize, input_fd, input_size, timeout) == -1) {
|
|
free(pkt);
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
free(pkt);
|
|
|
|
for(;;) {
|
|
rlen = uwsgi_waitfd(uwsgi_fd, timeout);
|
|
if (rlen > 0) {
|
|
rlen = read(uwsgi_fd, buf, 4096);
|
|
if (rlen < 0) {
|
|
uwsgi_error("read()");
|
|
break;
|
|
}
|
|
else if (rlen > 0) {
|
|
lua_pushlstring(L, buf, rlen);
|
|
items++;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else if (rlen == 0) {
|
|
uwsgi_log("uwsgi request timed out waiting for response\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
close(uwsgi_fd);
|
|
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
static int uwsgi_api_cl(lua_State *L) {
|
|
|
|
struct wsgi_request *wsgi_req = current_wsgi_req();
|
|
|
|
lua_pushnumber(L, wsgi_req->post_cl);
|
|
return 1;
|
|
}
|
|
|
|
static int uwsgi_api_req_fd(lua_State *L) {
|
|
|
|
struct wsgi_request *wsgi_req = current_wsgi_req();
|
|
|
|
lua_pushnumber(L, wsgi_req->poll.fd);
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_reg uwsgi_api[] = {
|
|
{"log", uwsgi_api_log},
|
|
{"cl", uwsgi_api_cl},
|
|
{"req_fd", uwsgi_api_req_fd},
|
|
{"send_message", uwsgi_api_send_message},
|
|
{"cache_get", uwsgi_api_cache_get},
|
|
{"cache_set", uwsgi_api_cache_set},
|
|
{"register_signal", uwsgi_api_register_signal},
|
|
{"register_rpc", uwsgi_api_register_rpc},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
|
|
|
|
static int uwsgi_lua_input(lua_State *L) {
|
|
|
|
struct wsgi_request *wsgi_req = current_wsgi_req();
|
|
ssize_t sum, len, total;
|
|
char *buf, *ptr;
|
|
|
|
int n = lua_gettop(L);
|
|
|
|
if (!wsgi_req->post_cl) {
|
|
lua_pushlstring(L, "", 0);
|
|
return 1;
|
|
}
|
|
|
|
sum = lua_tonumber(L, 2);
|
|
|
|
if (n > 1) {
|
|
uwsgi_log("requested %d bytes\n", sum);
|
|
}
|
|
|
|
buf = malloc(sum);
|
|
if (!buf) {
|
|
uwsgi_error("malloc()");
|
|
}
|
|
|
|
total = sum;
|
|
|
|
ptr = buf;
|
|
while(total) {
|
|
len = read(wsgi_req->poll.fd, ptr, total);
|
|
ptr += len;
|
|
total -= len;
|
|
}
|
|
|
|
lua_pushlstring(L, buf, sum);
|
|
free(buf);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int uwsgi_lua_init(){
|
|
|
|
uwsgi_log("Initializing Lua environment... (%d cores)\n", uwsgi.cores);
|
|
|
|
ulua.L = malloc( sizeof(lua_State*) * uwsgi.cores );
|
|
if (!ulua.L) {
|
|
uwsgi_error("malloc()");
|
|
exit(1);
|
|
}
|
|
|
|
// ok the lua engine is ready
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
void uwsgi_lua_app() {
|
|
int i;
|
|
|
|
if (ulua.filename) {
|
|
for(i=0;i<uwsgi.cores;i++) {
|
|
ulua.L[i] = luaL_newstate();
|
|
luaL_openlibs(ulua.L[i]);
|
|
luaL_register(ulua.L[i], "uwsgi", uwsgi_api);
|
|
if (luaL_loadfile(ulua.L[i], ulua.filename)) {
|
|
uwsgi_log("unable to load file %s\n", ulua.filename);
|
|
exit(1);
|
|
}
|
|
// use a pcall
|
|
//lua_call(ulua.L[i], 0, 1);
|
|
if (lua_pcall(ulua.L[i], 0, 1, 0) != 0) {
|
|
uwsgi_log("%s\n", lua_tostring(ulua.L[i], -1));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
int uwsgi_lua_request(struct wsgi_request *wsgi_req) {
|
|
|
|
int i;
|
|
int raw;
|
|
const char *http;
|
|
size_t slen;
|
|
ssize_t rlen;
|
|
char *ptrbuf;
|
|
lua_State *L = ulua.L[wsgi_req->async_id];
|
|
|
|
#ifdef UWSGI_ASYNC
|
|
if (wsgi_req->async_status == UWSGI_AGAIN) {
|
|
if ((i = lua_pcall(L, 0, 1, 0)) == 0) {
|
|
if (lua_type(L, -1) == LUA_TSTRING) {
|
|
http = lua_tolstring(L, -1, &slen);
|
|
if ( (rlen = write(wsgi_req->poll.fd, http, slen)) != (ssize_t) slen) {
|
|
perror("write()");
|
|
return UWSGI_OK;
|
|
}
|
|
wsgi_req->response_size += rlen;
|
|
}
|
|
lua_pop(L, 1);
|
|
lua_pushvalue(L, -1);
|
|
return UWSGI_AGAIN;
|
|
}
|
|
goto clear;
|
|
}
|
|
#endif
|
|
|
|
/* Standard WSAPI request */
|
|
if (!wsgi_req->uh.pktsize) {
|
|
uwsgi_log( "Invalid WSAPI request. skip.\n");
|
|
goto clear;
|
|
}
|
|
|
|
if (uwsgi_parse_vars(wsgi_req)) {
|
|
uwsgi_log("Invalid WSAPI request. skip.\n");
|
|
goto clear;
|
|
}
|
|
|
|
// put function in the stack
|
|
//lua_getfield(L, LUA_GLOBALSINDEX, "run");
|
|
lua_pushvalue(L, -1);
|
|
|
|
// put cgi vars in the stack
|
|
|
|
lua_newtable(L);
|
|
lua_pushstring(L, "");
|
|
lua_setfield(L, -2, "CONTENT_TYPE");
|
|
for(i=0;i<wsgi_req->var_cnt;i++) {
|
|
lua_pushlstring(L, (char *)wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len);
|
|
// transform it in a valid c string TODO this is ugly
|
|
ptrbuf = wsgi_req->hvec[i].iov_base+wsgi_req->hvec[i].iov_len;
|
|
*ptrbuf = 0;
|
|
lua_setfield(L, -2, (char *)wsgi_req->hvec[i].iov_base);
|
|
i++;
|
|
}
|
|
|
|
|
|
// put "input" table
|
|
lua_newtable(L);
|
|
lua_pushcfunction(L, uwsgi_lua_input);
|
|
lua_setfield(L, -2, "read");
|
|
lua_setfield(L, -2, "input");
|
|
|
|
|
|
// call function
|
|
i = lua_pcall(L, 1, 3, 0);
|
|
if (i != 0) {
|
|
uwsgi_log("%s\n", lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
goto clear;
|
|
}
|
|
|
|
//uwsgi_log("%d %s %s %s\n",i,lua_typename(L, lua_type(L, -3)), lua_typename(L, lua_type(L, -2)) , lua_typename(L, lua_type(L, -1)));
|
|
|
|
raw = 0;
|
|
// send status
|
|
if (lua_type(L, -3) == LUA_TSTRING || lua_type(L, -3) == LUA_TNUMBER) {
|
|
http = lua_tolstring(L, -3, &slen);
|
|
if (write(wsgi_req->poll.fd, wsgi_req->protocol, wsgi_req->protocol_len) != wsgi_req->protocol_len) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
if (write(wsgi_req->poll.fd, " ", 1) != 1) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
if (write(wsgi_req->poll.fd, http, slen) != (ssize_t) slen) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
// a performance hack
|
|
ptrbuf = (char *) http;
|
|
ptrbuf[3] = 0;
|
|
wsgi_req->status = atoi(ptrbuf);
|
|
if (write(wsgi_req->poll.fd, "\r\n", 2) != 2) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
}
|
|
else {
|
|
raw = 1;
|
|
wsgi_req->status = -1;
|
|
}
|
|
|
|
// send headers
|
|
|
|
lua_pushnil(L);
|
|
while(lua_next(L, -3) != 0) {
|
|
http = lua_tolstring(L, -2, &slen);
|
|
if (write(wsgi_req->poll.fd, http, slen) != (ssize_t) slen) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
if (write(wsgi_req->poll.fd, ": ", 2) != 2) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
http = lua_tolstring(L, -1, &slen);
|
|
if (write(wsgi_req->poll.fd, http, slen) != (ssize_t) slen) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
if (write(wsgi_req->poll.fd, "\r\n", 2) != 2) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
if (!raw) {
|
|
if (write(wsgi_req->poll.fd, "\r\n", 2) != 2) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
}
|
|
|
|
// send body with coroutine
|
|
lua_pushvalue(L, -1);
|
|
|
|
while ( (i = lua_pcall(L, 0, 1, 0)) == 0) {
|
|
if (lua_type(L, -1) == LUA_TSTRING) {
|
|
http = lua_tolstring(L, -1, &slen);
|
|
if ( (rlen = write(wsgi_req->poll.fd, http, slen)) != (ssize_t) slen) {
|
|
perror("write()");
|
|
goto clear;
|
|
}
|
|
wsgi_req->response_size += rlen;
|
|
}
|
|
lua_pop(L, 1);
|
|
lua_pushvalue(L, -1);
|
|
#ifdef UWSGI_ASYNC
|
|
if (uwsgi.async > 1) {
|
|
return UWSGI_AGAIN;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
clear:
|
|
|
|
lua_pop(L, 4);
|
|
|
|
// set frequency
|
|
lua_gc(L, LUA_GCCOLLECT, 0);
|
|
|
|
return UWSGI_OK;
|
|
|
|
}
|
|
|
|
void uwsgi_lua_after_request(struct wsgi_request *wsgi_req) {
|
|
|
|
if (uwsgi.shared->options[UWSGI_OPTION_LOGGING])
|
|
log_request(wsgi_req);
|
|
}
|
|
|
|
int uwsgi_lua_manage_options(int i, char *optarg) {
|
|
|
|
switch(i) {
|
|
case LONG_ARGS_LUA:
|
|
ulua.filename = optarg;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int uwsgi_lua_magic(char *mountpoint, char *lazy) {
|
|
|
|
if (!strcmp(lazy+strlen(lazy)-4, ".lua")) {
|
|
ulua.filename = lazy;
|
|
return 1;
|
|
}
|
|
else if (!strcmp(lazy+strlen(lazy)-3, ".ws")) {
|
|
ulua.filename = lazy;
|
|
return 1;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int uwsgi_lua_signal_handler(uint8_t sig, void *handler, char *payload, uint8_t payload_size) {
|
|
|
|
struct wsgi_request *wsgi_req = current_wsgi_req();
|
|
|
|
lua_State *L = ulua.L[wsgi_req->async_id];
|
|
|
|
uwsgi_log("managing signal handler on core %d\n", wsgi_req->async_id);
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, (long) handler);
|
|
|
|
lua_pushnumber(L, sig);
|
|
if (!payload_size) {
|
|
lua_pushlstring(L, "", 0);
|
|
}
|
|
else {
|
|
lua_pushlstring(L, payload, payload_size);
|
|
}
|
|
|
|
|
|
if (lua_pcall(L, 2, 1, 0) != 0) {
|
|
uwsgi_log("error running function `f': %s",
|
|
lua_tostring(L, -1));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
uint16_t uwsgi_lua_rpc(void * func, uint8_t argc, char **argv, char *buffer) {
|
|
|
|
uint8_t i;
|
|
const char *sv;
|
|
size_t sl;
|
|
long lfunc = (long) func;
|
|
int ifunc = lfunc;
|
|
|
|
struct wsgi_request *wsgi_req = current_wsgi_req();
|
|
|
|
lua_State *L = ulua.L[wsgi_req->async_id];
|
|
|
|
uwsgi_log("get function %d\n", ifunc);
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, ifunc);
|
|
|
|
for(i=0;i<argc;i++) {
|
|
lua_pushstring(L, argv[i]);
|
|
}
|
|
|
|
if (lua_pcall(L, argc, 1, 0) != 0) {
|
|
uwsgi_log("error running function `f': %s", lua_tostring(L, -1));
|
|
return 0;
|
|
}
|
|
|
|
|
|
sv = lua_tolstring(L, -1, &sl);
|
|
|
|
uwsgi_log("sv = %s sl = %d\n", sv, sl);
|
|
if (sl <= 0xffff) {
|
|
memcpy(buffer, sv, sl);
|
|
return sl;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
struct uwsgi_plugin lua_plugin = {
|
|
|
|
.name = "lua",
|
|
.modifier1 = 6,
|
|
.init = uwsgi_lua_init,
|
|
.options = uwsgi_lua_options,
|
|
.manage_opt = uwsgi_lua_manage_options,
|
|
.request = uwsgi_lua_request,
|
|
.after_request = uwsgi_lua_after_request,
|
|
.init_apps = uwsgi_lua_app,
|
|
.magic = uwsgi_lua_magic,
|
|
.signal_handler = uwsgi_lua_signal_handler,
|
|
.rpc = uwsgi_lua_rpc,
|
|
|
|
};
|
|
|