WIP Impl. config file parsing for http templates
TODO: Still need to test "HTML_FILE". Response to HTTP request.
This commit is contained in:
parent
efd4b39311
commit
b4a56a7918
6 changed files with 290 additions and 25 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/c_simple_http
|
/c_simple_http
|
||||||
/objs/
|
/objs/
|
||||||
|
/unit_test
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -28,15 +28,19 @@ SOURCES = \
|
||||||
OBJECT_DIR = objs
|
OBJECT_DIR = objs
|
||||||
OBJECTS = $(addprefix ${OBJECT_DIR}/,$(patsubst %.c,%.c.o,${SOURCES}))
|
OBJECTS = $(addprefix ${OBJECT_DIR}/,$(patsubst %.c,%.c.o,${SOURCES}))
|
||||||
|
|
||||||
all: c_simple_http
|
all: c_simple_http unit_test
|
||||||
|
|
||||||
c_simple_http: ${OBJECTS}
|
c_simple_http: ${OBJECTS}
|
||||||
gcc -o c_simple_http ${CFLAGS} $^
|
gcc -o c_simple_http ${CFLAGS} $^
|
||||||
|
|
||||||
|
unit_test: $(filter-out ${OBJECT_DIR}/src/main.c.o,${OBJECTS}) ${OBJECT_DIR}/src/test.c.o
|
||||||
|
gcc -o unit_test ${CFLAGS} $^
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f c_simple_http
|
rm -f c_simple_http
|
||||||
|
rm -f unit_test
|
||||||
rm -rf ${OBJECT_DIR}
|
rm -rf ${OBJECT_DIR}
|
||||||
|
|
||||||
${OBJECT_DIR}/%.c.o: %.c
|
${OBJECT_DIR}/%.c.o: %.c
|
||||||
|
|
|
@ -20,5 +20,6 @@
|
||||||
#define C_SIMPLE_HTTP_SLEEP_NANOS 1000000
|
#define C_SIMPLE_HTTP_SLEEP_NANOS 1000000
|
||||||
#define C_SIMPLE_HTTP_RECV_BUF_SIZE 1024
|
#define C_SIMPLE_HTTP_RECV_BUF_SIZE 1024
|
||||||
#define C_SIMPLE_HTTP_CONFIG_BUF_SIZE 256
|
#define C_SIMPLE_HTTP_CONFIG_BUF_SIZE 256
|
||||||
|
#define C_SIMPLE_HTTP_QUOTE_COUNT_MAX 3
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
122
src/http.c
122
src/http.c
|
@ -24,18 +24,64 @@
|
||||||
#include <helpers.h>
|
#include <helpers.h>
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
typedef struct HashMapWrapper {
|
#define SINGLE_QUOTE_DECREMENT() \
|
||||||
SDArchiverHashMap *hash_map;
|
for(; single_quote_count > 0; --single_quote_count) { \
|
||||||
} HashMapWrapper;
|
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; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#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) {
|
void c_simple_http_hash_map_wrapper_cleanup_hashmap_fn(void *data) {
|
||||||
HashMapWrapper *wrapper = data;
|
HashMapWrapper *wrapper = data;
|
||||||
simple_archiver_hash_map_free(&wrapper->hash_map);
|
simple_archiver_hash_map_free(&wrapper->paths);
|
||||||
free(wrapper);
|
free(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
void c_simple_http_hash_map_wrapper_cleanup(HashMapWrapper *wrapper) {
|
void c_simple_http_hash_map_wrapper_cleanup(HashMapWrapper *wrapper) {
|
||||||
simple_archiver_hash_map_free(&wrapper->hash_map);
|
simple_archiver_hash_map_free(&wrapper->paths);
|
||||||
free(wrapper);
|
free(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,19 +106,67 @@ HTTPTemplates c_simple_http_set_up_templates(const char *config_filename) {
|
||||||
// xxx1 - reading value
|
// xxx1 - reading value
|
||||||
// xx0x - does not have "HTML" key
|
// xx0x - does not have "HTML" key
|
||||||
// xx1x - does 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 int state = 0;
|
||||||
|
unsigned char single_quote_count = 0;
|
||||||
|
unsigned char double_quote_count = 0;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while (feof(f) == 0) {
|
while (feof(f) == 0) {
|
||||||
c = fgetc(f);
|
c = fgetc(f);
|
||||||
if (c == EOF) {
|
if (c == EOF) {
|
||||||
break;
|
break;
|
||||||
} else if (c == ' ' || c == '\t') {
|
} else if ((state & 0xC) == 0 && (c == ' ' || c == '\t')) {
|
||||||
// Ignore whitespace.
|
// Ignore whitespace when not quoted.
|
||||||
|
SINGLE_QUOTE_DECREMENT();
|
||||||
|
DOUBLE_QUOTE_DECREMENT();
|
||||||
continue;
|
continue;
|
||||||
} else if ((state & 1) == 0 && (c == '\r' || c == '\n')) {
|
} else if ((state & 1) == 0
|
||||||
// Ignore newlines when parsing for key.
|
&& (state & 0xC) == 0
|
||||||
|
&& (c == '\r' || c == '\n')) {
|
||||||
|
// Ignore newlines when parsing for key and when not quoted.
|
||||||
|
SINGLE_QUOTE_DECREMENT();
|
||||||
|
DOUBLE_QUOTE_DECREMENT();
|
||||||
continue;
|
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 ((state & 1) == 0) {
|
||||||
if (c != '=') {
|
if (c != '=') {
|
||||||
|
@ -99,7 +193,7 @@ HTTPTemplates c_simple_http_set_up_templates(const char *config_filename) {
|
||||||
state |= 1;
|
state |= 1;
|
||||||
}
|
}
|
||||||
} else if ((state & 1) == 1) {
|
} else if ((state & 1) == 1) {
|
||||||
if (c != '\n' && c != '\r') {
|
if ((c != '\n' && c != '\r') || (state & 0xC) != 0) {
|
||||||
value_buf[value_idx++] = c;
|
value_buf[value_idx++] = c;
|
||||||
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
@ -165,15 +259,15 @@ HTTPTemplates c_simple_http_set_up_templates(const char *config_filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMapWrapper *wrapper = malloc(sizeof(HashMapWrapper));
|
HashMapWrapper *wrapper = malloc(sizeof(HashMapWrapper));
|
||||||
wrapper->hash_map = hash_map;
|
wrapper->paths = hash_map;
|
||||||
|
|
||||||
if (simple_archiver_hash_map_insert(
|
if (simple_archiver_hash_map_insert(
|
||||||
&templates.paths,
|
&templates.paths,
|
||||||
wrapper,
|
wrapper,
|
||||||
value,
|
value,
|
||||||
value_idx,
|
value_idx,
|
||||||
simple_archiver_helper_datastructure_cleanup_nop,
|
c_simple_http_hash_map_wrapper_cleanup_hashmap_fn,
|
||||||
c_simple_http_hash_map_wrapper_cleanup_hashmap_fn) != 0) {
|
simple_archiver_helper_datastructure_cleanup_nop) != 0) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ERROR: Failed to insert new hash map for new PATH block!\n");
|
"ERROR: Failed to insert new hash map for new PATH block!\n");
|
||||||
c_simple_http_clean_up_templates(&templates);
|
c_simple_http_clean_up_templates(&templates);
|
||||||
|
@ -253,7 +347,7 @@ HTTPTemplates c_simple_http_set_up_templates(const char *config_filename) {
|
||||||
memcpy(value, value_buf, value_idx);
|
memcpy(value, value_buf, value_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simple_archiver_hash_map_insert(&hash_map_wrapper->hash_map,
|
if (simple_archiver_hash_map_insert(&hash_map_wrapper->paths,
|
||||||
value,
|
value,
|
||||||
key,
|
key,
|
||||||
key_idx,
|
key_idx,
|
||||||
|
|
23
src/http.h
23
src/http.h
|
@ -26,17 +26,20 @@ typedef struct HTTPTemplates {
|
||||||
SDArchiverHashMap *paths;
|
SDArchiverHashMap *paths;
|
||||||
} HTTPTemplates;
|
} HTTPTemplates;
|
||||||
|
|
||||||
|
typedef HTTPTemplates HashMapWrapper;
|
||||||
|
|
||||||
/// Each line in the config should be a key-value pair separated by an equals
|
/// 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. If there
|
/// sign. All whitespace is ignored unless if the value is quoted. A part of a
|
||||||
/// exists a line with the key "PATH", then the value must be a path like
|
/// string can be "quoted" if it is surrounded by three single-quotes or three
|
||||||
/// "/cache" and all the following key-value pairs are associated with that PATH
|
/// double-quotes. If there exists a line with the key "PATH", then the value
|
||||||
/// until the next "PATH" key or EOF. Each "PATH" "block" should have a "HTML"
|
/// must be a path like "/cache" and all the following key-value pairs are
|
||||||
/// key-value pair where the value is a HTML template. Inside such HTML
|
/// associated with that PATH until the next "PATH" key or EOF. Each "PATH"
|
||||||
/// templates should be strings like "{{{{example_key}}}}" which will be
|
/// "block" should have a "HTML" key-value pair where the value is a HTML
|
||||||
/// replaced by the string value of the key name deliminated by the four curly
|
/// template. Inside such HTML templates should be strings like
|
||||||
/// braces. "HTML_FILE" specifies a filename to read instead of using a literal
|
/// "{{{{example_key}}}}" which will be replaced by the string value of the key
|
||||||
/// string in the config file. It will store the contents of the specified file
|
/// name deliminated by the four curly braces. "HTML_FILE" specifies a filename
|
||||||
/// with the "HTML" key internally.
|
/// 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);
|
HTTPTemplates c_simple_http_set_up_templates(const char *config_filename);
|
||||||
|
|
||||||
void c_simple_http_clean_up_templates(HTTPTemplates *templates);
|
void c_simple_http_clean_up_templates(HTTPTemplates *templates);
|
||||||
|
|
162
src/test.c
Normal file
162
src/test.c
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
// Standard library includes.
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Local includes.
|
||||||
|
#include "http.h"
|
||||||
|
|
||||||
|
// Third party includes.
|
||||||
|
#include <helpers.h>
|
||||||
|
#include <hash_map.h>
|
||||||
|
|
||||||
|
static int checks_checked = 0;
|
||||||
|
static int checks_passed = 0;
|
||||||
|
|
||||||
|
#define RETURN() \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "checked %d\npassed %d\n", checks_checked, checks_passed);\
|
||||||
|
return checks_checked == checks_passed ? 0 : 1; \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define CHECK_TRUE(x) \
|
||||||
|
do { \
|
||||||
|
++checks_checked; \
|
||||||
|
if (!(x)) { \
|
||||||
|
printf("CHECK_TRUE at line %u failed: %s\n", __LINE__, #x); \
|
||||||
|
} else { \
|
||||||
|
++checks_passed; \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
#define CHECK_FALSE(x) \
|
||||||
|
do { \
|
||||||
|
++checks_checked; \
|
||||||
|
if (x) { \
|
||||||
|
printf("CHECK_FALSE at line %u failed: %s\n", __LINE__, #x); \
|
||||||
|
} else { \
|
||||||
|
++checks_passed; \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
#define CHECK_STREQ(a, b) \
|
||||||
|
do { \
|
||||||
|
++checks_checked; \
|
||||||
|
if (strcmp((a), (b)) == 0) { \
|
||||||
|
++checks_passed; \
|
||||||
|
} else { \
|
||||||
|
printf("CHECK_STREQ at line %u failed: %s != %s\n", __LINE__, #a, #b); \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define ASSERT_TRUE(x) \
|
||||||
|
do { \
|
||||||
|
++checks_checked; \
|
||||||
|
if (!(x)) { \
|
||||||
|
printf("ASSERT_TRUE at line %u failed: %s\n", __LINE__, #x); \
|
||||||
|
RETURN() \
|
||||||
|
} else { \
|
||||||
|
++checks_passed; \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
#define ASSERT_FALSE(x) \
|
||||||
|
do { \
|
||||||
|
++checks_checked; \
|
||||||
|
if (x) { \
|
||||||
|
printf("ASSERT_FALSE at line %u failed: %s\n", __LINE__, #x); \
|
||||||
|
RETURN() \
|
||||||
|
} else { \
|
||||||
|
++checks_passed; \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
#define ASSERT_STREQ(a, b) \
|
||||||
|
do { \
|
||||||
|
++checks_checked; \
|
||||||
|
if (strcmp((a), (b)) == 0) { \
|
||||||
|
++checks_passed; \
|
||||||
|
} else { \
|
||||||
|
printf("ASSERT_STREQ at line %u failed: %s != %s\n", __LINE__, #a, #b); \
|
||||||
|
RETURN() \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Test set up templates.
|
||||||
|
{
|
||||||
|
const char *test_config_filename = "/tmp/c_simple_http_test.config";
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *test_file = fopen(test_config_filename, "w");
|
||||||
|
ASSERT_TRUE(test_file);
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("PATH=/\nHTML=''' one two three '''\n", 1, 34, test_file)
|
||||||
|
== 34);
|
||||||
|
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("TEST3=\"\"\" \"one two \"three ''\"\"\"\n", 1, 32, test_file)
|
||||||
|
== 32);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("TEST4=''' \"\"\"one two \"\"\"three '''\n", 1, 34, test_file)
|
||||||
|
== 34);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite("TEST5=\"\"\" '''one two '''three \"\"\"\n", 1, 34, test_file)
|
||||||
|
== 34);
|
||||||
|
simple_archiver_helper_cleanup_FILE(&test_file);
|
||||||
|
|
||||||
|
__attribute__((cleanup(c_simple_http_clean_up_templates)))
|
||||||
|
HTTPTemplates templates =
|
||||||
|
c_simple_http_set_up_templates(test_config_filename);
|
||||||
|
ASSERT_TRUE(templates.paths);
|
||||||
|
|
||||||
|
HashMapWrapper *first_path_map_wrapper =
|
||||||
|
simple_archiver_hash_map_get(templates.paths, "/", 2);
|
||||||
|
ASSERT_TRUE(first_path_map_wrapper);
|
||||||
|
|
||||||
|
const char *value =
|
||||||
|
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "PATH", 5);
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
ASSERT_STREQ(value, "/");
|
||||||
|
|
||||||
|
value =
|
||||||
|
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "HTML", 5);
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
// printf("%s\n", value);
|
||||||
|
ASSERT_STREQ(value, " one two three ");
|
||||||
|
|
||||||
|
value =
|
||||||
|
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST", 5);
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
// printf("%s\n", value);
|
||||||
|
ASSERT_STREQ(value, " \"one two \"three ");
|
||||||
|
|
||||||
|
value =
|
||||||
|
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST2", 6);
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
// printf("%s\n", value);
|
||||||
|
ASSERT_STREQ(value, "'\"onetwo\"three''");
|
||||||
|
|
||||||
|
value =
|
||||||
|
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST3", 6);
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
// printf("%s\n", value);
|
||||||
|
ASSERT_STREQ(value, " \"one two \"three ''");
|
||||||
|
|
||||||
|
value =
|
||||||
|
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST4", 6);
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
// printf("%s\n", value);
|
||||||
|
ASSERT_STREQ(value, " \"\"\"one two \"\"\"three ");
|
||||||
|
|
||||||
|
value =
|
||||||
|
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST5", 6);
|
||||||
|
ASSERT_TRUE(value);
|
||||||
|
// printf("%s\n", value);
|
||||||
|
ASSERT_STREQ(value, " '''one two '''three ");
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN()
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: ts=2 sts=2 sw=2
|
Loading…
Reference in a new issue