/*Export kore's functions to lua*/ #include #include #ifdef BUILD_PROD #include #endif #include #include #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); 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); }