diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..09faabf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.{c,cpp,h}] +indent_style = space +indent_size = 2 diff --git a/Makefile b/Makefile index 66b402f..0b7201e 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ prod: cmake-release .PHONY: run run: build - @ LANDER_API_KEY=test ./build/Debug/lander + @ LANDER_BASE_URL=http://localhost:18080/ LANDER_API_KEY=test ./build/Debug/lander .PHONY: clean clean: diff --git a/landerctl b/landerctl index a2e94cc..262db1f 100755 --- a/landerctl +++ b/landerctl @@ -6,10 +6,9 @@ URL=http://localhost:18080 if [ "$1" = add ]; then curl \ -XPOST \ - -d "$3" \ + -d "$2" \ -H "X-Api-Key: $API_KEY" \ - "$URL/$2" - echo "$URL/$2" + "$URL/$3" elif [ "$1" = get ]; then curl -is "$URL/$2" | grep -Po '(?<=location: ).*' diff --git a/src/main.cpp b/src/main.cpp index b076fe4..2123aa7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,15 +4,23 @@ extern "C" { #include "ternarytrie.h" } -int main() { - // Read in API key - char *api_key = getenv("LANDER_API_KEY"); - - if (api_key == NULL) { - printf("No API key provided."); - return 1; +#define ENV(var, env_var) \ + const char *var = getenv(env_var); \ + if (var == NULL) { \ + printf("Missing environment variable %s.\n", env_var); \ + return 1; \ } +#define AUTH() \ + std::string provided_api_key = req.get_header_value("X-Api-Key"); \ + if (strcmp(api_key, provided_api_key.c_str()) != 0) { \ + return crow::response(crow::status::UNAUTHORIZED); \ + } + +int main() { + ENV(api_key, "LANDER_API_KEY"); + ENV(base_url, "LANDER_BASE_URL"); + TernaryTrie *trie = ternarytrie_init(); std::string file_path = "lander.data"; @@ -20,24 +28,8 @@ int main() { crow::SimpleApp app; - CROW_ROUTE(app, "/") - .methods(crow::HTTPMethod::Post)( - [api_key, trie](const crow::request &req, std::string s) { - // Authenticate request - std::string provided_api_key = req.get_header_value("X-Api-Key"); + app.loglevel(crow::LogLevel::Warning); - if (strcmp(api_key, provided_api_key.c_str()) != 0) { - return crow::response(crow::status::UNAUTHORIZED); - } - - bool res = ternarytrie_add(trie, s.c_str(), req.body.c_str()); - - if (!res) { - return crow::response(crow::status::CONFLICT); - } - - return crow::response(crow::status::NO_CONTENT); - }); CROW_ROUTE(app, "/") .methods(crow::HTTPMethod::Get)( [trie](crow::response &res, std::string s) { @@ -51,6 +43,31 @@ int main() { res.end(); }); + CROW_ROUTE(app, "/").methods(crow::HTTPMethod::Post)( + [api_key, base_url, trie](const crow::request req) { + AUTH(); + + char *key = ternarytrie_add_random(trie, req.body.c_str()); + + if (key == NULL) { + return crow::response(crow::status::INTERNAL_SERVER_ERROR); + } + + return crow::response(key); + }); + CROW_ROUTE(app, "/") + .methods(crow::HTTPMethod::Post)( + [api_key, trie](const crow::request &req, std::string s) { + AUTH(); + + bool res = ternarytrie_add(trie, s.c_str(), req.body.c_str()); + + if (!res) { + return crow::response(crow::status::CONFLICT); + } + + return crow::response(crow::status::NO_CONTENT); + }); app.port(18080).multithreaded().run(); } diff --git a/tries/include/ternarytrie.h b/tries/include/ternarytrie.h index ffed4f5..a214d78 100644 --- a/tries/include/ternarytrie.h +++ b/tries/include/ternarytrie.h @@ -10,6 +10,12 @@ #include #include +static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +static const size_t charset_len = sizeof(charset) - 1; + +// Length of randomly generated keys +#define RANDOM_KEY_LENGTH 4 + /** * Type definition for the struct representing the current Trie. * @@ -51,6 +57,14 @@ char * ternarytrie_search(TernaryTrie* trie, const char* string); */ bool ternarytrie_add(TernaryTrie* trie, const char* string, const char *payload); +/** + * Add a payload by generating a random string as the key. + * + * @param trie + * @return the generated key + */ +char *ternarytrie_add_random(TernaryTrie *trie, const char *payload); + /** * Remove a string from this trie. * diff --git a/tries/src/ternarytrie.c b/tries/src/ternarytrie.c index fe95468..7232a0d 100644 --- a/tries/src/ternarytrie.c +++ b/tries/src/ternarytrie.c @@ -1,14 +1,17 @@ -#include "ternarytrie.h" -#include "ternarytrie_node.c" #include #include #include #include +#include + +#include "ternarytrie.h" +#include "ternarytrie_node.c" typedef struct ttrie { TernaryTrieNode *root; size_t size; char* file_path; + pthread_rwlock_t lock; } TernaryTrie; /** @@ -17,10 +20,11 @@ typedef struct ttrie { * @return pointer to the empty TernaryTrie */ TernaryTrie *ternarytrie_init() { - TernaryTrie *node = calloc(1, sizeof(TernaryTrie)); - node->root = ttnode_init(); + TernaryTrie *trie = calloc(1, sizeof(TernaryTrie)); + trie->root = ttnode_init(); + pthread_rwlock_init(&trie->lock, NULL); - return node; + return trie; } /** @@ -36,44 +40,44 @@ void ternarytrie_free(TernaryTrie *trie) { bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, const char *payload); void ternarytrie_populate(TernaryTrie *trie, const char *file_path) { - trie->file_path = my_strdup(file_path); + trie->file_path = my_strdup(file_path); - FILE* fp = fopen(file_path, "r"); + FILE* fp = fopen(file_path, "r"); - // TODO properly handle this - if (fp == NULL) { - return; + // TODO properly handle this + if (fp == NULL) { + return; + } + + // We read in lines of at most 8192 characters (sounds like enough) + char buffer[8192]; + int i, j; + + while (fgets(buffer, 8192, fp)) { + printf("%s", buffer); + // Find index of space character + i = 0; + + while (buffer[i] != ' ') { + i++; } - // We read in lines of at most 8192 characters (sounds like enough) - char buffer[8192]; - int i, j; + // Split the buffer into two strings, the key and the payload + buffer[i] = '\0'; - while (fgets(buffer, 8192, fp)) { - printf("%s", buffer); - // Find index of space character - i = 0; + j = i + 1; - while (buffer[i] != ' ') { - i++; - } - - // Split the buffer into two strings, the key and the payload - buffer[i] = '\0'; - - j = i + 1; - - // Now remove the newline character - while (buffer[j] != '\n') { - j++; - } - - buffer[j] = '\0'; - - ternarytrie_add_internal(trie, buffer, buffer + i + 1); + // Now remove the newline character + while (buffer[j] != '\n') { + j++; } - fclose(fp); + buffer[j] = '\0'; + + ternarytrie_add_internal(trie, buffer, buffer + i + 1); + } + + fclose(fp); } typedef struct searchresult { @@ -140,13 +144,19 @@ SearchResult ternarytrie_search_node(TernaryTrie *trie, const char *string) { * @return true if the string is present in the trie, false otherwise */ char * ternarytrie_search(TernaryTrie *trie, const char *string) { + pthread_rwlock_rdlock(&trie->lock); + SearchResult res = ternarytrie_search_node(trie, string); + + char* return_value = NULL; if (res.child != NULL) { - return res.child->payload; + return_value = res.child->payload; } - return NULL; + pthread_rwlock_unlock(&trie->lock); + + return return_value; } /** @@ -240,28 +250,78 @@ bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, const char return true; } -bool ternarytrie_add(TernaryTrie *trie, const char *string, const char *payload) { - if (trie->file_path != NULL) { - // Easiest way to make sure we don't add duplicate entries - if (ternarytrie_search(trie, string) != NULL) { - return false; - } +bool ternarytrie_add_persistent(TernaryTrie *trie, const char *string, const char *payload) { + bool return_value = false; - FILE *fp = fopen(trie->file_path, "a"); - - if (fp == NULL) { - return false; - } - - fputs(string, fp); - fputs(" ", fp); - fputs(payload, fp); - fputs("\n", fp); - - fclose(fp); + if (trie->file_path != NULL) { + // Easiest way to make sure we don't add duplicate entries + // We use an internal function that doesn't require a read lock, as we're + // already inside a write lock + if (ternarytrie_search_node(trie, string).child != NULL) { + return false; } - return ternarytrie_add_internal(trie, string, payload); + FILE *fp = fopen(trie->file_path, "a"); + + if (fp == NULL) { + return false; + } + + fputs(string, fp); + fputs(" ", fp); + fputs(payload, fp); + fputs("\n", fp); + + fclose(fp); + } + + // This function *should* always return true. Otherwise, the function would've + // exited because the string was found in the trie. + return ternarytrie_add_internal(trie, string, payload); +} + +bool ternarytrie_add(TernaryTrie *trie, const char *string, const char *payload) { + pthread_rwlock_wrlock(&trie->lock); + + bool return_value = ternarytrie_add_persistent(trie, string, payload); + + pthread_rwlock_unlock(&trie->lock); + + return return_value; +} + +char* ternarytrie_add_random(TernaryTrie *trie, const char *payload) { + pthread_rwlock_wrlock(&trie->lock); + + // Generate random key + bool ok = false; + char *key = malloc(RANDOM_KEY_LENGTH + 1); + key[RANDOM_KEY_LENGTH] = '\0'; + + // We naively generate new keys until we find a key that isn't in the trie + // yet. With charset_len ** RANDOM_KEY_LENGTH sufficiently large, this isn't a + // problem, because the chances of collisions are extremely small. + while (!ok) { + for (int i = 0; i < RANDOM_KEY_LENGTH; i++) { + key[i] = charset[rand() % charset_len]; + } + + ok = ternarytrie_search_node(trie, key).child == NULL; + } + + bool res = ternarytrie_add_persistent(trie, key, payload); + char *return_value; + + if (res) { + return_value = key; + } else { + return_value = NULL; + free(key); + } + + pthread_rwlock_unlock(&trie->lock); + + return return_value; } @@ -273,13 +333,18 @@ bool ternarytrie_add(TernaryTrie *trie, const char *string, const char *payload) * @return true if the string was in the trie and thus removed, false otherwise */ bool ternarytrie_remove(TernaryTrie *trie, const char *string) { + pthread_rwlock_wrlock(&trie->lock); + + bool return_value = false; + SearchResult res = ternarytrie_search_node(trie, string); if (res.child == NULL) { - return false; + goto end; } trie->size--; + return_value = true; if (res.parent != NULL) { // We're removing a full leaf, so we calculate the offset of the character @@ -303,7 +368,7 @@ bool ternarytrie_remove(TernaryTrie *trie, const char *string) { } else { res.child->type = 0; - return true; + goto end; } ttnode_free(res.child); @@ -313,7 +378,10 @@ bool ternarytrie_remove(TernaryTrie *trie, const char *string) { res.child->type = 0; } - return true; +end: + pthread_rwlock_unlock(&trie->lock); + + return return_value; } /**