feat: added randomly generated URLs
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
parent
94f7400169
commit
51fc2867a8
|
@ -0,0 +1,5 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*.{c,cpp,h}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
2
Makefile
2
Makefile
|
@ -31,7 +31,7 @@ prod: cmake-release
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build
|
run: build
|
||||||
@ LANDER_API_KEY=test ./build/Debug/lander
|
@ LANDER_BASE_URL=http://localhost:18080/ LANDER_API_KEY=test ./build/Debug/lander
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -6,10 +6,9 @@ URL=http://localhost:18080
|
||||||
if [ "$1" = add ]; then
|
if [ "$1" = add ]; then
|
||||||
curl \
|
curl \
|
||||||
-XPOST \
|
-XPOST \
|
||||||
-d "$3" \
|
-d "$2" \
|
||||||
-H "X-Api-Key: $API_KEY" \
|
-H "X-Api-Key: $API_KEY" \
|
||||||
"$URL/$2"
|
"$URL/$3"
|
||||||
echo "$URL/$2"
|
|
||||||
|
|
||||||
elif [ "$1" = get ]; then
|
elif [ "$1" = get ]; then
|
||||||
curl -is "$URL/$2" | grep -Po '(?<=location: ).*'
|
curl -is "$URL/$2" | grep -Po '(?<=location: ).*'
|
||||||
|
|
65
src/main.cpp
65
src/main.cpp
|
@ -4,15 +4,23 @@ extern "C" {
|
||||||
#include "ternarytrie.h"
|
#include "ternarytrie.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
#define ENV(var, env_var) \
|
||||||
// Read in API key
|
const char *var = getenv(env_var); \
|
||||||
char *api_key = getenv("LANDER_API_KEY");
|
if (var == NULL) { \
|
||||||
|
printf("Missing environment variable %s.\n", env_var); \
|
||||||
if (api_key == NULL) {
|
return 1; \
|
||||||
printf("No API key provided.");
|
|
||||||
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();
|
TernaryTrie *trie = ternarytrie_init();
|
||||||
|
|
||||||
std::string file_path = "lander.data";
|
std::string file_path = "lander.data";
|
||||||
|
@ -20,24 +28,8 @@ int main() {
|
||||||
|
|
||||||
crow::SimpleApp app;
|
crow::SimpleApp app;
|
||||||
|
|
||||||
CROW_ROUTE(app, "/<string>")
|
app.loglevel(crow::LogLevel::Warning);
|
||||||
.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");
|
|
||||||
|
|
||||||
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, "/<string>")
|
CROW_ROUTE(app, "/<string>")
|
||||||
.methods(crow::HTTPMethod::Get)(
|
.methods(crow::HTTPMethod::Get)(
|
||||||
[trie](crow::response &res, std::string s) {
|
[trie](crow::response &res, std::string s) {
|
||||||
|
@ -51,6 +43,31 @@ int main() {
|
||||||
|
|
||||||
res.end();
|
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, "/<string>")
|
||||||
|
.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();
|
app.port(18080).multithreaded().run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,12 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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.
|
* 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);
|
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.
|
* Remove a string from this trie.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
#include "ternarytrie.h"
|
|
||||||
#include "ternarytrie_node.c"
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "ternarytrie.h"
|
||||||
|
#include "ternarytrie_node.c"
|
||||||
|
|
||||||
typedef struct ttrie {
|
typedef struct ttrie {
|
||||||
TernaryTrieNode *root;
|
TernaryTrieNode *root;
|
||||||
size_t size;
|
size_t size;
|
||||||
char* file_path;
|
char* file_path;
|
||||||
|
pthread_rwlock_t lock;
|
||||||
} TernaryTrie;
|
} TernaryTrie;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,10 +20,11 @@ typedef struct ttrie {
|
||||||
* @return pointer to the empty TernaryTrie
|
* @return pointer to the empty TernaryTrie
|
||||||
*/
|
*/
|
||||||
TernaryTrie *ternarytrie_init() {
|
TernaryTrie *ternarytrie_init() {
|
||||||
TernaryTrie *node = calloc(1, sizeof(TernaryTrie));
|
TernaryTrie *trie = calloc(1, sizeof(TernaryTrie));
|
||||||
node->root = ttnode_init();
|
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);
|
bool ternarytrie_add_internal(TernaryTrie *trie, const char *string, const char *payload);
|
||||||
|
|
||||||
void ternarytrie_populate(TernaryTrie *trie, const char *file_path) {
|
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
|
// TODO properly handle this
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
return;
|
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)
|
// Split the buffer into two strings, the key and the payload
|
||||||
char buffer[8192];
|
buffer[i] = '\0';
|
||||||
int i, j;
|
|
||||||
|
|
||||||
while (fgets(buffer, 8192, fp)) {
|
j = i + 1;
|
||||||
printf("%s", buffer);
|
|
||||||
// Find index of space character
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
while (buffer[i] != ' ') {
|
// Now remove the newline character
|
||||||
i++;
|
while (buffer[j] != '\n') {
|
||||||
}
|
j++;
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
buffer[j] = '\0';
|
||||||
|
|
||||||
|
ternarytrie_add_internal(trie, buffer, buffer + i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct searchresult {
|
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
|
* @return true if the string is present in the trie, false otherwise
|
||||||
*/
|
*/
|
||||||
char * ternarytrie_search(TernaryTrie *trie, const char *string) {
|
char * ternarytrie_search(TernaryTrie *trie, const char *string) {
|
||||||
|
pthread_rwlock_rdlock(&trie->lock);
|
||||||
|
|
||||||
SearchResult res = ternarytrie_search_node(trie, string);
|
SearchResult res = ternarytrie_search_node(trie, string);
|
||||||
|
|
||||||
|
char* return_value = NULL;
|
||||||
|
|
||||||
if (res.child != 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ternarytrie_add(TernaryTrie *trie, const char *string, const char *payload) {
|
bool ternarytrie_add_persistent(TernaryTrie *trie, const char *string, const char *payload) {
|
||||||
if (trie->file_path != NULL) {
|
bool return_value = false;
|
||||||
// Easiest way to make sure we don't add duplicate entries
|
|
||||||
if (ternarytrie_search(trie, string) != NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *fp = fopen(trie->file_path, "a");
|
if (trie->file_path != NULL) {
|
||||||
|
// Easiest way to make sure we don't add duplicate entries
|
||||||
if (fp == NULL) {
|
// We use an internal function that doesn't require a read lock, as we're
|
||||||
return false;
|
// already inside a write lock
|
||||||
}
|
if (ternarytrie_search_node(trie, string).child != NULL) {
|
||||||
|
return false;
|
||||||
fputs(string, fp);
|
|
||||||
fputs(" ", fp);
|
|
||||||
fputs(payload, fp);
|
|
||||||
fputs("\n", fp);
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* @return true if the string was in the trie and thus removed, false otherwise
|
||||||
*/
|
*/
|
||||||
bool ternarytrie_remove(TernaryTrie *trie, const char *string) {
|
bool ternarytrie_remove(TernaryTrie *trie, const char *string) {
|
||||||
|
pthread_rwlock_wrlock(&trie->lock);
|
||||||
|
|
||||||
|
bool return_value = false;
|
||||||
|
|
||||||
SearchResult res = ternarytrie_search_node(trie, string);
|
SearchResult res = ternarytrie_search_node(trie, string);
|
||||||
|
|
||||||
if (res.child == NULL) {
|
if (res.child == NULL) {
|
||||||
return false;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
trie->size--;
|
trie->size--;
|
||||||
|
return_value = true;
|
||||||
|
|
||||||
if (res.parent != NULL) {
|
if (res.parent != NULL) {
|
||||||
// We're removing a full leaf, so we calculate the offset of the character
|
// 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 {
|
} else {
|
||||||
res.child->type = 0;
|
res.child->type = 0;
|
||||||
|
|
||||||
return true;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
ttnode_free(res.child);
|
ttnode_free(res.child);
|
||||||
|
@ -313,7 +378,10 @@ bool ternarytrie_remove(TernaryTrie *trie, const char *string) {
|
||||||
res.child->type = 0;
|
res.child->type = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
end:
|
||||||
|
pthread_rwlock_unlock(&trie->lock);
|
||||||
|
|
||||||
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue