diff --git a/src/libkore.c b/src/libkore.c index 1972be0..cae519f 100644 --- a/src/libkore.c +++ b/src/libkore.c @@ -9,6 +9,7 @@ #include //#include //linux only I guess #include "libkore.h" +#include "smr.h" //Where the error handler code is #include #define LUA_PUSH_CONST(L,a) lua_pushnumber(L,a); lua_setfield(L,-2,#a); @@ -25,12 +26,18 @@ luaL_checkrequest(lua_State *L, int pos){ } /* -http_response(request::userdata, errcode::number, data::string) +http_response(request::userdata, errcode::number, (data::string | nil)) */ int lhttp_response(lua_State *L){ size_t size; - const char *data = luaL_checklstring(L,-1,&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); @@ -38,6 +45,150 @@ lhttp_response(lua_State *L){ 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 */ @@ -278,6 +429,27 @@ lhttp_file_get(lua_State *L){ 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 */ @@ -292,6 +464,7 @@ lkore_log(lua_State *L){ 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}, @@ -306,6 +479,8 @@ static const luaL_Reg kore_funcs[] = { {"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} }; @@ -334,6 +509,15 @@ load_kore_libs(lua_State *L){ 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); diff --git a/src/libkore.h b/src/libkore.h index 48b424b..6cfd984 100644 --- a/src/libkore.h +++ b/src/libkore.h @@ -1,5 +1,12 @@ +struct co_obj { + lua_State *L; + int ref; + struct connection *c; +}; int lhttp_response(lua_State *L); +int coroutine_iter_sent(struct netbuf *buf); +int coroutine_iter_next(struct co_obj *obj); int lhttp_response_header(lua_State *L); int lhttp_method_text(lua_State *L); int lhttp_request_get_path(lua_State *L); diff --git a/src/lua/config.lua.in b/src/lua/config.lua.in index 7d70695..c7c5621 100644 --- a/src/lua/config.lua.in +++ b/src/lua/config.lua.in @@ -5,4 +5,5 @@ A one-stop-shop for runtime configuration return { domain = "<{get domain}>", production = false, + legacy_url_cutoff = 144 } diff --git a/src/lua/endpoints/archive_get.lua b/src/lua/endpoints/archive_get.lua index 1e7004a..41a374f 100644 --- a/src/lua/endpoints/archive_get.lua +++ b/src/lua/endpoints/archive_get.lua @@ -1,7 +1,55 @@ local function archive(req) - local archive = assert(io.open("data/archive.zip","r")) - http_response_header(req,"Content-Type","application/zip") + local archive = assert(io.open("data/archive.zip","rb")) + --[=[ + local archive_size = archive:seek("end") + archive:seek("set") + local archive_cursor = 0 + local co = coroutine.create(function() + print("Inside coroutine!") + --[[ + for i = 1,10 do + local str = {tostring(i),":",} + for i = 1,10 do + table.insert(str,tostring(math.random())) + end + coroutine.yield(table.concat(str)) + end + ]] + for i = 1, 1000 do + coroutine.yield("Hello, world!" .. tostring(i)) + end + --[[ + while archive_cursor ~= archive_size do + print("Inside while") + local bytes_left = archive_size - archive_cursor + local next_chunk = math.min(4096,bytes_left) + print("Before yield") + coroutine.yield(archive:read(next_chunk)) + print("After yield") + end + archive:close() + ]] + end) + print("co status:",coroutine.status(co)) + --local bytes_start,bytes_end = 0, 200 + --http_response_header(req,"content-type","application/zip") + --http_response_header(req,"accept-ranges","bytes") + http_response_header(req,"transfer-encoding","chunked") + http_response_co(req,co) + print("a print after our response") + --[[ + local bytes_start,bytes_end = 0, 200 + http_response_header(req,"content-type","application/zip") + http_response_header(req,"accept-ranges","bytes") + assert(archive:seek("set",bytes_start)) + local data = assert(archive:read(bytes_end - bytes_start)) + http_response_stream(req,200,data,function() + print("Callback completed!") + end) + ]] + ]=] http_response_header(req,"Content-Disposition","attachment; filename=\"slash_monster_archive.zip\"") http_response(req,200,archive:read("*a")) + archive:close() end return archive diff --git a/src/lua/pages.lua b/src/lua/pages.lua index 87f60c6..f5c63ec 100644 --- a/src/lua/pages.lua +++ b/src/lua/pages.lua @@ -17,6 +17,7 @@ local pagenames = { "author_paste", "author_edit", "search", + "error", } local pages = {} for k,v in pairs(pagenames) do diff --git a/src/lua/util.lua b/src/lua/util.lua index b07350d..acd58ea 100644 --- a/src/lua/util.lua +++ b/src/lua/util.lua @@ -1,5 +1,6 @@ local sql = require("lsqlite3") +local config = require("config") local util = {} --[[ @@ -84,10 +85,18 @@ local url_characters = [[ABCDEFGHIJKLMNOPQRSTUVWXYZ]].. [[0123456789]] +local url_characters_legacy = + url_characters .. + [[$-+!*'(),]] + local url_characters_rev = {} for i = 1,string.len(url_characters) do url_characters_rev[string.sub(url_characters,i,i)] = i end +local url_characters_rev_legacy = {} +for i = 1,string.len(url_characters_legacy) do + url_characters_rev_legacy[string.sub(url_characters_legacy,i,i)] = i +end --[[ Encode a number to a shorter HTML-safe url path ]] @@ -117,6 +126,7 @@ function util.decode_id(s) return n end) if res then + print("returning id:",id) return id else print("Failed to decode id:" .. s) @@ -124,6 +134,32 @@ function util.decode_id(s) end end +--[[ +Legacy code, try to decode with invalid characters in the url first +]] +local new_decode = util.decode_id +function util.decode_id(s) + local res, id = pcall(function() + local n = 0 + local charlen = string.len(url_characters_legacy) + for i = 1,string.len(s) do + local char = string.sub(s,i,i) + local pos = url_characters_rev_legacy[char] - 1 + n = n + (pos * math.pow(charlen,i-1)) + end + return n + end) + if res then + if id > config.legacy_url_cutoff then + return new_decode(s) + else + return id + end + else + return false,"Failed to decode id:" .. s + end +end + --arbitary data to hex encoded string function util.encode_unlisted(str) assert(type(str) == "string","Tried to encode something not a string:" .. type(Str)) diff --git a/src/smr.h b/src/smr.h index e2e029f..fbe93e3 100644 --- a/src/smr.h +++ b/src/smr.h @@ -1 +1,3 @@ #define SM_INIT "init.lua" + +int errhandeler(lua_State *);