diff --git a/http/dllmain.cpp b/http/dllmain.cpp index 5a8f126..3e7b2d7 100644 --- a/http/dllmain.cpp +++ b/http/dllmain.cpp @@ -1,229 +1,311 @@ -#include "dllmain.hpp" - -/* - local http = require("http") - - http.request("google.com", function(data, url, status, headers) - print(data) - end) -*/ - -void lua_parseresponse(lua_State* L, cpr::Response response) { - lua_createtable(L, 0, sizeof(cpr::Response)); - - lua_pushstring(L, response.text.c_str()); - lua_setfield(L, -2, "body"); - - lua_pushstring(L, response.url.c_str()); - lua_setfield(L, -2, "url"); - - lua_pushnumber(L, response.status_code); - lua_setfield(L, -2, "status"); - - lua_pushnumber(L, (int)response.error.code); - lua_setfield(L, -2, "error_code"); - - lua_pushnumber(L, (int)response.redirect_count); - lua_setfield(L, -2, "redirect_count"); - - lua_pushstring(L, response.error.message.c_str()); - lua_setfield(L, -2, "error"); - - lua_pushstring(L, response.reason.c_str()); - lua_setfield(L, -2, "reason"); - - lua_createtable(L, 0, response.header.size()); - for (const auto& header : response.header) { - lua_pushstring(L, header.second.c_str()); - lua_setfield(L, -2, header.first.c_str()); - } - lua_setfield(L, -2, "headers"); +#include +#include // https://docs.libcpr.org/ +#include + +#include + +struct Request +{ + inline Request(cpr::AsyncResponse&& future_response, int callbackref, lua_State* state) : + m_future_response(std::move(future_response)), + m_callbackRef(callbackref), + m_state(state) + {} + + inline static std::vector Queue; + + cpr::AsyncResponse m_future_response; + int m_callbackRef; + lua_State* m_state; +}; + +enum class ERequestType : std::uint8_t +{ + // Post reqeusts + post, + put, + patch, + + // Get requests + get, + del, + head, + options +}; + +static cpr::Header getDefaultHeader() +{ + return { + { + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + } + }; } -int tick(lua_State*) { - for (size_t i = requests->size(); i-- > 0;) { - auto& request = requests->at(i); - lua_State* L = request.m_state; - - if (request.m_future_response.wait_for(std::chrono::seconds(0)) != std::future_status::ready) continue; - - // call callback(data, url, status, headers) - cpr::Response response = request.m_future_response.get(); +static void lua_checkargs(lua_State* L, const int argc) +{ + const int args = lua_gettop(L); - lua_rawgeti(L, LUA_REGISTRYINDEX, request.m_callbackRef); - - lua_parseresponse(L, response); + if (args != argc) + { + luaL_error(L, "Expected %d argument, got %d", argc, args); + } +} - lua_call(L, 1, 0); - luaL_unref(L, LUA_REGISTRYINDEX, request.m_callbackRef); +static void lua_checkargs(lua_State* L, const int argc, bool optionals) +{ + const int args = lua_gettop(L); - requests->erase(requests->begin() + i); // pop the request + if (args < argc) + { + luaL_error(L, "Expected atleast %d argument, got %d", argc, args); } - - return 0; } -void pushResponse(lua_State* L, int narg, cpr::AsyncResponse&& m_future_response) { - lua_pushvalue(L, narg); +static cpr::Header lua_checkheaders(lua_State* L, int narg) +{ + luaL_checktype(L, narg, LUA_TTABLE); + cpr::Header header; + + lua_pushnil(L); + while (lua_next(L, narg) != 0) + { + size_t key_size; + size_t value_size; + const char* key = luaL_checklstring(L, -2, &key_size); + const char* value = luaL_checklstring(L, -1, &value_size); + + header[std::string(key, key_size)] = std::string(value, value_size); + lua_pop(L, 1); + } - int ref = luaL_ref(L, LUA_REGISTRYINDEX); - requests->emplace_back(std::move(m_future_response), ref, L); + return header; } -void genericGetRequest(lua_State* L, getRquestEnum method) { - lua_checkargs(L, 2, true); - - std::shared_ptr session = std::make_shared(); +static cpr::Payload lua_checkpayload(lua_State* L, int narg) +{ + luaL_checktype(L, narg, LUA_TTABLE); + std::vector pairs; + + lua_pushnil(L); + while (lua_next(L, narg) != 0) + { + size_t key_size; + size_t value_size; + const char* key = luaL_checklstring(L, -2, &key_size); + const char* value = luaL_checklstring(L, -1, &value_size); + + pairs.emplace_back(std::string(key, key_size), std::string(value, value_size)); + lua_pop(L, 1); + } - size_t size; - const char* url = luaL_checklstring(L, 1, &size); + return cpr::Payload(pairs.begin(), pairs.end()); +} - session->SetUrl(cpr::Url(url, size)); - session->SetTimeout(cpr::Timeout{ 60 * 1000 }); +static void lua_parseresponse(lua_State* L, const cpr::Response& response) +{ + lua_createtable(L, 0, 8); - luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushlstring(L, response.text.c_str(), response.text.size()); + lua_setfield(L, -2, "body"); - cpr::Header headers = getDefaultHeader(); - if (lua_gettop(L) == 3) { // [headers] - headers = lua_checkheaders(L, 3); - } + lua_pushlstring(L, response.url.str().c_str(), response.url.str().size()); + lua_setfield(L, -2, "url"); - session->SetHeader(headers); + lua_pushnumber(L, (int)response.status_code); + lua_setfield(L, -2, "status"); - switch (method) { - case getRquestEnum::get: - pushResponse(L, 2, std::move(session->GetAsync())); + lua_pushnumber(L, (int)response.error.code); + lua_setfield(L, -2, "error_code"); - break; - case getRquestEnum::del: - pushResponse(L, 2, std::move(session->DeleteAsync())); + lua_pushnumber(L, (int)response.redirect_count); + lua_setfield(L, -2, "redirect_count"); - break; - case getRquestEnum::head: - pushResponse(L, 2, std::move(session->HeadAsync())); + lua_pushlstring(L, response.error.message.c_str(), response.error.message.size()); + lua_setfield(L, -2, "error"); - break; - case getRquestEnum::options: - pushResponse(L, 2, std::move(session->OptionsAsync())); + lua_pushlstring(L, response.reason.c_str(), response.reason.size()); + lua_setfield(L, -2, "reason"); - break; - default: - assert(); - return; - } + lua_createtable(L, 0, (int)response.header.size()); + for (const auto& header : response.header) + { + lua_pushlstring(L, header.second.c_str(), header.second.size()); + lua_setfield(L, -2, header.first.c_str()); + } + lua_setfield(L, -2, "headers"); } -void genericPostRequest(lua_State* L, postRquestEnum method) { - lua_checkargs(L, 3, true); - - std::shared_ptr session = std::make_shared(); +static int tick(lua_State*) +{ + auto& v_queue = Request::Queue; - size_t size; - const char* url = luaL_checklstring(L, 1, &size); + for (size_t i = v_queue.size(); i-- > 0;) + { + auto& request = v_queue[i]; + if (request.m_future_response.wait_for(std::chrono::seconds(0)) != std::future_status::ready) + continue; - session->SetUrl(cpr::Url(url, size)); - session->SetTimeout(cpr::Timeout{ 60 * 1000 }); + cpr::Response response = request.m_future_response.get(); + lua_State* L = request.m_state; - int type = lua_type(L, 2); - if (type == LUA_TTABLE) { - session->SetPayload(lua_checkpayload(L, 2)); - } - else if (type == LUA_TSTRING) { - size_t size; - const char* str = luaL_checklstring(L, 2, &size); - session->SetBody(cpr::Body(str, size)); - } - else { - luaL_error(L, "Expected either a table or a string, got %s", lua_typename(L, type)); - } + lua_rawgeti(L, LUA_REGISTRYINDEX, request.m_callbackRef); + lua_parseresponse(L, response); + lua_call(L, 1, 0); - luaL_checktype(L, 3, LUA_TFUNCTION); - - cpr::Header headers = getDefaultHeader(); - if (lua_gettop(L) == 4) { // [headers] - headers = lua_checkheaders(L, 4); - } - - session->SetHeader(headers); - - switch (method) { - case postRquestEnum::post: - pushResponse(L, 3, std::move(session->PostAsync())); - - break; - case postRquestEnum::patch: - pushResponse(L, 3, std::move(session->PatchAsync())); - - break; - case postRquestEnum::put: - pushResponse(L, 3, std::move(session->PutAsync())); + luaL_unref(L, LUA_REGISTRYINDEX, request.m_callbackRef); + v_queue.erase(v_queue.begin() + i); // pop the request + } - break; - default: - assert(); - return; - } + return 0; } -int get(lua_State* L) { - genericGetRequest(L, getRquestEnum::get); - return 0; -} +inline static void pushResponse(lua_State* L, int narg, cpr::AsyncResponse&& m_future_response) +{ + lua_pushvalue(L, narg); + int ref = luaL_ref(L, LUA_REGISTRYINDEX); -int del(lua_State* L) { - genericGetRequest(L, getRquestEnum::del); - return 0; + Request::Queue.emplace_back(std::move(m_future_response), ref, L); } -int head(lua_State* L) { - genericGetRequest(L, getRquestEnum::head); - return 0; +static void luaGetRequest(lua_State* L, ERequestType method) +{ + // Do all the checks that might throw exceptions before allocating any memory + lua_checkargs(L, 2, true); + luaL_checktype(L, 2, LUA_TFUNCTION); + + size_t size; + const char* url = luaL_checklstring(L, 1, &size); + + cpr::Session session; + session.SetUrl(cpr::Url(url, size)); + session.SetTimeout(cpr::Timeout{ 60 * 1000 }); + session.SetHeader( + (lua_gettop(L) == 3) + ? lua_checkheaders(L, 3) + : getDefaultHeader() + ); + + switch (method) + { + case ERequestType::get: + pushResponse(L, 2, std::move(session.GetAsync())); + break; + case ERequestType::del: + pushResponse(L, 2, std::move(session.DeleteAsync())); + break; + case ERequestType::head: + pushResponse(L, 2, std::move(session.HeadAsync())); + break; + case ERequestType::options: + pushResponse(L, 2, std::move(session.OptionsAsync())); + break; + default: + assert("Invalid get request method"); + return; + } } -int options(lua_State* L) { - genericGetRequest(L, getRquestEnum::options); - return 0; -} +static void luaPostRequest(lua_State* L, ERequestType method) +{ + lua_checkargs(L, 3, true); + luaL_checktype(L, 3, LUA_TFUNCTION); -int post(lua_State* L) { - genericPostRequest(L, postRquestEnum::post); - return 0; -} + size_t size; + const char* url = luaL_checklstring(L, 1, &size); -int put(lua_State* L) { - genericPostRequest(L, postRquestEnum::put); - return 0; -} + cpr::Session session; + session.SetUrl(cpr::Url(url, size)); + session.SetTimeout(cpr::Timeout{ 60 * 1000 }); -int patch(lua_State* L) { - genericPostRequest(L, postRquestEnum::patch); - return 0; -} + const int payloadType = lua_type(L, 2); + if (payloadType == LUA_TTABLE) + { + session.SetPayload(lua_checkpayload(L, 2)); + } + else if (payloadType == LUA_TSTRING) + { + size_t size; + const char* str = luaL_checklstring(L, 2, &size); -int cleanup(lua_State* L) { - if (requests) { - delete requests; - requests = nullptr; - } + session.SetBody(cpr::Body(str, size)); + } + else + { + luaL_error(L, "Expected either a table or a string, got %s", lua_typename(L, payloadType)); + return; + } - return 0; + session.SetHeader( + (lua_gettop(L) == 4) + ? lua_checkheaders(L, 4) + : getDefaultHeader() + ); + + switch (method) + { + case ERequestType::post: + pushResponse(L, 3, std::move(session.PostAsync())); + break; + case ERequestType::patch: + pushResponse(L, 3, std::move(session.PatchAsync())); + break; + case ERequestType::put: + pushResponse(L, 3, std::move(session.PutAsync())); + break; + default: + assert("Invalid post request method"); + return; + } } -extern "C" { - __declspec(dllexport) int luaopen_http(lua_State* L) { - requests = new std::vector(); - - luaL_register(L, "http", functions); +static int cleanup(lua_State* L) +{ + Request::Queue.clear(); + Request::Queue.shrink_to_fit(); + return 0; +} - // for cleaning up our stuff - lua_newuserdata(L, sizeof(void*)); - luaL_newmetatable(L, "http.cleanup"); - lua_pushcfunction(L, cleanup); - lua_setfield(L, -2, "__gc"); +template +inline constexpr int luaRequestT(lua_State* L) +{ + TReqFunc(L, t_req_type); + return 0; +} - lua_setmetatable(L, -2); - luaL_ref(L, LUA_REGISTRYINDEX); - - return 1; - } +static const struct luaL_Reg g_httpFunctions[] = +{ + // GET requests + { "get" , luaRequestT }, + { "del" , luaRequestT }, + { "head" , luaRequestT }, + { "options", luaRequestT }, + + // POST requests + { "post" , luaRequestT }, + { "put" , luaRequestT }, + { "patch", luaRequestT }, + + { "tick", tick }, + { NULL, NULL } +}; + +extern "C" +{ + __declspec(dllexport) int luaopen_http(lua_State* L) + { + luaL_register(L, "http", g_httpFunctions); + + // for cleaning up our stuff + lua_newuserdata(L, sizeof(void*)); + luaL_newmetatable(L, "http.cleanup"); + lua_pushcfunction(L, cleanup); + lua_setfield(L, -2, "__gc"); + + lua_setmetatable(L, -2); + luaL_ref(L, LUA_REGISTRYINDEX); + + return 1; + } } \ No newline at end of file diff --git a/http/dllmain.hpp b/http/dllmain.hpp deleted file mode 100644 index c13f4d6..0000000 --- a/http/dllmain.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include -#include // https://docs.libcpr.org/ -#include -#include - -inline cpr::Header getDefaultHeader() { - return { - {"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"} - }; -} - -struct Request { - cpr::AsyncResponse m_future_response; - int m_callbackRef; - lua_State* m_state; - - Request(cpr::AsyncResponse&& future_response, int callbackref, lua_State* state) : - m_future_response(std::move(future_response)), - m_callbackRef(callbackref), - m_state(state) - {} -}; - -static std::vector* requests = nullptr; - -void lua_checkargs(lua_State* L, const int argc) { - const int args = lua_gettop(L); - - if (args != argc) - { - luaL_error(L, "Expected %d argument, got %d", argc, args); - } -} - -void lua_checkargs(lua_State* L, const int argc, bool optionals) { - const int args = lua_gettop(L); - - if (args < argc) - { - luaL_error(L, "Expected atleast %d argument, got %d", argc, args); - } -} - -cpr::Header lua_checkheaders(lua_State* L, int narg) { - luaL_checktype(L, narg, LUA_TTABLE); - cpr::Header header; - - lua_pushnil(L); - while (lua_next(L, narg) != 0) { - size_t key_size; - size_t value_size; - const char* key = luaL_checklstring(L, -2, &key_size); - const char* value = luaL_checklstring(L, -1, &value_size); - - header[std::string(key, key_size)] = std::string(value, value_size); - lua_pop(L, 1); - } - - return header; -} - -cpr::Payload lua_checkpayload(lua_State* L, int narg) { - luaL_checktype(L, narg, LUA_TTABLE); - std::vector pairs; - - lua_pushnil(L); - while (lua_next(L, narg) != 0) { - size_t key_size; - size_t value_size; - const char* key = luaL_checklstring(L, -2, &key_size); - const char* value = luaL_checklstring(L, -1, &value_size); - - pairs.emplace_back(std::string(key, key_size), std::string(value, value_size)); - lua_pop(L, 1); - } - - return cpr::Payload(pairs.begin(), pairs.end()); -} - -enum class postRquestEnum { - post, - put, - patch -}; - -enum class getRquestEnum { - get, - del, - head, - options -}; - -int tick(lua_State*); - -int get(lua_State* L); -int del(lua_State* L); -int head(lua_State* L); -int options(lua_State* L); - -int post(lua_State* L); -int put(lua_State* L); -int patch(lua_State* L); - -static const struct luaL_Reg functions[] = { - {"get", get}, - {"del", del}, - {"head", head}, - {"options", options}, - - {"post", post}, - {"put", put}, - {"patch", patch}, - - {"tick", tick}, - {NULL, NULL} -}; \ No newline at end of file diff --git a/http/http.vcxproj b/http/http.vcxproj index 6570c2b..acd4b49 100644 --- a/http/http.vcxproj +++ b/http/http.vcxproj @@ -151,9 +151,6 @@ - - - diff --git a/http/http.vcxproj.filters b/http/http.vcxproj.filters index d9eb4f7..e86417e 100644 --- a/http/http.vcxproj.filters +++ b/http/http.vcxproj.filters @@ -19,9 +19,4 @@ Source Files - - - Source Files - - \ No newline at end of file