From 92cd086fb4b95bf6b896330c12220490b354cb3a Mon Sep 17 00:00:00 2001 From: auyer Date: Sat, 30 Sep 2023 00:09:07 -0300 Subject: [PATCH 01/36] First tests building a c SDK. Time to remeber C! --- .gitignore | 1 + sdks/c/CMakeLists.txt | 15 +++ sdks/c/Makefile | 5 + sdks/c/libMemoryKV/CMakeLists.txt | 6 ++ sdks/c/libMemoryKV/libMemoryKV.c | 157 ++++++++++++++++++++++++++++++ sdks/c/libMemoryKV/libMemoryKV.h | 7 ++ sdks/c/main.c | 29 ++++++ 7 files changed, 220 insertions(+) create mode 100644 sdks/c/CMakeLists.txt create mode 100644 sdks/c/Makefile create mode 100644 sdks/c/libMemoryKV/CMakeLists.txt create mode 100644 sdks/c/libMemoryKV/libMemoryKV.c create mode 100644 sdks/c/libMemoryKV/libMemoryKV.h create mode 100644 sdks/c/main.c diff --git a/.gitignore b/.gitignore index ea8c4bf..890769c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +sdks/c/build \ No newline at end of file diff --git a/sdks/c/CMakeLists.txt b/sdks/c/CMakeLists.txt new file mode 100644 index 0000000..146ce41 --- /dev/null +++ b/sdks/c/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.27.6) + +project(CMemoryKV) + +add_executable(${PROJECT_NAME} main.c) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(libcurl REQUIRED IMPORTED_TARGET libcurl>=7.17.0) + +add_subdirectory(libMemoryKV) + +target_include_directories(${PROJECT_NAME} PUBLIC libMemoryKV/) +target_link_directories(${PROJECT_NAME} PUBLIC libMemoryKV/) + +target_link_libraries(${PROJECT_NAME} PUBLIC PkgConfig::libcurl libMemoryKV) diff --git a/sdks/c/Makefile b/sdks/c/Makefile new file mode 100644 index 0000000..a5f0f49 --- /dev/null +++ b/sdks/c/Makefile @@ -0,0 +1,5 @@ +cmake: + cmake -S . -B build + +cmake_make: + cd build && make \ No newline at end of file diff --git a/sdks/c/libMemoryKV/CMakeLists.txt b/sdks/c/libMemoryKV/CMakeLists.txt new file mode 100644 index 0000000..fdc8aba --- /dev/null +++ b/sdks/c/libMemoryKV/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(libMemoryKV libMemoryKV.c) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(libcurl REQUIRED IMPORTED_TARGET libcurl>=7.17.0) + +target_link_libraries(${PROJECT_NAME} PUBLIC PkgConfig::libcurl) \ No newline at end of file diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c new file mode 100644 index 0000000..d4daf30 --- /dev/null +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -0,0 +1,157 @@ +#include +#include + +char get_key(char *key) { + + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if (curl) { + + char base_url[] = "http://localhost:8080/"; + + char url[sizeof(key) + sizeof(base_url)]; + + snprintf(url, sizeof(url), "http://localhost:8080/%s", key); + + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } else { + return 0; + } +} + +char delete_key(char *key) { + + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if (curl) { + char base_url[] = "http://localhost:8080/"; + + char url[sizeof(key) + sizeof(base_url)]; + + snprintf(url, sizeof(url), "http://localhost:8080/%s", key); + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + // set custom request to delete + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } else { + return 0; + } +} + +char put_key(char *key, char *put_body) { + CURL *curl; + CURLcode res; + // start the easy interface in curl + curl = curl_easy_init(); + if (curl) { + char base_url[] = "http://localhost:8080/"; + + char url[sizeof(key) + sizeof(base_url)]; + + snprintf(url, sizeof(url), "http://localhost:8080/%s", key); + + struct curl_slist *headers = NULL; + + /* default type with postfields is application/x-www-form-urlencoded, + change it if you want */ + headers = + curl_slist_append(headers, "Content-Type: application/octet-stream"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + /* pass on content in request body. When CURLOPT_POSTFIELDSIZE is not used, + curl does strlen to get the size. */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); + + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + + /* override the POST implied by CURLOPT_POSTFIELDS + * + * Warning: CURLOPT_CUSTOMREQUEST is problematic, especially if you want + * to follow redirects. Be aware. + */ + + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + + /* free headers */ + curl_slist_free_all(headers); + } else { + return 0; + } +} + +char list_keys() { + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_ALL); + + // start the easy interface in curl + curl = curl_easy_init(); + if (curl) { + + char base_url[] = "http://localhost:8080/"; + char key[] = "keys"; + + char url[sizeof(key) + sizeof(base_url)]; + + snprintf(url, sizeof(url), "http://localhost:8080/%s", key); + + curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/keys"); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } else { + return 0; + } +} \ No newline at end of file diff --git a/sdks/c/libMemoryKV/libMemoryKV.h b/sdks/c/libMemoryKV/libMemoryKV.h new file mode 100644 index 0000000..05c05bb --- /dev/null +++ b/sdks/c/libMemoryKV/libMemoryKV.h @@ -0,0 +1,7 @@ +char get_key(char *key); + +char delete_key(char *key); + +char put_key(char *key, char *put_body); + +char list_keys(); \ No newline at end of file diff --git a/sdks/c/main.c b/sdks/c/main.c new file mode 100644 index 0000000..e558842 --- /dev/null +++ b/sdks/c/main.c @@ -0,0 +1,29 @@ +#include +#include +#include + +int main(void) { + + char key[] = "c_sdk2"; + + // put key + fprintf(stdout, "\nPut Keys Request\n"); + + static const char put_body[] = "{" + " \"name\" : \"c_sdk\"," + " \"content\" : \"json\"" + "}"; + put_key(key, put_body); + + list_keys(); + + // get key + fprintf(stdout, "\nMaking a Get Key Request\n"); + + get_key(key); + + // delete key + fprintf(stdout, "\nMaking a delete Key Request\n"); + + delete_key(key); +} From 08b205ce2252389f2a187b40ae6b781c4325f11f Mon Sep 17 00:00:00 2001 From: auyer Date: Mon, 2 Oct 2023 23:11:11 -0300 Subject: [PATCH 02/36] Client, Client builder, url builder --- sdks/c/libMemoryKV/libMemoryKV.c | 276 ++++++++++++++++--------------- sdks/c/libMemoryKV/libMemoryKV.h | 16 +- sdks/c/main.c | 36 ++-- 3 files changed, 172 insertions(+), 156 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index d4daf30..6d819cb 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -1,157 +1,159 @@ #include #include +#include +#include -char get_key(char *key) { +typedef struct memkv_client { + char *host; +} memkv_client; - CURL *curl; - CURLcode res; - - curl_global_init(CURL_GLOBAL_ALL); - curl = curl_easy_init(); - if (curl) { - - char base_url[] = "http://localhost:8080/"; - - char url[sizeof(key) + sizeof(base_url)]; - - snprintf(url, sizeof(url), "http://localhost:8080/%s", key); - - curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - - /* always cleanup */ - curl_easy_cleanup(curl); - } else { - return 0; - } +void memkv_init_client(struct memkv_client *client, char *host) { + curl_global_init(CURL_GLOBAL_ALL); + client->host = host; } -char delete_key(char *key) { - - CURL *curl; - CURLcode res; - - curl_global_init(CURL_GLOBAL_ALL); - curl = curl_easy_init(); - if (curl) { - char base_url[] = "http://localhost:8080/"; - - char url[sizeof(key) + sizeof(base_url)]; - - snprintf(url, sizeof(url), "http://localhost:8080/%s", key); - curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - - // set custom request to delete - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); +memkv_client *memkv_client_new(char *host) { + memkv_client *client = malloc(sizeof(memkv_client)); + memkv_init_client(client, host); + return client; +} - /* always cleanup */ - curl_easy_cleanup(curl); - } else { - return 0; - } +char *build_url(const char *host, const char *params) { + // snprintf returns the number of characters that would have been written if called with NULL + int sizeneeded = snprintf(NULL, 0, "%s/%s", host, params); + // use that number to allocate a buffer of the right size + char *url = malloc(sizeneeded + 1); + // write the string to the buffer + sprintf(url, "%s/%s", host, params); + return url; } -char put_key(char *key, char *put_body) { - CURL *curl; - CURLcode res; - // start the easy interface in curl - curl = curl_easy_init(); - if (curl) { - char base_url[] = "http://localhost:8080/"; - - char url[sizeof(key) + sizeof(base_url)]; - - snprintf(url, sizeof(url), "http://localhost:8080/%s", key); - - struct curl_slist *headers = NULL; - - /* default type with postfields is application/x-www-form-urlencoded, - change it if you want */ - headers = - curl_slist_append(headers, "Content-Type: application/octet-stream"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - /* pass on content in request body. When CURLOPT_POSTFIELDSIZE is not used, - curl does strlen to get the size. */ - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); - - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - - /* override the POST implied by CURLOPT_POSTFIELDS - * - * Warning: CURLOPT_CUSTOMREQUEST is problematic, especially if you want - * to follow redirects. Be aware. - */ - - curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - - /* always cleanup */ - curl_easy_cleanup(curl); - - /* free headers */ - curl_slist_free_all(headers); - } else { - return 0; - } +char memkv_get_key(struct memkv_client *client, const char *key) { + + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + if (curl) { + char *url = build_url(client->host, key); + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } else { + return 0; + } } -char list_keys() { - CURL *curl; - CURLcode res; +char memkv_delete_key(struct memkv_client *client, const char *key) { - curl_global_init(CURL_GLOBAL_ALL); + CURL *curl; + CURLcode res; - // start the easy interface in curl - curl = curl_easy_init(); - if (curl) { + curl = curl_easy_init(); + if (curl) { - char base_url[] = "http://localhost:8080/"; - char key[] = "keys"; + char *url = build_url(client->host, key); + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - char url[sizeof(key) + sizeof(base_url)]; + // set custom request to delete + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - snprintf(url, sizeof(url), "http://localhost:8080/%s", key); + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/keys"); - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + /* always cleanup */ + curl_easy_cleanup(curl); + } else { + return 0; + } +} - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); +char memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { + CURL *curl; + CURLcode res; + // start the easy interface in curl + curl = curl_easy_init(); + if (curl) { + char *url = build_url(client->host, key); + curl_easy_setopt(curl, CURLOPT_URL, url); + + struct curl_slist *headers = NULL; + + /* default type with postfields is application/x-www-form-urlencoded, + change it if you want */ + headers = curl_slist_append(headers, "Content-Type: application/octet-stream"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + /* pass on content in request body. When CURLOPT_POSTFIELDSIZE is not used, + curl does strlen to get the size. */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); + + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + + /* override the POST implied by CURLOPT_POSTFIELDS + * + * Warning: CURLOPT_CUSTOMREQUEST is problematic, especially if you want + * to follow redirects. Be aware. + */ + + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + + /* free headers */ + curl_slist_free_all(headers); + } else { + return 0; + } +} - /* always cleanup */ - curl_easy_cleanup(curl); - } else { - return 0; - } +char memkv_list_keys(struct memkv_client *client) { + CURL *curl; + CURLcode res; + + // start the easy interface in curl + curl = curl_easy_init(); + if (curl) { + + char *key = "keys"; + char *url = build_url(client->host, key); + + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + } else { + return 0; + } } \ No newline at end of file diff --git a/sdks/c/libMemoryKV/libMemoryKV.h b/sdks/c/libMemoryKV/libMemoryKV.h index 05c05bb..97d600c 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.h +++ b/sdks/c/libMemoryKV/libMemoryKV.h @@ -1,7 +1,15 @@ -char get_key(char *key); +typedef struct memkv_client { + char *host; +} memkv_client; -char delete_key(char *key); +void memkv_init_client(struct memkv_client *client, char *host); -char put_key(char *key, char *put_body); +memkv_client *memkv_client_new(char *host); -char list_keys(); \ No newline at end of file +char memkv_get_key(struct memkv_client *client, const char *key); + +char memkv_delete_key(struct memkv_client *client, const char *key); + +char memkv_put_key(struct memkv_client *client, const char *key, const char *put_body); + +char memkv_list_keys(struct memkv_client *client); \ No newline at end of file diff --git a/sdks/c/main.c b/sdks/c/main.c index e558842..0a8dce8 100644 --- a/sdks/c/main.c +++ b/sdks/c/main.c @@ -4,26 +4,32 @@ int main(void) { - char key[] = "c_sdk2"; + char key[] = "c_sdk2"; - // put key - fprintf(stdout, "\nPut Keys Request\n"); + // put key + fprintf(stdout, "\nPut Key Request\n"); - static const char put_body[] = "{" - " \"name\" : \"c_sdk\"," - " \"content\" : \"json\"" - "}"; - put_key(key, put_body); + static const char put_body[] = "{" + " \"name\" : \"c_sdk\"," + " \"content\" : \"json\"" + "}"; - list_keys(); + memkv_client *client; + client = memkv_client_new("http://localhost:8080"); - // get key - fprintf(stdout, "\nMaking a Get Key Request\n"); + memkv_put_key(client, key, put_body); - get_key(key); + fprintf(stdout, "\nList Keys Request\n"); + // list key + memkv_list_keys(client); - // delete key - fprintf(stdout, "\nMaking a delete Key Request\n"); + // get key + fprintf(stdout, "\nMaking a Get Key Request\n"); - delete_key(key); + memkv_get_key(client, key); + + // delete key + fprintf(stdout, "\nMaking a delete Key Request\n"); + + memkv_delete_key(client, key); } From a6b28174cc0ed017ab175d5a871237a2712c0d45 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 6 Oct 2023 17:57:13 -0300 Subject: [PATCH 03/36] returning the reponses as *char --- sdks/c/libMemoryKV/libMemoryKV.c | 115 +++++++++++++++++++++++-------- sdks/c/libMemoryKV/libMemoryKV.h | 8 +-- sdks/c/main.c | 29 +++++--- 3 files changed, 113 insertions(+), 39 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index 6d819cb..8d94497 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -3,6 +3,44 @@ #include #include + +// string struct to store the body results from curl with easy reallocation +struct string { + char *ptr; + size_t len; +}; + +// init_string initializes the string struct to store the body results from curl +void init_string(struct string *s) { + s->len = 0; + s->ptr = malloc(s->len+1); + if (s->ptr == NULL) { + // TODO: figure out a better way to handle errors in C + fprintf(stderr, "malloc() failed\n"); + exit(EXIT_FAILURE); + } + s->ptr[0] = '\0'; +} + +// writefunc is the callback function to read the body from libcurl into a string +size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) +{ + // new length to realocate response chunks from libcurl + size_t new_len = s->len + size*nmemb; + s->ptr = realloc(s->ptr, new_len+1); + if (s->ptr == NULL) { + // TODO: figure out a better way to handle errors in C + fprintf(stderr, "realloc() failed\n"); + exit(EXIT_FAILURE); + } + memcpy(s->ptr+s->len, ptr, size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + + return size*nmemb; +} + + typedef struct memkv_client { char *host; } memkv_client; @@ -28,32 +66,42 @@ char *build_url(const char *host, const char *params) { return url; } -char memkv_get_key(struct memkv_client *client, const char *key) { + +// Request functions: +// + +char *memkv_get_key(struct memkv_client *client, const char *key) { CURL *curl; CURLcode res; curl = curl_easy_init(); if (curl) { + struct string s; + init_string(&s); + char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK) + if (res != CURLE_OK){ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - + } /* always cleanup */ curl_easy_cleanup(curl); - } else { - return 0; + free(url); + return (char*)s.ptr; } + return 0; } -char memkv_delete_key(struct memkv_client *client, const char *key) { +char *memkv_delete_key(struct memkv_client *client, const char *key) { CURL *curl; CURLcode res; @@ -61,34 +109,42 @@ char memkv_delete_key(struct memkv_client *client, const char *key) { curl = curl_easy_init(); if (curl) { - char *url = build_url(client->host, key); + struct string s; + init_string(&s); + + char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // set custom request to delete curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + if (res != CURLE_OK){ + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + } + free(url); + return (char*)s.ptr; - /* always cleanup */ - curl_easy_cleanup(curl); - } else { - return 0; } + return 0; } -char memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { +char *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { CURL *curl; CURLcode res; // start the easy interface in curl curl = curl_easy_init(); if (curl) { - char *url = build_url(client->host, key); + struct string s; + init_string(&s); + + char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); struct curl_slist *headers = NULL; @@ -116,44 +172,49 @@ char memkv_put_key(struct memkv_client *client, const char *key, const char *put /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK) + if (res != CURLE_OK){ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - + } /* always cleanup */ curl_easy_cleanup(curl); /* free headers */ curl_slist_free_all(headers); - } else { - return 0; + free(url); + return (char*)s.ptr; } + return 0; } -char memkv_list_keys(struct memkv_client *client) { +char *memkv_list_keys(struct memkv_client *client) { CURL *curl; CURLcode res; // start the easy interface in curl curl = curl_easy_init(); if (curl) { + struct string s; + init_string(&s); - char *key = "keys"; + const char *key = "keys"; char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK) + if (res != CURLE_OK){ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - + } /* always cleanup */ curl_easy_cleanup(curl); free(url); - } else { - return 0; + return (char*)s.ptr; } -} \ No newline at end of file + return 0; +} diff --git a/sdks/c/libMemoryKV/libMemoryKV.h b/sdks/c/libMemoryKV/libMemoryKV.h index 97d600c..91a8abe 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.h +++ b/sdks/c/libMemoryKV/libMemoryKV.h @@ -6,10 +6,10 @@ void memkv_init_client(struct memkv_client *client, char *host); memkv_client *memkv_client_new(char *host); -char memkv_get_key(struct memkv_client *client, const char *key); +char *memkv_get_key(struct memkv_client *client, const char *key); -char memkv_delete_key(struct memkv_client *client, const char *key); +char *memkv_delete_key(struct memkv_client *client, const char *key); -char memkv_put_key(struct memkv_client *client, const char *key, const char *put_body); +char *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body); -char memkv_list_keys(struct memkv_client *client); \ No newline at end of file +char *memkv_list_keys(struct memkv_client *client); diff --git a/sdks/c/main.c b/sdks/c/main.c index 0a8dce8..172a23c 100644 --- a/sdks/c/main.c +++ b/sdks/c/main.c @@ -1,8 +1,12 @@ #include #include #include +#include int main(void) { + memkv_client *client; + + client = memkv_client_new("http://localhost:8080"); char key[] = "c_sdk2"; @@ -14,22 +18,31 @@ int main(void) { " \"content\" : \"json\"" "}"; - memkv_client *client; - client = memkv_client_new("http://localhost:8080"); - - memkv_put_key(client, key, put_body); + char *response; + response = memkv_put_key(client, key, put_body); + printf("%s\n", response); + free(response); fprintf(stdout, "\nList Keys Request\n"); // list key - memkv_list_keys(client); + response = memkv_list_keys(client); + printf("%s\n", response); + free(response); // get key fprintf(stdout, "\nMaking a Get Key Request\n"); - - memkv_get_key(client, key); + + response = memkv_get_key(client, key); + printf("%s\n", response); + free(response); // delete key fprintf(stdout, "\nMaking a delete Key Request\n"); + + + response = memkv_delete_key(client, key); + printf("%s\n", response); + free(response); - memkv_delete_key(client, key); + free(client); } From 83c65211b6998000150056cd5c884015dd81a694 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 6 Oct 2023 17:57:42 -0300 Subject: [PATCH 04/36] compile_flags (for clangd LSP), c11 std --- sdks/c/CMakeLists.txt | 2 ++ sdks/c/compile_flags.txt | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 sdks/c/compile_flags.txt diff --git a/sdks/c/CMakeLists.txt b/sdks/c/CMakeLists.txt index 146ce41..64fbae4 100644 --- a/sdks/c/CMakeLists.txt +++ b/sdks/c/CMakeLists.txt @@ -4,6 +4,8 @@ project(CMemoryKV) add_executable(${PROJECT_NAME} main.c) +set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) + find_package(PkgConfig REQUIRED) pkg_check_modules(libcurl REQUIRED IMPORTED_TARGET libcurl>=7.17.0) diff --git a/sdks/c/compile_flags.txt b/sdks/c/compile_flags.txt new file mode 100644 index 0000000..f960fd9 --- /dev/null +++ b/sdks/c/compile_flags.txt @@ -0,0 +1,3 @@ +-I./libMemoryKV +-llibMemoryKV +-std=c11 From 24200643e5dde0a8e99a0c160659b7ee85a5212f Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 6 Oct 2023 18:00:19 -0300 Subject: [PATCH 05/36] clang-tidy, clang-format + reformating with clangd --- sdks/c/.clang-format | 57 ++++++++++++++ sdks/c/.clang-tidy | 121 ++++++++++++++++++++++++++++++ sdks/c/libMemoryKV/libMemoryKV.c | 125 +++++++++++++++---------------- sdks/c/main.c | 29 ++++--- 4 files changed, 252 insertions(+), 80 deletions(-) create mode 100644 sdks/c/.clang-format create mode 100644 sdks/c/.clang-tidy diff --git a/sdks/c/.clang-format b/sdks/c/.clang-format new file mode 100644 index 0000000..0a1bab2 --- /dev/null +++ b/sdks/c/.clang-format @@ -0,0 +1,57 @@ +--- +Language: Cpp +# BasedOnStyle: Mozilla +AlignAfterOpenBracket: AlwaysBreak +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakStringLiterals: true +ColumnLimit: 120 +ContinuationIndentWidth: 4 +DerivePointerAlignment: false +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 100000 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Always +... diff --git a/sdks/c/.clang-tidy b/sdks/c/.clang-tidy new file mode 100644 index 0000000..2f39995 --- /dev/null +++ b/sdks/c/.clang-tidy @@ -0,0 +1,121 @@ +--- +Checks: ' + -*, + clang-*, + bugprone-assert-side-effect, + bugprone-bool-pointer-implicit-conversion, + bugprone-incorrect-roundings, + bugprone-integer-division, + bugprone-macro-parentheses, + bugprone-macro-repeated-side-effects, + bugprone-misplaced-widening-cast, + bugprone-multiple-statement-macro, + bugprone-sizeof-expression, + bugprone-suspicious-enum-usage, + bugprone-suspicious-missing-comma, + bugprone-suspicious-semicolon, + bugprone-terminating-continue, + bugprone-too-small-loop-variable, + cppcoreguidelines-avoid-goto, + misc-definitions-in-headers, + misc-misplaced-const, + misc-redundant-expression, + misc-unused-parameters, + readability-braces-around-statements, + readability-const-return-type, + readability-else-after-return, + readability-function-size, + readability-implicit-bool-conversion, + readability-inconsistent-declaration-parameter-name, + readability-isolate-declaration, + readability-magic-numbers, + readability-misplaced-array-index, + readability-named-parameter, + readability-non-const-parameter, + readability-redundant-control-flow, + readability-redundant-declaration, + readability-redundant-preprocessor, + readability-uppercase-literal-suffix, + readability-identifier-naming, + ' +WarningsAsErrors: ' + bugprone-assert-side-effect, + bugprone-bool-pointer-implicit-conversion, + bugprone-incorrect-roundings, + bugprone-integer-division, + bugprone-macro-parentheses, + bugprone-macro-repeated-side-effects, + bugprone-misplaced-widening-cast, + bugprone-multiple-statement-macro, + bugprone-sizeof-expression, + bugprone-suspicious-enum-usage, + bugprone-suspicious-missing-comma, + bugprone-suspicious-semicolon, + bugprone-terminating-continue, + bugprone-too-small-loop-variable, + cppcoreguidelines-avoid-goto, + misc-definitions-in-headers, + misc-misplaced-const, + misc-redundant-expression, + misc-unused-parameters, + readability-braces-around-statements, + readability-const-return-type, + readability-else-after-return, + readability-function-size, + readability-implicit-bool-conversion, + readability-inconsistent-declaration-parameter-name, + readability-isolate-declaration, + readability-magic-numbers, + readability-misplaced-array-index, + readability-named-parameter, + readability-non-const-parameter, + readability-redundant-control-flow, + readability-redundant-declaration, + readability-redundant-preprocessor, + readability-uppercase-literal-suffix, + # readability-identifier-naming, + ' + +# From the docs: "Output warnings from headers matching this filter" +# But the goal should be to exclude(!) the headers for which clang-tidy is not called, +# e.g., for naming convention checks. DO NOT USE this field if you don't want to analyze +# header files just because they're included (seems to work). +# HeaderFilterRegex: '$' +# https://github.com/Kitware/CMake/blob/master/.clang-tidy +HeaderFilterRegex: '.*\.(h|hxx|cxx)$' +AnalyzeTemporaryDtors: false +FormatStyle: none +User: martin +CheckOptions: + - { key: bugprone-assert-side-effect.AssertMacros, value: assert } + - { key: bugprone-assert-side-effect.CheckFunctionCalls, value: '0' } + - { key: bugprone-misplaced-widening-cast.CheckImplicitCasts, value: '1' } + - { key: bugprone-sizeof-expression.WarnOnSizeOfConstant, value: '1' } + - { key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression, value: '1' } + - { key: bugprone-sizeof-expression.WarnOnSizeOfThis, value: '1' } + - { key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant, value: '1' } + - { key: bugprone-suspicious-enum-usage.StrictMode, value: '0' } + - { key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens, value: '5' } + - { key: bugprone-suspicious-missing-comma.RatioThreshold, value: '0.200000' } + - { key: bugprone-suspicious-missing-comma.SizeThreshold, value: '5' } + - { key: misc-definitions-in-headers.HeaderFileExtensions, value: ',h,hh,hpp,hxx' } + - { key: misc-definitions-in-headers.UseHeaderFileExtension, value: '1' } + - { key: readability-braces-around-statements.ShortStatementLines, value: '1' } + - { key: readability-function-size.LineThreshold, value: '500' } + - { key: readability-function-size.StatementThreshold, value: '800' } + - { key: readability-function-size.ParameterThreshold, value: '10' } + - { key: readability-function-size.NestingThreshold, value: '6' } + - { key: readability-function-size.VariableThreshold, value: '15' } + - { key: readability-implicit-bool-conversion.AllowIntegerConditions, value: '0' } + - { key: readability-implicit-bool-conversion.AllowPointerConditions, value: '0' } + - { key: readability-implicit-bool-conversion.AllowPointerConditions, value: '0' } + - { key: readability-inconsistent-declaration-parameter-name.IgnoreMacros, value: '1' } + - { key: readability-inconsistent-declaration-parameter-name.Strict, value: '1' } + - { key: readability-magic-numbers.IgnoredFloatingPointValues, value: '1.0;100.0;' } + - { key: readability-magic-numbers.IgnoredIntegerValues, value: '1;2;3;4;' } + - { key: readability-magic-numbers.IgnorePowersOf2IntegerValues, value: '0' } + - { key: readability-magic-numbers.IgnoreAllFloatingPointValues, value: '0' } + - { key: readability-redundant-declaration.IgnoreMacros, value: '1' } + - { key: readability-redundant-function-ptr-dereference, value: '1' } + - { key: readability-uppercase-literal-suffix.IgnoreMacros, value: '0' } + - { key: readability-uppercase-literal-suffix.IgnoreMacros, value: '0' } \ No newline at end of file diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index 8d94497..ff090fa 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -3,43 +3,40 @@ #include #include - // string struct to store the body results from curl with easy reallocation struct string { - char *ptr; - size_t len; + char *ptr; + size_t len; }; // init_string initializes the string struct to store the body results from curl void init_string(struct string *s) { - s->len = 0; - s->ptr = malloc(s->len+1); - if (s->ptr == NULL) { - // TODO: figure out a better way to handle errors in C - fprintf(stderr, "malloc() failed\n"); - exit(EXIT_FAILURE); - } - s->ptr[0] = '\0'; + s->len = 0; + s->ptr = malloc(s->len + 1); + if (s->ptr == NULL) { + // TODO: figure out a better way to handle errors in C + fprintf(stderr, "malloc() failed\n"); + exit(EXIT_FAILURE); + } + s->ptr[0] = '\0'; } // writefunc is the callback function to read the body from libcurl into a string -size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) -{ - // new length to realocate response chunks from libcurl - size_t new_len = s->len + size*nmemb; - s->ptr = realloc(s->ptr, new_len+1); - if (s->ptr == NULL) { - // TODO: figure out a better way to handle errors in C - fprintf(stderr, "realloc() failed\n"); - exit(EXIT_FAILURE); - } - memcpy(s->ptr+s->len, ptr, size*nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - - return size*nmemb; -} +size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) { + // new length to realocate response chunks from libcurl + size_t new_len = s->len + size * nmemb; + s->ptr = realloc(s->ptr, new_len + 1); + if (s->ptr == NULL) { + // TODO: figure out a better way to handle errors in C + fprintf(stderr, "realloc() failed\n"); + exit(EXIT_FAILURE); + } + memcpy(s->ptr + s->len, ptr, size * nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return size * nmemb; +} typedef struct memkv_client { char *host; @@ -66,7 +63,6 @@ char *build_url(const char *host, const char *params) { return url; } - // Request functions: // @@ -77,26 +73,26 @@ char *memkv_get_key(struct memkv_client *client, const char *key) { curl = curl_easy_init(); if (curl) { - struct string s; - init_string(&s); - + struct string s; + init_string(&s); + char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK){ + if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } + } /* always cleanup */ curl_easy_cleanup(curl); - free(url); - return (char*)s.ptr; + free(url); + return (char *)s.ptr; } return 0; } @@ -109,28 +105,27 @@ char *memkv_delete_key(struct memkv_client *client, const char *key) { curl = curl_easy_init(); if (curl) { - struct string s; - init_string(&s); - - char *url = build_url(client->host, key); + struct string s; + init_string(&s); + + char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // set custom request to delete curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK){ - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } - free(url); - return (char*)s.ptr; - + if (res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + } + free(url); + return (char *)s.ptr; } return 0; } @@ -141,10 +136,10 @@ char *memkv_put_key(struct memkv_client *client, const char *key, const char *pu // start the easy interface in curl curl = curl_easy_init(); if (curl) { - struct string s; - init_string(&s); - - char *url = build_url(client->host, key); + struct string s; + init_string(&s); + + char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); struct curl_slist *headers = NULL; @@ -172,16 +167,16 @@ char *memkv_put_key(struct memkv_client *client, const char *key, const char *pu /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK){ + if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } + } /* always cleanup */ curl_easy_cleanup(curl); /* free headers */ curl_slist_free_all(headers); - free(url); - return (char*)s.ptr; + free(url); + return (char *)s.ptr; } return 0; } @@ -193,8 +188,8 @@ char *memkv_list_keys(struct memkv_client *client) { // start the easy interface in curl curl = curl_easy_init(); if (curl) { - struct string s; - init_string(&s); + struct string s; + init_string(&s); const char *key = "keys"; char *url = build_url(client->host, key); @@ -202,19 +197,19 @@ char *memkv_list_keys(struct memkv_client *client) { curl_easy_setopt(curl, CURLOPT_URL, url); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK){ + if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } + } /* always cleanup */ curl_easy_cleanup(curl); free(url); - return (char*)s.ptr; + return (char *)s.ptr; } return 0; } diff --git a/sdks/c/main.c b/sdks/c/main.c index 172a23c..55c33ac 100644 --- a/sdks/c/main.c +++ b/sdks/c/main.c @@ -4,9 +4,9 @@ #include int main(void) { - memkv_client *client; - - client = memkv_client_new("http://localhost:8080"); + memkv_client *client; + + client = memkv_client_new("http://localhost:8080"); char key[] = "c_sdk2"; @@ -18,31 +18,30 @@ int main(void) { " \"content\" : \"json\"" "}"; - char *response; + char *response; response = memkv_put_key(client, key, put_body); - printf("%s\n", response); - free(response); + printf("%s\n", response); + free(response); fprintf(stdout, "\nList Keys Request\n"); // list key response = memkv_list_keys(client); - printf("%s\n", response); - free(response); + printf("%s\n", response); + free(response); // get key fprintf(stdout, "\nMaking a Get Key Request\n"); - + response = memkv_get_key(client, key); - printf("%s\n", response); - free(response); + printf("%s\n", response); + free(response); // delete key fprintf(stdout, "\nMaking a delete Key Request\n"); - response = memkv_delete_key(client, key); - printf("%s\n", response); - free(response); + printf("%s\n", response); + free(response); - free(client); + free(client); } From 1d31526e60210b8eb8ded358a5fefcd1c98b247e Mon Sep 17 00:00:00 2001 From: auyer Date: Sat, 7 Oct 2023 10:56:58 -0300 Subject: [PATCH 06/36] adds memkv_result structure to handle errors --- sdks/c/libMemoryKV/libMemoryKV.c | 297 ++++++++++++++++++------------- sdks/c/libMemoryKV/libMemoryKV.h | 22 ++- sdks/c/main.c | 22 ++- 3 files changed, 209 insertions(+), 132 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index ff090fa..3c094ec 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -55,7 +56,7 @@ memkv_client *memkv_client_new(char *host) { char *build_url(const char *host, const char *params) { // snprintf returns the number of characters that would have been written if called with NULL - int sizeneeded = snprintf(NULL, 0, "%s/%s", host, params); + unsigned long sizeneeded = snprintf(NULL, 0, "%s/%s", host, params); // use that number to allocate a buffer of the right size char *url = malloc(sizeneeded + 1); // write the string to the buffer @@ -63,153 +64,211 @@ char *build_url(const char *host, const char *params) { return url; } +typedef struct { + bool success : 1; + union { + char *result; + char *error; + }; +} memkv_result; + // Request functions: // -char *memkv_get_key(struct memkv_client *client, const char *key) { +memkv_result *make_curl_error(memkv_result *r, char *err) { + r->success = false; + // create and alloc error + unsigned long sizeneeded = snprintf(NULL, 0, "%s :%s", "Error from curl", err); + r->error = malloc(sizeneeded + 1); + sprintf(r->error, "%s :%s", "Error from curl", err); + return r; +} + +memkv_result *init_memkv_result() { + + memkv_result *r = malloc(sizeof(memkv_result)); + memkv_result rinit = {.success = false, .error = ""}; + r = &rinit; + return r; +} + +memkv_result *memkv_get_key(struct memkv_client *client, const char *key) { + memkv_result *r = init_memkv_result(); CURL *curl; CURLcode res; curl = curl_easy_init(); - if (curl) { - struct string s; - init_string(&s); - - char *url = build_url(client->host, key); - curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } - /* always cleanup */ - curl_easy_cleanup(curl); - free(url); - return (char *)s.ptr; + if (!curl) { + return make_curl_error(r, "Failed startig curl"); + } + + struct string s; + init_string(&s); + + char *url = build_url(client->host, key); + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) { + r->success = false; + const char *err = curl_easy_strerror(res); + return make_curl_error(r, err); } - return 0; + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + + r->success = true; + r->result = malloc(strlen(s.ptr) + 1); + + strcpy(r->result, s.ptr); + return r; } -char *memkv_delete_key(struct memkv_client *client, const char *key) { +memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { + memkv_result *r = init_memkv_result(); CURL *curl; CURLcode res; curl = curl_easy_init(); - if (curl) { - - struct string s; - init_string(&s); - - char *url = build_url(client->host, key); - curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - - // set custom request to delete - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } - free(url); - return (char *)s.ptr; + if (!curl) { + return make_curl_error(r, "Failed startig curl"); + } + + struct string s; + init_string(&s); + + char *url = build_url(client->host, key); + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + // set custom request to delete + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) { + r->success = false; + const char *err = curl_easy_strerror(res); + return make_curl_error(r, err); } - return 0; + free(url); + r->success = true; + r->result = malloc(strlen(s.ptr) + 1); + + strcpy(r->result, s.ptr); + return r; } -char *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { +memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { + memkv_result *r = init_memkv_result(); + CURL *curl; CURLcode res; // start the easy interface in curl curl = curl_easy_init(); - if (curl) { - struct string s; - init_string(&s); - - char *url = build_url(client->host, key); - curl_easy_setopt(curl, CURLOPT_URL, url); - - struct curl_slist *headers = NULL; - - /* default type with postfields is application/x-www-form-urlencoded, - change it if you want */ - headers = curl_slist_append(headers, "Content-Type: application/octet-stream"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - /* pass on content in request body. When CURLOPT_POSTFIELDSIZE is not used, - curl does strlen to get the size. */ - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); - - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - - /* override the POST implied by CURLOPT_POSTFIELDS - * - * Warning: CURLOPT_CUSTOMREQUEST is problematic, especially if you want - * to follow redirects. Be aware. - */ - - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } - /* always cleanup */ - curl_easy_cleanup(curl); - - /* free headers */ - curl_slist_free_all(headers); - free(url); - return (char *)s.ptr; + if (!curl) { + return make_curl_error(r, "Failed startig curl"); + } + struct string s; + init_string(&s); + + char *url = build_url(client->host, key); + curl_easy_setopt(curl, CURLOPT_URL, url); + + struct curl_slist *headers = NULL; + + /* default type with postfields is application/x-www-form-urlencoded, + change it if you want */ + headers = curl_slist_append(headers, "Content-Type: application/octet-stream"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + /* pass on content in request body. When CURLOPT_POSTFIELDSIZE is not used, + curl does strlen to get the size. */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); + + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + + /* override the POST implied by CURLOPT_POSTFIELDS + * + * Warning: CURLOPT_CUSTOMREQUEST is problematic, especially if you want + * to follow redirects. Be aware. + */ + + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) { + r->success = false; + const char *err = curl_easy_strerror(res); + return make_curl_error(r, err); } - return 0; + /* always cleanup */ + curl_easy_cleanup(curl); + + /* free headers */ + curl_slist_free_all(headers); + free(url); + r->success = true; + r->result = malloc(strlen(s.ptr) + 1); + + strcpy(r->result, s.ptr); + return r; } -char *memkv_list_keys(struct memkv_client *client) { +memkv_result *memkv_list_keys(struct memkv_client *client) { + memkv_result *r = init_memkv_result(); + CURL *curl; CURLcode res; // start the easy interface in curl curl = curl_easy_init(); - if (curl) { - struct string s; - init_string(&s); - - const char *key = "keys"; - char *url = build_url(client->host, key); - - curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if (res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - } - /* always cleanup */ - curl_easy_cleanup(curl); - free(url); - return (char *)s.ptr; + if (!curl) { + return make_curl_error(r, "Failed startig curl"); } - return 0; -} + struct string s; + init_string(&s); + + const char *key = "keys"; + char *url = build_url(client->host, key); + + curl_easy_setopt(curl, CURLOPT_URL, url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) { + r->success = false; + const char *err = curl_easy_strerror(res); + return make_curl_error(r, err); + } + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + + r->success = true; + r->result = malloc(strlen(s.ptr) + 1); + + strcpy(r->result, s.ptr); + return r; +} \ No newline at end of file diff --git a/sdks/c/libMemoryKV/libMemoryKV.h b/sdks/c/libMemoryKV/libMemoryKV.h index 91a8abe..4ae928d 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.h +++ b/sdks/c/libMemoryKV/libMemoryKV.h @@ -1,15 +1,29 @@ +#include + typedef struct memkv_client { char *host; } memkv_client; +// memkv_result stores a success boolean to indicate if the request was successfull or not. +// the success variable should be checked before reading results +// in case of success, the result will be stored in the result attribure `memkv_result->result` +// in case of error, the error reason will be stored in the error attribure `memkv_result->error` +typedef struct { + bool success : 1; + union { + char *result; + char *error; + }; +} memkv_result; + void memkv_init_client(struct memkv_client *client, char *host); memkv_client *memkv_client_new(char *host); -char *memkv_get_key(struct memkv_client *client, const char *key); +memkv_result *memkv_get_key(struct memkv_client *client, const char *key); -char *memkv_delete_key(struct memkv_client *client, const char *key); +memkv_result *memkv_delete_key(struct memkv_client *client, const char *key); -char *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body); +memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body); -char *memkv_list_keys(struct memkv_client *client); +memkv_result *memkv_list_keys(struct memkv_client *client); diff --git a/sdks/c/main.c b/sdks/c/main.c index 55c33ac..bea33c4 100644 --- a/sdks/c/main.c +++ b/sdks/c/main.c @@ -3,6 +3,14 @@ #include #include +void print_memkv_result(memkv_result *response) { + if (response->success) { + printf("Success: %s\n", response->result); + } else { + printf("Error: %s\n", response->error); + } +} + int main(void) { memkv_client *client; @@ -18,30 +26,26 @@ int main(void) { " \"content\" : \"json\"" "}"; - char *response; + memkv_result *response; response = memkv_put_key(client, key, put_body); - printf("%s\n", response); - free(response); + print_memkv_result(response); fprintf(stdout, "\nList Keys Request\n"); // list key response = memkv_list_keys(client); - printf("%s\n", response); - free(response); + print_memkv_result(response); // get key fprintf(stdout, "\nMaking a Get Key Request\n"); response = memkv_get_key(client, key); - printf("%s\n", response); - free(response); + print_memkv_result(response); // delete key fprintf(stdout, "\nMaking a delete Key Request\n"); response = memkv_delete_key(client, key); - printf("%s\n", response); - free(response); + print_memkv_result(response); free(client); } From 33fcb9d48a9d170fd1b4db30c7cee65c946f5568 Mon Sep 17 00:00:00 2001 From: auyer Date: Tue, 10 Oct 2023 23:57:51 -0300 Subject: [PATCH 07/36] adds Result like return value for error handling --- sdks/c/libMemoryKV/libMemoryKV.c | 91 +++++++++++++++++++------------- sdks/c/main.c | 6 ++- 2 files changed, 57 insertions(+), 40 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index 3c094ec..2bf2c34 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -15,6 +15,7 @@ void init_string(struct string *s) { s->len = 0; s->ptr = malloc(s->len + 1); if (s->ptr == NULL) { + printf("Error in init_string"); // TODO: figure out a better way to handle errors in C fprintf(stderr, "malloc() failed\n"); exit(EXIT_FAILURE); @@ -28,6 +29,7 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) { size_t new_len = s->len + size * nmemb; s->ptr = realloc(s->ptr, new_len + 1); if (s->ptr == NULL) { + printf("Error in Callback"); // TODO: figure out a better way to handle errors in C fprintf(stderr, "realloc() failed\n"); exit(EXIT_FAILURE); @@ -65,30 +67,44 @@ char *build_url(const char *host, const char *params) { } typedef struct { - bool success : 1; + bool success; union { char *result; char *error; }; } memkv_result; -// Request functions: -// +static const char base_curl_error[] = "Error from curl"; +static const char unknown_error_msg[] = "unknown error"; -memkv_result *make_curl_error(memkv_result *r, char *err) { +void make_curl_error(memkv_result *r, char *err) { r->success = false; - // create and alloc error - unsigned long sizeneeded = snprintf(NULL, 0, "%s :%s", "Error from curl", err); + + if (strlen(err) == 0) { + r->error = malloc(sizeof(unknown_error_msg)); + strcpy(r->error, unknown_error_msg); + return; + } + + unsigned long sizeneeded = snprintf(NULL, 0, "%s : %s", base_curl_error, err); + r->error = malloc(sizeneeded + 1); - sprintf(r->error, "%s :%s", "Error from curl", err); - return r; + + snprintf(r->error, sizeneeded + 1, "%s : %s", base_curl_error, err); } memkv_result *init_memkv_result() { memkv_result *r = malloc(sizeof(memkv_result)); - memkv_result rinit = {.success = false, .error = ""}; - r = &rinit; + + r->result = malloc(sizeof(char)); + if (r->result == NULL) { + free(r); + printf("Error in init_memkv_result"); + return NULL; + } + + r->success = false; return r; } @@ -100,7 +116,8 @@ memkv_result *memkv_get_key(struct memkv_client *client, const char *key) { curl = curl_easy_init(); if (!curl) { - return make_curl_error(r, "Failed startig curl"); + make_curl_error(r, "Failed startig curl"); + return r; } struct string s; @@ -108,18 +125,15 @@ memkv_result *memkv_get_key(struct memkv_client *client, const char *key) { char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); - /* Check for errors */ if (res != CURLE_OK) { - r->success = false; const char *err = curl_easy_strerror(res); - return make_curl_error(r, err); + make_curl_error(r, err); + return r; } /* always cleanup */ curl_easy_cleanup(curl); @@ -140,7 +154,8 @@ memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { curl = curl_easy_init(); if (!curl) { - return make_curl_error(r, "Failed startig curl"); + make_curl_error(r, "Failed startig curl"); + return r; } struct string s; @@ -148,11 +163,11 @@ memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // set custom request to delete curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); @@ -160,9 +175,10 @@ memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { res = curl_easy_perform(curl); /* Check for errors */ if (res != CURLE_OK) { - r->success = false; const char *err = curl_easy_strerror(res); - return make_curl_error(r, err); + make_curl_error(r, err); + return r; + ; } free(url); r->success = true; @@ -170,17 +186,18 @@ memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { strcpy(r->result, s.ptr); return r; + ; } memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { memkv_result *r = init_memkv_result(); - CURL *curl; CURLcode res; // start the easy interface in curl curl = curl_easy_init(); if (!curl) { - return make_curl_error(r, "Failed startig curl"); + make_curl_error(r, "Failed startig curl"); + return r; } struct string s; init_string(&s); @@ -198,29 +215,23 @@ memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const /* pass on content in request body. When CURLOPT_POSTFIELDSIZE is not used, curl does strlen to get the size. */ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - - /* override the POST implied by CURLOPT_POSTFIELDS - * - * Warning: CURLOPT_CUSTOMREQUEST is problematic, especially if you want - * to follow redirects. Be aware. - */ - - /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ if (res != CURLE_OK) { - r->success = false; const char *err = curl_easy_strerror(res); - return make_curl_error(r, err); + + make_curl_error(r, err); + return r; + ; } /* always cleanup */ curl_easy_cleanup(curl); - /* free headers */ curl_slist_free_all(headers); free(url); @@ -229,6 +240,7 @@ memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const strcpy(r->result, s.ptr); return r; + ; } memkv_result *memkv_list_keys(struct memkv_client *client) { @@ -240,7 +252,8 @@ memkv_result *memkv_list_keys(struct memkv_client *client) { // start the easy interface in curl curl = curl_easy_init(); if (!curl) { - return make_curl_error(r, "Failed startig curl"); + make_curl_error(r, "Failed startig curl"); + return r; } struct string s; init_string(&s); @@ -249,7 +262,6 @@ memkv_result *memkv_list_keys(struct memkv_client *client) { char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); - /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); @@ -260,7 +272,9 @@ memkv_result *memkv_list_keys(struct memkv_client *client) { if (res != CURLE_OK) { r->success = false; const char *err = curl_easy_strerror(res); - return make_curl_error(r, err); + make_curl_error(r, err); + return r; + ; } /* always cleanup */ curl_easy_cleanup(curl); @@ -271,4 +285,5 @@ memkv_result *memkv_list_keys(struct memkv_client *client) { strcpy(r->result, s.ptr); return r; -} \ No newline at end of file + ; +} diff --git a/sdks/c/main.c b/sdks/c/main.c index bea33c4..48bc5da 100644 --- a/sdks/c/main.c +++ b/sdks/c/main.c @@ -4,10 +4,11 @@ #include void print_memkv_result(memkv_result *response) { + fprintf(stdout, "In print\n"); if (response->success) { printf("Success: %s\n", response->result); } else { - printf("Error: %s\n", response->error); + fprintf(stderr, "Error: %s\n", response->error); } } @@ -26,8 +27,9 @@ int main(void) { " \"content\" : \"json\"" "}"; - memkv_result *response; + memkv_result *response = malloc(sizeof(memkv_result)); response = memkv_put_key(client, key, put_body); + print_memkv_result(response); fprintf(stdout, "\nList Keys Request\n"); From b9253223db8441999ca24b5cfd2d0b0533e8643a Mon Sep 17 00:00:00 2001 From: auyer Date: Wed, 11 Oct 2023 10:22:13 -0300 Subject: [PATCH 08/36] adds error handling to the internal string type --- sdks/c/libMemoryKV/libMemoryKV.c | 143 ++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 50 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index 2bf2c34..ac6bee5 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -4,39 +4,45 @@ #include #include -// string struct to store the body results from curl with easy reallocation -struct string { +// string_carrier struct to store the body results from curl with easy reallocation +struct string_carrier { char *ptr; size_t len; + bool error_flag; }; -// init_string initializes the string struct to store the body results from curl -void init_string(struct string *s) { +// init_string_carrier initializes the string_carrier struct to store the body results from curl +void init_string_carrier(struct string_carrier *s) { s->len = 0; s->ptr = malloc(s->len + 1); + s->error_flag = false; if (s->ptr == NULL) { - printf("Error in init_string"); // TODO: figure out a better way to handle errors in C - fprintf(stderr, "malloc() failed\n"); - exit(EXIT_FAILURE); + fprintf(stderr, "memkv client: Error in Callback, malloc() failed\n"); + // skip exit and return error instead + // exit(EXIT_FAILURE); + s->error_flag = true; + } else { + s->ptr[0] = '\0'; } - s->ptr[0] = '\0'; } -// writefunc is the callback function to read the body from libcurl into a string -size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) { +// string_carrier_writefunc is the callback function to read the body from libcurl into a string_carrier +size_t string_carrier_writefunc(void *ptr, size_t size, size_t nmemb, struct string_carrier *s) { // new length to realocate response chunks from libcurl size_t new_len = s->len + size * nmemb; s->ptr = realloc(s->ptr, new_len + 1); if (s->ptr == NULL) { - printf("Error in Callback"); // TODO: figure out a better way to handle errors in C - fprintf(stderr, "realloc() failed\n"); - exit(EXIT_FAILURE); + fprintf(stderr, "memkv client: Error in string_carrier_writefunc Callback, realloc() failed\n"); + // skip exit and return error instead + // exit(EXIT_FAILURE); + s->error_flag = true; + } else { + memcpy(s->ptr + s->len, ptr, size * nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; } - memcpy(s->ptr + s->len, ptr, size * nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; return size * nmemb; } @@ -61,7 +67,7 @@ char *build_url(const char *host, const char *params) { unsigned long sizeneeded = snprintf(NULL, 0, "%s/%s", host, params); // use that number to allocate a buffer of the right size char *url = malloc(sizeneeded + 1); - // write the string to the buffer + // write the string_carrier to the buffer sprintf(url, "%s/%s", host, params); return url; } @@ -77,7 +83,7 @@ typedef struct { static const char base_curl_error[] = "Error from curl"; static const char unknown_error_msg[] = "unknown error"; -void make_curl_error(memkv_result *r, char *err) { +void make_curl_error(memkv_result *r, const char *err) { r->success = false; if (strlen(err) == 0) { @@ -93,6 +99,7 @@ void make_curl_error(memkv_result *r, char *err) { snprintf(r->error, sizeneeded + 1, "%s : %s", base_curl_error, err); } +// init_memkv_result initializes the memkv_result struct with success as false memkv_result *init_memkv_result() { memkv_result *r = malloc(sizeof(memkv_result)); @@ -120,25 +127,34 @@ memkv_result *memkv_get_key(struct memkv_client *client, const char *key) { return r; } - struct string s; - init_string(&s); + struct string_carrier s; + init_string_carrier(&s); + if (s.error_flag) { + make_curl_error(r, "Failed to initialize string_carrier"); + return r; + } char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + if (res != CURLE_OK) { const char *err = curl_easy_strerror(res); make_curl_error(r, err); return r; } - /* always cleanup */ - curl_easy_cleanup(curl); - free(url); - + if (s.error_flag) { + make_curl_error(r, "Failed to get results from server"); + return r; + } r->success = true; r->result = malloc(strlen(s.ptr) + 1); @@ -158,8 +174,12 @@ memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { return r; } - struct string s; - init_string(&s); + struct string_carrier s; + init_string_carrier(&s); + if (s.error_flag) { + make_curl_error(r, "Failed to initialize string_carrier"); + return r; + } char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); @@ -168,25 +188,32 @@ memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { // set custom request to delete curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + /* Check for errors */ if (res != CURLE_OK) { const char *err = curl_easy_strerror(res); make_curl_error(r, err); return r; - ; } - free(url); + + if (s.error_flag) { + make_curl_error(r, "Failed to get results from server"); + return r; + } r->success = true; r->result = malloc(strlen(s.ptr) + 1); strcpy(r->result, s.ptr); return r; - ; } memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { @@ -199,8 +226,12 @@ memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const make_curl_error(r, "Failed startig curl"); return r; } - struct string s; - init_string(&s); + struct string_carrier s; + init_string_carrier(&s); + if (s.error_flag) { + make_curl_error(r, "Failed to initialize string_carrier"); + return r; + } char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); @@ -217,30 +248,34 @@ memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + /* free headers */ + curl_slist_free_all(headers); + /* Check for errors */ if (res != CURLE_OK) { const char *err = curl_easy_strerror(res); - make_curl_error(r, err); return r; - ; } - /* always cleanup */ - curl_easy_cleanup(curl); - /* free headers */ - curl_slist_free_all(headers); - free(url); + + if (s.error_flag) { + make_curl_error(r, "Failed to get results from server"); + return r; + } r->success = true; r->result = malloc(strlen(s.ptr) + 1); strcpy(r->result, s.ptr); return r; - ; } memkv_result *memkv_list_keys(struct memkv_client *client) { @@ -255,35 +290,43 @@ memkv_result *memkv_list_keys(struct memkv_client *client) { make_curl_error(r, "Failed startig curl"); return r; } - struct string s; - init_string(&s); + struct string_carrier s; + init_string_carrier(&s); + if (s.error_flag) { + make_curl_error(r, "Failed to initialize string_carrier"); + return r; + } const char *key = "keys"; char *url = build_url(client->host, key); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + /* Check for errors */ if (res != CURLE_OK) { r->success = false; const char *err = curl_easy_strerror(res); make_curl_error(r, err); return r; - ; } - /* always cleanup */ - curl_easy_cleanup(curl); - free(url); + if (s.error_flag) { + make_curl_error(r, "Failed to get results from server"); + return r; + } r->success = true; r->result = malloc(strlen(s.ptr) + 1); strcpy(r->result, s.ptr); return r; - ; } From 13c486733d77cb973c57cf807cc4a1e71f90f6ad Mon Sep 17 00:00:00 2001 From: auyer Date: Wed, 11 Oct 2023 10:22:28 -0300 Subject: [PATCH 09/36] documentation on header calls --- sdks/c/libMemoryKV/libMemoryKV.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.h b/sdks/c/libMemoryKV/libMemoryKV.h index 4ae928d..73aa331 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.h +++ b/sdks/c/libMemoryKV/libMemoryKV.h @@ -1,5 +1,7 @@ #include +// memkv_client stores the configurations used to connect to the MemoryKV server +// use `memkv_client_new` to create one, or `memkv_init_client` to initialize an existing instance typedef struct memkv_client { char *host; } memkv_client; @@ -16,14 +18,20 @@ typedef struct { }; } memkv_result; -void memkv_init_client(struct memkv_client *client, char *host); - +// memkv_client_new creates a new memkv_client instance with a host url pre filled memkv_client *memkv_client_new(char *host); +// memkv_init_client initializes an existing memkv_client with a provided URL +void memkv_init_client(struct memkv_client *client, char *host); + +// memkv_get_key fetches the value of a key from the MemoryKV server memkv_result *memkv_get_key(struct memkv_client *client, const char *key); +// memkv_delete_key deletes a key from the MemoryKV server, and return a value if it was deleted memkv_result *memkv_delete_key(struct memkv_client *client, const char *key); +// memkv_put_key puts a key value pair in the MemoryKV server, and return a value if there was one in the key beafore memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body); +// memkv_list_keys lists all the keys in the MemoryKV server memkv_result *memkv_list_keys(struct memkv_client *client); From 9a4ae3910aa41d04fbe31d4750c90a198a5d6ad5 Mon Sep 17 00:00:00 2001 From: auyer Date: Wed, 11 Oct 2023 10:39:41 -0300 Subject: [PATCH 10/36] more use cases in the example --- sdks/c/main.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/sdks/c/main.c b/sdks/c/main.c index 48bc5da..4ca01d5 100644 --- a/sdks/c/main.c +++ b/sdks/c/main.c @@ -4,7 +4,6 @@ #include void print_memkv_result(memkv_result *response) { - fprintf(stdout, "In print\n"); if (response->success) { printf("Success: %s\n", response->result); } else { @@ -17,18 +16,20 @@ int main(void) { client = memkv_client_new("http://localhost:8080"); - char key[] = "c_sdk2"; + memkv_result *response = malloc(sizeof(memkv_result)); + + char key[] = "c_sdk"; + char key2[] = "c_sdk2"; // put key - fprintf(stdout, "\nPut Key Request\n"); - static const char put_body[] = "{" " \"name\" : \"c_sdk\"," " \"content\" : \"json\"" "}"; - memkv_result *response = malloc(sizeof(memkv_result)); - response = memkv_put_key(client, key, put_body); + + fprintf(stdout, "\nPut Key Request\n"); + response = memkv_put_key(client, key, put_body); print_memkv_result(response); @@ -43,11 +44,26 @@ int main(void) { response = memkv_get_key(client, key); print_memkv_result(response); + fprintf(stdout, "\nPut Key Request 2\n"); + response = memkv_put_key(client, key2, put_body); + + print_memkv_result(response); + + // list key + fprintf(stdout, "\nList Keys Request 2\n"); + response = memkv_list_keys(client); + print_memkv_result(response); + // delete key fprintf(stdout, "\nMaking a delete Key Request\n"); response = memkv_delete_key(client, key); print_memkv_result(response); + fprintf(stdout, "\nMaking a delete Key Request2\n"); + + response = memkv_delete_key(client, key2); + print_memkv_result(response); + free(client); } From da0b696fb4051c96c46f7dafa98bc9555cc70270 Mon Sep 17 00:00:00 2001 From: auyer Date: Wed, 11 Oct 2023 11:41:19 -0300 Subject: [PATCH 11/36] adds list/delete with prefix, add delete all --- sdks/c/libMemoryKV/libMemoryKV.c | 172 ++++++++++++++++++++++++++++++- sdks/c/libMemoryKV/libMemoryKV.h | 9 ++ sdks/c/main.c | 64 ++++++++---- 3 files changed, 223 insertions(+), 22 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index ac6bee5..fb68ef6 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -47,6 +46,7 @@ size_t string_carrier_writefunc(void *ptr, size_t size, size_t nmemb, struct str return size * nmemb; } +// memkv_client typedef struct memkv_client { char *host; } memkv_client; @@ -300,6 +300,176 @@ memkv_result *memkv_list_keys(struct memkv_client *client) { const char *key = "keys"; char *url = build_url(client->host, key); + // set custom request to delete + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + + /* Check for errors */ + if (res != CURLE_OK) { + r->success = false; + const char *err = curl_easy_strerror(res); + make_curl_error(r, err); + return r; + } + + if (s.error_flag) { + make_curl_error(r, "Failed to get results from server"); + return r; + } + r->success = true; + r->result = malloc(strlen(s.ptr) + 1); + + strcpy(r->result, s.ptr); + return r; +} + +memkv_result *memkv_list_keys_with_prefix(struct memkv_client *client, const char *key_prefix) { + memkv_result *r = init_memkv_result(); + + CURL *curl; + CURLcode res; + + // start the easy interface in curl + curl = curl_easy_init(); + if (!curl) { + make_curl_error(r, "Failed startig curl"); + return r; + } + struct string_carrier s; + init_string_carrier(&s); + if (s.error_flag) { + make_curl_error(r, "Failed to initialize string_carrier"); + return r; + } + + const char *key = "keys"; + char *url_part1 = build_url(client->host, key); + // TODO: build URL in one call + char *url = build_url(url_part1, key_prefix); + free(url_part1); + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + + /* Check for errors */ + if (res != CURLE_OK) { + r->success = false; + const char *err = curl_easy_strerror(res); + make_curl_error(r, err); + return r; + } + + if (s.error_flag) { + make_curl_error(r, "Failed to get results from server"); + return r; + } + r->success = true; + r->result = malloc(strlen(s.ptr) + 1); + + strcpy(r->result, s.ptr); + return r; +} + +memkv_result *memkv_delete_keys_with_prefix(struct memkv_client *client, const char *key_prefix) { + memkv_result *r = init_memkv_result(); + + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + if (!curl) { + make_curl_error(r, "Failed startig curl"); + return r; + } + + struct string_carrier s; + init_string_carrier(&s); + if (s.error_flag) { + make_curl_error(r, "Failed to initialize string_carrier"); + return r; + } + + const char *key = "keys"; + char *url_part1 = build_url(client->host, key); + // TODO: build URL in one call + char *url = build_url(url_part1, key_prefix); + free(url_part1); + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + // set custom request to delete + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + + /* always cleanup */ + curl_easy_cleanup(curl); + free(url); + + /* Check for errors */ + if (res != CURLE_OK) { + const char *err = curl_easy_strerror(res); + make_curl_error(r, err); + return r; + } + + if (s.error_flag) { + make_curl_error(r, "Failed to get results from server"); + return r; + } + r->success = true; + r->result = malloc(strlen(s.ptr) + 1); + + strcpy(r->result, s.ptr); + return r; +} + +memkv_result *memkv_delete_all_keys(struct memkv_client *client) { + memkv_result *r = init_memkv_result(); + + CURL *curl; + CURLcode res; + + // start the easy interface in curl + curl = curl_easy_init(); + if (!curl) { + make_curl_error(r, "Failed startig curl"); + return r; + } + struct string_carrier s; + init_string_carrier(&s); + if (s.error_flag) { + make_curl_error(r, "Failed to initialize string_carrier"); + return r; + } + + const char *key = "keys"; + char *url = build_url(client->host, key); + + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); diff --git a/sdks/c/libMemoryKV/libMemoryKV.h b/sdks/c/libMemoryKV/libMemoryKV.h index 73aa331..ac344f4 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.h +++ b/sdks/c/libMemoryKV/libMemoryKV.h @@ -35,3 +35,12 @@ memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const // memkv_list_keys lists all the keys in the MemoryKV server memkv_result *memkv_list_keys(struct memkv_client *client); + +// memkv_list_keys_with_prefix lists all the keys in the MemoryKV server with a given prefix +memkv_result *memkv_list_keys_with_prefix(struct memkv_client *client, const char *key_prefix); + +// memkv_delete_key deletes a key from the MemoryKV server, and return the keys that were deleted +memkv_result *memkv_delete_keys_with_prefix(struct memkv_client *client, const char *key_prefix); + +// memkv_delete_all_keys deletes all keys from the MemoryKV server, and returns list of deletes keys +memkv_result *memkv_delete_all_keys(struct memkv_client *client); \ No newline at end of file diff --git a/sdks/c/main.c b/sdks/c/main.c index 4ca01d5..37ef84a 100644 --- a/sdks/c/main.c +++ b/sdks/c/main.c @@ -5,7 +5,7 @@ void print_memkv_result(memkv_result *response) { if (response->success) { - printf("Success: %s\n", response->result); + printf("Success! Result: %s\n", response->result); } else { fprintf(stderr, "Error: %s\n", response->error); } @@ -17,52 +17,74 @@ int main(void) { client = memkv_client_new("http://localhost:8080"); memkv_result *response = malloc(sizeof(memkv_result)); - - char key[] = "c_sdk"; - char key2[] = "c_sdk2"; + + char key[] = "c_sdk"; + char key2[] = "c_sdk2"; + char key3[] = "a_sdk"; + + fprintf(stdout, "This example will create, and delete in 3 keys: %s, %s and %s\n", key, key2, key3); // put key static const char put_body[] = "{" - " \"name\" : \"c_sdk\"," + " \"name\" : \"MemoryKV Example Body\"," " \"content\" : \"json\"" "}"; - - fprintf(stdout, "\nPut Key Request\n"); - response = memkv_put_key(client, key, put_body); - + fprintf(stdout, "\nPut Key '%s'\n", key); + response = memkv_put_key(client, key, put_body); print_memkv_result(response); - fprintf(stdout, "\nList Keys Request\n"); // list key + fprintf(stdout, "\nList Keys\n"); response = memkv_list_keys(client); print_memkv_result(response); // get key - fprintf(stdout, "\nMaking a Get Key Request\n"); - + fprintf(stdout, "\nGet Key '%s'\n", key); response = memkv_get_key(client, key); print_memkv_result(response); - fprintf(stdout, "\nPut Key Request 2\n"); - response = memkv_put_key(client, key2, put_body); + fprintf(stdout, "\nPut Key '%s'\n", key2); + response = memkv_put_key(client, key2, put_body); + print_memkv_result(response); + fprintf(stdout, "\nPut Key '%s'\n", key3); + response = memkv_put_key(client, key3, put_body); print_memkv_result(response); - // list key - fprintf(stdout, "\nList Keys Request 2\n"); - response = memkv_list_keys(client); + fprintf(stdout, "\nPut Key '%s' (again)\n", key3); + response = memkv_put_key(client, key3, put_body); + print_memkv_result(response); + + char prefix[] = "c"; + + fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix); + response = memkv_list_keys_with_prefix(client, prefix); + print_memkv_result(response); + + prefix[0] = 'a'; + fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix); + response = memkv_list_keys_with_prefix(client, prefix); print_memkv_result(response); // delete key - fprintf(stdout, "\nMaking a delete Key Request\n"); + prefix[0] = 'c'; + fprintf(stdout, "\nMaking a delete Key Request with Prefix '%s'\n", prefix); + response = memkv_delete_keys_with_prefix(client, prefix); + print_memkv_result(response); - response = memkv_delete_key(client, key); + // delete all keys + fprintf(stdout, "\nDelete all Keys\n"); + response = memkv_delete_all_keys(client); print_memkv_result(response); - fprintf(stdout, "\nMaking a delete Key Request2\n"); + fprintf(stdout, "\nMaking a delete Key '%s'\n", key3); + response = memkv_delete_key(client, key3); + print_memkv_result(response); - response = memkv_delete_key(client, key2); + // list key + fprintf(stdout, "\nList Keys\n"); + response = memkv_list_keys(client); print_memkv_result(response); free(client); From ad3323992240b70e2162502aa710d1493508bf69 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 11:24:42 -0300 Subject: [PATCH 12/36] Rename main.c to example.c, set std to 23/2x --- sdks/c/CMakeLists.txt | 6 +++--- sdks/c/compile_flags.txt | 2 +- sdks/c/{main.c => example.c} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename sdks/c/{main.c => example.c} (100%) diff --git a/sdks/c/CMakeLists.txt b/sdks/c/CMakeLists.txt index 64fbae4..56186c6 100644 --- a/sdks/c/CMakeLists.txt +++ b/sdks/c/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.27.6) -project(CMemoryKV) +project(MemoryKV) -add_executable(${PROJECT_NAME} main.c) +add_executable(${PROJECT_NAME} example.c) -set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) +set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 23) find_package(PkgConfig REQUIRED) pkg_check_modules(libcurl REQUIRED IMPORTED_TARGET libcurl>=7.17.0) diff --git a/sdks/c/compile_flags.txt b/sdks/c/compile_flags.txt index f960fd9..8e4a4e0 100644 --- a/sdks/c/compile_flags.txt +++ b/sdks/c/compile_flags.txt @@ -1,3 +1,3 @@ -I./libMemoryKV -llibMemoryKV --std=c11 +-std=c2x diff --git a/sdks/c/main.c b/sdks/c/example.c similarity index 100% rename from sdks/c/main.c rename to sdks/c/example.c From dd48b78e33baf796e6c957af2d4bbcebf37c93e4 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 11:34:04 -0300 Subject: [PATCH 13/36] adds note about the SDK in the readme --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 08268e9..0657f89 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ What I wanted to create: - In the Rust programming language - With a live feed of changes (it felt like a cool demo for my personal site) -I started implementing an http web server using Axum. I did this because I am used to working with http and rest interfaces. I might implement my own connection method, but this will be fine for now. +I started implementing an http web server using Axum. I did this because I am used to working with http and rest interfaces. I might implement my own connection protocol, but this will be fine for now. Since I was using Axum I also added a `/metrics` endpoint for Prometheus. The "database" part I created with a simple HashMap, and protected it behind a RWMutex. @@ -38,5 +38,16 @@ A sample of the WAL received by a WebSocket client: ``` The live demo is available in my personal website: https://rcpassos.me/projects/kv -And the source to that is here: -https://github.com/auyer/auyer.github.io/blob/main/src/lib/services.ts +And the source for that is here: +https://github.com/auyer/auyer.github.io/blob/main/src/lib/services.js + +# SDKs +I am also creating SDKs for this project using different languages. +Since the server uses REST with HTTP for the database interface, this is the main part that needs to be implemented in the SDKs. +The Live Feed is implemented using WebSockets, and is a less important feature. + + +| Feature/SDK | C | Rust +|---------------- |--- |------ +| Rest Endpoints | x | +| Live Feed (WS) | | From 0b320805487f1c3c9eefa742c191e355dffe2715 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 13:51:24 -0300 Subject: [PATCH 14/36] adds clang-tidy action --- .github/workflows/clang-tidy.yaml | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/clang-tidy.yaml diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml new file mode 100644 index 0000000..9ce0df1 --- /dev/null +++ b/.github/workflows/clang-tidy.yaml @@ -0,0 +1,34 @@ +name: clang-tidy-review + +on: + pull_request: + paths: + - '**.c' + - '**.h' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + # Optionally generate compile_commands.json + + - uses: ZedThree/clang-tidy-review@v0.12.0 + id: review + with: + # Tell clang-tidy-review the base directory. + # This will get replaced by the new working + # directory inside the action + base_dir: ./sdks/c/ + config_file: ".clang-tidy" + cmake_command: cmake -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=on -B build + + # Uploads an artefact containing clang_fixes.json + - uses: ZedThree/clang-tidy-review/upload@v0.12.0 + id: upload-review + + # If there are any comments, fail the check + - if: steps.review.outputs.total_comments > 0 + run: exit 1 From 71c3123bdd985d8845c13882382f807a2047ca48 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 13:53:25 -0300 Subject: [PATCH 15/36] update action from v0.12 to v0.14 --- .github/workflows/clang-tidy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 9ce0df1..e3b0aed 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -15,7 +15,7 @@ jobs: # Optionally generate compile_commands.json - - uses: ZedThree/clang-tidy-review@v0.12.0 + - uses: ZedThree/clang-tidy-review@v0.14.0 id: review with: # Tell clang-tidy-review the base directory. @@ -26,7 +26,7 @@ jobs: cmake_command: cmake -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=on -B build # Uploads an artefact containing clang_fixes.json - - uses: ZedThree/clang-tidy-review/upload@v0.12.0 + - uses: ZedThree/clang-tidy-review/upload@v0.14.0 id: upload-review # If there are any comments, fail the check From c26aba4a20be98983bcef1c173ff15d37f45140e Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 14:03:55 -0300 Subject: [PATCH 16/36] use a different action for clang-tidy --- .github/workflows/clang-tidy.yaml | 53 ++++++++++++++++++------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index e3b0aed..85ff7a9 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -7,28 +7,37 @@ on: - '**.h' jobs: - build: + clang-tidy: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - # Optionally generate compile_commands.json - - - uses: ZedThree/clang-tidy-review@v0.14.0 - id: review + - uses: actions/checkout@v2 with: - # Tell clang-tidy-review the base directory. - # This will get replaced by the new working - # directory inside the action - base_dir: ./sdks/c/ - config_file: ".clang-tidy" - cmake_command: cmake -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=on -B build - - # Uploads an artefact containing clang_fixes.json - - uses: ZedThree/clang-tidy-review/upload@v0.14.0 - id: upload-review - - # If there are any comments, fail the check - - if: steps.review.outputs.total_comments > 0 - run: exit 1 + fetch-depth: 2 + - name: Install clang-tidy + run: | + sudo apt-get update + sudo apt-get install -y clang-tidy + - name: Prepare compile_commands.json + run: | + cd sdks/c/ && + cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + - name: Create results directory + run: | + mkdir clang-tidy-result + - name: Analyze + run: | + git diff -U0 HEAD^ | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml + - name: Run clang-tidy-pr-comments action + uses: platisd/clang-tidy-pr-comments@master + with: + # The GitHub token (or a personal access token) + github_token: ${{ secrets.GITHUB_TOKEN }} + # The path to the clang-tidy fixes generated previously + clang_tidy_fixes: clang-tidy-result/fixes.yml + # Optionally set to true if you want the Action to request + # changes in case warnings are found + request_changes: true + # Optionally set the number of comments per review + # to avoid GitHub API timeouts for heavily loaded + # pull requests + suggestions_per_comment: 10 From 26515c6650a5f8d52d1abf7a3ded854121b04712 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 14:10:39 -0300 Subject: [PATCH 17/36] add libcurl dep --- .github/workflows/clang-tidy.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 85ff7a9..326e972 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -16,7 +16,7 @@ jobs: - name: Install clang-tidy run: | sudo apt-get update - sudo apt-get install -y clang-tidy + sudo apt-get install -y clang-tidy curl libcurl4 - name: Prepare compile_commands.json run: | cd sdks/c/ && @@ -30,8 +30,6 @@ jobs: - name: Run clang-tidy-pr-comments action uses: platisd/clang-tidy-pr-comments@master with: - # The GitHub token (or a personal access token) - github_token: ${{ secrets.GITHUB_TOKEN }} # The path to the clang-tidy fixes generated previously clang_tidy_fixes: clang-tidy-result/fixes.yml # Optionally set to true if you want the Action to request From 13e8cc05707679f5edc61c9ad19625041540efc8 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 14:20:43 -0300 Subject: [PATCH 18/36] simpler action with reviewdog --- .github/workflows/clang-tidy.yaml | 37 +++++++++---------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 326e972..dc92069 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -10,32 +10,17 @@ jobs: clang-tidy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 2 - - name: Install clang-tidy - run: | - sudo apt-get update - sudo apt-get install -y clang-tidy curl libcurl4 - - name: Prepare compile_commands.json - run: | - cd sdks/c/ && - cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - - name: Create results directory - run: | - mkdir clang-tidy-result - - name: Analyze - run: | - git diff -U0 HEAD^ | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml - - name: Run clang-tidy-pr-comments action - uses: platisd/clang-tidy-pr-comments@master + + - name: CMake + env: + CC: clang + run: cd ./sdks/c && cmake -B ./build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + - name: reviewdog with clang-tidy + uses: arkedge/action-clang-tidy with: - # The path to the clang-tidy fixes generated previously - clang_tidy_fixes: clang-tidy-result/fixes.yml - # Optionally set to true if you want the Action to request - # changes in case warnings are found - request_changes: true - # Optionally set the number of comments per review - # to avoid GitHub API timeouts for heavily loaded - # pull requests - suggestions_per_comment: 10 + workdir: cd ./sdks/c/build + - name: Build + run: cd ./sdks/c && cmake -S . --build ./build From b2e646df0b03b74de8b0fccebf40ae1faae626d2 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 17:36:04 -0300 Subject: [PATCH 19/36] adds workflow_dispatch --- .github/workflows/clang-tidy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index dc92069..5bdbdfa 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -5,6 +5,7 @@ on: paths: - '**.c' - '**.h' + workflow_dispatch: jobs: clang-tidy: From a03e86310ed0f6823ed4f8fa46cdc3bb84d009d7 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 17:46:16 -0300 Subject: [PATCH 20/36] set version for action-clang-tidy --- .github/workflows/clang-tidy.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 5bdbdfa..8a3bf4a 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -12,15 +12,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 2 - name: CMake env: CC: clang run: cd ./sdks/c && cmake -B ./build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - name: reviewdog with clang-tidy - uses: arkedge/action-clang-tidy + uses: arkedge/action-clang-tidy@v1.2.0 with: workdir: cd ./sdks/c/build - name: Build From 4c296c6dc91240d09e6b326839afbe06ba63dfac Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 17:55:47 -0300 Subject: [PATCH 21/36] install curl and libcurl in actions --- .github/workflows/clang-tidy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 8a3bf4a..680f050 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -13,6 +13,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install libcurl + run: sudo apt-get install -y curl libcurl-dev + - name: CMake env: CC: clang From 800295a8903e27081e78060de279799ab9c18dfe Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:32:32 -0300 Subject: [PATCH 22/36] changes libcurl-dev to libcurl4-openssl-dev --- .github/workflows/clang-tidy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 680f050..8cf8710 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Install libcurl - run: sudo apt-get install -y curl libcurl-dev + run: sudo apt-get install -y curl libcurl4-openssl-dev - name: CMake env: From d982f9a622249c74172d6013aab69ecd4bb7409f Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:33:55 -0300 Subject: [PATCH 23/36] apt update before install --- .github/workflows/clang-tidy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 8cf8710..6139f28 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Install libcurl - run: sudo apt-get install -y curl libcurl4-openssl-dev + run: sudp apt-get update && sudo apt-get install -y curl libcurl4-openssl-dev - name: CMake env: From 47924560f2cf752e9037dcfbc1ad1c828227f361 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:35:45 -0300 Subject: [PATCH 24/36] fixes typo in sudo --- .github/workflows/clang-tidy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 6139f28..3caef9c 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Install libcurl - run: sudp apt-get update && sudo apt-get install -y curl libcurl4-openssl-dev + run: sudo apt-get update && sudo apt-get install -y curl libcurl4-openssl-dev - name: CMake env: From 874bea347be12cc68920d3ae2c36ded278453d08 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:39:14 -0300 Subject: [PATCH 25/36] add gitkeep int build dir --- sdks/c/build/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 sdks/c/build/.gitkeep diff --git a/sdks/c/build/.gitkeep b/sdks/c/build/.gitkeep new file mode 100644 index 0000000..e69de29 From 06f61bad3821d2c8c98032e12f8bbe2623a84784 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:43:34 -0300 Subject: [PATCH 26/36] fixes directories --- .github/workflows/clang-tidy.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 3caef9c..4fba676 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -19,10 +19,12 @@ jobs: - name: CMake env: CC: clang - run: cd ./sdks/c && cmake -B ./build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + run: cmake -B ./build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - name: reviewdog with clang-tidy uses: arkedge/action-clang-tidy@v1.2.0 with: - workdir: cd ./sdks/c/build + workdir: ./sdks/c/ - name: Build - run: cd ./sdks/c && cmake -S . --build ./build + run: cmake -S . --build ./build + with: + workdir: ./sdks/c/ From ad28f494cf48a1fd7a01c117d31cd954619441b0 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:48:55 -0300 Subject: [PATCH 27/36] uses in build step working-directory --- .github/workflows/clang-tidy.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 4fba676..9209f5e 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -25,6 +25,5 @@ jobs: with: workdir: ./sdks/c/ - name: Build + working-directory: ./sdks/c/ run: cmake -S . --build ./build - with: - workdir: ./sdks/c/ From d51ff57ec427d17c7ed7989d990c4af170f2585c Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:51:07 -0300 Subject: [PATCH 28/36] adds missing workflow and spacing --- .github/workflows/clang-tidy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 9209f5e..8add011 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -17,13 +17,16 @@ jobs: run: sudo apt-get update && sudo apt-get install -y curl libcurl4-openssl-dev - name: CMake + working-directory: ./sdks/c/ env: CC: clang run: cmake -B ./build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + - name: reviewdog with clang-tidy uses: arkedge/action-clang-tidy@v1.2.0 with: workdir: ./sdks/c/ + - name: Build working-directory: ./sdks/c/ run: cmake -S . --build ./build From c680c4cd82f33872030136526bf2ee6ad9df0912 Mon Sep 17 00:00:00 2001 From: auyer Date: Fri, 13 Oct 2023 18:52:36 -0300 Subject: [PATCH 29/36] use -B instead os --build --- .github/workflows/clang-tidy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 8add011..2346c8d 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -29,4 +29,4 @@ jobs: - name: Build working-directory: ./sdks/c/ - run: cmake -S . --build ./build + run: cmake -S . -B ./build From 529dff18f3d13dc6a81d514f4816a9b9990084df Mon Sep 17 00:00:00 2001 From: auyer Date: Sun, 15 Oct 2023 00:58:55 -0300 Subject: [PATCH 30/36] fixes memory leak in string_carrier, apply style fixes --- sdks/c/libMemoryKV/libMemoryKV.c | 44 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index fb68ef6..bc13e25 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -2,44 +2,48 @@ #include #include #include +#include + // string_carrier struct to store the body results from curl with easy reallocation -struct string_carrier { +typedef struct string_carrier { char *ptr; size_t len; bool error_flag; -}; +} string_carrier; // init_string_carrier initializes the string_carrier struct to store the body results from curl void init_string_carrier(struct string_carrier *s) { - s->len = 0; - s->ptr = malloc(s->len + 1); - s->error_flag = false; if (s->ptr == NULL) { - // TODO: figure out a better way to handle errors in C - fprintf(stderr, "memkv client: Error in Callback, malloc() failed\n"); - // skip exit and return error instead - // exit(EXIT_FAILURE); - s->error_flag = true; + s->len = 0; + s->ptr = malloc(s->len + 1); + s->error_flag = false; } else { - s->ptr[0] = '\0'; - } + memset(s, 0, sizeof *s); + } } // string_carrier_writefunc is the callback function to read the body from libcurl into a string_carrier size_t string_carrier_writefunc(void *ptr, size_t size, size_t nmemb, struct string_carrier *s) { // new length to realocate response chunks from libcurl size_t new_len = s->len + size * nmemb; - s->ptr = realloc(s->ptr, new_len + 1); - if (s->ptr == NULL) { + // s->ptr = realloc(s->ptr, new_len + 1); + void *const tmp = realloc(s->ptr, new_len + 1); + if (!tmp) { // TODO: figure out a better way to handle errors in C fprintf(stderr, "memkv client: Error in string_carrier_writefunc Callback, realloc() failed\n"); // skip exit and return error instead // exit(EXIT_FAILURE); s->error_flag = true; + + /* realloc() failed to reallocate memory. The original memory is still + * there and needs to be freed. + */ + // handle_failure(); } else { + /* Now s->ptr points to the new chunk of memory. */ + s->ptr = tmp; memcpy(s->ptr + s->len, ptr, size * nmemb); - s->ptr[new_len] = '\0'; s->len = new_len; } @@ -64,9 +68,9 @@ memkv_client *memkv_client_new(char *host) { char *build_url(const char *host, const char *params) { // snprintf returns the number of characters that would have been written if called with NULL - unsigned long sizeneeded = snprintf(NULL, 0, "%s/%s", host, params); + unsigned long size_needed = snprintf(NULL, 0, "%s/%s", host, params); // use that number to allocate a buffer of the right size - char *url = malloc(sizeneeded + 1); + char *url = malloc(size_needed + 1); // write the string_carrier to the buffer sprintf(url, "%s/%s", host, params); return url; @@ -92,11 +96,11 @@ void make_curl_error(memkv_result *r, const char *err) { return; } - unsigned long sizeneeded = snprintf(NULL, 0, "%s : %s", base_curl_error, err); + unsigned long size_needed = snprintf(NULL, 0, "%s : %s", base_curl_error, err); - r->error = malloc(sizeneeded + 1); + r->error = malloc(size_needed + 1); - snprintf(r->error, sizeneeded + 1, "%s : %s", base_curl_error, err); + snprintf(r->error, size_needed + 1, "%s : %s", base_curl_error, err); } // init_memkv_result initializes the memkv_result struct with success as false From d4ed0c7c9e21facdb3eb8384a83d8674fa8008b0 Mon Sep 17 00:00:00 2001 From: auyer Date: Tue, 17 Oct 2023 00:25:51 -0300 Subject: [PATCH 31/36] creates memkv_execute_request for code reusability, handle more errors --- sdks/c/example.c | 18 +- sdks/c/libMemoryKV/libMemoryKV.c | 431 ++++++++----------------------- sdks/c/libMemoryKV/libMemoryKV.h | 11 +- 3 files changed, 127 insertions(+), 333 deletions(-) diff --git a/sdks/c/example.c b/sdks/c/example.c index 37ef84a..6a1aa60 100644 --- a/sdks/c/example.c +++ b/sdks/c/example.c @@ -4,6 +4,10 @@ #include void print_memkv_result(memkv_result *response) { + if (!response) { + fprintf(stderr, "Error: response is NULL\n"); + return; + } if (response->success) { printf("Success! Result: %s\n", response->result); } else { @@ -16,7 +20,7 @@ int main(void) { client = memkv_client_new("http://localhost:8080"); - memkv_result *response = malloc(sizeof(memkv_result)); + memkv_result *response; char key[] = "c_sdk"; char key2[] = "c_sdk2"; @@ -33,59 +37,71 @@ int main(void) { fprintf(stdout, "\nPut Key '%s'\n", key); response = memkv_put_key(client, key, put_body); print_memkv_result(response); + free(response); // list key fprintf(stdout, "\nList Keys\n"); response = memkv_list_keys(client); print_memkv_result(response); + free(response); // get key fprintf(stdout, "\nGet Key '%s'\n", key); response = memkv_get_key(client, key); print_memkv_result(response); + free(response); fprintf(stdout, "\nPut Key '%s'\n", key2); response = memkv_put_key(client, key2, put_body); print_memkv_result(response); + free(response); fprintf(stdout, "\nPut Key '%s'\n", key3); response = memkv_put_key(client, key3, put_body); print_memkv_result(response); + free(response); fprintf(stdout, "\nPut Key '%s' (again)\n", key3); response = memkv_put_key(client, key3, put_body); print_memkv_result(response); + free(response); char prefix[] = "c"; fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix); response = memkv_list_keys_with_prefix(client, prefix); print_memkv_result(response); + free(response); prefix[0] = 'a'; fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix); response = memkv_list_keys_with_prefix(client, prefix); print_memkv_result(response); + free(response); // delete key prefix[0] = 'c'; fprintf(stdout, "\nMaking a delete Key Request with Prefix '%s'\n", prefix); response = memkv_delete_keys_with_prefix(client, prefix); print_memkv_result(response); + free(response); // delete all keys fprintf(stdout, "\nDelete all Keys\n"); response = memkv_delete_all_keys(client); print_memkv_result(response); + free(response); fprintf(stdout, "\nMaking a delete Key '%s'\n", key3); response = memkv_delete_key(client, key3); print_memkv_result(response); + free(response); // list key fprintf(stdout, "\nList Keys\n"); response = memkv_list_keys(client); print_memkv_result(response); + free(response); free(client); } diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index bc13e25..afcc7e4 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -1,9 +1,8 @@ #include #include +#include #include #include -#include - // string_carrier struct to store the body results from curl with easy reallocation typedef struct string_carrier { @@ -13,14 +12,33 @@ typedef struct string_carrier { } string_carrier; // init_string_carrier initializes the string_carrier struct to store the body results from curl -void init_string_carrier(struct string_carrier *s) { +void init_string_carrier(string_carrier *s) { + if (s == NULL) { + printf("Error in init_string_carrier"); + return; + } + s->len = 0; + s->ptr = malloc(s->len + 1); + s->error_flag = false; if (s->ptr == NULL) { - s->len = 0; - s->ptr = malloc(s->len + 1); - s->error_flag = false; + // TODO: figure out a better way to handle errors in C + fprintf(stderr, "memkv client: Error in INIT, malloc() failed\n"); + // skip exit and return error instead + // exit(EXIT_FAILURE); + s->error_flag = true; } else { - memset(s, 0, sizeof *s); - } + s->ptr[0] = '\0'; + } +} + +string_carrier *new_string_carrier() { + string_carrier *s = malloc(sizeof *s); + init_string_carrier(s); + if (s == NULL || s->error_flag || s->ptr == NULL) { + printf("Error in new_string_carrier\n"); + return NULL; + } + return s; } // string_carrier_writefunc is the callback function to read the body from libcurl into a string_carrier @@ -55,13 +73,17 @@ typedef struct memkv_client { char *host; } memkv_client; -void memkv_init_client(struct memkv_client *client, char *host) { +void memkv_init_client(memkv_client *client, char *host) { curl_global_init(CURL_GLOBAL_ALL); client->host = host; } memkv_client *memkv_client_new(char *host) { - memkv_client *client = malloc(sizeof(memkv_client)); + memkv_client *client = malloc(sizeof *client); + if (client == NULL) { + printf("Error in memkv_client_new"); + return NULL; + } memkv_init_client(client, host); return client; } @@ -92,7 +114,9 @@ void make_curl_error(memkv_result *r, const char *err) { if (strlen(err) == 0) { r->error = malloc(sizeof(unknown_error_msg)); + // strlcpy(r->error, unknown_error_msg, sizeof(unknown_error_msg) + sizeof(r->error)); strcpy(r->error, unknown_error_msg); + return; } @@ -106,9 +130,9 @@ void make_curl_error(memkv_result *r, const char *err) { // init_memkv_result initializes the memkv_result struct with success as false memkv_result *init_memkv_result() { - memkv_result *r = malloc(sizeof(memkv_result)); + memkv_result *r = malloc(sizeof *r); - r->result = malloc(sizeof(char)); + r->result = malloc(1); if (r->result == NULL) { free(r); printf("Error in init_memkv_result"); @@ -119,388 +143,141 @@ memkv_result *init_memkv_result() { return r; } -memkv_result *memkv_get_key(struct memkv_client *client, const char *key) { +memkv_result *memkv_execute_request( + const char *url, + const char *custom_req, + const char *content_type, + const char *post_data) { + memkv_result *r = init_memkv_result(); - CURL *curl; CURLcode res; - curl = curl_easy_init(); + // TODO move this to the client ? + CURL *curl = curl_easy_init(); + if (!curl) { - make_curl_error(r, "Failed startig curl"); + make_curl_error(r, "Failed starting curl."); return r; } - struct string_carrier s; - init_string_carrier(&s); - if (s.error_flag) { + string_carrier *s = new_string_carrier(); + + if (s == NULL || s->error_flag) { make_curl_error(r, "Failed to initialize string_carrier"); return r; } - char *url = build_url(client->host, key); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + int curl_parameter_responses = 0; + curl_parameter_responses += curl_easy_setopt(curl, CURLOPT_URL, url); - res = curl_easy_perform(curl); + if (custom_req) { + curl_parameter_responses += curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req); + } - /* always cleanup */ - curl_easy_cleanup(curl); - free(url); + struct curl_slist *headers = NULL; - if (res != CURLE_OK) { - const char *err = curl_easy_strerror(res); - make_curl_error(r, err); - return r; - } - if (s.error_flag) { - make_curl_error(r, "Failed to get results from server"); - return r; + if (content_type) { + /* default type with postfields is application/x-www-form-urlencoded, + change it if you want. */ + headers = curl_slist_append(headers, content_type); + curl_parameter_responses += curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); } - r->success = true; - r->result = malloc(strlen(s.ptr) + 1); - - strcpy(r->result, s.ptr); - return r; -} -memkv_result *memkv_delete_key(struct memkv_client *client, const char *key) { - memkv_result *r = init_memkv_result(); + if (post_data) { + curl_parameter_responses += curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); + } - CURL *curl; - CURLcode res; + curl_parameter_responses += curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl = curl_easy_init(); - if (!curl) { - make_curl_error(r, "Failed startig curl"); - return r; - } + printf("Curl parameters responses %d %s\n", curl_parameter_responses, curl_parameter_responses ? "true" : "false"); - struct string_carrier s; - init_string_carrier(&s); - if (s.error_flag) { - make_curl_error(r, "Failed to initialize string_carrier"); - return r; - } + int curl_body_responses = 0; - char *url = build_url(client->host, key); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_body_responses += curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); + curl_body_responses += curl_easy_setopt(curl, CURLOPT_WRITEDATA, s); - // set custom request to delete - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + printf("Curl body responses %d %s\n", curl_body_responses, curl_body_responses ? "true" : "false"); - /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); - /* always cleanup */ curl_easy_cleanup(curl); - free(url); - /* Check for errors */ + if (headers) { + curl_slist_free_all(headers); + } + if (res != CURLE_OK) { const char *err = curl_easy_strerror(res); make_curl_error(r, err); return r; } - if (s.error_flag) { - make_curl_error(r, "Failed to get results from server"); + if (s->error_flag) { + make_curl_error(r, "Failed to get results from server."); return r; } + r->success = true; - r->result = malloc(strlen(s.ptr) + 1); + r->result = malloc(strlen(s->ptr) + 1); - strcpy(r->result, s.ptr); + strcpy(r->result, s->ptr); + // strlcpy(r->result, s->ptr, sizeof(r->result) + sizeof(s->ptr)); return r; } -memkv_result *memkv_put_key(struct memkv_client *client, const char *key, const char *put_body) { - memkv_result *r = init_memkv_result(); - CURL *curl; - CURLcode res; - // start the easy interface in curl - curl = curl_easy_init(); - if (!curl) { - make_curl_error(r, "Failed startig curl"); - return r; - } - struct string_carrier s; - init_string_carrier(&s); - if (s.error_flag) { - make_curl_error(r, "Failed to initialize string_carrier"); - return r; - } - +memkv_result *memkv_get_key(memkv_client *client, const char *key) { char *url = build_url(client->host, key); - curl_easy_setopt(curl, CURLOPT_URL, url); - - struct curl_slist *headers = NULL; - - /* default type with postfields is application/x-www-form-urlencoded, - change it if you want */ - headers = curl_slist_append(headers, "Content-Type: application/octet-stream"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - /* pass on content in request body. When CURLOPT_POSTFIELDSIZE is not used, - curl does strlen to get the size. */ - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, put_body); - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - - /* always cleanup */ - curl_easy_cleanup(curl); + memkv_result *r = memkv_execute_request(url, NULL, NULL, NULL); free(url); - /* free headers */ - curl_slist_free_all(headers); - - /* Check for errors */ - if (res != CURLE_OK) { - const char *err = curl_easy_strerror(res); - make_curl_error(r, err); - return r; - } - - if (s.error_flag) { - make_curl_error(r, "Failed to get results from server"); - return r; - } - r->success = true; - r->result = malloc(strlen(s.ptr) + 1); - - strcpy(r->result, s.ptr); return r; } -memkv_result *memkv_list_keys(struct memkv_client *client) { - memkv_result *r = init_memkv_result(); - - CURL *curl; - CURLcode res; - - // start the easy interface in curl - curl = curl_easy_init(); - if (!curl) { - make_curl_error(r, "Failed startig curl"); - return r; - } - struct string_carrier s; - init_string_carrier(&s); - if (s.error_flag) { - make_curl_error(r, "Failed to initialize string_carrier"); - return r; - } - - const char *key = "keys"; +memkv_result *memkv_delete_key(memkv_client *client, const char *key) { char *url = build_url(client->host, key); - - // set custom request to delete - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - - /* always cleanup */ - curl_easy_cleanup(curl); + memkv_result *r = memkv_execute_request(url, "DELETE", NULL, NULL); free(url); - - /* Check for errors */ - if (res != CURLE_OK) { - r->success = false; - const char *err = curl_easy_strerror(res); - make_curl_error(r, err); - return r; - } - - if (s.error_flag) { - make_curl_error(r, "Failed to get results from server"); - return r; - } - r->success = true; - r->result = malloc(strlen(s.ptr) + 1); - - strcpy(r->result, s.ptr); return r; } -memkv_result *memkv_list_keys_with_prefix(struct memkv_client *client, const char *key_prefix) { - memkv_result *r = init_memkv_result(); - - CURL *curl; - CURLcode res; +memkv_result *memkv_put_key(memkv_client *client, const char *key, const char *put_body) { + char *url = build_url(client->host, key); + memkv_result *r = memkv_execute_request(url, "PUT", "Content-Type: application/octet-stream", put_body); + free(url); + return r; +} - // start the easy interface in curl - curl = curl_easy_init(); - if (!curl) { - make_curl_error(r, "Failed startig curl"); - return r; - } - struct string_carrier s; - init_string_carrier(&s); - if (s.error_flag) { - make_curl_error(r, "Failed to initialize string_carrier"); - return r; - } +memkv_result *memkv_list_keys(memkv_client *client) { + char *url = build_url(client->host, "keys"); + memkv_result *r = memkv_execute_request(url, NULL, NULL, NULL); + free(url); + return r; +} - const char *key = "keys"; - char *url_part1 = build_url(client->host, key); +memkv_result *memkv_list_keys_with_prefix(memkv_client *client, const char *key_prefix) { + char *url_part1 = build_url(client->host, "keys"); // TODO: build URL in one call char *url = build_url(url_part1, key_prefix); free(url_part1); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - - /* always cleanup */ - curl_easy_cleanup(curl); + memkv_result *r = memkv_execute_request(url, NULL, NULL, NULL); free(url); - - /* Check for errors */ - if (res != CURLE_OK) { - r->success = false; - const char *err = curl_easy_strerror(res); - make_curl_error(r, err); - return r; - } - - if (s.error_flag) { - make_curl_error(r, "Failed to get results from server"); - return r; - } - r->success = true; - r->result = malloc(strlen(s.ptr) + 1); - - strcpy(r->result, s.ptr); return r; } -memkv_result *memkv_delete_keys_with_prefix(struct memkv_client *client, const char *key_prefix) { - memkv_result *r = init_memkv_result(); - - CURL *curl; - CURLcode res; - - curl = curl_easy_init(); - if (!curl) { - make_curl_error(r, "Failed startig curl"); - return r; - } - - struct string_carrier s; - init_string_carrier(&s); - if (s.error_flag) { - make_curl_error(r, "Failed to initialize string_carrier"); - return r; - } - - const char *key = "keys"; - char *url_part1 = build_url(client->host, key); +memkv_result *memkv_delete_keys_with_prefix(memkv_client *client, const char *key_prefix) { + char *url_part1 = build_url(client->host, "keys"); // TODO: build URL in one call char *url = build_url(url_part1, key_prefix); free(url_part1); - - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - - // set custom request to delete - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - - /* always cleanup */ - curl_easy_cleanup(curl); + memkv_result *r = memkv_execute_request(url, "DELETE", NULL, NULL); free(url); - - /* Check for errors */ - if (res != CURLE_OK) { - const char *err = curl_easy_strerror(res); - make_curl_error(r, err); - return r; - } - - if (s.error_flag) { - make_curl_error(r, "Failed to get results from server"); - return r; - } - r->success = true; - r->result = malloc(strlen(s.ptr) + 1); - - strcpy(r->result, s.ptr); return r; } -memkv_result *memkv_delete_all_keys(struct memkv_client *client) { - memkv_result *r = init_memkv_result(); - - CURL *curl; - CURLcode res; - - // start the easy interface in curl - curl = curl_easy_init(); - if (!curl) { - make_curl_error(r, "Failed startig curl"); - return r; - } - struct string_carrier s; - init_string_carrier(&s); - if (s.error_flag) { - make_curl_error(r, "Failed to initialize string_carrier"); - return r; - } - - const char *key = "keys"; - char *url = build_url(client->host, key); - - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, string_carrier_writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - - /* always cleanup */ - curl_easy_cleanup(curl); +memkv_result *memkv_delete_all_keys(memkv_client *client) { + char *url = build_url(client->host, "keys"); + memkv_result *r = memkv_execute_request(url, "DELETE", NULL, NULL); free(url); - - /* Check for errors */ - if (res != CURLE_OK) { - r->success = false; - const char *err = curl_easy_strerror(res); - make_curl_error(r, err); - return r; - } - - if (s.error_flag) { - make_curl_error(r, "Failed to get results from server"); - return r; - } - r->success = true; - r->result = malloc(strlen(s.ptr) + 1); - - strcpy(r->result, s.ptr); return r; } diff --git a/sdks/c/libMemoryKV/libMemoryKV.h b/sdks/c/libMemoryKV/libMemoryKV.h index ac344f4..d8ba046 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.h +++ b/sdks/c/libMemoryKV/libMemoryKV.h @@ -6,10 +6,10 @@ typedef struct memkv_client { char *host; } memkv_client; -// memkv_result stores a success boolean to indicate if the request was successfull or not. +// memkv_result stores a success boolean to indicate if the request was successful or not. // the success variable should be checked before reading results -// in case of success, the result will be stored in the result attribure `memkv_result->result` -// in case of error, the error reason will be stored in the error attribure `memkv_result->error` +// in case of success, the result will be stored in the result attribute `memkv_result->result` +// in case of error, the error reason will be stored in the error attribute `memkv_result->error` typedef struct { bool success : 1; union { @@ -22,7 +22,8 @@ typedef struct { memkv_client *memkv_client_new(char *host); // memkv_init_client initializes an existing memkv_client with a provided URL -void memkv_init_client(struct memkv_client *client, char *host); +// If this function returns non-zero, something went wrong and you cannot use the other underlying curl functions. +int memkv_init_client(struct memkv_client *client, char *host); // memkv_get_key fetches the value of a key from the MemoryKV server memkv_result *memkv_get_key(struct memkv_client *client, const char *key); @@ -43,4 +44,4 @@ memkv_result *memkv_list_keys_with_prefix(struct memkv_client *client, const cha memkv_result *memkv_delete_keys_with_prefix(struct memkv_client *client, const char *key_prefix); // memkv_delete_all_keys deletes all keys from the MemoryKV server, and returns list of deletes keys -memkv_result *memkv_delete_all_keys(struct memkv_client *client); \ No newline at end of file +memkv_result *memkv_delete_all_keys(struct memkv_client *client); From 6b9c996eee7322d150928eb2fbd38542b5d58a9d Mon Sep 17 00:00:00 2001 From: auyer Date: Tue, 17 Oct 2023 10:59:45 -0300 Subject: [PATCH 32/36] fixes allocation of result --- sdks/c/libMemoryKV/libMemoryKV.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index afcc7e4..8eb13d5 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -219,10 +219,9 @@ memkv_result *memkv_execute_request( } r->success = true; - r->result = malloc(strlen(s->ptr) + 1); + r->result = malloc(s->len + 1); + strlcpy(r->result, s->ptr, s->len + 1); - strcpy(r->result, s->ptr); - // strlcpy(r->result, s->ptr, sizeof(r->result) + sizeof(s->ptr)); return r; } From a1725d18133cea6e1ce5ac85f05c0013c26b8d80 Mon Sep 17 00:00:00 2001 From: auyer Date: Tue, 17 Oct 2023 11:21:12 -0300 Subject: [PATCH 33/36] simplify init, fixes reallocation, removes unecessary of result --- sdks/c/libMemoryKV/libMemoryKV.c | 33 +++++++++++--------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index 8eb13d5..1d34608 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -17,19 +17,15 @@ void init_string_carrier(string_carrier *s) { printf("Error in init_string_carrier"); return; } - s->len = 0; + memset(s, 0, sizeof *s); s->ptr = malloc(s->len + 1); - s->error_flag = false; + memset(s->ptr, 0, sizeof *s->ptr); + if (s->ptr == NULL) { - // TODO: figure out a better way to handle errors in C fprintf(stderr, "memkv client: Error in INIT, malloc() failed\n"); - // skip exit and return error instead - // exit(EXIT_FAILURE); s->error_flag = true; - } else { - s->ptr[0] = '\0'; - } -} + } + } string_carrier *new_string_carrier() { string_carrier *s = malloc(sizeof *s); @@ -44,24 +40,16 @@ string_carrier *new_string_carrier() { // string_carrier_writefunc is the callback function to read the body from libcurl into a string_carrier size_t string_carrier_writefunc(void *ptr, size_t size, size_t nmemb, struct string_carrier *s) { // new length to realocate response chunks from libcurl - size_t new_len = s->len + size * nmemb; - // s->ptr = realloc(s->ptr, new_len + 1); + size_t new_len = s->len + (size * nmemb); void *const tmp = realloc(s->ptr, new_len + 1); if (!tmp) { - // TODO: figure out a better way to handle errors in C fprintf(stderr, "memkv client: Error in string_carrier_writefunc Callback, realloc() failed\n"); - // skip exit and return error instead - // exit(EXIT_FAILURE); s->error_flag = true; - - /* realloc() failed to reallocate memory. The original memory is still - * there and needs to be freed. - */ - // handle_failure(); } else { /* Now s->ptr points to the new chunk of memory. */ s->ptr = tmp; - memcpy(s->ptr + s->len, ptr, size * nmemb); + // we already know the length of the string, so we can just copy it with memcpy instead of strlcpy + memcpy(s->ptr + s->len, ptr, new_len +1); s->len = new_len; } @@ -219,8 +207,9 @@ memkv_result *memkv_execute_request( } r->success = true; - r->result = malloc(s->len + 1); - strlcpy(r->result, s->ptr, s->len + 1); + // updates the result pointer to point to the string_carrier->ptr + r-> result = s->ptr; + free(s); return r; } From c8a97aecee3a4f467bab64f0b919ed60779f324f Mon Sep 17 00:00:00 2001 From: auyer Date: Tue, 17 Oct 2023 11:46:59 -0300 Subject: [PATCH 34/36] improves init_string, fixes linter warnings, make internal functions static --- sdks/c/example.c | 18 ++--------- sdks/c/libMemoryKV/libMemoryKV.c | 53 +++++++++++++++++--------------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/sdks/c/example.c b/sdks/c/example.c index 6a1aa60..daaf8f5 100644 --- a/sdks/c/example.c +++ b/sdks/c/example.c @@ -6,13 +6,13 @@ void print_memkv_result(memkv_result *response) { if (!response) { fprintf(stderr, "Error: response is NULL\n"); - return; - } - if (response->success) { + + } else if (response->success) { printf("Success! Result: %s\n", response->result); } else { fprintf(stderr, "Error: %s\n", response->error); } + free(response); } int main(void) { @@ -37,71 +37,59 @@ int main(void) { fprintf(stdout, "\nPut Key '%s'\n", key); response = memkv_put_key(client, key, put_body); print_memkv_result(response); - free(response); // list key fprintf(stdout, "\nList Keys\n"); response = memkv_list_keys(client); print_memkv_result(response); - free(response); // get key fprintf(stdout, "\nGet Key '%s'\n", key); response = memkv_get_key(client, key); print_memkv_result(response); - free(response); fprintf(stdout, "\nPut Key '%s'\n", key2); response = memkv_put_key(client, key2, put_body); print_memkv_result(response); - free(response); fprintf(stdout, "\nPut Key '%s'\n", key3); response = memkv_put_key(client, key3, put_body); print_memkv_result(response); - free(response); fprintf(stdout, "\nPut Key '%s' (again)\n", key3); response = memkv_put_key(client, key3, put_body); print_memkv_result(response); - free(response); char prefix[] = "c"; fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix); response = memkv_list_keys_with_prefix(client, prefix); print_memkv_result(response); - free(response); prefix[0] = 'a'; fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix); response = memkv_list_keys_with_prefix(client, prefix); print_memkv_result(response); - free(response); // delete key prefix[0] = 'c'; fprintf(stdout, "\nMaking a delete Key Request with Prefix '%s'\n", prefix); response = memkv_delete_keys_with_prefix(client, prefix); print_memkv_result(response); - free(response); // delete all keys fprintf(stdout, "\nDelete all Keys\n"); response = memkv_delete_all_keys(client); print_memkv_result(response); - free(response); fprintf(stdout, "\nMaking a delete Key '%s'\n", key3); response = memkv_delete_key(client, key3); print_memkv_result(response); - free(response); // list key fprintf(stdout, "\nList Keys\n"); response = memkv_list_keys(client); print_memkv_result(response); - free(response); free(client); } diff --git a/sdks/c/libMemoryKV/libMemoryKV.c b/sdks/c/libMemoryKV/libMemoryKV.c index 1d34608..6cd3078 100644 --- a/sdks/c/libMemoryKV/libMemoryKV.c +++ b/sdks/c/libMemoryKV/libMemoryKV.c @@ -12,25 +12,29 @@ typedef struct string_carrier { } string_carrier; // init_string_carrier initializes the string_carrier struct to store the body results from curl -void init_string_carrier(string_carrier *s) { - if (s == NULL) { - printf("Error in init_string_carrier"); - return; - } - memset(s, 0, sizeof *s); +static void init_string_carrier(string_carrier *s) { + s->len = 0; + s->error_flag = false; s->ptr = malloc(s->len + 1); - memset(s->ptr, 0, sizeof *s->ptr); - - if (s->ptr == NULL) { - fprintf(stderr, "memkv client: Error in INIT, malloc() failed\n"); + if (s->ptr != NULL) { + // set ptr as null terminator + s->ptr[0] = '\0'; + } else { + // set error flag to true if malloc fails s->error_flag = true; - } - } + fprintf(stderr, "memkv client: Error in INIT, malloc() failed\n"); + } +} -string_carrier *new_string_carrier() { +static string_carrier *new_string_carrier() { string_carrier *s = malloc(sizeof *s); + if (!s) { + free(s); + return NULL; + } init_string_carrier(s); - if (s == NULL || s->error_flag || s->ptr == NULL) { + if (s->error_flag || s->ptr == NULL) { + free(s); printf("Error in new_string_carrier\n"); return NULL; } @@ -38,7 +42,7 @@ string_carrier *new_string_carrier() { } // string_carrier_writefunc is the callback function to read the body from libcurl into a string_carrier -size_t string_carrier_writefunc(void *ptr, size_t size, size_t nmemb, struct string_carrier *s) { +static size_t string_carrier_writefunc(void *ptr, size_t size, size_t nmemb, struct string_carrier *s) { // new length to realocate response chunks from libcurl size_t new_len = s->len + (size * nmemb); void *const tmp = realloc(s->ptr, new_len + 1); @@ -48,15 +52,16 @@ size_t string_carrier_writefunc(void *ptr, size_t size, size_t nmemb, struct str } else { /* Now s->ptr points to the new chunk of memory. */ s->ptr = tmp; - // we already know the length of the string, so we can just copy it with memcpy instead of strlcpy - memcpy(s->ptr + s->len, ptr, new_len +1); + // we already know the length of the string, so we can just copy it with memcpy instead of strlcpy + memcpy(s->ptr + s->len, ptr, new_len + 1); s->len = new_len; } return size * nmemb; } -// memkv_client +// memkv_client stores the configurations used to connect to the MemoryKV server +// use `memkv_client_new` to create one, or `memkv_init_client` to initialize an existing instance typedef struct memkv_client { char *host; } memkv_client; @@ -76,7 +81,7 @@ memkv_client *memkv_client_new(char *host) { return client; } -char *build_url(const char *host, const char *params) { +static char *build_url(const char *host, const char *params) { // snprintf returns the number of characters that would have been written if called with NULL unsigned long size_needed = snprintf(NULL, 0, "%s/%s", host, params); // use that number to allocate a buffer of the right size @@ -97,7 +102,7 @@ typedef struct { static const char base_curl_error[] = "Error from curl"; static const char unknown_error_msg[] = "unknown error"; -void make_curl_error(memkv_result *r, const char *err) { +static void make_curl_error(memkv_result *r, const char *err) { r->success = false; if (strlen(err) == 0) { @@ -131,7 +136,7 @@ memkv_result *init_memkv_result() { return r; } -memkv_result *memkv_execute_request( +static memkv_result *memkv_execute_request( const char *url, const char *custom_req, const char *content_type, @@ -207,9 +212,9 @@ memkv_result *memkv_execute_request( } r->success = true; - // updates the result pointer to point to the string_carrier->ptr - r-> result = s->ptr; - free(s); + // updates the result pointer to point to the string_carrier->ptr + r->result = s->ptr; + free(s); return r; } From d285d853ff0d7c950c3e97a6afc89982cabbbbce Mon Sep 17 00:00:00 2001 From: auyer Date: Tue, 17 Oct 2023 11:54:57 -0300 Subject: [PATCH 35/36] fixes workdir for clang-tidy --- .github/workflows/clang-tidy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-tidy.yaml b/.github/workflows/clang-tidy.yaml index 2346c8d..85e9f6d 100644 --- a/.github/workflows/clang-tidy.yaml +++ b/.github/workflows/clang-tidy.yaml @@ -25,7 +25,7 @@ jobs: - name: reviewdog with clang-tidy uses: arkedge/action-clang-tidy@v1.2.0 with: - workdir: ./sdks/c/ + workdir: ./sdks/c/build/ - name: Build working-directory: ./sdks/c/ From ef12f645120f15d4059400179dcdae6b4d966c79 Mon Sep 17 00:00:00 2001 From: auyer Date: Tue, 17 Oct 2023 11:57:03 -0300 Subject: [PATCH 36/36] adds c sdk build to .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 890769c..1ec8302 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -sdks/c/build \ No newline at end of file +sdks/c/build +.vscode/settings.json +