From a5ef765524d59e8688cf4d60f4175e598c90b406 Mon Sep 17 00:00:00 2001 From: jyelon Date: Fri, 22 Jul 2022 17:07:40 -0400 Subject: [PATCH] HTTP stuff now uses keyword parser. --- luprex/core/cpp/http.cpp | 170 +++++++++++++++-------------- luprex/core/cpp/http.hpp | 8 +- luprex/core/cpp/luastack.cpp | 25 ++++- luprex/core/cpp/luastack.hpp | 14 ++- luprex/core/cpp/world-accessor.cpp | 10 +- luprex/core/cpp/world-core.cpp | 9 +- 6 files changed, 135 insertions(+), 101 deletions(-) diff --git a/luprex/core/cpp/http.cpp b/luprex/core/cpp/http.cpp index 0be8eef9..659b4e2d 100644 --- a/luprex/core/cpp/http.cpp +++ b/luprex/core/cpp/http.cpp @@ -734,50 +734,55 @@ void HttpClientRequest::set_defaults() { } } -void HttpClientRequest::set_config(LuaStack &LS0, LuaSlot tab) { - LuaVar key, val; - LuaStack LS(LS0.state(), key, val); - LS.set(key, LuaNil); - while (LS.next(tab, key, val)) { - eng::string kstr; - if (LS.isstring(key)) kstr = LS.ckstring(key); - if (kstr == "method") { - set_method(LS, val); - } else if (kstr == "host") { - set_host(LS, val); - } else if (kstr == "port") { - set_port(LS, val); - } else if (kstr == "path") { - set_path(LS, val); - } else if (kstr == "params") { - set_params(LS, val); - } else if (kstr == "url") { - set_url(LS, val); - } else if (kstr == "verifycertificate") { - set_verify_certificate(LS, val); - } else if (kstr == "mimetype") { - set_mime_type(LS, val); - } else if (kstr == "content") { - set_content(LS, val); - } else if (kstr == "html") { - set_content(LS, val); - set_mime_type("text/html"); - } else if (kstr == "text") { - set_content(LS, val); - set_mime_type("text/plain"); - } else if (kstr == "json") { - set_content(LS, val); - set_mime_type("application/json"); - } else if (kstr == "bytes") { - set_content(LS, val); - set_mime_type("application/octet-stream"); - } else if (kstr == "jsonvalue") { - set_jsonvalue(LS, val); - } else if (kstr == "") { - check_fail(util::ss("configuration parameter names must be strings.")); - } else { - check_fail(util::ss("unrecognized request configuration parameter: ", kstr)); - } +void HttpClientRequest::configure(LuaKeywordParser &kp) { + LuaVar val; + LuaStack LS(kp.state(), val); + if (kp.parse(val, "method")) { + set_method(LS, val); + } + if (kp.parse(val, "host")) { + set_host(LS, val); + } + if (kp.parse(val, "port")) { + set_port(LS, val); + } + if (kp.parse(val, "path")) { + set_path(LS, val); + } + if (kp.parse(val, "params")) { + set_params(LS, val); + } + if (kp.parse(val, "url")) { + set_url(LS, val); + } + if (kp.parse(val, "verifycertificate")) { + set_verify_certificate(LS, val); + } + if (kp.parse(val, "mimetype")) { + set_mime_type(LS, val); + } + if (kp.parse(val, "content")) { + set_content(LS, val); + } + if (kp.parse(val, "html")) { + set_content(LS, val); + set_mime_type("text/html"); + } + + if (kp.parse(val, "text")) { + set_content(LS, val); + set_mime_type("text/plain"); + } + if (kp.parse(val, "json")) { + set_content(LS, val); + set_mime_type("application/json"); + } + if (kp.parse(val, "bytes")) { + set_content(LS, val); + set_mime_type("application/octet-stream"); + } + if (kp.parse(val, "jsonvalue")) { + set_jsonvalue(LS, val); } } @@ -1025,40 +1030,39 @@ void HttpServerResponse::set_jsonvalue(LuaStack &LS, LuaSlot val) { set_mime_type("application/json"); } -void HttpServerResponse::set_config(LuaStack &LS0, LuaSlot tab) { - LuaVar key, val; - LuaStack LS(LS0.state(), key, val); - LS.set(key, LuaNil); - while (LS.next(tab, key, val)) { - eng::string kstr; - if (LS.isstring(key)) kstr = LS.ckstring(key); - if (kstr == "status") { - set_status(LS, val); - } else if (kstr == "maxage") { - set_max_age(LS, val); - } else if (kstr == "mimetype") { - set_mime_type(LS, val); - } else if (kstr == "content") { - set_content(LS, val); - } else if (kstr == "html") { - set_content(LS, val); - set_mime_type("text/html"); - } else if (kstr == "text") { - set_content(LS, val); - set_mime_type("text/plain"); - } else if (kstr == "json") { - set_content(LS, val); - set_mime_type("application/json"); - } else if (kstr == "bytes") { - set_content(LS, val); - set_mime_type("application/octet-stream"); - } else if (kstr == "jsonvalue") { - set_jsonvalue(LS, val); - } else if (kstr == "") { - check_fail(util::ss("response configuration parameters must be strings.")); - } else { - check_fail(util::ss("unrecognized response configuration parameter: ", kstr)); - } +void HttpServerResponse::configure(LuaKeywordParser &kp) { + LuaVar val; + LuaStack LS(kp.state(), val); + if (kp.parse(val, "status")) { + set_status(LS, val); + } + if (kp.parse(val, "maxage")) { + set_max_age(LS, val); + } + if (kp.parse(val, "mimetype")) { + set_mime_type(LS, val); + } + if (kp.parse(val, "content")) { + set_content(LS, val); + } + if (kp.parse(val, "html")) { + set_content(LS, val); + set_mime_type("text/html"); + } + if (kp.parse(val, "text")) { + set_content(LS, val); + set_mime_type("text/plain"); + } + if (kp.parse(val, "json")) { + set_content(LS, val); + set_mime_type("application/json"); + } + if (kp.parse(val, "bytes")) { + set_content(LS, val); + set_mime_type("application/octet-stream"); + } + if (kp.parse(val, "jsonvalue")) { + set_jsonvalue(LS, val); } } @@ -1712,8 +1716,10 @@ LuaDefine(http_clientrequest, "request", LuaArg tab; LuaRet str; LuaStack LS(L, tab, str); + LuaKeywordParser kp(LS, tab); HttpClientRequest req; - req.set_config(LS, tab); + req.configure(kp); + kp.final_check_throw(); req.set_defaults(); eng::string error = req.check(); if (!error.empty()) { @@ -1882,8 +1888,10 @@ LuaDefine(http_serverresponse, "response", LuaArg tab; LuaRet str; LuaStack LS(L, tab, str); + LuaKeywordParser kp(LS, tab); HttpServerResponse resp; - resp.set_config(LS, tab); + resp.configure(kp); + kp.final_check_throw(); resp.set_defaults(); LS.set(str, resp.debug_string()); return LS.result(); @@ -1915,4 +1923,4 @@ LuaDefine(http_statuscode, "(statusstring)", "Convert a string to a 3-digit stat LS.set(code, status_code_from_string(sstr)); int iresult = LS.result(); return iresult; -} \ No newline at end of file +} diff --git a/luprex/core/cpp/http.hpp b/luprex/core/cpp/http.hpp index 07d174b0..4bb40e5a 100644 --- a/luprex/core/cpp/http.hpp +++ b/luprex/core/cpp/http.hpp @@ -104,8 +104,9 @@ public: void set_defaults(); // Populate request-related fields from a Lua table. - void set_config(LuaStack &LS0, LuaSlot tab); - + // Doesn't throw errors, instead, returns errors via check(). + void configure(LuaKeywordParser &kp); + // Get or Set the request IDs. // // This class does not use these fields, it just stores @@ -208,8 +209,9 @@ public: void set_defaults(); // Populate request-related fields from a Lua table. + // Doesn't throw errors, instead, returns them via check() // - void set_config(LuaStack &LS0, LuaSlot tab); + void configure(LuaKeywordParser &kp); // Set the keep_alive flag. // diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index 7ba295e9..919e9301 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -5,6 +5,7 @@ #include #include "wrap-string.hpp" #include "wrap-set.hpp" +#include "wrap-sstream.hpp" LuaSpecial LuaRegistry(LUA_REGISTRYINDEX); LuaNilMarker LuaNil; @@ -468,8 +469,10 @@ void LuaStack::setvisited(LuaSlot tab, bool visited) const { LuaKeywordParser::LuaKeywordParser(lua_State *L, int slot) { L_ = L; slot_ = slot; - if (!lua_istable(L_, slot_)) { - luaL_error(L_, "expected an argument which is a table full of keywords"); + not_table_ = !lua_istable(L_, slot_); + if (not_table_) { + lua_newtable(L_); + lua_replace(L_, slot_); } } @@ -485,20 +488,32 @@ bool LuaKeywordParser::parse(LuaSlot out, const char *kw) { } }; -void LuaKeywordParser::check_unparsed_keywords() { +eng::string LuaKeywordParser::final_check() { + if (not_table_) { + return "expected a keyword table"; + } if (lua_nkeys(L_, slot_) != int(parsed_.size())) { lua_pushnil(L_); while (lua_next(L_, slot_) != 0) { lua_pop(L_, 1); // Don't need the value. if (!lua_isstring(L_, -1)) { - luaL_error(L_, "keyword table contains non-string key"); + return "keyword table contains non-string key"; } const char *kw = lua_tostring(L_, -1); if (parsed_.find(kw) == parsed_.end()) { - luaL_error(L_, "keyword %s not known", kw); + eng::ostringstream oss; + oss << "keyword " << kw << " not known"; + return oss.str(); } } assert(false && "should never get here in check_unparsed_keywords"); } + return ""; } +void LuaKeywordParser::final_check_throw() { + eng::string err = final_check(); + if (!err.empty()) { + luaL_error(L_, "%s", err.c_str()); + } +} diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index 09523431..321d5c24 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -531,14 +531,15 @@ class LuaKeywordParser { }; }; private: + bool not_table_; lua_State *L_; int slot_; eng::set parsed_; void init(const lua_State *L, int slot); public: - // The constructor will throw a lua error if the - // slot is not a table. + // If the slot is not a table, sets the not_table + // flag and creates a dummy table in the slot. LuaKeywordParser(lua_State *L, int slot); LuaKeywordParser(const LuaStack &LS, LuaSlot slot) : LuaKeywordParser(LS.state(), slot.index()) {} @@ -546,9 +547,12 @@ public: // Return true if the value is non-nil. bool parse(LuaSlot slot, const char *kw); - // Check if there are any keywords in the table that - // were never parsed. If so, throw an error. - void check_unparsed_keywords(); + // Check if there were any errors. If so, return an + // error message. + eng::string final_check(); + + // Check if there are any errors. If so, throw a lua error. + void final_check_throw(); // Fetch the state pointer. lua_State *state() const { return L_; } diff --git a/luprex/core/cpp/world-accessor.cpp b/luprex/core/cpp/world-accessor.cpp index 55624272..fe18f595 100644 --- a/luprex/core/cpp/world-accessor.cpp +++ b/luprex/core/cpp/world-accessor.cpp @@ -64,7 +64,7 @@ LuaDefine(tangible_animate, "tan,configtable", int64_t id = w->alloc_id_predictable(); AnimStep step; step.configure(kp, tan->anim_queue_.back()); - kp.check_unparsed_keywords(); + kp.final_check_throw(); if (step.action() == "") { luaL_error(L, "animation action must be specified"); } @@ -152,7 +152,7 @@ LuaDefine(tangible_build, "config", // Parse the initial animation step. AnimStep initstep, blank; initstep.configure(kp, blank); - kp.check_unparsed_keywords(); + kp.final_check_throw(); if (!initstep.has_xyz()) { luaL_error(L, "You must specify (X,Y,Z) for new tangible"); } @@ -368,7 +368,7 @@ LuaDefine(tangible_find, "config", LuaKeywordParser kw(LS, config); PlaneScan scan; scan.configure(kw); - kw.check_unparsed_keywords(); + kw.final_check_throw(); // When the configure routine sees the 'near' flag, it stores the tangible // ID, but not the center and plane, because doing so would require it to @@ -761,12 +761,14 @@ int lfn_http_request(lua_State *L, const char *method) { LuaArg request; LuaRet response; LuaStack LS(L, request, response); + LuaKeywordParser kp(LS, request); HttpClientRequest req; // Parse the request and make sure it's valid. // If not, immediately pass a '400 bad request' back to lua. req.set_method(method); - req.set_config(LS, request); + req.configure(kp); + kp.final_check_throw(); req.set_defaults(); eng::string error = req.check(); if (!error.empty()) { diff --git a/luprex/core/cpp/world-core.cpp b/luprex/core/cpp/world-core.cpp index f889d7f5..7b0c7f1b 100644 --- a/luprex/core/cpp/world-core.cpp +++ b/luprex/core/cpp/world-core.cpp @@ -533,10 +533,13 @@ HttpServerResponse World::http_serve(const HttpParser &request) { } // Try to convert the table into a response. - // If this results in an error, we don't have to do - // anything special, set_config will store the error. - response.set_config(LS, LuaSpecial(newtop)); + LuaKeywordParser kp(LS, LuaSpecial(newtop)); + response.configure(kp); response.set_defaults(); + eng::string kperr = kp.final_check(); + if (!kperr.empty()) { + response.fail(500, kperr); + } LS.result(); return response; }