diff --git a/APIs/api.lua b/APIs/api.lua index f2e3caa..2c6cd1d 100644 --- a/APIs/api.lua +++ b/APIs/api.lua @@ -1,10 +1,70 @@ + +package.cpath = __api__.apiroot..'/?.so' +require("antd") + +local readline = function() + local s = "" + repeat + local c = modules.std().antd_recv(HTTP_REQUEST.id,1) + if c ~= 0 and c ~= 10 then + s = s..utf8.char(c) + end + until(c == 0 or c == 10) + return s +end +local read_header =function() + repeat + local l = readline() + if l ~= '\r' then + if l == "HTTP_REQUEST" or l == "request" or l == "COOKIE" or l == "REQUEST_HEADER" or l == "REQUEST_DATA" then + coroutine.yield(l, "LUA_TABLE") + else + local l1 = readline() + if l1 ~= '\r' then + coroutine.yield(l, l1) + end + l = l1 + end + end + until l == '\r' +end + + +local read_headers = function() + local co = coroutine.create(function () read_header() end) + return function () -- iterator + local code, k, v = coroutine.resume(co) + return k,v + end +end + +local parse_headers =function() + local lut = { + HTTP_REQUEST = HTTP_REQUEST + } + local curr_tbl = "HTTP_REQUEST" + for k,v in read_headers() do + if v == "LUA_TABLE" then + if not lut[k] then + lut[k] = {} + end + curr_tbl = k + else + lut[curr_tbl][k] = v + end + end + HTTP_REQUEST.request = lut.request + HTTP_REQUEST.request.COOKIE = lut.COOKIE + HTTP_REQUEST.request.REQUEST_HEADER = lut.REQUEST_HEADER + HTTP_REQUEST.request.REQUEST_DATA = lut.REQUEST_DATA +end + +-- parsing the header +parse_headers() -- root dir -__ROOT__ = __api__.root +__ROOT__ = HTTP_REQUEST.request.SERVER_WWW_ROOT -- set require path package.path = __ROOT__ .. '/?.lua;'..__api__.apiroot..'/?.lua' -package.cpath = __api__.apiroot..'/?.so' - -require("antd") require("std") require("utils") require("extra_mime") @@ -24,6 +84,14 @@ if HEADER["User-Agent"] and HEADER["User-Agent"]:match("Mobi") then HEADER.mobile = true end +function LOG_INFO(fmt,...) + ulib.syslog(5,string.format(fmt, table.unpack({...}))) +end + +function LOG_ERROR(fmt,...) + ulib.syslog(3,string.format(fmt, table.unpack({...}))) +end + function has_module(m) if utils.file_exists(__ROOT__..'/'..m) then if m:find("%.ls$") then @@ -102,7 +170,6 @@ function loadscript(file, args) end end pro = pro.."\""..utils.escape(tmp:gsub("%%","%%%%")).."\")\n" - --print(pro) else html = html..std.trim(line," "):gsub("%%","%%%%").."\n" end @@ -118,12 +185,121 @@ function loadscript(file, args) pro = pro.."echo(\""..utils.escape(html).."\")\n" end pro = pro.."\nend \n return fn" - --print(pro) local r,e = load(pro) if r then return r(), e else return nil,e end end end +local decode_post_data = function(ctype, clen, is_url) + local raw_data,size = std.antd_recv(HTTP_REQUEST.id, clen) + if not raw_data or size ~= clen then + LOG_ERROR("Unable to read request data: received %d bytes expected %d bytes", size, clen) + return 400, "Unable to read request data" + end + if is_url then + local str = tostring(raw) + local arr = explode(str, "&") + LOG_INFO("encoded POST URI: %s", str) + for i,v in ipairs(arr) do + local assoc = explode(v,"=") + if #assoc == 2 then + REQUEST[assoc[1]] = untils.decodeURI(assoc[2]) + else + REQUEST[assoc[1]] = "" + end + end + else + local key = ctype:gsub("^[^/]*", "") + REQUEST[key] = raw_data + end + return 0 +end + +local decode_multi_part = function(ctype, clen) + --[[ + local arr = explode(ctype, "=") + if #arr ~= 2 then + LOG_ERROR("Unable to parsed boundary for: %s", ctype) + return 400, "Multipart Boundary not found" + end + local boundary = std.trim(arr[2]," ") + local boundary_end = boundary.."--" + LOG_INFO("Boundary found: %s", boundary) + local line = nil + repeat + line = readline() + until not line or line:find(boundary) or line == "" + if not line or line == "" then + LOG_ERROR("Cannot find first match for boundary %s", boundary) + return 400, "Unable to decode data based on content boundary" + end + repeat + line = readline() + until not line or line:find("Content-Disposition:") or line == "" + if not line or line == "" then + LOG_ERROR("Content-Disposition meta data not fond") + return 400, "Unable to query Content-Disposition from request" + end + line = line:gsub("Content-Disposition:",""):gsub("\r\n","") + -- extract parameters from header + arr = explode(line,";") + local part_name, part_file = nil, nil + for i,v in ipairs(arr) do + LOG_INFO('Decoding: %s', v) + local assoc = explode(v, "=") + local key = std.trim(assoc[1]) + local val = assoc[1] + if val then + val = std.trim(val, " ") + if key == "name" then + LOG_INFO("Part name: %s", val) + part_name = val + end + if key == "filename" then + LOG_INFO("Part file: %s", val) + part_file = val + end + end + end + -- TODO: to be continue + ]] + return 0 +end +-- decode post data if any +local decode_request = function() + LOG_INFO("Request method %s", REQUEST.method) + if (not REQUEST.method) + or (REQUEST.method ~= "POST" + and REQUEST.method ~= "PUT" + and REQUEST.method ~= "PATCH") then + return 0 + end + local ctype = HEADER['Content-Type'] + local clen = HEADER['Content-Length'] or -1 + if clen then + clen = tonumber(clen) + end + if not ctype or clen == -1 then + LOG_ERROR("Invalid content type %s or content length %d", ctype, clen) + return 400, "Bad Request, missing content description" + end + if ctype == "application/x-www-form-urlencoded" then + return decode_post_data(ctype, clen, true) + elseif ctype == "multipart/form-data" then + return decode_multi_part(ctype, clen) + else + return decode_post_data(ctype, clen, false) + end +end + +local code, error = decode_request() + +if code ~= 0 then + LOG_ERROR(error) + std.error(code, error) + return +end + -- OOP support --require("OOP") -- load sqlite helper diff --git a/APIs/std.lua b/APIs/std.lua index 4ff179f..b221a89 100644 --- a/APIs/std.lua +++ b/APIs/std.lua @@ -61,7 +61,7 @@ function std.b(s) std._b(HTTP_REQUEST.id,s) end function std.f(v) - std._f(HTTP_REQUEST.id,v) + ulib.send_file(v, HTTP_REQUEST.socket) end function std.setCookie(v) diff --git a/APIs/web.lua b/APIs/web.lua deleted file mode 100644 index 3f32633..0000000 --- a/APIs/web.lua +++ /dev/null @@ -1,74 +0,0 @@ --- require the utils library to work ---require("utils") --- require("std") -local wurl = require("wurl") - -local web = {} - -web.understand = function(proto) - if proto == "http" or proto == "https" then - return true - else - return false - end -end - -web.get = function(url) - local obj = utils.url_parser(url) - if web.understand(obj.protocol) then - return wurl._get(obj.hostname,obj.port, obj.query) - else - return nil,"Protocol is unsupported: "..obj.protocol - end -end - - -web.post = function(url,data) - local obj = utils.url_parser(url) - if web.understand(obj.protocol) then - if type(data) == "string" then - return wurl._post(obj.hostname, - obj.port, - obj.query, - "application/x-www-form-urlencoded",data) - else - return wurl._post(obj.hostname, - obj.port, - obj.query, - data.contentType,data.value) - end - else - return nil,"Protocol is unsupported: "..obj.protocol - end -end - -web.download = function(url,to) - local obj = utils.url_parser(url) - if web.understand(obj.protocol) then - local file - if std.is_dir(to) then - -- need to find file name here - local pattern = "^[^%?]*/([%w.]*)%??.*$" - local filename = string.gsub(obj.query,pattern,"%1") - if filename == "" then filename = "index" end - file = to.."/"..filename - else - file = to - end - local obj = utils.url_parser(url) - return wurl._download(obj.hostname,obj.port,obj.query,file) - else - return false,"Protocol is unsupported: "..obj.protocol - end -end - -web.upload = function(url,name,file) - local obj = utils.url_parser(url) - if web.understand(obj.protocol) then - return wurl._upload(obj.hostname,obj.port,obj.query,name,file) - else - return nil,"Protocol is unsupported: "..obj.protocol - end -end - -return web \ No newline at end of file diff --git a/lib/asl/Makefile.am b/lib/asl/Makefile.am index 1e7774a..56de2bb 100644 --- a/lib/asl/Makefile.am +++ b/lib/asl/Makefile.am @@ -19,10 +19,6 @@ json_la_LDFLAGS = -module -avoid-version -shared json_la_SOURCES = 3rd/jsmn/jsmn.c json.c -lib_LTLIBRARIES += wurl.la -wurl_la_LDFLAGS = -module -avoid-version -shared -wurl_la_SOURCES = wurl.c - if HAS_FFI lib_LTLIBRARIES += ffi.la ffi_la_LDFLAGS = -module -avoid-version -shared -lffi diff --git a/lib/asl/antd.c b/lib/asl/antd.c index 503a821..71d1576 100644 --- a/lib/asl/antd.c +++ b/lib/asl/antd.c @@ -255,6 +255,32 @@ static int l_t (lua_State *L) { return 1; /* number of results */ } + +static int l_antd_recv (lua_State *L) { + void* client = lua_touserdata (L, 1); + int len = luaL_checknumber(L,2); + if(len == 0) + { + lua_pushnil(L); + return 1; + } + if(len == 1) + { + char c = 0; + len = antd_recv(client, &c, len); + lua_pushnumber(L, c); + return 1; + } + else + { + lua_new_byte_array(L,len); + byte_array_t * arr = l_check_barray(L,-1); + len = antd_recv(client, arr->data, len); + lua_pushnumber(L,len); + return 2; /* number of results */ + } +} + // TODO: add __b to LUA //int __b(int, const unsigned char*, int); static int l_b (lua_State *L) { @@ -697,6 +723,7 @@ static const struct luaL_Reg standard [] = { //{"_text", l_text}, //{"_json", l_json}, //{"_jpeg", l_jpeg}, + {"antd_recv", l_antd_recv}, {"_error", l_std_error}, {"_send_header", l_send_header}, {"b64encode", l_base64_encode}, diff --git a/lib/asl/handle.c b/lib/asl/handle.c index 5f660bf..e0bfdd9 100644 --- a/lib/asl/handle.c +++ b/lib/asl/handle.c @@ -1,49 +1,34 @@ #include -#include -#include #include +#include /* See NOTES */ +#include +#include +#include +#include +#include +#include +#include #include "../lualib.h" -/** +typedef struct { + plugin_header_t* __plugin__; + int fd; +} lua_thread_data_t; -* convert antd dictionary to lua table -* -*/ -static void push_dict_to_lua(lua_State* L, dictionary_t d) +void* lua_handle(void* ptr) { - lua_newtable(L); - - chain_t as; - if(d) - for_each_assoc(as, d) - { - lua_pushstring(L,as->key); - //printf("KEY %s\n", as->key); - if(EQU(as->key,"COOKIE") || EQU(as->key,"REQUEST_HEADER") || EQU(as->key,"REQUEST_DATA") ) - push_dict_to_lua(L, (dictionary_t)as->value); - else - { - if(strncmp(as->key,"octet-stream",12) == 0) - { - lua_pushlightuserdata(L, as->value); - } - else - { - lua_pushstring(L,as->value); - } - //printf("VALUE : %s\n",as->value ); - } - lua_settable(L, -3); - } -} -void* lua_handle(void* data, void* meta) -{ - antd_request_t* rq = (antd_request_t*) data; - char buf[BUFFLEN]; - plugin_header_t* __plugin__ = meta; + lua_thread_data_t* data = (lua_thread_data_t**)ptr; lua_State* L = NULL; - //char * index = __s("%s/%s",__plugin__.htdocs,"router.lua"); - char* cnf = __s("%s%s%s", __plugin__->pdir,DIR_SEP, __plugin__->name); + antd_client_t cl = {0}; + cl.sock = data->fd; + time(&cl.last_io); + cl.ssl = NULL; + cl.state = ANTD_CLIENT_PLUGIN_EXEC; + cl.z_status = 0; + cl.z_level = ANTD_CNONE; + cl.zstream = NULL; + //char * index = __s("%s/%s",__plugin__->htdocs,"router.lua"); + char* cnf = __s("%s%s%s", data->__plugin__->pdir,DIR_SEP, data->__plugin__->name); char * apis = __s("%s/%s",cnf,"api.lua"); L = luaL_newstate(); luaL_openlibs(L); @@ -54,25 +39,24 @@ void* lua_handle(void* data, void* meta) // API header lua_newtable(L); lua_pushstring(L,"name"); - lua_pushstring(L, __plugin__->name); + lua_pushstring(L, data->__plugin__->name); lua_settable(L,-3); - lua_pushstring(L,"root"); - htdocs(rq, buf); - lua_pushstring(L, buf); - lua_settable(L,-3); + //lua_pushstring(L,"root"); + //htdocs(rq, buf); + //lua_pushstring(L, data->__plugin__->htdocs); + //lua_settable(L,-3); lua_pushstring(L,"apiroot"); lua_pushstring(L, cnf); lua_settable(L,-3); lua_pushstring(L,"tmpdir"); - tmpdir(buf); - lua_pushstring(L, buf); + lua_pushstring(L, data->__plugin__->tmpdir); lua_settable(L,-3); lua_pushstring(L,"dbpath"); - lua_pushstring(L, __plugin__->dbpath); + lua_pushstring(L, data->__plugin__->dbpath); lua_settable(L,-3); lua_setglobal(L, "__api__"); @@ -80,20 +64,32 @@ void* lua_handle(void* data, void* meta) // Request lua_newtable(L); lua_pushstring(L,"id"); - lua_pushlightuserdata(L, rq->client); + lua_pushlightuserdata(L, &cl); //lua_pushnumber(L,client); lua_settable(L, -3); - lua_pushstring(L,"request"); - push_dict_to_lua(L,rq->request); + lua_pushstring(L,"socket"); + lua_pushnumber(L, cl.sock); + //lua_pushnumber(L,client); lua_settable(L, -3); + + int flag = 1; + + if (setsockopt(cl.sock, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) == -1) + { + ERROR("Unable to set TCP_NODELAY on %d - setsockopt: %s", cl.sock, strerror(errno)); + } + //lua_pushstring(L,"request"); + //push_dict_to_lua(L,rq->request); + //lua_settable(L, -3); lua_setglobal(L, "HTTP_REQUEST"); - + free(ptr); // load major apis if(is_file(apis)) if (luaL_loadfile(L, apis) || lua_pcall(L, 0, 0, 0)) { ERROR( "cannot start API file: [%s] %s\n", apis, lua_tostring(L, -1)); + antd_error(&cl, 503, "Internal server error"); } /*if (luaL_loadfile(L, index) || lua_pcall(L, 0, 0, 0)) @@ -102,7 +98,7 @@ void* lua_handle(void* data, void* meta) __t(client, "Cannot run router: %s", lua_tostring(L, -1)); } free(index);*/ - LOG("LUA handle exit on %d", rq->client->sock); + LOG("LUA handle exit on %d", cl.sock); // clear request if(L) lua_close(L); @@ -110,6 +106,7 @@ void* lua_handle(void* data, void* meta) free(cnf); if(apis) free(apis); - return antd_create_task(NULL, (void*)rq, NULL,rq->client->last_io); + (void) close(cl.sock); + return 0; //lua_close(L); } diff --git a/lib/asl/ulib.c b/lib/asl/ulib.c index be339eb..de9eec0 100644 --- a/lib/asl/ulib.c +++ b/lib/asl/ulib.c @@ -381,43 +381,79 @@ static int l_file_move(lua_State* L) static int l_send_file(lua_State* L) { - const char* old = luaL_checkstring(L,1); - const char* new = luaL_checkstring(L,2); int fromfd, tofd, ret; + size_t sz = 0; + char* old = NULL; + char* new = NULL; + if(lua_isnumber(L,1)) + { + fromfd = (int)luaL_checknumber(L,1); + } + else + { + old = (char*)luaL_checkstring(L,1); + if((fromfd = open(old, O_RDONLY)) < 0) + { + lua_pushboolean(L,0); + goto end_send_file; + } + struct stat st; + if(stat(old, &st)!=0) + { + lua_pushboolean(L,0); + goto end_send_file; + } + sz = st.st_size; + } + if(lua_isnumber(L,2)) + { + tofd = (int) luaL_checknumber(L,2); + } + else + { + new = (char*)luaL_checkstring(L,2); + if (unlink(new) < 0 && errno != ENOENT) { + lua_pushboolean(L,0); + goto end_send_file; + } + if((tofd = open(new, O_WRONLY | O_CREAT, 0600)) < 0) + { + lua_pushboolean(L,0); + goto end_send_file; + } + } + + if(lua_isnumber(L,3)) + { + sz = (size_t) luaL_checknumber(L,3); + } + off_t off = 0; - if (unlink(new) < 0 && errno != ENOENT) { - lua_pushboolean(L,0); - goto end_send_file; - } - if ((fromfd = open(old, O_RDONLY)) < 0 || - (tofd = open(new, O_WRONLY | O_CREAT, 0600)) < 0) { - lua_pushboolean(L,0); - goto end_send_file; - } - struct stat st; - if(stat(old, &st)!=0) - { - lua_pushboolean(L,0); - goto end_send_file; - } - size_t sz = st.st_size; int read = 0; - while (read != sz && (ret = sendfile(tofd, fromfd, &off, sz - read)) == 0) + while ( + sz > 0 && + read != sz && + ( + ((ret = sendfile(tofd, fromfd, &off, sz - read)) >= 0) || + (errno == EAGAIN) + ) + ) { + if(ret < 0) ret = 0; read += ret; } - if(ret != sz) + if(read != sz) { lua_pushboolean(L,0); goto end_send_file; } lua_pushboolean(L,1); end_send_file: - if(fromfd >= 0) + if(fromfd >= 0 && old) { (void) close(fromfd); } - if(tofd >= 0) + if(tofd >= 0 && new) { (void) close(tofd); } diff --git a/lib/asl/wurl.c b/lib/asl/wurl.c deleted file mode 100644 index ecd2586..0000000 --- a/lib/asl/wurl.c +++ /dev/null @@ -1,633 +0,0 @@ -#include //printf -#include //memset -#include //for exit(0); -#include //For errno - the error number -#include //hostent -#include -#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../lualib.h" -//#include "../../lua-api.h" -#include -#include - -#define CLIENT_NAME "wurl" -#define CLIENT_HOST "192.168.9.249" -#define CONN_TIME_OUT_S 3 -#define MAX_BUFF 1024 -#define REQUEST_BOUNDARY "------wURLFormBoundaryVo4QYVaSVseFNpeK" -#define GET 0 -#define POST 1 -typedef struct{ - int type; // POST(1) or GET(0) - char* resource; // path - char* ctype; // content type, used by POST - int clen; // content length, used by POST - unsigned char* data ; -} wurl_header_t; - -byte_array_t *l_check_barray (lua_State *L,int idx) { - void *ud = luaL_checkudata(L, idx, BYTEARRAY); - luaL_argcheck(L, ud != NULL, idx, "`byte array' expected"); - return (byte_array_t *)ud; -} - -void lua_new_byte_array(lua_State*L, int n) -{ - size_t nbytes = sizeof(byte_array_t) + (n-1)*sizeof(unsigned char); - byte_array_t *a = (byte_array_t *)lua_newuserdata(L, nbytes); - luaL_getmetatable(L, BYTEARRAY); - lua_setmetatable(L, -2); - a->size = n; -} - -/*get the ip by hostname*/ -int wurl_ip_from_hostname(const char * hostname , char* ip) -{ - struct hostent *he; - struct in_addr **addr_list; - int i; - if ( (he = gethostbyname( hostname ) ) == NULL) - { - // get the host info - herror("gethostbyname"); - return -1; - } - addr_list = (struct in_addr **) he->h_addr_list; - - for(i = 0; addr_list[i] != NULL; i++) - { - //Return the first one; - strcpy(ip , inet_ntoa(*addr_list[i]) ); - return 0; - } - return -1; -} - -/* -send a request -*/ -int wurl_request_socket(const char* ip, int port) -{ - int sockfd; - struct sockaddr_in dest; - - // time out setting - struct timeval timeout; - timeout.tv_sec = CONN_TIME_OUT_S; - timeout.tv_usec = 0; - if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) - { - perror("Socket"); - return -1; - } - if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0) - perror("setsockopt failed\n"); - - if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0) - perror("setsockopt failed\n"); - - bzero(&dest, sizeof(dest)); - dest.sin_family = AF_INET; - dest.sin_port = htons(port); - if ( inet_aton(ip, &dest.sin_addr) == 0 ) - { - perror(ip); - close(sockfd); - return -1; - } - if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 ) - { - close(sockfd); - perror("Connect"); - return -1; - } - return sockfd; -} -/* -POST %s HTTP/1.0\r\n -Host: %s\r\n -User-Agent: %s\r\n -Content-Type: %s\r\n -Content-Length: %d\r\n\r\n" - -maybe cookie support? this can cause security problem - -GET %s HTTP/1.0\r\n -Host: %s\r\n -User-Agent: %s\r\n\r\n" - -multipart -POST %s HTTP/1.1 -Host: %s -User-Agent: %s -Content-Type: multipart/form-data; boundary=----------287032381131322 -Content-Length: %d - -------------287032381131322 -Content-Disposition: form-data; name="datafile1"; filename="r.gif" -Content-Type: image/gif - -GIF87a.............,...........D..; -------------287032381131322 -Content-Disposition: form-data; name="datafile2"; filename="g.gif" -Content-Type: image/gif - -GIF87a.............,...........D..; -------------287032381131322 -Content-Disposition: form-data; name="datafile3"; filename="b.gif" -Content-Type: image/gif - -GIF87a.............,...........D..; -------------287032381131322-- -*/ - -int wurl_header(int sockfd, wurl_header_t rq) -{ - char buff[MAX_BUFF]; - if(sockfd < 0) return -1; - - if(rq.type == GET) // GET - { - send(sockfd,"GET ",4,0); - send(sockfd,rq.resource, strlen(rq.resource),0); - send(sockfd," HTTP/1.0\r\n",11,0); - } - else - { - send(sockfd,"POST ",5, 0); - send(sockfd,rq.resource, strlen(rq.resource),0); - send(sockfd," HTTP/1.0\r\n",11,0); - sprintf(buff,"Content-Type: %s\r\n", rq.ctype); - send(sockfd,buff,strlen(buff),0); - sprintf(buff,"Content-Length: %d\r\n", rq.clen); - send(sockfd,buff,strlen(buff),0); - } - // host dont need to send the host - //sprintf(buff,"Host: %s\r\n",CLIENT_HOST); - //send(sockfd,buff,strlen(buff),0); - // user agent - sprintf(buff,"User-Agent: %s\r\n",CLIENT_NAME); - send(sockfd,buff,strlen(buff),0); - // terminate request - send(sockfd,"\r\n",2,0); - - // if there is data, send out - if(rq.type == POST && rq.data) - { - send(sockfd,rq.data,rq.clen,0); - } - return 0; -} - -/* - send multiple files to server - using multipart/form-data -*/ - -void wurl_send_files(int sockfd,char* resource, int n, char* name [], char* files[]) -{ - char buff[MAX_BUFF]; - wurl_header_t rq; - rq.type = POST; - rq.resource = resource; - // get the total size of data - int totalsize = 0; - FILE* fd; - struct stat st; - for(int i = 0; i < n; ++i) - { - if(stat(files[i], &st) != 0) continue; - totalsize += st.st_size; - } - rq.clen = totalsize; - sprintf(buff,"%s; boundary=%s","multipart/form-data",REQUEST_BOUNDARY); - rq.ctype = buff; - - // now send the header - wurl_header(sockfd,rq); - // now send the files - size_t size; - for(int i = 0; i < n; ++i) - { - fd = fopen(files[i],"rb"); - if(!fd) continue; - // first send the boundary - //printf("sending file %s name:%s\n", files[i],name[i]); - sprintf(buff,"%s\r\n",REQUEST_BOUNDARY); - send(sockfd, buff, strlen(buff),0); - // content disposition - sprintf(buff,"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", - name[i],basename(files[i])); - send(sockfd,buff, strlen(buff),0); - // content type - sprintf(buff,"Content-Type: %s\r\n\r\n",mime(files[i])); - send(sockfd,buff, strlen(buff),0); - // now send the file - while(!feof(fd)) - { - size = fread(buff,1,MAX_BUFF,fd); - send(sockfd,buff,size,0); - //if(!__b(client,buffer,size)) return; - } - fclose(fd); - send(sockfd,"\r\n",2,0); - } - //end the boudary - sprintf(buff,"%s--\r\n",REQUEST_BOUNDARY); - send(sockfd,buff,strlen(buff),0); -} - -/** - * Read the socket request in to a buffer or size - * The data is read until the buffer is full or - * there are a carrier return character - * @param sock socket - * @param buf buffer - * @param size size of buffer - * @return number of bytes read - */ -int wurl_read_buf(int sock, char*buf,int size) -{ - int i = 0; - char c = '\0'; - int n; - while ((i < size - 1) && (c != '\n')) - { - n = recv(sock, &c, 1, 0); - if (n > 0) - { - buf[i] = c; - i++; - } - else - c = '\n'; - } - buf[i] = '\0'; - return i; -} -/* - POST example - wurl_header_t rq; - rq.resource = path; - rq.type = POST; - rq.data = "s=a&q=b#test";//"{\"name\":\"sang\"}"; - rq.clen = strlen(rq.data); - rq.ctype = "application/x-www-form-urlencoded";//"application/json"; - - if(wurl_request(hostname,port,&rq,1) == 0) - { - printf(rq.data); - } - else - { - printf("Cannot connect to host\n"); - } - - DOWNLOAD - wurl_header_t rq; - rq.resource = path; - rq.type = GET; - - if(wurl_download(hostname,port,&rq,file) == 0) - { - printf("Download sucess ful\n"); - } - else - { - printf("Cannot connect to host\n"); - } - - - upload example - // send files - char * names[2]; - names[0] = "zip"; - names[1] = "text"; - char* files[2]; - files[0] = "/Users/mrsang/tmp/Archive.zip"; - files[1] = "/Users/mrsang/tmp/test.py"; - - wurl_send_files(sock,path,2,names, files); - printf("RETURN:\n"); - size = wurl_read_buf(sock,buff, MAX_BUFF); - while(size > 0) - { - printf("%s", buff); - size = wurl_read_buf(sock,buff, MAX_BUFF); - } - close(sock); -*/ -void wurl_response_header(int sock, wurl_header_t* header) -{ - char buff[MAX_BUFF]; - int size = wurl_read_buf(sock,buff,MAX_BUFF); - char* token; - while (size > 0 && strcmp("\r\n",buff)) - { - char* line = strdup(buff); - //printf("LINE %s\n", line); - token = strsep(&line,":"); - trim(token,' '); - if(token != NULL &&strcasecmp(token,"Content-Type") == 0) - { - header->ctype = strsep(&line,":"); - trim(header->ctype,' '); - trim(header->ctype,'\n'); - trim(header->ctype,'\r'); - } else if(token != NULL &&strcasecmp(token,"Content-Length") == 0) - { - token = strsep(&line,":"); - trim(token,' '); - header->clen = atoi(token); - } - //if(line) free(line); - size = wurl_read_buf(sock,buff,MAX_BUFF); - } -} -int wurl_read_data(int sock,char** din) -{ - // read line by line, ignore content length - int total_length = 0; - char* tmp = NULL; - int CHUNK = 512; - char buff[MAX_BUFF]; - char * data = ( char*) malloc(CHUNK); - int cursize = CHUNK; - int size = wurl_read_buf(sock,buff,MAX_BUFF); - while(size > 0) - { - if(total_length+size > cursize) - { - tmp = (char*) realloc(data,total_length + size+ CHUNK ); - - if(!tmp) - { - if(data) free(data); - break; - } - cursize = total_length + size+ CHUNK; - data = tmp; - } - memcpy(data+total_length,buff,size); - total_length += size; - size = wurl_read_buf(sock,buff,MAX_BUFF); - } - data[total_length] = '\0'; - close(sock); - *din = data; - return total_length; -} -/* - hostname - port - header for request and respond - lazy : if 1, all data is read to the header - if 0, user has the responsibility to handler it -*/ -int wurl_request(const char* hostname, int port, wurl_header_t* header, int lazy) -{ - char ip[100]; - wurl_ip_from_hostname(hostname ,ip); - int sock = wurl_request_socket(ip, port); - - if(sock <= 0) return -1; - // send header - wurl_header(sock,*header); - // read respond header - wurl_response_header(sock,header); - - if(header->ctype == NULL || header->clen == -1) - { - LOG("Bad data\n"); - return -1; - } - - // read data if lazy - if(lazy) - { - // read line by line, ignore content length - header->clen = wurl_read_data(sock,(char**)&header->data); - return 0; - } - return sock; -} - -int wurl_download(const char* hostname, int port, wurl_header_t* h, const char* to) -{ - // we will handler the data reading - int sock = wurl_request(hostname, port,h,0); - char buff[MAX_BUFF]; - if(sock < 0) return -1; - - FILE* fp = fopen(to,"wb"); - int size; - if(fp) - { - while((size = wurl_read_buf(sock,buff, MAX_BUFF)) > 0) - { - fwrite(buff, size, 1, fp); - } - fclose(fp); - } - else - { - close(sock); - return -1; - } - close(sock); - return 0; -} - -/* -int main (int argc, char const *argv[]) -{ - if(argc < 4) - { - printf("wurl [host] [port] [path]\n"); - exit(1); - } - - char *hostname = argv[1]; - char* path = argv[3]; - int port = atoi(argv[2]); - char*file = argv[4]; - wurl_header_t rq; - rq.resource = path; - rq.type = POST; - rq.data = "s=a&q=b#test";//"{\"name\":\"sang\"}"; - rq.clen = strlen(rq.data); - rq.ctype = "application/x-www-form-urlencoded";//"application/json"; - - if(wurl_download(hostname,port,&rq,file) == 0) - { - printf("Download sucess ful\n"); - } - else - { - printf("Cannot connect to host\n"); - } - return 0; -}*/ - -static int l_get(lua_State *L) -{ - const char* host = luaL_checkstring(L,1); - int port = luaL_checknumber(L,2); - const char* resource = luaL_checkstring(L,3); - - wurl_header_t rq; - rq.resource = (char*)resource; - rq.type = GET; - - if(wurl_request(host,port,&rq,1) == 0) - { - //printf("Content type is %s\n", rq.ctype); - //mime_t m = mime_from_type(rq.ctype); - lua_newtable(L); - lua_pushstring(L,"contentType"); - lua_pushstring(L,rq.ctype); - lua_settable(L,-3); - - /* - lua_pushstring(L,"binary"); - lua_pushboolean(L,m.bin); - lua_settable(L,-3);*/ - - lua_pushstring(L,"data"); - // byte array, that can be convert to string later - lua_new_byte_array(L,rq.clen); - byte_array_t* arr = l_check_barray(L,-1); - memcpy(arr->data,rq.data,rq.clen); - free(rq.data); // be careful - lua_settable(L,-3); - return 1; - } - else - { - lua_pushnil(L); - return 1; - } -} -static int l_post(lua_State *L) -{ - const char* host = luaL_checkstring(L,1); // host - int port = luaL_checknumber(L,2); // port - const char* res = luaL_checkstring(L,3); // resource - const char* ctype = luaL_checkstring(L,4); // content type - const char* data = luaL_checkstring(L,5); // post data - wurl_header_t rq; - rq.resource = (char*)res; - rq.type = POST; - rq.ctype = (char*)ctype; - rq.clen = strlen(data); - rq.data = (unsigned char*)data; - - if(wurl_request(host,port,&rq,1) == 0) - { - - //printf("Content type is %s\n", rq.ctype); - mime_t m = mime_from_type(rq.ctype); - lua_newtable(L); - lua_pushstring(L,"contentType"); - lua_pushstring(L,rq.ctype); - lua_settable(L,-3); - - /*lua_pushstring(L,"binary"); - lua_pushboolean(L,m.bin); - lua_settable(L,-3); - */ - - lua_pushstring(L,"data"); - lua_new_byte_array(L,rq.clen); - byte_array_t* arr = l_check_barray(L,-1); - memcpy(arr->data,rq.data,rq.clen); - free(rq.data); // be careful - lua_settable(L,-3); - return 1; - } - else - { - lua_pushnil(L); - return 1; - } - -} -static int l_download(lua_State* L) -{ - // we user only GET for down load and POST for upload - const char* host = luaL_checkstring(L,1); // host - int port = luaL_checknumber(L,2); // port - const char* res = luaL_checkstring(L,3); // resource - const char* file = luaL_checkstring(L,4); // file - wurl_header_t rq; - rq.resource = (char*)res; - rq.type = GET; - if(wurl_download(host,port,&rq,file) == 0) - lua_pushboolean(L, true); - else - lua_pushboolean(L,false); - return 1; -} -static int l_upload(lua_State* L) -{ - const char* host = luaL_checkstring(L,1); - int port = luaL_checknumber(L,2); - const char* resource = luaL_checkstring(L,3); - const char* name = luaL_checkstring(L,4); - const char* file = luaL_checkstring(L,5); - - if(!_exist(file)) goto fail; - - // request socket - char ip[100]; - wurl_ip_from_hostname(host ,ip); - int sock = wurl_request_socket(ip, port); - - if(sock <= 0) goto fail; - - char * names[2]; - names[0] = (char*)name; - char* files[2]; - //files[0] = "/Users/mrsang/tmp/Archive.zip"; - files[0] = (char*)file; - - //printf("SENDIND DILE\n"); - wurl_send_files(sock, (char*)resource,1,names,files); - wurl_header_t header; - //printf("READ HEADER\n"); - wurl_response_header(sock, &header); - //printf("read data\n"); - wurl_read_data(sock,(char**)&header.data); - if(header.ctype != NULL && header.data) - { - lua_pushstring(L,(const char*)header.data); - free(header.data); - return 1; - } - fail: - lua_pushnil(L); - return 1; -} -static const struct luaL_Reg _lib [] = { - {"_get",l_get}, - {"_post",l_post}, - {"_download",l_download}, - {"_upload",l_upload}, - {NULL,NULL} -}; - -int luaopen_wurl(lua_State *L) -{ - luaL_newlib(L, _lib); - return 1; -} \ No newline at end of file diff --git a/lua-api.c b/lua-api.c index f43bad2..c11c085 100644 --- a/lua-api.c +++ b/lua-api.c @@ -2,21 +2,102 @@ #include #include #include -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include -#include "lib/lualib.h" #define LUA_HDL_FN "lua_handle" -static void* core = NULL; -static void* lua_handle = NULL; -static void *(*handle_fn)(void *, void*); +#define MAX_SOCK_NAME 64 +#define SOCKET_NAME "lua.sock" -void init() +#define MAX_SESSION_TIMEOUT (15u * 60u) //15 min +#define PING_INTERVAL 10u // 10s +#define PROCESS_TIMEOUT 200u //100ms + +typedef struct { + plugin_header_t* __plugin__; + int fd; +} lua_thread_data_t; + +static pid_t pid = 0; + +static int open_unix_socket() { + struct sockaddr_un address; + address.sun_family = AF_UNIX; + char path[sizeof(address.sun_path)]; + (void)snprintf(path, sizeof(address.sun_path), "%s/%s", __plugin__.tmpdir, SOCKET_NAME); + (void) strncpy(address.sun_path, path, sizeof(address.sun_path)); + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if(fd == -1) + { + ERROR( "Unable to create Unix domain socket: %s", strerror(errno)); + return -1; + } + if(connect(fd, (struct sockaddr*)(&address), sizeof(address)) == -1) + { + ERROR( "Unable to connect to socket '%s': %s", address.sun_path, strerror(errno)); + return -1; + } + LOG( "Socket %s is created successfully", path); + return fd; +} + +static int mk_socket() +{ + struct sockaddr_un address; + char path[sizeof(address.sun_path)]; + (void)snprintf(path, sizeof(address.sun_path), "%s/%s", __plugin__.tmpdir, SOCKET_NAME); + address.sun_family = AF_UNIX; + // create the socket + (void)snprintf(path, sizeof(address.sun_path), "%s/%s", __plugin__.tmpdir, SOCKET_NAME); + + (void)strncpy(address.sun_path, path, sizeof(address.sun_path)); + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + { + ERROR("Unable to create Unix domain socket: %s", strerror(errno)); + return -1; + } + if (bind(fd, (struct sockaddr *)(&address), sizeof(address)) == -1) + { + ERROR("Unable to bind name: %s to a socket: %s", address.sun_path, strerror(errno)); + return -1; + } + // mark the socket as passive mode + if (listen(fd, 500) == -1) + { + ERROR("Unable to listen to socket: %d (%s): %s", fd, path, strerror(errno)); + return -1; + } + LOG("Socket %s is created successfully: %d", path, fd); + return fd; +} + +static void lua_serve() +{ + void* core = NULL; + void* lua_handle = NULL; + void *(*handle_fn)(void*); + char path[BUFFLEN]; char* error; - char* path = __s("%s/lua/core.so", __plugin__.pdir); + (void)snprintf(path, BUFFLEN, "%s/lua/core.so", __plugin__.pdir); core = dlopen(path, RTLD_NOW| RTLD_GLOBAL); - free(path); if(!core) { ERROR("Cannot load Lua core: %s", dlerror()); @@ -24,42 +105,208 @@ void init() } LOG("Lua core loaded"); // now load the handle - path = __s("%s/lua/handle.so", __plugin__.pdir); + (void)snprintf(path, BUFFLEN, "%s/lua/handle.so", __plugin__.pdir); lua_handle = dlopen(path, RTLD_LAZY); - free(path); if(!lua_handle) { ERROR("Cannot load lua_handle: %s", dlerror()); return; } // find the fn - handle_fn = (void *(*)(void *))dlsym(lua_handle, LUA_HDL_FN); + handle_fn = (void *(*)(void*))dlsym(lua_handle, LUA_HDL_FN); if ((error = dlerror()) != NULL) { ERROR("Problem when finding %s method from handle : %s", LUA_HDL_FN, error); handle_fn = NULL; return; } - LOG("Lua module initialized"); -} - -void* handle(void* data) -{ - plugin_header_t* meta_ptr = (void*)meta(); - antd_request_t *rq = (antd_request_t *)data; - // find the handle function and execute it - if(!handle_fn) + int socket = mk_socket(); + if(socket != -1) { - antd_error(rq->client, 503, "Requested service not found"); - return antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io); + int fd; + if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1) + { + ERROR("Unable to set reuse address on %d - setsockopt: %s", socket, strerror(errno)); + } + while((fd = accept(socket, NULL, NULL)) > 0) + { + pthread_t thread; + lua_thread_data_t* data = (lua_thread_data_t*)malloc(sizeof(lua_thread_data_t)); + data->__plugin__ = &__plugin__; + data->fd = fd; + set_nonblock(fd); + if (pthread_create(&thread, NULL, (void *(*)(void*))handle_fn, (void *)data) != 0) + { + ERROR("pthread_create: cannot create lua thread: %s", strerror(errno)); + (void)close(fd); + } + else + { + LOG("Serve thread created for %d", fd); + pthread_detach(thread); + } + + } + if (fd < 0) + { + ERROR("Unable to accept the new connection: %s", strerror(errno)); + } } - return handle_fn(data, meta_ptr); -} -void destroy() -{ if(core) (void)dlclose(core); if(lua_handle) (void)dlclose(lua_handle); + LOG("lua_serve: stop serve due to error"); +} + +void init() +{ + use_raw_body(); + pid = fork(); + if (pid == 0) + { + // child + lua_serve(); + } + // parent + LOG("Lua module initialized"); +} + +static void push_dict_to_socket(antd_client_t* cl, char* name, char* parent_name, dictionary_t d) +{ + antd_send(cl,name, strlen(name)); + antd_send(cl,"\n", 1); + chain_t as; + if(d) + for_each_assoc(as, d) + { + if(EQU(as->key,"COOKIE") || EQU(as->key,"REQUEST_HEADER") || EQU(as->key,"REQUEST_DATA") ) + push_dict_to_socket(cl, as->key, name, (dictionary_t)as->value); + else if(as->value) + { + antd_send(cl,as->key, strlen(as->key)); + antd_send(cl,"\n", 1); + antd_send(cl,as->value, strlen(as->value)); + antd_send(cl,"\n", 1); + } + } + antd_send(cl,parent_name, strlen(parent_name)); + antd_send(cl,"\n", 1); +} + +static void *process(void *data) +{ + fd_set fd_in; + antd_request_t *rq = (antd_request_t *)data; + antd_client_t* cl = (antd_client_t* ) dvalue(rq->request, "LUA_CL_DATA"); + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = PROCESS_TIMEOUT; + FD_ZERO(&fd_in); + FD_SET(rq->client->sock, &fd_in); + FD_SET(cl->sock, &fd_in); + int max_fdm = rq->client->sock > cl->sock ? rq->client->sock : cl->sock; + int rc = select(max_fdm + 1, &fd_in, NULL, NULL, &timeout); + antd_task_t* task; + uint8_t buff[BUFFLEN]; + int ret; + switch (rc) + { + case -1: + ERROR("Error on select(): %s", strerror(errno)); + close(cl->sock); + return antd_create_task(NULL, data, NULL, rq->client->last_io); + case 0: + // time out + task = antd_create_task(process, (void *)rq, NULL, time(NULL)); + //antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + //antd_task_bind_event(task, cl->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + return task; + // we have data + default: + // If data is on webserver + if (FD_ISSET(rq->client->sock, &fd_in)) + { + while((ret = antd_recv_upto(rq->client,buff, BUFFLEN)) > 0) + { + LOG("Receive %d bytes from antd ", ret); + // write data to the other side + if(antd_send(cl,buff, ret) != ret) + { + ERROR("Error atnd_send(): %s", strerror(errno)); + close(cl->sock); + return antd_create_task(NULL, data, NULL, rq->client->last_io); + } + } + if(ret <= 0) + { + ERROR("Error antd_recv_upto() on %d: %s",rq->client->sock, strerror(errno)); + close(cl->sock); + return antd_create_task(NULL, data, NULL, rq->client->last_io); + } + } + else if(FD_ISSET(cl->sock, &fd_in)) + { + while((ret = antd_recv_upto(cl,buff, BUFFLEN)) > 0) + { + LOG("Receive %d bytes from LUA %d", ret, cl->sock); + // write data to the other side + if(antd_send(rq->client,buff, ret) != ret) + { + ERROR("Error atnd_send(): %s", strerror(errno)); + close(cl->sock); + return antd_create_task(NULL, data, NULL, rq->client->last_io); + } + } + if(ret < 0) + { + ERROR("Error antd_recv_upto() on %d: %s", cl->sock, strerror(errno)); + close(cl->sock); + return antd_create_task(NULL, data, NULL, rq->client->last_io); + } + } + task = antd_create_task(process, (void *)rq, NULL, time(NULL)); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + antd_task_bind_event(task, cl->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + return task; + } +} + +void* handle(void* data) +{ + antd_request_t *rq = (antd_request_t *)data; + // connect to socket + int fd = open_unix_socket(); + if(fd < 0) + { + antd_error(rq->client, 503, "Service unavailable"); + return antd_create_task(NULL, data, NULL, rq->client->last_io); + } + LOG("Connected to lua server at %d", fd); + set_nonblock(fd); + // write all header to lua + antd_client_t* cl = (antd_client_t*) malloc(sizeof(antd_client_t)); + (void)memset(cl, 0, sizeof(antd_client_t)); + cl->sock = fd; + time(&cl->last_io); + cl->ssl = NULL; + cl->state = ANTD_CLIENT_PLUGIN_EXEC; + cl->z_status = 0; + cl->z_level = ANTD_CNONE; + cl->zstream = NULL; + push_dict_to_socket(cl, "request","HTTP_REQUEST", rq->request); + antd_send(cl,"\r\n", 2); + dput(rq->request, "LUA_CL_DATA", cl); + antd_task_t* task = antd_create_task(process, (void *)rq, NULL, time(NULL)); + antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); + antd_task_bind_event(task, fd, 0, TASK_EVT_ON_READABLE); + return task; +} +void destroy() +{ + if(pid > 0) + { + kill(pid, SIGKILL); + } LOG("Exit LUA Handle"); }