Fix id<->url decoding

Some urls should have been url-encoded, fix this issue in a way
that won't break old urls
This commit is contained in:
Robin Malley 2021-02-09 02:40:57 +00:00
parent 45e1ba3fcb
commit 0f17393cd8
7 changed files with 283 additions and 4 deletions

View File

@ -9,6 +9,7 @@
#include <lualib.h> #include <lualib.h>
//#include <inet/in.h>//linux only I guess //#include <inet/in.h>//linux only I guess
#include "libkore.h" #include "libkore.h"
#include "smr.h" //Where the error handler code is
#include <syslog.h> #include <syslog.h>
#define LUA_PUSH_CONST(L,a) lua_pushnumber(L,a); lua_setfield(L,-2,#a); #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 int
lhttp_response(lua_State *L){ lhttp_response(lua_State *L){
size_t size; 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); int httpcode = luaL_checkint(L,-2);
struct http_request *req = luaL_checkrequest(L,-3); struct http_request *req = luaL_checkrequest(L,-3);
http_response(req,httpcode,data,size); http_response(req,httpcode,data,size);
@ -38,6 +45,150 @@ lhttp_response(lua_State *L){
return 0; 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 http_method_text(request::userdata)::string
*/ */
@ -278,6 +429,27 @@ lhttp_file_get(lua_State *L){
return 1; 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 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[] = { static const luaL_Reg kore_funcs[] = {
{"http_response", lhttp_response}, {"http_response", lhttp_response},
{"http_response_co", lhttp_response_co},
{"http_response_header", lhttp_response_header}, {"http_response_header", lhttp_response_header},
{"http_request_header", lhttp_request_header}, {"http_request_header", lhttp_request_header},
{"http_method_text",lhttp_method_text}, {"http_method_text",lhttp_method_text},
@ -306,6 +479,8 @@ static const luaL_Reg kore_funcs[] = {
{"http_populate_cookies",lhttp_populate_cookies}, {"http_populate_cookies",lhttp_populate_cookies},
{"http_populate_multipart_form",lhttp_populate_multipart_form}, {"http_populate_multipart_form",lhttp_populate_multipart_form},
{"http_file_get",lhttp_file_get}, {"http_file_get",lhttp_file_get},
{"http_set_flags",lhttp_set_flags},
{"http_get_flags",lhttp_get_flags},
{"log",lkore_log}, {"log",lkore_log},
{NULL,NULL} {NULL,NULL}
}; };
@ -334,6 +509,15 @@ load_kore_libs(lua_State *L){
LUA_PUSH_CONST(L,LOG_INFO); LUA_PUSH_CONST(L,LOG_INFO);
LUA_PUSH_CONST(L,LOG_DEBUG); 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 //Set a global variable "PRODUCTION" true or false
#ifdef BUILD_PROD #ifdef BUILD_PROD
lua_pushboolean(L,1); lua_pushboolean(L,1);

View File

@ -1,5 +1,12 @@
struct co_obj {
lua_State *L;
int ref;
struct connection *c;
};
int lhttp_response(lua_State *L); 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_response_header(lua_State *L);
int lhttp_method_text(lua_State *L); int lhttp_method_text(lua_State *L);
int lhttp_request_get_path(lua_State *L); int lhttp_request_get_path(lua_State *L);

View File

@ -5,4 +5,5 @@ A one-stop-shop for runtime configuration
return { return {
domain = "<{get domain}>", domain = "<{get domain}>",
production = false, production = false,
legacy_url_cutoff = 144
} }

View File

@ -1,7 +1,55 @@
local function archive(req) local function archive(req)
local archive = assert(io.open("data/archive.zip","r")) local archive = assert(io.open("data/archive.zip","rb"))
http_response_header(req,"Content-Type","application/zip") --[=[
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_header(req,"Content-Disposition","attachment; filename=\"slash_monster_archive.zip\"")
http_response(req,200,archive:read("*a")) http_response(req,200,archive:read("*a"))
archive:close()
end end
return archive return archive

View File

@ -17,6 +17,7 @@ local pagenames = {
"author_paste", "author_paste",
"author_edit", "author_edit",
"search", "search",
"error",
} }
local pages = {} local pages = {}
for k,v in pairs(pagenames) do for k,v in pairs(pagenames) do

View File

@ -1,5 +1,6 @@
local sql = require("lsqlite3") local sql = require("lsqlite3")
local config = require("config")
local util = {} local util = {}
--[[ --[[
@ -84,10 +85,18 @@ local url_characters =
[[ABCDEFGHIJKLMNOPQRSTUVWXYZ]].. [[ABCDEFGHIJKLMNOPQRSTUVWXYZ]]..
[[0123456789]] [[0123456789]]
local url_characters_legacy =
url_characters ..
[[$-+!*'(),]]
local url_characters_rev = {} local url_characters_rev = {}
for i = 1,string.len(url_characters) do for i = 1,string.len(url_characters) do
url_characters_rev[string.sub(url_characters,i,i)] = i url_characters_rev[string.sub(url_characters,i,i)] = i
end 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 Encode a number to a shorter HTML-safe url path
]] ]]
@ -117,6 +126,7 @@ function util.decode_id(s)
return n return n
end) end)
if res then if res then
print("returning id:",id)
return id return id
else else
print("Failed to decode id:" .. s) print("Failed to decode id:" .. s)
@ -124,6 +134,32 @@ function util.decode_id(s)
end end
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 --arbitary data to hex encoded string
function util.encode_unlisted(str) function util.encode_unlisted(str)
assert(type(str) == "string","Tried to encode something not a string:" .. type(Str)) assert(type(str) == "string","Tried to encode something not a string:" .. type(Str))

View File

@ -1 +1,3 @@
#define SM_INIT "init.lua" #define SM_INIT "init.lua"
int errhandeler(lua_State *);