530 lines
14 KiB
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);
|
|
}
|