smr/src/libkore.c

530 lines
14 KiB
C

/*Export kore's functions to lua*/
#include <kore/kore.h>
#include <kore/http.h>
#ifdef BUILD_PROD
#include <luajit.h>
#endif
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
//#include <inet/in.h>//linux only I guess
#include "libkore.h"
#include "smr.h" //Where the error handler code is
#include <syslog.h>
#define LUA_PUSH_CONST(L,a) lua_pushnumber(L,a); lua_setfield(L,-2,#a);
struct http_request*
luaL_checkrequest(lua_State *L, int pos){
if(!lua_isuserdata(L,pos)){
lua_pushstring(L,"Bad argument, expected userdata, got ");
lua_pushstring(L,lua_typename(L,lua_type(L,pos)));
lua_concat(L,2);
lua_error(L);
}
return lua_touserdata(L,pos);
}
/*
http_response(request::userdata, errcode::number, (data::string | nil))
*/
int
lhttp_response(lua_State *L){
size_t size;
const char *data;
if(lua_isnil(L,-1)){
data = NULL;
size = 0;
}else{
data = luaL_checklstring(L,-1,&size);
}
int httpcode = luaL_checkint(L,-2);
struct http_request *req = luaL_checkrequest(L,-3);
http_response(req,httpcode,data,size);
lua_pop(L,3);
return 0;
}
/*Helpers for response coroutines*/
int
coroutine_iter_sent(struct netbuf *buf){
printf("Iter sent called\n");
struct co_obj *obj = (struct co_obj*)buf->extra;
lua_State *L = obj->L;
printf("\tbuf:%p\n",(void*)buf);
printf("\tobj:%p\n",(void*)obj);
printf("\tL:%p\n",(void*)L);
printf("Top is: %d\n",lua_gettop(L));
printf("Getting status...\n");
lua_getglobal(L,"coroutine");
printf("Found coroutine...\n");
lua_getfield(L,-1,"status");
printf("Found status...\n");
lua_rawgeti(L,LUA_REGISTRYINDEX,obj->ref);
printf("About to get status\n");
lua_call(L,1,1);
printf("Status got\n");
const char *status = luaL_checklstring(L,-1,NULL);
printf("status in sent: %s\n",status);
if(strcmp(status,"dead") == 0){
printf("Cleanup\n");
return KORE_RESULT_OK;
}else{
printf("About to call iter_next from iter_sent\n");
return coroutine_iter_next(obj);
}
}
const char response[] = "0\r\n\r\n";
int coroutine_iter_next(struct co_obj *obj){
printf("Coroutine iter next called\n");
lua_State *L = obj->L;
lua_getglobal(L,"coroutine");
lua_getfield(L,-1,"status");
lua_rawgeti(L,LUA_REGISTRYINDEX,obj->ref);
lua_call(L,1,1);
const char *status = luaL_checklstring(L,-1,NULL);
printf("status in next: %s\n",status);
lua_pop(L,lua_gettop(L));
printf("Calling resume\n");
lua_getglobal(L,"coroutine");
printf("Getting resume()\n");
lua_getfield(L,-1,"resume");
printf("Getting function\n");
lua_rawgeti(L,LUA_REGISTRYINDEX,obj->ref);
printf("Checking type\n");
luaL_checktype(L,-1,LUA_TTHREAD);
printf("About to call resume()\n");
int err = lua_pcall(L,1,2,0);
printf("Done calling resume()\n");
if(!lua_toboolean(L,-2)){ //Runtime error
printf("Runtime error\n");
lua_pushstring(L,":\n");//"error",":"
printf("top1:%d\n",lua_gettop(L));
lua_getglobal(L,"debug");//"error",":",{debug}
printf("top2:%d\n",lua_gettop(L));
lua_getfield(L,-1,"traceback");//"error",":",{debug},debug.traceback()
printf("top3:%d\n",lua_gettop(L));
lua_call(L,0,1);//"error",":",{debug},"traceback"
printf("top4:%d\n",lua_gettop(L));
lua_remove(L,-2);//"error",":","traceback"
printf("top5:%d\n",lua_gettop(L));
lua_concat(L,3);
printf("top6:%d\n",lua_gettop(L));
size_t size;
const char *s = luaL_checklstring(L,-1,&size);
printf("Error: %s\n",s);
lua_pop(L,lua_gettop(L));
return (KORE_RESULT_ERROR);
}
//No runtime error
if(lua_type(L,-1) == LUA_TSTRING){
printf("Data yielded\n");
size_t size;
const char *data = luaL_checklstring(L,-1,&size);
struct netbuf *nb;
printf("Yielding data stream size %lld\n",size);
struct kore_buf *kb = kore_buf_alloc(0);
kore_buf_appendf(kb,"%x\r\n",size);
kore_buf_append(kb,data,size);
size_t ssize;
char *sstr = kore_buf_stringify(kb,&ssize);
net_send_stream(obj->c, sstr, ssize, coroutine_iter_sent, &nb);
nb->extra = obj;
lua_pop(L,lua_gettop(L));
kore_buf_free(kb);
return (KORE_RESULT_RETRY);
//return err == 0 ? (KORE_RESULT_OK) : (KORE_RESULT_RETRY);
}else if(lua_type(L,-1) == LUA_TNIL){
printf("Done with function\n");
struct netbuf *nb;
printf("About to send final bit\n");
net_send_stream(obj->c, response, strlen(response) + 1, coroutine_iter_sent, &nb);
nb->extra = obj;
printf("Done sending final bit\n");
lua_pop(L,lua_gettop(L));
printf("Poped everything\n");
return (KORE_RESULT_OK);
}else{
printf("Coroutine used for response returned something that was not a string:%s\n",lua_typename(L,lua_type(L,-1)));
return (KORE_RESULT_ERROR);
}
}
static void
coroutine_disconnect(struct connection *c){
printf("Disconnect routine called\n");
struct co_obj *obj = (struct co_obj*)c->hdlr_extra;
lua_State *L = obj->L;
int ref = obj->ref;
luaL_unref(L,LUA_REGISTRYINDEX,ref);
free(obj);
printf("Done with disconnect\n");
}
/*
The coroutine passed to this function should yield() the data to send to the
client, then return when done.
http_response_co(request::userdata, co::coroutine)
*/
int
lhttp_response_co(lua_State *L){
printf("Start response coroutine\n");
int coroutine_ref = luaL_ref(L,LUA_REGISTRYINDEX);
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
req->flags |= HTTP_REQUEST_NO_CONTENT_LENGTH;
struct co_obj *obj = (struct co_obj*)malloc(sizeof(struct co_obj));
obj->L = lua_newthread(L);
obj->ref = coroutine_ref;
obj->c = req->owner;
obj->c->flags |= CONN_IS_BUSY;
obj->c->disconnect = coroutine_disconnect;
obj->c->hdlr_extra = obj;
printf("About to call iter next\n");
http_response(req,200,NULL,0);
coroutine_iter_next(obj);
printf("Done calling iter next\n");
}
/*
http_method_text(request::userdata)::string
*/
int
lhttp_method_text(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
const char *method = http_method_text(req->method);
lua_pushstring(L,method);
return 1;
}
/*
http_request_get_path(request::userdata)::string
*/
int
lhttp_request_get_path(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
const char *path = req->path;
lua_pushstring(L,path);
return 1;
}
/*
http_request_get_host(request::userdata)::string
*/
int
lhttp_request_get_host(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
const char *host = req->host;
lua_pushstring(L,host);
return 1;
}
/*
http_request_populate_post(request::userdata)
*/
int
lhttp_request_populate_post(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_post(req);
return 0;
}
/*
http_request_populate_qs(request::userdata)
*/
int
lhttp_request_populate_qs(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_qs(req);
return 0;
}
/*
http_response_header(request::userdata, header::string, value::string)
*/
int
lhttp_response_header(lua_State *L){
const char *value = luaL_checkstring(L,-1);
const char *header = luaL_checkstring(L,-2);
struct http_request *req = luaL_checkrequest(L,-3);
lua_pop(L,3);
http_response_header(req, header, value);
return 0;
}
/*
http_request_header(request::userdata, header::string)::(string || false, string)
*/
int
lhttp_request_header(lua_State *L){
const char *header = luaL_checkstring(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
const char *data;
int err = http_request_header(req,header,&data);
if(err == KORE_RESULT_OK){
lua_pushstring(L,data);
return 1;
}else{
lua_pushboolean(L,0);
lua_pushstring(L,"Failed to get header: ");
lua_pushstring(L,header);
lua_concat(L,2);
return 2;
}
}
/*
http_response_cookie(req::userdata, name::string, value::string, path::string, expires::number, maxage::number)
*/
int
lhttp_response_cookie(lua_State *L){
//int flags = luaL_checkint(L,-1);//TODO: flags
int maxage = luaL_checkint(L,-1);
int expires = luaL_checkint(L,-2);
const char *path = luaL_checkstring(L,-3);
const char *value = luaL_checkstring(L,-4);
const char *name = luaL_checkstring(L,-5);
struct http_request *req = luaL_checkrequest(L,-6);
lua_pop(L,6);
http_response_cookie(req,name,value,path,expires,maxage,NULL);
return 0;
}
/*
http_request_cookie(req::userdata, name::string)::string | nil
*/
int
lhttp_request_cookie(lua_State *L){
char *value;
const char *name = luaL_checkstring(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
if(http_request_cookie(req,name,&value)){
lua_pushstring(L,value);
}else{
lua_pushnil(L);
}
return 1;
}
/*
This method may fail, if it does, it will return false plus an error message
http_argument_get_string(request::userdata, name::string)::(string || false, string)
*/
int
lhttp_argument_get_string(lua_State *L){
const char *name = luaL_checkstring(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
const char *value;
int err = http_argument_get_string(req,name,&value);
if(err == KORE_RESULT_OK){
lua_pushstring(L,value);
return 1;
}else{
lua_pushboolean(L,0);
lua_pushstring(L,"Failed to find argument: ");
lua_pushstring(L,name);
lua_concat(L,2);
return 2;
}
}
/*
Get the ip of this connection, ipv6 supported
http_request_get_ip(request::userdata)::string
*/
int
lhttp_request_get_ip(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
char addr[INET6_ADDRSTRLEN];
switch(req->owner->family){
case AF_INET:
inet_ntop(
req->owner->family,
&(req->owner->addr.ipv4.sin_addr),
addr,
sizeof(addr)
);
break;
case AF_INET6:
inet_ntop(
req->owner->family,
&(req->owner->addr.ipv6.sin6_addr),
addr,
sizeof(addr)
);
break;
case AF_UNIX:
default:
lua_pushstring(L,"Tried to get IP from unknown socket family:");
lua_getglobal(L,"tostring");
lua_pushnumber(L,req->owner->family);
lua_call(L,1,1);
lua_concat(L,2);
lua_error(L);
break;
}
lua_pushstring(L,addr);
return 1;
}
/*
http_populate_cookies(request::userdata)
*/
int
lhttp_populate_cookies(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_cookies(req);
return 0;
}
/*
http_populate_multipart_form(request::userdata)
*/
int
lhttp_populate_multipart_form(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
http_populate_multipart_form(req);
return 0;
}
/*
http_file_get(request::userdata, name::string)::string
*/
int
lhttp_file_get(lua_State *L){
const char *name = luaL_checkstring(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
struct http_file *f = http_file_lookup(req, name);
if(f == NULL){
lua_pushstring(L,"No such file:");
lua_pushstring(L,name);
lua_concat(L,2);
lua_error(L);
}
char s[f->length + 1];
size_t read = http_file_read(f,s,f->length);
if(read < f->length){
lua_pushstring(L,"Failed to read contents of:");
lua_pushstring(L,name);
lua_concat(L,2);
lua_error(L);
}
s[f->length] = '\0';
lua_pushlstring(L,s,read);
return 1;
}
/*
http_set_flags(request::userdata, flags::number)
*/
int
lhttp_set_flags(lua_State *L){
int flags = luaL_checkint(L,-1);
struct http_request *req = luaL_checkrequest(L,-2);
lua_pop(L,2);
req->flags = flags;
}
/*
http_get_flags(request::userdata) :: number
*/
int
lhttp_get_flags(lua_State *L){
struct http_request *req = luaL_checkrequest(L,-1);
lua_pop(L,1);
lua_pushnumber(L,req->flags);
}
/*
log(priority::integer,message::string) //formating must be done before calling
*/
int
lkore_log(lua_State *L){
const char *str = luaL_checkstring(L,-1);
int prio = luaL_checkint(L,-2);
lua_pop(L,2);
kore_log(prio,"%s",str);
return 0;
}
static const luaL_Reg kore_funcs[] = {
{"http_response", lhttp_response},
{"http_response_co", lhttp_response_co},
{"http_response_header", lhttp_response_header},
{"http_request_header", lhttp_request_header},
{"http_method_text",lhttp_method_text},
{"http_request_get_path",lhttp_request_get_path},
{"http_request_get_host",lhttp_request_get_host},
{"http_request_populate_post",lhttp_request_populate_post},
{"http_request_populate_qs",lhttp_request_populate_qs},
{"http_request_cookie",lhttp_request_cookie},
{"http_response_cookie",lhttp_response_cookie},
{"http_argument_get_string",lhttp_argument_get_string},
{"http_request_get_ip",lhttp_request_get_ip},
{"http_populate_cookies",lhttp_populate_cookies},
{"http_populate_multipart_form",lhttp_populate_multipart_form},
{"http_file_get",lhttp_file_get},
{"http_set_flags",lhttp_set_flags},
{"http_get_flags",lhttp_get_flags},
{"log",lkore_log},
{NULL,NULL}
};
static const luaL_Reg http_request_meta[] = {
{NULL,NULL}
};
void
load_kore_libs(lua_State *L){
luaL_newmetatable(L,"http_request");//{m_http_request}
lua_newtable(L);//{m_http_request},{}
luaL_register(L,NULL,http_request_meta);//{m_http_request},{meta}
lua_setfield(L,-2,"__index");//{m_http_request}
lua_pop(L,1);
lua_getglobal(L,"_G");
luaL_register(L,NULL,kore_funcs);
//Push priority constants for use with log()
LUA_PUSH_CONST(L,LOG_EMERG);
LUA_PUSH_CONST(L,LOG_ALERT);
LUA_PUSH_CONST(L,LOG_CRIT);
LUA_PUSH_CONST(L,LOG_ERR);
LUA_PUSH_CONST(L,LOG_WARNING);
LUA_PUSH_CONST(L,LOG_NOTICE);
LUA_PUSH_CONST(L,LOG_INFO);
LUA_PUSH_CONST(L,LOG_DEBUG);
//Push flags for use with http_set_flags()
LUA_PUSH_CONST(L,HTTP_REQUEST_COMPLETE);
LUA_PUSH_CONST(L,HTTP_REQUEST_DELETE);
LUA_PUSH_CONST(L,HTTP_REQUEST_SLEEPING);
LUA_PUSH_CONST(L,HTTP_REQUEST_EXPECT_BODY);
LUA_PUSH_CONST(L,HTTP_REQUEST_RETAIN_EXTRA);
LUA_PUSH_CONST(L,HTTP_REQUEST_NO_CONTENT_LENGTH);
LUA_PUSH_CONST(L,HTTP_REQUEST_AUTHED);
//Set a global variable "PRODUCTION" true or false
#ifdef BUILD_PROD
lua_pushboolean(L,1);
#else
lua_pushboolean(L,0);
#endif
lua_setfield(L,-2,"PRODUCTION");
lua_pop(L,1);
}