From bebbca1f92fb954e0a6da45f485ecc20d9baddbe Mon Sep 17 00:00:00 2001 From: Ivan Polyakov Date: Mon, 21 Nov 2022 22:32:28 +0300 Subject: [PATCH] non-unique keyval storage --- c/keyval.c | 33 ++++++++++++++++++++++++++++++++- c/response.c | 1 + include/KeyVal.hxx | 18 ++++++++++++++++++ include/keyval.h | 16 ++++++++++++++++ servers/fcgi.c | 1 + servers/tcp.c | 1 + tests/app.cxx | 2 +- tests/keyval.cxx | 18 ++++++++++++++++++ 8 files changed, 88 insertions(+), 2 deletions(-) diff --git a/c/keyval.c b/c/keyval.c index cf8fa9b..41ae2f8 100644 --- a/c/keyval.c +++ b/c/keyval.c @@ -19,6 +19,7 @@ static int rpd_keyval_realloc(rpd_keyval *keyval, int capacity); int rpd_keyval_init(rpd_keyval *keyval, int capacity) { keyval->size = keyval->capacity = 0; + keyval->unique = 1; if (capacity == 0) { keyval->items = NULL; @@ -45,7 +46,7 @@ int rpd_keyval_insert(rpd_keyval *keyval, const char *key, const char *value) } rpd_keyval_item *item = rpd_keyval_find(keyval, key); - if (item) { + if (item && keyval->unique) { free(item->key); item->key = NULL; if (item->val) { @@ -75,6 +76,36 @@ rpd_keyval_item *rpd_keyval_find(const rpd_keyval *keyval, const char *key) return NULL; } +rpd_keyval_item **rpd_keyval_findall(const rpd_keyval *keyval, const char *key) +{ + int i = 0, n = 0; + rpd_keyval_item **items = NULL; + while (i < keyval->size) { + if (!strcmp(keyval->items[i].key, key)) { + n++; + } + i++; + } + if (!n) + return NULL; + + items = malloc(sizeof(rpd_keyval_item *) * (n + 1)); + if (!items) + return NULL; + + items[n] = NULL; + i = 0, n = 0; + while (i < keyval->size) { + if (!strcmp(keyval->items[i].key, key)) { + items[n] = keyval->items + i; + n++; + } + i++; + } + + return items; +} + void rpd_keyval_cleanup(rpd_keyval *keyval) { if (keyval->items) { diff --git a/c/response.c b/c/response.c index b040dfb..4125582 100644 --- a/c/response.c +++ b/c/response.c @@ -15,6 +15,7 @@ void rpd_res_init(rpd_res *dest) dest->status = rpd_res_st_ok; dest->body = NULL; rpd_keyval_init(&dest->headers, 5); + dest->headers.unique = 0; } int rpd_res_headers_str(char **dest, const rpd_res *src) diff --git a/include/KeyVal.hxx b/include/KeyVal.hxx index ace50e2..3adfdde 100644 --- a/include/KeyVal.hxx +++ b/include/KeyVal.hxx @@ -36,6 +36,24 @@ public: _keyval = keyval; } + /*! + * \brief Is the storage unique? + */ + bool unique() const + { + return _keyval->unique; + } + + /*! + * \brief Sets the uniqueness flag. + * + * \param is_unique Uniqueness flag. + */ + void unique(bool is_unique) + { + _keyval->unique = is_unique; + } + /*! * \brief Returns real key-value storage. * diff --git a/include/keyval.h b/include/keyval.h index 4c91307..a532019 100644 --- a/include/keyval.h +++ b/include/keyval.h @@ -30,6 +30,7 @@ typedef struct { rpd_keyval_item *items; /**< Key-value pairs array. */ int size; /**< Number of elements in rpd_keyval::items. */ int capacity; /**< Current capacity (allocated memory). */ + int unique; /**< Unique flag */ } rpd_keyval; /*! @@ -70,6 +71,21 @@ int rpd_keyval_insert(rpd_keyval *keyval, const char *key, const char *value); */ rpd_keyval_item *rpd_keyval_find(const rpd_keyval *keyval, const char *key); +/*! + * \brief Finds all key-value pair by key. + * + * Useful when storage is not unique. + * In request header fields, for example. + * + * Last item will be NULL. + * + * \param keyval Key-value storage instance. + * \param key Key to search. + * + * \return Found items or NULL. + */ +rpd_keyval_item **rpd_keyval_findall(const rpd_keyval *keyval, const char *key); + /*! * \brief Free key-val pairs and reset. * diff --git a/servers/fcgi.c b/servers/fcgi.c index f4670a5..e8dec1b 100644 --- a/servers/fcgi.c +++ b/servers/fcgi.c @@ -165,6 +165,7 @@ static int fcgx_to_rpd_req(rpd_req *dest, FCGX_Request *req) // dest->cookie = FCGX_GetParam("HTTP_COOKIE", req->envp); rpd_keyval_init(&dest->params, 0); rpd_keyval_init(&dest->headers, 0); + dest->headers.unique = 0; char **env = req->envp; char *key = NULL, *val = NULL; diff --git a/servers/tcp.c b/servers/tcp.c index 392ea12..1093e9f 100644 --- a/servers/tcp.c +++ b/servers/tcp.c @@ -47,6 +47,7 @@ static int mg_to_rpd_req(rpd_req *req, struct mg_http_message *msg) size_t i, max = sizeof(msg->headers) / sizeof(msg->headers[0]); rpd_keyval_init(&req->headers, max); + req->headers.unique = 0; // Iterate over request headers char *key = NULL, *val = NULL; diff --git a/tests/app.cxx b/tests/app.cxx index e49e2b5..7c4cd7f 100644 --- a/tests/app.cxx +++ b/tests/app.cxx @@ -9,7 +9,7 @@ using namespace rpd; TEST_CASE("Application") { rpd_app app; - int res = rpd_app_create(&app, "/tmp/rapida.test.socket"); + int res = rpd_app_create(&app); SECTION("App creation") { diff --git a/tests/keyval.cxx b/tests/keyval.cxx index a217efe..3fa95df 100644 --- a/tests/keyval.cxx +++ b/tests/keyval.cxx @@ -36,6 +36,24 @@ TEST_CASE("Key-value storage") REQUIRE(std::string(item->val) == "val"); } + SECTION("Passing duplicates to non-unique storage and finding them") + { + int sz = keyval.size; + keyval.unique = 0; + + rpd_keyval_insert(&keyval, "Set-Cookie", "param=val"); + rpd_keyval_insert(&keyval, "Set-Cookie", "param1=val1"); + REQUIRE(keyval.size == sz + 2); + + rpd_keyval_item **cookies = rpd_keyval_findall(&keyval, "Set-Cookie"); + REQUIRE(cookies != NULL); + REQUIRE(std::string(cookies[0]->key) == "Set-Cookie"); + REQUIRE(std::string(cookies[0]->val) == "param=val"); + REQUIRE(std::string(cookies[1]->key) == "Set-Cookie"); + REQUIRE(std::string(cookies[1]->val) == "param1=val1"); + REQUIRE(cookies[2] == NULL); + } + SECTION("Cleanup") { rpd_keyval_cleanup(&keyval);