Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parsing query parameters #4

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
src/*.o
*.o
.cache/
*swp*
76 changes: 76 additions & 0 deletions include/Parameters.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#ifndef PARAMETERS_H
#define PARAMETERS_H

#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

/*
* Implementation notes: (22 Jan 2023)
*
* Parameters will be stored as an array sorted on-demand.
* If calling paramGet and it's unsorted, it will sort automatically.
* If calling paramGet and it's already sorted, it doesn't touch it.
* Adding a new parameter will toggle the is_sorted to false.
*
* I was considering using the tree implementation of Route but I think
* it would make more sense to move the logic into its own data structure
* that can be used across different components (like Route & Parameter)
* because otherwise it would be confusing if we had a Route struct inside
* of a Parameter. For now, I'll use this array structure.
*
*/

struct Parameter
{
char* key;
char* value;
};

struct ParameterArray
{
struct Parameter * parameters;
bool is_sorted;
size_t n_members;
size_t capacity;
};

// Initialise array of parameters with capacity.
// Returns pointer to array on success, NULL on failure
struct ParameterArray * paramInit(const size_t capacity);

// Parse parameters from a string (e.g., when receiving ROUTE url).
// Takes an existing Parameter pointer and adds new parameters parsed from string.
// This will MODIFY the url inplace to remove all the parameter text before it's passed
// on to render a static template.
void paramParse(
struct ParameterArray * params,
char* url);

// Clear all keys & values and set n_members back to zero.
// This allows us to reuse the struct for multiple requests.
void paramClear(struct ParameterArray * params);

// Add new parameter to parameter array.
// Returns true on success and false on failure (e.g., memory allocation)
bool paramAdd(
struct ParameterArray * params,
const char* key,
const char* value);

// Sort parameters by keys ascending. This is primarily used internally
// but can be used externally if needed for whatever reason.
void paramSort(struct ParameterArray * params);

// Return a pointer to a parameter with a given key.
// Any modifications done will be reflected in parameter array.
struct Parameter * paramGet(
struct ParameterArray * params,
const char* key);

// Free all memory consumed by parameters
void paramFree(struct ParameterArray * params);

#endif
175 changes: 175 additions & 0 deletions src/Parameters.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#include "Parameters.h"

static void _print_critical()
{
printf("============ CRITICAL ============\n");
printf("Memory could not be allocated for parameters.\n");
}

struct ParameterArray * paramInit(const size_t capacity)
{
struct ParameterArray * params = malloc(sizeof(struct ParameterArray));

if (!params)
{
_print_critical();
return NULL;
}

params->parameters = calloc(capacity, sizeof(struct Parameter));

if (!params->parameters)
{
_print_critical();
return NULL;
}

params->is_sorted = false;
params->n_members = 0;
params->capacity = capacity;

for (size_t i = 0; i < capacity; ++i)
{
params->parameters[i].key = NULL;
params->parameters[i].value = NULL;
}

return params;
}

bool paramAdd(
struct ParameterArray * params,
const char* key,
const char* value)
{
const size_t i = params->n_members;

params->parameters[i].key = strdup(key);
params->parameters[i].value = strdup(value);

params->is_sorted = false;
params->n_members++;

// follow doubling rule
if (params->n_members == params->capacity)
{
void* alloc = realloc(params->parameters, params->capacity * 2 * sizeof(struct Parameter));
if (!alloc)
{
_print_critical();
return false;
}

params->capacity *= 2;
params->parameters = alloc;
}

return true;
}

static int key_cmp(const void* a, const void* b)
{
const struct Parameter * _a = a;
const struct Parameter * _b = b;

return strcmp(_a->key, _b->key);
}

void paramSort(struct ParameterArray * params)
{
qsort(params->parameters, params->n_members, sizeof(struct Parameter), &key_cmp);
params->is_sorted = true;
}

struct Parameter * paramGet(
struct ParameterArray * params,
const char* key)
{
if (!params->is_sorted)
paramSort(params);

struct Parameter search_key;
search_key.key = (char*)key; // try to silent warning about losing const-ness

return bsearch(&search_key, params->parameters, params->n_members, sizeof(struct Parameter), &key_cmp);

}

void paramParse(
struct ParameterArray * params,
char* url)
{
char* url_copy = strdup(url);
memset(url, 0, strlen(url));

// temporary large buffers to record the key/values before passing
// them to paramAdd
char key[1024] = {0};
char value[1024] = {0};

/*
* Parsing Rule:
* k = keys
* v = values
*/
char parsing_rule = 0;

for (size_t i = 0; i < strlen(url_copy); ++i)
{
switch (url_copy[i])
{
case '?':
parsing_rule = 'k';
continue;
case '=':
parsing_rule = 'v';
continue;
case '&':
paramAdd(params, key, value);
memset(key, 0, 1024);
memset(value, 0, 1024);
parsing_rule = 'k';
continue;
default:
if (parsing_rule == 0)
strncat(url, &url_copy[i], 1);
break;
}

switch (parsing_rule)
{
case 'k':
strncat(key, &url_copy[i], 1);
break;
case 'v':
strncat(value, &url_copy[i], 1);
break;
}
}

paramAdd(params, key, value);
free(url_copy);
}

void paramClear(struct ParameterArray * params)
{
for (size_t i = 0; i < params->n_members; ++i)
{
free(params->parameters[i].key);
params->parameters[i].key = NULL;

free(params->parameters[i].value);
params->parameters[i].value = NULL;
}

params->n_members = 0;
}

void paramFree(struct ParameterArray * params)
{
paramClear(params);
free(params->parameters);
params->parameters = NULL;
free(params);
params = NULL;
}
21 changes: 20 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "HTTP_Server.h"
#include "Routes.h"
#include "Response.h"
#include "Parameters.h"

int main() {
// initiate HTTP_Server
Expand All @@ -23,9 +24,12 @@ int main() {
struct Route * route = initRoute("/", "index.html");
addRoute(route, "/about", "about.html");

// create dynamic parameters array
struct ParameterArray * params = paramInit(10);


printf("\n====================================\n");
printf("=========ALL VAILABLE ROUTES========\n");
printf("=========ALL AVAILABLE ROUTES========\n");
// display all available routes
inorder(route);

Expand Down Expand Up @@ -64,6 +68,21 @@ int main() {
printf("The method is %s\n", method);
printf("The route is %s\n", urlRoute);

if (strlen(urlRoute) > 0)
{
paramParse(params, urlRoute);

printf("*** PARSED PARAMETERS ***\n");
for (size_t i = 0; i < params->n_members; ++i)
{
printf("KEY: %s\n", params->parameters[i].key);
printf("VALUE: %s\n\n", params->parameters[i].value);
}
// do something with parameters and can clear them to re-use for the next
// request
paramClear(params);
}


char template[100] = "";

Expand Down
4 changes: 4 additions & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# this is not a general makefile but something quick and dirty so I can actually run the debugger
# (I suck at raw makefiles, too much time using CMake that I'm out of touch :L)
all:
gcc -g -O0 param_parser.c ../src/Parameters.c -I../include -o param_parser
Binary file added test/param_parser
Binary file not shown.
41 changes: 41 additions & 0 deletions test/param_parser.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "Parameters.h"

/*
* Just testing the functionality locally so I don't have to
* keep running/killing the server over and over.
*
* These could be converted to "proper" tests but for now
* they are purely visual.
*/
int main()
{
char* url = strdup("/someNested/route?name=Zach&val=100&jimmy=John");
struct ParameterArray * params = paramInit(10);
paramParse(params, url);
printf("PARSED PARAMS:\n\n");
for (size_t i = 0; i < params->n_members; ++i)
{
printf("KEY: %s\n", params->parameters[i].key);
printf("VALUE: %s\n\n", params->parameters[i].value);
}
printf("CLEANED URL: %s\n\n", url);

struct Parameter* search = NULL;

printf("SEARCH KEY: name\n");
search = paramGet(params, "name");
printf("FOUND VALUE: %s\n\n", search->value);

printf("SEARCH KEY: val\n");
search = paramGet(params, "val");
printf("FOUND VALUE: %s\n\n", search->value);

printf("SEARCH KEY: xyz\n");
search = paramGet(params, "xyz");
if (!search)
printf("KEY DOESN'T EXIST\n\n");

paramFree(params);
free(url);
return 0;
}