From 229a5dc5397bfb2e118521ea9e7280e61def62c7 Mon Sep 17 00:00:00 2001 From: dormando Date: Sun, 4 Aug 2024 19:11:08 -0700 Subject: [PATCH] proxy: inspector flagis `{ t = "flagis", flag = "L", str = "foo" }` returns `exists, match` booleans if the flag exists and if it matches. --- proxy_inspector.c | 172 +++++++++++++++++++++++++++++++++++----------- t/proxyins.lua | 9 ++- t/proxyins.t | 8 +++ 3 files changed, 148 insertions(+), 41 deletions(-) diff --git a/proxy_inspector.c b/proxy_inspector.c index 141daac83..0f4c2595c 100644 --- a/proxy_inspector.c +++ b/proxy_inspector.c @@ -14,6 +14,7 @@ enum mcp_ins_steptype { mcp_ins_step_hasflag, mcp_ins_step_flagtoken, mcp_ins_step_flagint, + mcp_ins_step_flagis, }; // START STEP STRUCTS @@ -24,22 +25,33 @@ struct mcp_ins_sepkey { int mapref; }; -struct mcp_ins_keybegin { +struct mcp_ins_string { unsigned int str; // arena offset for match string. unsigned int len; }; struct mcp_ins_flag { - char f; uint64_t bit; // flag converted for bitmask test + char f; +}; + +// TODO: the pattern of all these accessors looks like just flattening the +// possibilities direclty into mcp_ins_step might be best. can also align +// things like the chars to compact the struct better. +struct mcp_ins_flagstr { + unsigned int str; + unsigned int len; + uint64_t bit; // flag bit + char f; }; struct mcp_ins_step { enum mcp_ins_steptype type; union { struct mcp_ins_sepkey sepkey; - struct mcp_ins_keybegin keybegin; + struct mcp_ins_string string; struct mcp_ins_flag flag; + struct mcp_ins_flagstr flagstr; } c; }; @@ -93,6 +105,41 @@ static int mcp_inspector_flag_i_g(lua_State *L, int tidx, int sc, struct mcp_ins return 0; } +static int mcp_inspector_string_c_g(lua_State *L, int tidx) { + size_t len = 0; + + if (lua_getfield(L, tidx, "str") != LUA_TNIL) { + lua_tolstring(L, -1, &len); + if (len < 1) { + proxy_lua_ferror(L, "inspector step %d: 'str' must have nonzero length", tidx); + } + } else { + proxy_lua_ferror(L, "inspector step %d: must provide 'str' argument", tidx); + } + lua_pop(L, 1); // val or nil + + return len; +} + +static int mcp_inspector_string_i_g(lua_State *L, int tidx, int sc, struct mcp_inspector *ins) { + struct mcp_ins_step *s = &ins->steps[sc]; + struct mcp_ins_string *c = &s->c.string; + size_t len = 0; + + // store our match string in the arena space that we reserved before. + if (lua_getfield(L, tidx, "str") != LUA_TNIL) { + const char *str = lua_tolstring(L, -1, &len); + c->str = ins->aused; + c->len = len; + char *a = ins->arena + ins->aused; + memcpy(a, str, len); + ins->aused += len; + } + lua_pop(L, 1); // val or nil + + return len; +} + // END COMMMON ARG HANDLERS // TODO: @@ -207,44 +254,9 @@ static int mcp_inspector_sepkey_r(lua_State *L, struct mcp_inspector *ins, struc return 1; } -static int mcp_inspector_keybegin_c(lua_State *L, int tidx) { - size_t len = 0; - - if (lua_getfield(L, tidx, "str") != LUA_TNIL) { - lua_tolstring(L, -1, &len); - if (len < 1) { - proxy_lua_ferror(L, "inspector step %d: 'str' must have nonzero length", tidx); - } - } else { - proxy_lua_ferror(L, "inspector step %d: must provide 'str' argument", tidx); - } - lua_pop(L, 1); // val or nil - - return len; -} - -static int mcp_inspector_keybegin_i(lua_State *L, int tidx, int sc, struct mcp_inspector *ins) { - struct mcp_ins_step *s = &ins->steps[sc]; - struct mcp_ins_keybegin *c = &s->c.keybegin; - size_t len = 0; - - // store our match string in the arena space that we reserved before. - if (lua_getfield(L, tidx, "str") != LUA_TNIL) { - const char *str = lua_tolstring(L, -1, &len); - c->str = ins->aused; - c->len = len; - char *a = ins->arena + ins->aused; - memcpy(a, str, len); - ins->aused += len; - } - lua_pop(L, 1); // val or nil - - return len; -} - static int mcp_inspector_keybegin_r(lua_State *L, struct mcp_inspector *ins, struct mcp_ins_step *s, void *arg) { mcp_request_t *rq = arg; - struct mcp_ins_keybegin *c = &s->c.keybegin; + struct mcp_ins_string *c = &s->c.string; const char *key = MCP_PARSER_KEY(rq->pr); int klen = rq->pr.klen; @@ -367,6 +379,81 @@ static int mcp_inspector_flagint_r(lua_State *L, struct mcp_inspector *ins, stru return 2; } +static int mcp_inspector_flagstr_c(lua_State *L, int tidx) { + mcp_inspector_flag_c_g(L, tidx); + int size = mcp_inspector_string_c_g(L, tidx); + return size; +} + +static int mcp_inspector_flagstr_i(lua_State *L, int tidx, int sc, struct mcp_inspector *ins) { + // TODO: if we never use mcp_ins_step we can remove it and just pass parts + // of the relevant structs down into these functions. + struct mcp_ins_step *s = &ins->steps[sc]; + struct mcp_ins_flagstr *c = &s->c.flagstr; + size_t len = 0; + + if (lua_getfield(L, tidx, "flag") != LUA_TNIL) { + const char *flag = lua_tostring(L, -1); + c->f = flag[0]; + c->bit = (uint64_t)1 << (c->f - 65); + } + lua_pop(L, 1); // val or nil + + if (lua_getfield(L, tidx, "str") != LUA_TNIL) { + const char *str = lua_tolstring(L, -1, &len); + c->str = ins->aused; + c->len = len; + char *a = ins->arena + ins->aused; + memcpy(a, str, len); + ins->aused += len; + } + lua_pop(L, 1); // val or nil + + return len; +} + +// FIXME: size_t vs int consistency for tlen would shorten the code. +static int mcp_inspector_flagis_r(lua_State *L, struct mcp_inspector *ins, struct mcp_ins_step *s, void *arg) { + struct mcp_ins_flagstr *c = &s->c.flagstr; + const char *str = ins->arena + c->str; + if (ins->type == INS_REQ) { + mcp_request_t *rq = arg; + + if (rq->pr.t.meta.flags & c->bit) { + lua_pushboolean(L, 1); // flag exists + const char *tok = NULL; + size_t tlen = 0; + mcp_request_find_flag_token(rq, c->f, &tok, &tlen); + if (tlen == c->len && strncmp(tok, str, c->len) == 0) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 2; + } + } else { + mcp_resp_t *res = arg; + if (res->resp.type == MCMC_RESP_META) { + mcmc_tokenize_res(res->buf, res->resp.reslen, &res->tok); + if (mcmc_token_has_flag_bit(&res->tok, c->bit) == MCMC_OK) { + lua_pushboolean(L, 1); // flag exists + int tlen = 0; + const char *tok = mcmc_token_get_flag(res->buf, &res->tok, c->f, &tlen); + if (tlen == c->len && strncmp(tok, str, c->len) == 0) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 2; + } + } + } + lua_pushboolean(L, 0); + lua_pushnil(L); + + return 2; +} + // END STEPS typedef int (*mcp_ins_c)(lua_State *L, int tidx); @@ -379,13 +466,15 @@ struct mcp_ins_entry { mcp_ins_r r; }; +// TODO: add str as the first option then do a for loop search in steptype? static const struct mcp_ins_entry mcp_ins_entries[] = { [mcp_ins_step_none] = {NULL, NULL, NULL}, [mcp_ins_step_sepkey] = {mcp_inspector_sepkey_c, mcp_inspector_sepkey_i, mcp_inspector_sepkey_r}, - [mcp_ins_step_keybegin] = {mcp_inspector_keybegin_c, mcp_inspector_keybegin_i, mcp_inspector_keybegin_r}, + [mcp_ins_step_keybegin] = {mcp_inspector_string_c_g, mcp_inspector_string_i_g, mcp_inspector_keybegin_r}, [mcp_ins_step_hasflag] = {mcp_inspector_flag_c_g, mcp_inspector_flag_i_g, mcp_inspector_hasflag_r}, [mcp_ins_step_flagtoken] = {mcp_inspector_flag_c_g, mcp_inspector_flag_i_g, mcp_inspector_flagtoken_r}, [mcp_ins_step_flagint] = {mcp_inspector_flag_c_g, mcp_inspector_flag_i_g, mcp_inspector_flagint_r}, + [mcp_ins_step_flagis] = {mcp_inspector_flagstr_c, mcp_inspector_flagstr_i, mcp_inspector_flagis_r}, }; // call with type string on top @@ -402,6 +491,8 @@ static enum mcp_ins_steptype mcp_inspector_steptype(lua_State *L) { return mcp_ins_step_flagtoken; } else if (strcmp(type, "flagint") == 0) { return mcp_ins_step_flagint; + } else if (strcmp(type, "flagis") == 0) { + return mcp_ins_step_flagis; } return mcp_ins_step_none; } @@ -530,6 +621,7 @@ int mcplib_inspector_gc(lua_State *L) { case mcp_ins_step_hasflag: case mcp_ins_step_flagtoken: case mcp_ins_step_flagint: + case mcp_ins_step_flagis: case mcp_ins_step_none: break; } diff --git a/t/proxyins.lua b/t/proxyins.lua index bacb07841..243cddce1 100644 --- a/t/proxyins.lua +++ b/t/proxyins.lua @@ -42,12 +42,19 @@ function mcp_config_routes(p) { t = "flagtoken", flag = "O" }, { t = "flagint", flag = "t" } ) + + local mgresflagis_ins = mcp.res_inspector_new( + { t = "flagis", flag = "O", str = "baz" } + ) mgreshasf:ready({ n = "reshasflag", f = function(rctx) return function(r) local key = r:key() local res = rctx:enqueue_and_wait(r, mgreshasfh) - if key == "reshasf/tokenint" then + if key == "reshasf/flagis" then + local exists, matches = mgresflagis_ins(res) + return string.format("SERVER_ERROR exists[%q] matches[%q]\r\n", exists, matches) + elseif key == "reshasf/tokenint" then local has_O, O, has_t, t = mgresflaga_ins(res) return string.format("SERVER_ERROR O[%q]: %s t[%q]: %d\r\n", has_O, O, has_t, t) diff --git a/t/proxyins.t b/t/proxyins.t index 3bda1fb86..d35389724 100644 --- a/t/proxyins.t +++ b/t/proxyins.t @@ -69,6 +69,14 @@ sub test_mgres { $t->c_recv("SERVER_ERROR O[true]: moo t[true]: 60\r\n"); $t->clear(); }; + + subtest 'flagis' => sub { + $t->c_send("mg reshasf/flagis Obaz\r\n"); + $t->be_recv_c(0); + $t->be_send(0, "HD f Obaz\r\n"); + $t->c_recv("SERVER_ERROR exists[true] matches[true]\r\n"); + $t->clear(); + }; } done_testing();