#include "network_manager.h" #include #include namespace mosis { NetworkManager::NetworkManager(const std::string& app_id, const NetworkLimits& limits) : m_app_id(app_id) , m_limits(limits) , m_mock_mode(true) { } NetworkManager::~NetworkManager() { } void NetworkManager::SetAllowedDomains(const std::vector& domains) { m_validator.SetAllowedDomains(domains); } void NetworkManager::ClearDomainRestrictions() { m_validator.ClearDomainRestrictions(); } bool NetworkManager::ValidateRequest(const HttpRequest& request, std::string& error) { // Validate URL auto parsed = m_validator.Validate(request.url, error); if (!parsed) { return false; } // Validate method std::string method = request.method; std::transform(method.begin(), method.end(), method.begin(), [](unsigned char c) { return std::toupper(c); }); static const std::vector allowed_methods = { "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS" }; bool method_valid = false; for (const auto& m : allowed_methods) { if (method == m) { method_valid = true; break; } } if (!method_valid) { error = "Invalid HTTP method: " + request.method; return false; } // Validate request body size if (request.body.size() > m_limits.max_request_body) { error = "Request body too large: " + std::to_string(request.body.size()) + " bytes (max " + std::to_string(m_limits.max_request_body) + ")"; return false; } // Validate timeout if (request.timeout_ms > m_limits.max_timeout_ms) { error = "Timeout too large: " + std::to_string(request.timeout_ms) + "ms (max " + std::to_string(m_limits.max_timeout_ms) + "ms)"; return false; } // Check concurrent request limit if (m_active_requests.load() >= m_limits.max_concurrent_requests) { error = "Too many concurrent requests (max " + std::to_string(m_limits.max_concurrent_requests) + ")"; return false; } return true; } HttpResponse NetworkManager::Request(const HttpRequest& request, std::string& error) { HttpResponse response; // Validate the request if (!ValidateRequest(request, error)) { response.error = error; return response; } // In mock mode, we don't actually make network calls // This is for testing the validation logic if (m_mock_mode) { error = "Network requests disabled in mock mode"; response.error = error; return response; } // Track active requests m_active_requests++; // In a real implementation, we would make the HTTP request here // For now, just return an error indicating no network implementation error = "Network requests not implemented on this platform"; response.error = error; m_active_requests--; return response; } int NetworkManager::GetActiveRequestCount() const { return m_active_requests.load(); } // Lua API implementation // Get NetworkManager from upvalue static NetworkManager* GetManager(lua_State* L) { return static_cast(lua_touserdata(L, lua_upvalueindex(1))); } // network.request(options) -> response, error static int L_network_request(lua_State* L) { NetworkManager* manager = GetManager(L); if (!manager) { lua_pushnil(L); lua_pushstring(L, "NetworkManager not available"); return 2; } // Expect table argument luaL_checktype(L, 1, LUA_TTABLE); HttpRequest request; // Get URL (required) lua_getfield(L, 1, "url"); if (!lua_isstring(L, -1)) { lua_pushnil(L); lua_pushstring(L, "url is required and must be a string"); return 2; } request.url = lua_tostring(L, -1); lua_pop(L, 1); // Get method (optional, default GET) lua_getfield(L, 1, "method"); if (lua_isstring(L, -1)) { request.method = lua_tostring(L, -1); } lua_pop(L, 1); // Get headers (optional) lua_getfield(L, 1, "headers"); if (lua_istable(L, -1)) { lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_isstring(L, -2) && lua_isstring(L, -1)) { request.headers[lua_tostring(L, -2)] = lua_tostring(L, -1); } lua_pop(L, 1); } } lua_pop(L, 1); // Get body (optional) lua_getfield(L, 1, "body"); if (lua_isstring(L, -1)) { size_t len; const char* body = lua_tolstring(L, -1, &len); request.body = std::string(body, len); } lua_pop(L, 1); // Get timeout (optional) lua_getfield(L, 1, "timeout"); if (lua_isnumber(L, -1)) { request.timeout_ms = static_cast(lua_tointeger(L, -1)); } lua_pop(L, 1); // Make request std::string error; HttpResponse response = manager->Request(request, error); if (!error.empty()) { lua_pushnil(L); lua_pushstring(L, error.c_str()); return 2; } // Return response as table lua_newtable(L); lua_pushinteger(L, response.status_code); lua_setfield(L, -2, "status"); lua_pushstring(L, response.body.c_str()); lua_setfield(L, -2, "body"); // Headers table lua_newtable(L); for (const auto& [key, value] : response.headers) { lua_pushstring(L, value.c_str()); lua_setfield(L, -2, key.c_str()); } lua_setfield(L, -2, "headers"); if (!response.error.empty()) { lua_pushstring(L, response.error.c_str()); lua_setfield(L, -2, "error"); } return 1; // Return response table } // Helper to set a global in the real _G (bypassing any proxy) static void SetGlobalInRealG(lua_State* L, const char* name) { // Stack: value to set as global lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); // Check if it's a proxy with __index pointing to real _G if (lua_getmetatable(L, -1)) { lua_getfield(L, -1, "__index"); if (lua_istable(L, -1)) { // This is the real _G, set our value there lua_pushvalue(L, -4); // Push the value lua_setfield(L, -2, name); lua_pop(L, 4); // Pop __index, metatable, proxy, (value already consumed) return; } lua_pop(L, 2); // Pop __index and metatable } // No proxy, set directly lua_pushvalue(L, -2); // Push the value lua_setfield(L, -2, name); lua_pop(L, 2); // Pop globals table and original value } void RegisterNetworkAPI(lua_State* L, NetworkManager* manager) { // Create network table lua_newtable(L); // Add request function with manager as upvalue lua_pushlightuserdata(L, manager); lua_pushcclosure(L, L_network_request, 1); lua_setfield(L, -2, "request"); // Set as global SetGlobalInRealG(L, "network"); } } // namespace mosis