Move config-related code from http.c to config.c
Also make config-related code more generic and improve unit tests of config parsing.
This commit is contained in:
parent
edffbf0656
commit
f5507c8412
6 changed files with 619 additions and 397 deletions
1
Makefile
1
Makefile
|
@ -17,6 +17,7 @@ SOURCES = \
|
||||||
src/signal_handling.c \
|
src/signal_handling.c \
|
||||||
src/globals.c \
|
src/globals.c \
|
||||||
src/http.c \
|
src/http.c \
|
||||||
|
src/config.c \
|
||||||
third_party/SimpleArchiver/src/helpers.c \
|
third_party/SimpleArchiver/src/helpers.c \
|
||||||
third_party/SimpleArchiver/src/data_structures/linked_list.c \
|
third_party/SimpleArchiver/src/data_structures/linked_list.c \
|
||||||
third_party/SimpleArchiver/src/data_structures/hash_map.c \
|
third_party/SimpleArchiver/src/data_structures/hash_map.c \
|
||||||
|
|
493
src/config.c
Normal file
493
src/config.c
Normal file
|
@ -0,0 +1,493 @@
|
||||||
|
// ISC License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2024 Stephen Seo
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
// Standard library includes.
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Third party includes.
|
||||||
|
#include <SimpleArchiver/src/helpers.h>
|
||||||
|
|
||||||
|
// Local includes
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#define SINGLE_QUOTE_DECREMENT() \
|
||||||
|
for(; single_quote_count > 0; --single_quote_count) { \
|
||||||
|
if ((state & 1) == 0) { \
|
||||||
|
key_buf[key_idx++] = '\''; \
|
||||||
|
if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
||||||
|
fprintf(stderr, \
|
||||||
|
"ERROR: config file \"key\" is larger than %u bytes!\n", \
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
||||||
|
c_simple_http_clean_up_parsed_config(&config); \
|
||||||
|
config.hash_map = NULL; \
|
||||||
|
return config; \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
value_buf[value_idx++] = '\''; \
|
||||||
|
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
||||||
|
fprintf(stderr, \
|
||||||
|
"ERROR: config file \"value\" is larger than %u bytes!\n", \
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
||||||
|
c_simple_http_clean_up_parsed_config(&config); \
|
||||||
|
config.hash_map = NULL; \
|
||||||
|
return config; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DOUBLE_QUOTE_DECREMENT() \
|
||||||
|
for(; double_quote_count > 0; --double_quote_count) { \
|
||||||
|
if ((state & 1) == 0) { \
|
||||||
|
key_buf[key_idx++] = '"'; \
|
||||||
|
if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
||||||
|
fprintf(stderr, \
|
||||||
|
"ERROR: config file \"key\" is larger than %u bytes!\n", \
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
||||||
|
c_simple_http_clean_up_parsed_config(&config); \
|
||||||
|
config.hash_map = NULL; \
|
||||||
|
return config; \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
value_buf[value_idx++] = '"'; \
|
||||||
|
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
||||||
|
fprintf(stderr, \
|
||||||
|
"ERROR: config file \"value\" is larger than %u bytes!\n", \
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
||||||
|
c_simple_http_clean_up_parsed_config(&config); \
|
||||||
|
config.hash_map = NULL; \
|
||||||
|
return config; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_simple_http_hash_map_wrapper_cleanup_hashmap_fn(void *data) {
|
||||||
|
C_SIMPLE_HTTP_HashMapWrapper *wrapper = data;
|
||||||
|
simple_archiver_hash_map_free(&wrapper->paths);
|
||||||
|
free(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_simple_http_hash_map_wrapper_cleanup(C_SIMPLE_HTTP_HashMapWrapper *wrapper) {
|
||||||
|
simple_archiver_hash_map_free(&wrapper->paths);
|
||||||
|
free(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_simple_http_hash_map_cleanup_helper(SDArchiverHashMap *hash_map) {
|
||||||
|
simple_archiver_hash_map_free(&hash_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredIter {
|
||||||
|
SDArchiverHashMap *hash_map;
|
||||||
|
const char *path;
|
||||||
|
} C_SIMPLE_HTTP_INTERNAL_RequiredIter;
|
||||||
|
|
||||||
|
int c_simple_http_required_iter_fn(void *data, void *ud) {
|
||||||
|
C_SIMPLE_HTTP_INTERNAL_RequiredIter *req_iter_struct = ud;
|
||||||
|
if (simple_archiver_hash_map_get(
|
||||||
|
req_iter_struct->hash_map,
|
||||||
|
data,
|
||||||
|
strlen(data) + 1) == NULL) {
|
||||||
|
if (req_iter_struct->path) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING: Map of PATH \"%s\" does not have required key \"%s\"!\n",
|
||||||
|
req_iter_struct->path,
|
||||||
|
(char*)data);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING: Map does not have required key \"%s\"!\n",
|
||||||
|
(char*)data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredCheck {
|
||||||
|
SDArchiverHashMap *map_of_paths_and_their_vars;
|
||||||
|
SDArchiverLinkedList *required;
|
||||||
|
} C_SIMPLE_HTTP_INTERNAL_RequiredCheck;
|
||||||
|
|
||||||
|
int c_simple_http_check_required_iter_fn(void *path_void_str, void *ud) {
|
||||||
|
C_SIMPLE_HTTP_INTERNAL_RequiredCheck *req = ud;
|
||||||
|
C_SIMPLE_HTTP_HashMapWrapper *wrapper =
|
||||||
|
simple_archiver_hash_map_get(
|
||||||
|
req->map_of_paths_and_their_vars,
|
||||||
|
path_void_str,
|
||||||
|
strlen(path_void_str) + 1);
|
||||||
|
if (!wrapper) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING: Map of paths does not have path \"%s\"!\n",
|
||||||
|
(char*)path_void_str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
C_SIMPLE_HTTP_INTERNAL_RequiredIter req_iter_struct;
|
||||||
|
req_iter_struct.hash_map = wrapper->hash_map;
|
||||||
|
req_iter_struct.path = path_void_str;
|
||||||
|
return simple_archiver_list_get(
|
||||||
|
req->required,
|
||||||
|
c_simple_http_required_iter_fn,
|
||||||
|
&req_iter_struct) != NULL ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
|
||||||
|
const char *config_filename,
|
||||||
|
const char *separating_key,
|
||||||
|
SDArchiverLinkedList *required_names
|
||||||
|
) {
|
||||||
|
C_SIMPLE_HTTP_ParsedConfig config;
|
||||||
|
config.hash_map = NULL;
|
||||||
|
|
||||||
|
if (!config_filename) {
|
||||||
|
fprintf(stderr, "ERROR: config_filename argument is NULL!\n");
|
||||||
|
return config;
|
||||||
|
} else if (!separating_key) {
|
||||||
|
fprintf(stderr, "ERROR: separating_key argument is NULL!\n");
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
const unsigned int separating_key_size = strlen(separating_key) + 1;
|
||||||
|
|
||||||
|
config.hash_map = simple_archiver_hash_map_init();
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
|
SDArchiverLinkedList *paths = simple_archiver_list_init();
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *f = fopen(config_filename, "r");
|
||||||
|
unsigned char key_buf[C_SIMPLE_HTTP_CONFIG_BUF_SIZE];
|
||||||
|
unsigned char value_buf[C_SIMPLE_HTTP_CONFIG_BUF_SIZE];
|
||||||
|
unsigned int key_idx = 0;
|
||||||
|
unsigned int value_idx = 0;
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *current_separating_key_value = NULL;
|
||||||
|
unsigned int current_separating_key_value_size = 0;
|
||||||
|
|
||||||
|
// xxx0 - reading key
|
||||||
|
// xxx1 - reading value
|
||||||
|
// 00xx - reading value is not quoted
|
||||||
|
// 01xx - reading value is single quoted
|
||||||
|
// 10xx - reading value is double quoted
|
||||||
|
unsigned int state = 0;
|
||||||
|
unsigned char single_quote_count = 0;
|
||||||
|
unsigned char double_quote_count = 0;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while (feof(f) == 0) {
|
||||||
|
c = fgetc(f);
|
||||||
|
if (c == EOF) {
|
||||||
|
break;
|
||||||
|
} else if ((state & 0xC) == 0 && (c == ' ' || c == '\t')) {
|
||||||
|
// Ignore whitespace when not quoted.
|
||||||
|
SINGLE_QUOTE_DECREMENT();
|
||||||
|
DOUBLE_QUOTE_DECREMENT();
|
||||||
|
continue;
|
||||||
|
} else if ((state & 1) == 0
|
||||||
|
&& (state & 0xC) == 0
|
||||||
|
&& (c == '\r' || c == '\n')) {
|
||||||
|
// Ignore newlines when parsing for key and when not quoted.
|
||||||
|
SINGLE_QUOTE_DECREMENT();
|
||||||
|
DOUBLE_QUOTE_DECREMENT();
|
||||||
|
continue;
|
||||||
|
} else if ((state & 1) == 1) {
|
||||||
|
if (c == '\'') {
|
||||||
|
++single_quote_count;
|
||||||
|
DOUBLE_QUOTE_DECREMENT();
|
||||||
|
|
||||||
|
if (((state & 0xC) == 0x4 || (state & 0xC) == 0)
|
||||||
|
&& single_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) {
|
||||||
|
single_quote_count = 0;
|
||||||
|
|
||||||
|
// (state & 0xC) is known to be either 0x4 or 0.
|
||||||
|
if ((state & 0xC) == 0) {
|
||||||
|
state |= 0x4;
|
||||||
|
} else {
|
||||||
|
state &= 0xFFFFFFF3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (c == '"') {
|
||||||
|
++double_quote_count;
|
||||||
|
SINGLE_QUOTE_DECREMENT();
|
||||||
|
|
||||||
|
if (((state & 0xC) == 0x8 || (state & 0xC) == 0)
|
||||||
|
&& double_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) {
|
||||||
|
double_quote_count = 0;
|
||||||
|
|
||||||
|
// (state & 0xC) is known to be either 0x8 or 0.
|
||||||
|
if ((state & 0xC) == 0) {
|
||||||
|
state |= 0x8;
|
||||||
|
} else {
|
||||||
|
state &= 0xFFFFFFF3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
SINGLE_QUOTE_DECREMENT();
|
||||||
|
DOUBLE_QUOTE_DECREMENT();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((state & 1) == 0) {
|
||||||
|
if (c != '=') {
|
||||||
|
key_buf[key_idx++] = c;
|
||||||
|
if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: config file \"key\" is larger than %u bytes!\n",
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (key_idx < C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
||||||
|
key_buf[key_idx++] = 0;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: config file \"key\" is larger than %u bytes!\n",
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
state |= 1;
|
||||||
|
}
|
||||||
|
} else if ((state & 1) == 1) {
|
||||||
|
if ((c != '\n' && c != '\r') || (state & 0xC) != 0) {
|
||||||
|
value_buf[value_idx++] = c;
|
||||||
|
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: config file \"value\" is larger than %u bytes!\n",
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (value_idx < C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
||||||
|
value_buf[value_idx++] = 0;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: config file \"value\" is larger than %u bytes!\n",
|
||||||
|
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
state &= 0xFFFFFFFE;
|
||||||
|
|
||||||
|
// Check if key is separating_key.
|
||||||
|
if (strcmp((char*)key_buf, separating_key) == 0) {
|
||||||
|
if (current_separating_key_value) {
|
||||||
|
if (required_names) {
|
||||||
|
C_SIMPLE_HTTP_HashMapWrapper *hash_map_wrapper =
|
||||||
|
simple_archiver_hash_map_get(
|
||||||
|
config.hash_map,
|
||||||
|
current_separating_key_value,
|
||||||
|
current_separating_key_value_size);
|
||||||
|
C_SIMPLE_HTTP_INTERNAL_RequiredIter req_iter_struct;
|
||||||
|
req_iter_struct.hash_map = hash_map_wrapper->hash_map;
|
||||||
|
if (paths->count != 0) {
|
||||||
|
req_iter_struct.path = paths->tail->prev->data;
|
||||||
|
} else {
|
||||||
|
req_iter_struct.path = NULL;
|
||||||
|
}
|
||||||
|
const char *missing_key = simple_archiver_list_get(
|
||||||
|
required_names,
|
||||||
|
c_simple_http_required_iter_fn,
|
||||||
|
&req_iter_struct);
|
||||||
|
if (missing_key) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING: config file did not have required key \"%s\"!"
|
||||||
|
" Returning NULL map!\n",
|
||||||
|
missing_key);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state &= 0xFFFFFFFD;
|
||||||
|
free(current_separating_key_value);
|
||||||
|
}
|
||||||
|
current_separating_key_value = malloc(value_idx);
|
||||||
|
memcpy(current_separating_key_value, value_buf, value_idx);
|
||||||
|
current_separating_key_value_size = value_idx;
|
||||||
|
// At this point, key is separating_key.
|
||||||
|
SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
|
||||||
|
unsigned char *key = malloc(separating_key_size);
|
||||||
|
strncpy((char*)key, separating_key, separating_key_size);
|
||||||
|
unsigned char *value = malloc(value_idx);
|
||||||
|
memcpy(value, value_buf, value_idx);
|
||||||
|
if (simple_archiver_hash_map_insert(&hash_map,
|
||||||
|
value,
|
||||||
|
key,
|
||||||
|
separating_key_size,
|
||||||
|
NULL,
|
||||||
|
NULL) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: Failed to create hash map for new separating_key "
|
||||||
|
"block!\n");
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
C_SIMPLE_HTTP_HashMapWrapper *wrapper = malloc(sizeof(C_SIMPLE_HTTP_HashMapWrapper));
|
||||||
|
wrapper->paths = hash_map;
|
||||||
|
|
||||||
|
if (simple_archiver_hash_map_insert(
|
||||||
|
&config.hash_map,
|
||||||
|
wrapper,
|
||||||
|
value,
|
||||||
|
value_idx,
|
||||||
|
c_simple_http_hash_map_wrapper_cleanup_hashmap_fn,
|
||||||
|
simple_archiver_helper_datastructure_cleanup_nop) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: Failed to insert new hash map for new PATH block!\n");
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
c_simple_http_hash_map_wrapper_cleanup(wrapper);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
simple_archiver_list_add(paths, value,
|
||||||
|
simple_archiver_helper_datastructure_cleanup_nop);
|
||||||
|
} else if (!current_separating_key_value) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR: config file has invalid key: No preceding \"%s\" "
|
||||||
|
"key!\n", separating_key);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
} else {
|
||||||
|
// Non-separating_key key.
|
||||||
|
C_SIMPLE_HTTP_HashMapWrapper *hash_map_wrapper =
|
||||||
|
simple_archiver_hash_map_get(
|
||||||
|
config.hash_map,
|
||||||
|
current_separating_key_value,
|
||||||
|
current_separating_key_value_size);
|
||||||
|
if (!hash_map_wrapper) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: Internal error failed to get existing hash map with path "
|
||||||
|
"\"%s\"!", current_separating_key_value);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *key = malloc(key_idx);
|
||||||
|
memcpy(key, key_buf, key_idx);
|
||||||
|
unsigned char *value;
|
||||||
|
if (strcmp((char*)key_buf, "HTML_FILE") == 0) {
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *html_file = fopen((char*)value_buf, "r");
|
||||||
|
if (!html_file) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: Internal error failed to open HTML_FILE \"%s\"!",
|
||||||
|
value_buf);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(html_file, 0, SEEK_END);
|
||||||
|
long file_size = ftell(html_file);
|
||||||
|
if (file_size <= 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: Internal error HTML_FILE \"%s\" is invalid size!",
|
||||||
|
value_buf);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
fseek(html_file, 0, SEEK_SET);
|
||||||
|
unsigned long file_size_u = file_size;
|
||||||
|
|
||||||
|
unsigned char *read_buf = malloc(file_size_u);
|
||||||
|
size_t read_amount = 0;
|
||||||
|
read_amount = fread(read_buf, 1, file_size_u, html_file);
|
||||||
|
if (read_amount != file_size_u) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to read HTML_FILE \"%s\"!\n",
|
||||||
|
value_buf);
|
||||||
|
free(read_buf);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
value = read_buf;
|
||||||
|
} else {
|
||||||
|
value = malloc(value_idx);
|
||||||
|
memcpy(value, value_buf, value_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simple_archiver_hash_map_insert(&hash_map_wrapper->paths,
|
||||||
|
value,
|
||||||
|
key,
|
||||||
|
key_idx,
|
||||||
|
NULL,
|
||||||
|
NULL) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: Internal error failed to insert into hash map with path "
|
||||||
|
"\"%s\"!", current_separating_key_value);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key_idx = 0;
|
||||||
|
value_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simple_archiver_helper_cleanup_FILE(&f);
|
||||||
|
|
||||||
|
if (!current_separating_key_value) {
|
||||||
|
fprintf(stderr, "ERROR: Never got \"PATH\" key in config!\n");
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
} else if (required_names) {
|
||||||
|
C_SIMPLE_HTTP_INTERNAL_RequiredCheck required_check_struct;
|
||||||
|
required_check_struct.map_of_paths_and_their_vars = config.hash_map;
|
||||||
|
required_check_struct.required = required_names;
|
||||||
|
const char *path_with_missing_key = simple_archiver_list_get(
|
||||||
|
paths,
|
||||||
|
c_simple_http_check_required_iter_fn,
|
||||||
|
&required_check_struct);
|
||||||
|
if (path_with_missing_key) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING (parse end): config file with path \"%s\" did not have "
|
||||||
|
"required key(s)! Returning NULL map!\n",
|
||||||
|
path_with_missing_key);
|
||||||
|
c_simple_http_clean_up_parsed_config(&config);
|
||||||
|
config.hash_map = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_simple_http_clean_up_parsed_config(C_SIMPLE_HTTP_ParsedConfig *config) {
|
||||||
|
simple_archiver_hash_map_free(&config->paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: ts=2 sts=2 sw=2
|
58
src/config.h
Normal file
58
src/config.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// ISC License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2024 Stephen Seo
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_CONFIG_H_
|
||||||
|
#define SEODISPARATE_COM_C_SIMPLE_HTTP_CONFIG_H_
|
||||||
|
|
||||||
|
// Third party includes.
|
||||||
|
#include <SimpleArchiver/src/data_structures/linked_list.h>
|
||||||
|
#include <SimpleArchiver/src/data_structures/hash_map.h>
|
||||||
|
|
||||||
|
typedef struct C_SIMPLE_HTTP_ParsedConfig {
|
||||||
|
/// Each entry in this data structure is a hash map where its value for the
|
||||||
|
/// key "PATH" is the path it represents. The "key" value should match the
|
||||||
|
/// mentioned value for "PATH".
|
||||||
|
union {
|
||||||
|
SDArchiverHashMap *paths;
|
||||||
|
SDArchiverHashMap *hash_map;
|
||||||
|
};
|
||||||
|
} C_SIMPLE_HTTP_ParsedConfig;
|
||||||
|
|
||||||
|
typedef C_SIMPLE_HTTP_ParsedConfig C_SIMPLE_HTTP_HashMapWrapper;
|
||||||
|
|
||||||
|
/// Each line in the config should be a key-value pair separated by an equals
|
||||||
|
/// sign. All whitespace is ignored unless if the value is quoted. A part of a
|
||||||
|
/// string can be "quoted" if it is surrounded by three single-quotes or three
|
||||||
|
/// double-quotes. If there exists a line with the key "PATH", then the value
|
||||||
|
/// must be a path like "/cache" and all the following key-value pairs are
|
||||||
|
/// associated with that PATH until the next "PATH" key or EOF. Each "PATH"
|
||||||
|
/// "block" should have a "HTML" key-value pair where the value is a HTML
|
||||||
|
/// template. Inside such HTML templates should be strings like
|
||||||
|
/// "{{{{example_key}}}}" which will be replaced by the string value of the key
|
||||||
|
/// name deliminated by the four curly braces. "HTML_FILE" specifies a filename
|
||||||
|
/// to read instead of using a literal string in the config file. It will store
|
||||||
|
/// the contents of the specified file with the "HTML" key internally.
|
||||||
|
C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
|
||||||
|
const char *config_filename,
|
||||||
|
const char *separating_key,
|
||||||
|
SDArchiverLinkedList *required_names
|
||||||
|
);
|
||||||
|
|
||||||
|
void c_simple_http_clean_up_parsed_config(C_SIMPLE_HTTP_ParsedConfig *config);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// vim: ts=2 sts=2 sw=2
|
371
src/http.c
371
src/http.c
|
@ -21,375 +21,12 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// Local includes
|
// Local includes
|
||||||
#include <SimpleArchiver/src/helpers.h>
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
#define SINGLE_QUOTE_DECREMENT() \
|
char *c_simple_http_request_response(
|
||||||
for(; single_quote_count > 0; --single_quote_count) { \
|
const char *request,
|
||||||
if ((state & 1) == 0) { \
|
unsigned int size,
|
||||||
key_buf[key_idx++] = '\''; \
|
const C_SIMPLE_HTTP_HTTPTemplates *templates) {
|
||||||
if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
|
||||||
fprintf(stderr, \
|
|
||||||
"ERROR: config file \"key\" is larger than %u bytes!\n", \
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
|
||||||
c_simple_http_clean_up_templates(&templates); \
|
|
||||||
templates.paths = NULL; \
|
|
||||||
return templates; \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
value_buf[value_idx++] = '\''; \
|
|
||||||
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
|
||||||
fprintf(stderr, \
|
|
||||||
"ERROR: config file \"value\" is larger than %u bytes!\n", \
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
|
||||||
c_simple_http_clean_up_templates(&templates); \
|
|
||||||
templates.paths = NULL; \
|
|
||||||
return templates; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DOUBLE_QUOTE_DECREMENT() \
|
|
||||||
for(; double_quote_count > 0; --double_quote_count) { \
|
|
||||||
if ((state & 1) == 0) { \
|
|
||||||
key_buf[key_idx++] = '"'; \
|
|
||||||
if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
|
||||||
fprintf(stderr, \
|
|
||||||
"ERROR: config file \"key\" is larger than %u bytes!\n", \
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
|
||||||
c_simple_http_clean_up_templates(&templates); \
|
|
||||||
templates.paths = NULL; \
|
|
||||||
return templates; \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
value_buf[value_idx++] = '"'; \
|
|
||||||
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { \
|
|
||||||
fprintf(stderr, \
|
|
||||||
"ERROR: config file \"value\" is larger than %u bytes!\n", \
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); \
|
|
||||||
c_simple_http_clean_up_templates(&templates); \
|
|
||||||
templates.paths = NULL; \
|
|
||||||
return templates; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
void c_simple_http_hash_map_wrapper_cleanup_hashmap_fn(void *data) {
|
|
||||||
HashMapWrapper *wrapper = data;
|
|
||||||
simple_archiver_hash_map_free(&wrapper->paths);
|
|
||||||
free(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
void c_simple_http_hash_map_wrapper_cleanup(HashMapWrapper *wrapper) {
|
|
||||||
simple_archiver_hash_map_free(&wrapper->paths);
|
|
||||||
free(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
void c_simple_http_hash_map_cleanup_helper(SDArchiverHashMap *hash_map) {
|
|
||||||
simple_archiver_hash_map_free(&hash_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPTemplates c_simple_http_set_up_templates(const char *config_filename) {
|
|
||||||
HTTPTemplates templates;
|
|
||||||
templates.paths = simple_archiver_hash_map_init();
|
|
||||||
|
|
||||||
FILE *f = fopen(config_filename, "r");
|
|
||||||
unsigned char key_buf[C_SIMPLE_HTTP_CONFIG_BUF_SIZE];
|
|
||||||
unsigned char value_buf[C_SIMPLE_HTTP_CONFIG_BUF_SIZE];
|
|
||||||
unsigned int key_idx = 0;
|
|
||||||
unsigned int value_idx = 0;
|
|
||||||
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
|
||||||
char *current_path = NULL;
|
|
||||||
unsigned int current_path_size = 0;
|
|
||||||
|
|
||||||
// xxx0 - reading key
|
|
||||||
// xxx1 - reading value
|
|
||||||
// xx0x - does not have "HTML" key
|
|
||||||
// xx1x - does have "HTML" key
|
|
||||||
// 00xx - reading value is not quoted
|
|
||||||
// 01xx - reading value is single quoted
|
|
||||||
// 10xx - reading value is double quoted
|
|
||||||
unsigned int state = 0;
|
|
||||||
unsigned char single_quote_count = 0;
|
|
||||||
unsigned char double_quote_count = 0;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while (feof(f) == 0) {
|
|
||||||
c = fgetc(f);
|
|
||||||
if (c == EOF) {
|
|
||||||
break;
|
|
||||||
} else if ((state & 0xC) == 0 && (c == ' ' || c == '\t')) {
|
|
||||||
// Ignore whitespace when not quoted.
|
|
||||||
SINGLE_QUOTE_DECREMENT();
|
|
||||||
DOUBLE_QUOTE_DECREMENT();
|
|
||||||
continue;
|
|
||||||
} else if ((state & 1) == 0
|
|
||||||
&& (state & 0xC) == 0
|
|
||||||
&& (c == '\r' || c == '\n')) {
|
|
||||||
// Ignore newlines when parsing for key and when not quoted.
|
|
||||||
SINGLE_QUOTE_DECREMENT();
|
|
||||||
DOUBLE_QUOTE_DECREMENT();
|
|
||||||
continue;
|
|
||||||
} else if ((state & 1) == 1) {
|
|
||||||
if (c == '\'') {
|
|
||||||
++single_quote_count;
|
|
||||||
DOUBLE_QUOTE_DECREMENT();
|
|
||||||
|
|
||||||
if (((state & 0xC) == 0x4 || (state & 0xC) == 0)
|
|
||||||
&& single_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) {
|
|
||||||
single_quote_count = 0;
|
|
||||||
|
|
||||||
// (state & 0xC) is known to be either 0x4 or 0.
|
|
||||||
if ((state & 0xC) == 0) {
|
|
||||||
state |= 0x4;
|
|
||||||
} else {
|
|
||||||
state &= 0xFFFFFFF3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else if (c == '"') {
|
|
||||||
++double_quote_count;
|
|
||||||
SINGLE_QUOTE_DECREMENT();
|
|
||||||
|
|
||||||
if (((state & 0xC) == 0x8 || (state & 0xC) == 0)
|
|
||||||
&& double_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) {
|
|
||||||
double_quote_count = 0;
|
|
||||||
|
|
||||||
// (state & 0xC) is known to be either 0x8 or 0.
|
|
||||||
if ((state & 0xC) == 0) {
|
|
||||||
state |= 0x8;
|
|
||||||
} else {
|
|
||||||
state &= 0xFFFFFFF3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
SINGLE_QUOTE_DECREMENT();
|
|
||||||
DOUBLE_QUOTE_DECREMENT();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((state & 1) == 0) {
|
|
||||||
if (c != '=') {
|
|
||||||
key_buf[key_idx++] = c;
|
|
||||||
if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: config file \"key\" is larger than %u bytes!\n",
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (key_idx < C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
|
||||||
key_buf[key_idx++] = 0;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: config file \"key\" is larger than %u bytes!\n",
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
state |= 1;
|
|
||||||
}
|
|
||||||
} else if ((state & 1) == 1) {
|
|
||||||
if ((c != '\n' && c != '\r') || (state & 0xC) != 0) {
|
|
||||||
value_buf[value_idx++] = c;
|
|
||||||
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: config file \"value\" is larger than %u bytes!\n",
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (value_idx < C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
|
||||||
value_buf[value_idx++] = 0;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: config file \"value\" is larger than %u bytes!\n",
|
|
||||||
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
state &= 0xFFFFFFFE;
|
|
||||||
|
|
||||||
// Check if key is "PATH".
|
|
||||||
if (strcmp((char*)key_buf, "PATH") == 0) {
|
|
||||||
if (current_path) {
|
|
||||||
if ((state & 0x2) == 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: config file did not have \"HTML\" key for PATH!"
|
|
||||||
"\n");
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
state &= 0xFFFFFFFD;
|
|
||||||
free(current_path);
|
|
||||||
}
|
|
||||||
current_path = malloc(value_idx);
|
|
||||||
memcpy(current_path, value_buf, value_idx);
|
|
||||||
current_path_size = value_idx;
|
|
||||||
// At this point, key is "PATH".
|
|
||||||
SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
|
|
||||||
unsigned char *key = malloc(5);
|
|
||||||
key[0] = 'P';
|
|
||||||
key[1] = 'A';
|
|
||||||
key[2] = 'T';
|
|
||||||
key[3] = 'H';
|
|
||||||
key[4] = 0;
|
|
||||||
unsigned char *value = malloc(value_idx);
|
|
||||||
memcpy(value, value_buf, value_idx);
|
|
||||||
if (simple_archiver_hash_map_insert(&hash_map,
|
|
||||||
value,
|
|
||||||
key,
|
|
||||||
5,
|
|
||||||
NULL,
|
|
||||||
NULL) != 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Failed to create hash map for new PATH block!\n");
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
free(key);
|
|
||||||
free(value);
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMapWrapper *wrapper = malloc(sizeof(HashMapWrapper));
|
|
||||||
wrapper->paths = hash_map;
|
|
||||||
|
|
||||||
if (simple_archiver_hash_map_insert(
|
|
||||||
&templates.paths,
|
|
||||||
wrapper,
|
|
||||||
value,
|
|
||||||
value_idx,
|
|
||||||
c_simple_http_hash_map_wrapper_cleanup_hashmap_fn,
|
|
||||||
simple_archiver_helper_datastructure_cleanup_nop) != 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Failed to insert new hash map for new PATH block!\n");
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
c_simple_http_hash_map_wrapper_cleanup(wrapper);
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
} else if (!current_path) {
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"ERROR: config file has invalid key: No preceding \"PATH\" "
|
|
||||||
"key!\n");
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
} else {
|
|
||||||
// Non-"PATH" key.
|
|
||||||
if (strcmp((char*)key_buf, "HTML") == 0
|
|
||||||
|| strcmp((char*)key_buf, "HTML_FILE") == 0) {
|
|
||||||
state |= 0x2;
|
|
||||||
}
|
|
||||||
HashMapWrapper *hash_map_wrapper = simple_archiver_hash_map_get(
|
|
||||||
templates.paths,
|
|
||||||
current_path,
|
|
||||||
current_path_size);
|
|
||||||
if (!hash_map_wrapper) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Internal error failed to get existing hash map with path "
|
|
||||||
"\"%s\"!", current_path);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *key = malloc(key_idx);
|
|
||||||
memcpy(key, key_buf, key_idx);
|
|
||||||
unsigned char *value;
|
|
||||||
if (strcmp((char*)key_buf, "HTML_FILE") == 0) {
|
|
||||||
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
|
||||||
FILE *html_file = fopen((char*)value_buf, "r");
|
|
||||||
if (!html_file) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Internal error failed to open HTML_FILE \"%s\"!",
|
|
||||||
value_buf);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek(html_file, 0, SEEK_END);
|
|
||||||
long file_size = ftell(html_file);
|
|
||||||
if (file_size <= 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Internal error HTML_FILE \"%s\" is invalid size!",
|
|
||||||
value_buf);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
fseek(html_file, 0, SEEK_SET);
|
|
||||||
unsigned long file_size_u = file_size;
|
|
||||||
|
|
||||||
unsigned char *read_buf = malloc(file_size_u);
|
|
||||||
size_t read_amount = 0;
|
|
||||||
read_amount = fread(read_buf, 1, file_size_u, html_file);
|
|
||||||
if (read_amount != file_size_u) {
|
|
||||||
fprintf(stderr, "ERROR: Failed to read HTML_FILE \"%s\"!\n",
|
|
||||||
value_buf);
|
|
||||||
free(read_buf);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
value = read_buf;
|
|
||||||
} else {
|
|
||||||
value = malloc(value_idx);
|
|
||||||
memcpy(value, value_buf, value_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simple_archiver_hash_map_insert(&hash_map_wrapper->paths,
|
|
||||||
value,
|
|
||||||
key,
|
|
||||||
key_idx,
|
|
||||||
NULL,
|
|
||||||
NULL) != 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Internal error failed to insert into hash map with path "
|
|
||||||
"\"%s\"!", current_path);
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
free(key);
|
|
||||||
free(value);
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key_idx = 0;
|
|
||||||
value_idx = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
if (!current_path) {
|
|
||||||
fprintf(stderr, "ERROR: Never got \"PATH\" key in config!\n");
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
} else if ((state & 0x2) == 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: Current \"PATH\" did not get \"HTML\" key in config!\n");
|
|
||||||
c_simple_http_clean_up_templates(&templates);
|
|
||||||
templates.paths = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return templates;
|
|
||||||
}
|
|
||||||
|
|
||||||
void c_simple_http_clean_up_templates(HTTPTemplates *templates) {
|
|
||||||
simple_archiver_hash_map_free(&templates->paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *c_simple_http_request_response(const char *request, unsigned int size,
|
|
||||||
const HTTPTemplates *templates) {
|
|
||||||
// TODO
|
// TODO
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
34
src/http.h
34
src/http.h
|
@ -17,37 +17,21 @@
|
||||||
#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_H_
|
#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_H_
|
||||||
#define SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_H_
|
#define SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_H_
|
||||||
|
|
||||||
|
// Third party includes.
|
||||||
#include <SimpleArchiver/src/data_structures/hash_map.h>
|
#include <SimpleArchiver/src/data_structures/hash_map.h>
|
||||||
|
|
||||||
typedef struct HTTPTemplates {
|
// Local includes.
|
||||||
/// Each entry in this data structure is a hash map where its value for the
|
#include "config.h"
|
||||||
/// key "PATH" is the path it represents. The "key" value should match the
|
|
||||||
/// mentioned value for "PATH".
|
|
||||||
SDArchiverHashMap *paths;
|
|
||||||
} HTTPTemplates;
|
|
||||||
|
|
||||||
typedef HTTPTemplates HashMapWrapper;
|
typedef C_SIMPLE_HTTP_ParsedConfig C_SIMPLE_HTTP_HTTPTemplates;
|
||||||
|
|
||||||
/// Each line in the config should be a key-value pair separated by an equals
|
|
||||||
/// sign. All whitespace is ignored unless if the value is quoted. A part of a
|
|
||||||
/// string can be "quoted" if it is surrounded by three single-quotes or three
|
|
||||||
/// double-quotes. If there exists a line with the key "PATH", then the value
|
|
||||||
/// must be a path like "/cache" and all the following key-value pairs are
|
|
||||||
/// associated with that PATH until the next "PATH" key or EOF. Each "PATH"
|
|
||||||
/// "block" should have a "HTML" key-value pair where the value is a HTML
|
|
||||||
/// template. Inside such HTML templates should be strings like
|
|
||||||
/// "{{{{example_key}}}}" which will be replaced by the string value of the key
|
|
||||||
/// name deliminated by the four curly braces. "HTML_FILE" specifies a filename
|
|
||||||
/// to read instead of using a literal string in the config file. It will store
|
|
||||||
/// the contents of the specified file with the "HTML" key internally.
|
|
||||||
HTTPTemplates c_simple_http_set_up_templates(const char *config_filename);
|
|
||||||
|
|
||||||
void c_simple_http_clean_up_templates(HTTPTemplates *templates);
|
|
||||||
|
|
||||||
/// Returned buffer must be "free"d after use.
|
/// Returned buffer must be "free"d after use.
|
||||||
/// If the request is not valid, or 404, then the buffer will be NULL.
|
/// If the request is not valid, or 404, then the buffer will be NULL.
|
||||||
char *c_simple_http_request_response(const char *request, unsigned int size,
|
char *c_simple_http_request_response(
|
||||||
const HTTPTemplates *templates);
|
const char *request,
|
||||||
|
unsigned int size,
|
||||||
|
const C_SIMPLE_HTTP_HTTPTemplates *templates
|
||||||
|
);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
59
src/test.c
59
src/test.c
|
@ -3,7 +3,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// Local includes.
|
// Local includes.
|
||||||
#include "http.h"
|
#include "config.h"
|
||||||
|
|
||||||
// Third party includes.
|
// Third party includes.
|
||||||
#include <SimpleArchiver/src/helpers.h>
|
#include <SimpleArchiver/src/helpers.h>
|
||||||
|
@ -103,14 +103,39 @@ int main(void) {
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
fwrite("TEST5=\"\"\" '''one two '''three \"\"\"\n", 1, 34, test_file)
|
fwrite("TEST5=\"\"\" '''one two '''three \"\"\"\n", 1, 34, test_file)
|
||||||
== 34);
|
== 34);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("PATH=/derp\nHTML=''' one two three '''\n", 1, 38, test_file)
|
||||||
|
== 38);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("TEST=''' \"one two \"three '''\n", 1, 29, test_file)
|
||||||
|
== 29);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("TEST2=' \"one two \"three ''\n", 1, 27, test_file)
|
||||||
|
== 27);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("PATH=/doop\n", 1, 11, test_file)
|
||||||
|
== 11);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("TEST3=something\n", 1, 16, test_file)
|
||||||
|
== 16);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("TEST2=' \"one two \"three ''\n", 1, 27, test_file)
|
||||||
|
== 27);
|
||||||
simple_archiver_helper_cleanup_FILE(&test_file);
|
simple_archiver_helper_cleanup_FILE(&test_file);
|
||||||
|
|
||||||
__attribute__((cleanup(c_simple_http_clean_up_templates)))
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
HTTPTemplates templates =
|
SDArchiverLinkedList *required_names = simple_archiver_list_init();
|
||||||
c_simple_http_set_up_templates(test_config_filename);
|
simple_archiver_list_add(
|
||||||
|
required_names,
|
||||||
|
"TEST2",
|
||||||
|
simple_archiver_helper_datastructure_cleanup_nop);
|
||||||
|
|
||||||
|
__attribute__((cleanup(c_simple_http_clean_up_parsed_config)))
|
||||||
|
C_SIMPLE_HTTP_ParsedConfig templates =
|
||||||
|
c_simple_http_parse_config(test_config_filename, "PATH", required_names);
|
||||||
ASSERT_TRUE(templates.paths);
|
ASSERT_TRUE(templates.paths);
|
||||||
|
|
||||||
HashMapWrapper *first_path_map_wrapper =
|
C_SIMPLE_HTTP_ParsedConfig *first_path_map_wrapper =
|
||||||
simple_archiver_hash_map_get(templates.paths, "/", 2);
|
simple_archiver_hash_map_get(templates.paths, "/", 2);
|
||||||
ASSERT_TRUE(first_path_map_wrapper);
|
ASSERT_TRUE(first_path_map_wrapper);
|
||||||
|
|
||||||
|
@ -154,6 +179,30 @@ int main(void) {
|
||||||
ASSERT_TRUE(value);
|
ASSERT_TRUE(value);
|
||||||
// printf("%s\n", value);
|
// printf("%s\n", value);
|
||||||
ASSERT_STREQ(value, " '''one two '''three ");
|
ASSERT_STREQ(value, " '''one two '''three ");
|
||||||
|
|
||||||
|
simple_archiver_list_free(&required_names);
|
||||||
|
required_names = simple_archiver_list_init();
|
||||||
|
simple_archiver_list_add(
|
||||||
|
required_names,
|
||||||
|
"TEST3",
|
||||||
|
simple_archiver_helper_datastructure_cleanup_nop);
|
||||||
|
|
||||||
|
c_simple_http_clean_up_parsed_config(&templates);
|
||||||
|
templates =
|
||||||
|
c_simple_http_parse_config(test_config_filename, "PATH", required_names);
|
||||||
|
ASSERT_FALSE(templates.paths);
|
||||||
|
|
||||||
|
simple_archiver_list_free(&required_names);
|
||||||
|
required_names = simple_archiver_list_init();
|
||||||
|
simple_archiver_list_add(
|
||||||
|
required_names,
|
||||||
|
"TEST",
|
||||||
|
simple_archiver_helper_datastructure_cleanup_nop);
|
||||||
|
|
||||||
|
c_simple_http_clean_up_parsed_config(&templates);
|
||||||
|
templates =
|
||||||
|
c_simple_http_parse_config(test_config_filename, "PATH", required_names);
|
||||||
|
ASSERT_FALSE(templates.paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN()
|
RETURN()
|
||||||
|
|
Loading…
Reference in a new issue