HTTP stuff now uses keyword parser.

This commit is contained in:
2022-07-22 17:07:40 -04:00
parent 28ec61e47c
commit a5ef765524
6 changed files with 135 additions and 101 deletions

View File

@@ -734,50 +734,55 @@ void HttpClientRequest::set_defaults() {
} }
} }
void HttpClientRequest::set_config(LuaStack &LS0, LuaSlot tab) { void HttpClientRequest::configure(LuaKeywordParser &kp) {
LuaVar key, val; LuaVar val;
LuaStack LS(LS0.state(), key, val); LuaStack LS(kp.state(), val);
LS.set(key, LuaNil); if (kp.parse(val, "method")) {
while (LS.next(tab, key, val)) {
eng::string kstr;
if (LS.isstring(key)) kstr = LS.ckstring(key);
if (kstr == "method") {
set_method(LS, val); set_method(LS, val);
} else if (kstr == "host") { }
if (kp.parse(val, "host")) {
set_host(LS, val); set_host(LS, val);
} else if (kstr == "port") { }
if (kp.parse(val, "port")) {
set_port(LS, val); set_port(LS, val);
} else if (kstr == "path") { }
if (kp.parse(val, "path")) {
set_path(LS, val); set_path(LS, val);
} else if (kstr == "params") { }
if (kp.parse(val, "params")) {
set_params(LS, val); set_params(LS, val);
} else if (kstr == "url") { }
if (kp.parse(val, "url")) {
set_url(LS, val); set_url(LS, val);
} else if (kstr == "verifycertificate") { }
if (kp.parse(val, "verifycertificate")) {
set_verify_certificate(LS, val); set_verify_certificate(LS, val);
} else if (kstr == "mimetype") { }
if (kp.parse(val, "mimetype")) {
set_mime_type(LS, val); set_mime_type(LS, val);
} else if (kstr == "content") { }
if (kp.parse(val, "content")) {
set_content(LS, val); set_content(LS, val);
} else if (kstr == "html") { }
if (kp.parse(val, "html")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("text/html"); set_mime_type("text/html");
} else if (kstr == "text") { }
if (kp.parse(val, "text")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("text/plain"); set_mime_type("text/plain");
} else if (kstr == "json") { }
if (kp.parse(val, "json")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("application/json"); set_mime_type("application/json");
} else if (kstr == "bytes") { }
if (kp.parse(val, "bytes")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("application/octet-stream"); 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));
} }
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"); set_mime_type("application/json");
} }
void HttpServerResponse::set_config(LuaStack &LS0, LuaSlot tab) { void HttpServerResponse::configure(LuaKeywordParser &kp) {
LuaVar key, val; LuaVar val;
LuaStack LS(LS0.state(), key, val); LuaStack LS(kp.state(), val);
LS.set(key, LuaNil); if (kp.parse(val, "status")) {
while (LS.next(tab, key, val)) {
eng::string kstr;
if (LS.isstring(key)) kstr = LS.ckstring(key);
if (kstr == "status") {
set_status(LS, val); set_status(LS, val);
} else if (kstr == "maxage") { }
if (kp.parse(val, "maxage")) {
set_max_age(LS, val); set_max_age(LS, val);
} else if (kstr == "mimetype") { }
if (kp.parse(val, "mimetype")) {
set_mime_type(LS, val); set_mime_type(LS, val);
} else if (kstr == "content") { }
if (kp.parse(val, "content")) {
set_content(LS, val); set_content(LS, val);
} else if (kstr == "html") { }
if (kp.parse(val, "html")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("text/html"); set_mime_type("text/html");
} else if (kstr == "text") { }
if (kp.parse(val, "text")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("text/plain"); set_mime_type("text/plain");
} else if (kstr == "json") { }
if (kp.parse(val, "json")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("application/json"); set_mime_type("application/json");
} else if (kstr == "bytes") { }
if (kp.parse(val, "bytes")) {
set_content(LS, val); set_content(LS, val);
set_mime_type("application/octet-stream"); 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));
} }
if (kp.parse(val, "jsonvalue")) {
set_jsonvalue(LS, val);
} }
} }
@@ -1712,8 +1716,10 @@ LuaDefine(http_clientrequest, "request",
LuaArg tab; LuaArg tab;
LuaRet str; LuaRet str;
LuaStack LS(L, tab, str); LuaStack LS(L, tab, str);
LuaKeywordParser kp(LS, tab);
HttpClientRequest req; HttpClientRequest req;
req.set_config(LS, tab); req.configure(kp);
kp.final_check_throw();
req.set_defaults(); req.set_defaults();
eng::string error = req.check(); eng::string error = req.check();
if (!error.empty()) { if (!error.empty()) {
@@ -1882,8 +1888,10 @@ LuaDefine(http_serverresponse, "response",
LuaArg tab; LuaArg tab;
LuaRet str; LuaRet str;
LuaStack LS(L, tab, str); LuaStack LS(L, tab, str);
LuaKeywordParser kp(LS, tab);
HttpServerResponse resp; HttpServerResponse resp;
resp.set_config(LS, tab); resp.configure(kp);
kp.final_check_throw();
resp.set_defaults(); resp.set_defaults();
LS.set(str, resp.debug_string()); LS.set(str, resp.debug_string());
return LS.result(); return LS.result();

View File

@@ -104,7 +104,8 @@ public:
void set_defaults(); void set_defaults();
// Populate request-related fields from a Lua table. // 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. // Get or Set the request IDs.
// //
@@ -208,8 +209,9 @@ public:
void set_defaults(); void set_defaults();
// Populate request-related fields from a Lua table. // 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. // Set the keep_alive flag.
// //

View File

@@ -5,6 +5,7 @@
#include <climits> #include <climits>
#include "wrap-string.hpp" #include "wrap-string.hpp"
#include "wrap-set.hpp" #include "wrap-set.hpp"
#include "wrap-sstream.hpp"
LuaSpecial LuaRegistry(LUA_REGISTRYINDEX); LuaSpecial LuaRegistry(LUA_REGISTRYINDEX);
LuaNilMarker LuaNil; LuaNilMarker LuaNil;
@@ -468,8 +469,10 @@ void LuaStack::setvisited(LuaSlot tab, bool visited) const {
LuaKeywordParser::LuaKeywordParser(lua_State *L, int slot) { LuaKeywordParser::LuaKeywordParser(lua_State *L, int slot) {
L_ = L; L_ = L;
slot_ = slot; slot_ = slot;
if (!lua_istable(L_, slot_)) { not_table_ = !lua_istable(L_, slot_);
luaL_error(L_, "expected an argument which is a table full of keywords"); 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())) { if (lua_nkeys(L_, slot_) != int(parsed_.size())) {
lua_pushnil(L_); lua_pushnil(L_);
while (lua_next(L_, slot_) != 0) { while (lua_next(L_, slot_) != 0) {
lua_pop(L_, 1); // Don't need the value. lua_pop(L_, 1); // Don't need the value.
if (!lua_isstring(L_, -1)) { 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); const char *kw = lua_tostring(L_, -1);
if (parsed_.find(kw) == parsed_.end()) { 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"); 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());
}
}

View File

@@ -531,14 +531,15 @@ class LuaKeywordParser {
}; };
}; };
private: private:
bool not_table_;
lua_State *L_; lua_State *L_;
int slot_; int slot_;
eng::set<const char *, cmp_char> parsed_; eng::set<const char *, cmp_char> parsed_;
void init(const lua_State *L, int slot); void init(const lua_State *L, int slot);
public: public:
// The constructor will throw a lua error if the // If the slot is not a table, sets the not_table
// slot is not a table. // flag and creates a dummy table in the slot.
LuaKeywordParser(lua_State *L, int slot); LuaKeywordParser(lua_State *L, int slot);
LuaKeywordParser(const LuaStack &LS, LuaSlot slot) : LuaKeywordParser(LS.state(), slot.index()) {} LuaKeywordParser(const LuaStack &LS, LuaSlot slot) : LuaKeywordParser(LS.state(), slot.index()) {}
@@ -546,9 +547,12 @@ public:
// Return true if the value is non-nil. // Return true if the value is non-nil.
bool parse(LuaSlot slot, const char *kw); bool parse(LuaSlot slot, const char *kw);
// Check if there are any keywords in the table that // Check if there were any errors. If so, return an
// were never parsed. If so, throw an error. // error message.
void check_unparsed_keywords(); eng::string final_check();
// Check if there are any errors. If so, throw a lua error.
void final_check_throw();
// Fetch the state pointer. // Fetch the state pointer.
lua_State *state() const { return L_; } lua_State *state() const { return L_; }

View File

@@ -64,7 +64,7 @@ LuaDefine(tangible_animate, "tan,configtable",
int64_t id = w->alloc_id_predictable(); int64_t id = w->alloc_id_predictable();
AnimStep step; AnimStep step;
step.configure(kp, tan->anim_queue_.back()); step.configure(kp, tan->anim_queue_.back());
kp.check_unparsed_keywords(); kp.final_check_throw();
if (step.action() == "") { if (step.action() == "") {
luaL_error(L, "animation action must be specified"); luaL_error(L, "animation action must be specified");
} }
@@ -152,7 +152,7 @@ LuaDefine(tangible_build, "config",
// Parse the initial animation step. // Parse the initial animation step.
AnimStep initstep, blank; AnimStep initstep, blank;
initstep.configure(kp, blank); initstep.configure(kp, blank);
kp.check_unparsed_keywords(); kp.final_check_throw();
if (!initstep.has_xyz()) { if (!initstep.has_xyz()) {
luaL_error(L, "You must specify (X,Y,Z) for new tangible"); luaL_error(L, "You must specify (X,Y,Z) for new tangible");
} }
@@ -368,7 +368,7 @@ LuaDefine(tangible_find, "config",
LuaKeywordParser kw(LS, config); LuaKeywordParser kw(LS, config);
PlaneScan scan; PlaneScan scan;
scan.configure(kw); scan.configure(kw);
kw.check_unparsed_keywords(); kw.final_check_throw();
// When the configure routine sees the 'near' flag, it stores the tangible // 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 // 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; LuaArg request;
LuaRet response; LuaRet response;
LuaStack LS(L, request, response); LuaStack LS(L, request, response);
LuaKeywordParser kp(LS, request);
HttpClientRequest req; HttpClientRequest req;
// Parse the request and make sure it's valid. // Parse the request and make sure it's valid.
// If not, immediately pass a '400 bad request' back to lua. // If not, immediately pass a '400 bad request' back to lua.
req.set_method(method); req.set_method(method);
req.set_config(LS, request); req.configure(kp);
kp.final_check_throw();
req.set_defaults(); req.set_defaults();
eng::string error = req.check(); eng::string error = req.check();
if (!error.empty()) { if (!error.empty()) {

View File

@@ -533,10 +533,13 @@ HttpServerResponse World::http_serve(const HttpParser &request) {
} }
// Try to convert the table into a response. // Try to convert the table into a response.
// If this results in an error, we don't have to do LuaKeywordParser kp(LS, LuaSpecial(newtop));
// anything special, set_config will store the error. response.configure(kp);
response.set_config(LS, LuaSpecial(newtop));
response.set_defaults(); response.set_defaults();
eng::string kperr = kp.final_check();
if (!kperr.empty()) {
response.fail(500, kperr);
}
LS.result(); LS.result();
return response; return response;
} }