Impl. html cache (mostly done)
Some checks failed
Run Unit Tests / build-and-run-unit-tests (push) Failing after 4s
Some checks failed
Run Unit Tests / build-and-run-unit-tests (push) Failing after 4s
TODO: Invalidate cache if it is too old.
This commit is contained in:
parent
a017fccc27
commit
83e4a51985
9 changed files with 658 additions and 13 deletions
|
@ -108,6 +108,7 @@ Args parse_args(int32_t argc, char **argv) {
|
||||||
} else {
|
} else {
|
||||||
printf("Directory \"%s\" exists.\n", args.cache_dir);
|
printf("Directory \"%s\" exists.\n", args.cache_dir);
|
||||||
}
|
}
|
||||||
|
closedir(d);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "ERROR: Invalid args!\n");
|
fprintf(stderr, "ERROR: Invalid args!\n");
|
||||||
print_usage();
|
print_usage();
|
||||||
|
|
|
@ -21,6 +21,13 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// libc includes.
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
int c_simple_http_internal_get_string_part_full_size(void *data, void *ud) {
|
int c_simple_http_internal_get_string_part_full_size(void *data, void *ud) {
|
||||||
C_SIMPLE_HTTP_String_Part *part = data;
|
C_SIMPLE_HTTP_String_Part *part = data;
|
||||||
size_t *count = ud;
|
size_t *count = ud;
|
||||||
|
@ -216,4 +223,42 @@ char *c_simple_http_helper_unescape_uri(const char *uri) {
|
||||||
return c_simple_http_combine_string_parts(parts);
|
return c_simple_http_combine_string_parts(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int c_simple_http_helper_mkdir_tree(const char *path) {
|
||||||
|
// Check if dir already exists.
|
||||||
|
DIR *dir_ptr = opendir(path);
|
||||||
|
if (dir_ptr) {
|
||||||
|
// Directory already exists.
|
||||||
|
closedir(dir_ptr);
|
||||||
|
return 1;
|
||||||
|
} else if (errno == ENOENT) {
|
||||||
|
// Directory doesn't exist, create dir tree.
|
||||||
|
closedir(dir_ptr);
|
||||||
|
|
||||||
|
size_t buf_size = strlen(path) + 1;
|
||||||
|
char *buf = malloc(buf_size);
|
||||||
|
memcpy(buf, path, buf_size - 1);
|
||||||
|
buf[buf_size - 1] = 0;
|
||||||
|
|
||||||
|
char *dirname_buf = dirname(buf);
|
||||||
|
// Recursive call to ensure parent directories are created.
|
||||||
|
int ret = c_simple_http_helper_mkdir_tree(dirname_buf);
|
||||||
|
free(buf);
|
||||||
|
if (ret == 1 || ret == 0) {
|
||||||
|
// Parent directory should be created by now.
|
||||||
|
ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||||
|
if (ret != 0) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// Other directory error.
|
||||||
|
closedir(dir_ptr);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// vim: et ts=2 sts=2 sw=2
|
// vim: et ts=2 sts=2 sw=2
|
||||||
|
|
|
@ -58,6 +58,11 @@ char c_simple_http_helper_hex_to_value(const char upper, const char lower);
|
||||||
/// non-NULL, it must be free'd.
|
/// non-NULL, it must be free'd.
|
||||||
char *c_simple_http_helper_unescape_uri(const char *uri);
|
char *c_simple_http_helper_unescape_uri(const char *uri);
|
||||||
|
|
||||||
|
/// Returns zero if successful. "dirpath" will point to a directory on success.
|
||||||
|
/// Returns 1 if the directory already exists.
|
||||||
|
/// Other return values are errors.
|
||||||
|
int c_simple_http_helper_mkdir_tree(const char *dirpath);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// vim: et ts=2 sts=2 sw=2
|
// vim: et ts=2 sts=2 sw=2
|
||||||
|
|
368
src/html_cache.c
368
src/html_cache.c
|
@ -16,9 +16,15 @@
|
||||||
|
|
||||||
// Standard library includes.
|
// Standard library includes.
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// POSIX includes.
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
// Third-party includes.
|
// Third-party includes.
|
||||||
#include <SimpleArchiver/src/data_structures/linked_list.h>
|
#include <SimpleArchiver/src/data_structures/linked_list.h>
|
||||||
#include <SimpleArchiver/src/helpers.h>
|
#include <SimpleArchiver/src/helpers.h>
|
||||||
|
@ -26,6 +32,21 @@
|
||||||
// Local includes.
|
// Local includes.
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "http_template.h"
|
||||||
|
|
||||||
|
int c_simple_http_internal_write_filenames_to_cache_file(void *data, void *ud) {
|
||||||
|
char *filename = data;
|
||||||
|
FILE *cache_fd = ud;
|
||||||
|
|
||||||
|
const size_t filename_size = strlen(filename);
|
||||||
|
if (fwrite(filename, 1, filename_size, cache_fd) != filename_size) {
|
||||||
|
return 1;
|
||||||
|
} else if (fwrite("\n", 1, 1, cache_fd) != 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
char *c_simple_http_path_to_cache_filename(const char *path) {
|
char *c_simple_http_path_to_cache_filename(const char *path) {
|
||||||
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
@ -185,8 +206,353 @@ int c_simple_http_cache_path(
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *config_filename,
|
const char *config_filename,
|
||||||
const char *cache_dir,
|
const char *cache_dir,
|
||||||
|
const C_SIMPLE_HTTP_HTTPTemplates *templates,
|
||||||
char **buf_out) {
|
char **buf_out) {
|
||||||
// TODO
|
if (!path) {
|
||||||
|
fprintf(stderr, "ERROR cache_path function: path is NULL!\n");
|
||||||
|
return -9;
|
||||||
|
} else if (!config_filename) {
|
||||||
|
fprintf(stderr, "ERROR cache_path function: config_filename is NULL!\n");
|
||||||
|
return -10;
|
||||||
|
} else if (!cache_dir) {
|
||||||
|
fprintf(stderr, "ERROR cache_path function: cache_dir is NULL!\n");
|
||||||
|
return -11;
|
||||||
|
} else if (!templates) {
|
||||||
|
fprintf(stderr, "ERROR cache_path function: templates is NULL!\n");
|
||||||
|
return -12;
|
||||||
|
} else if (!buf_out) {
|
||||||
|
fprintf(stderr, "ERROR cache_path function: buf_out is NULL!\n");
|
||||||
|
return -13;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = c_simple_http_helper_mkdir_tree(cache_dir);
|
||||||
|
if (ret != 0 && ret != 1) {
|
||||||
|
fprintf(
|
||||||
|
stderr, "ERROR failed to ensure cache_dir \"%s\" exists!\n", cache_dir);
|
||||||
|
return -15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the cache filename from the path.
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *cache_filename = c_simple_http_path_to_cache_filename(path);
|
||||||
|
if (!cache_filename) {
|
||||||
|
fprintf(stderr, "ERROR Failed to convert path to cache_filename!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine the cache_dir with cache filename.
|
||||||
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
|
SDArchiverLinkedList *parts = simple_archiver_list_init();
|
||||||
|
|
||||||
|
c_simple_http_add_string_part(parts, cache_dir, 0);
|
||||||
|
c_simple_http_add_string_part(parts, "/", 0);
|
||||||
|
c_simple_http_add_string_part(parts, cache_filename, 0);
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *cache_filename_full = c_simple_http_combine_string_parts(parts);
|
||||||
|
|
||||||
|
simple_archiver_list_free(&parts);
|
||||||
|
parts = simple_archiver_list_init();
|
||||||
|
|
||||||
|
if (!cache_filename_full) {
|
||||||
|
fprintf(stderr, "ERROR Failed to create full-path to cache filename!\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get "stat" info on the cache filename.
|
||||||
|
uint_fast8_t force_cache_update = 0;
|
||||||
|
struct stat cache_file_stat;
|
||||||
|
ret = stat(cache_filename_full, &cache_file_stat);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
fprintf(stderr, "NOTICE cache file doesn't exist, will create...\n");
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR getting stat info on file \"%s\" (errno %d)! "
|
||||||
|
"Assuming out of date!\n",
|
||||||
|
cache_filename_full,
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
force_cache_update = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get "stat" info on config file.
|
||||||
|
struct stat config_file_stat;
|
||||||
|
ret = stat(config_filename, &config_file_stat);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
fprintf(
|
||||||
|
stderr, "ERROR config file \"%s\" doesn't exist!\n", config_filename);
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR getting stat info on config file \"%s\" (errno %d)!\n",
|
||||||
|
config_filename,
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force_cache_update) {
|
||||||
|
do {
|
||||||
|
// Check filenames in cache file.
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *cache_fd = fopen(cache_filename_full, "r");
|
||||||
|
const size_t buf_size = 1024;
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *buf = malloc(buf_size);
|
||||||
|
|
||||||
|
// Check header.
|
||||||
|
if (fread(buf, 1, 20, cache_fd) != 20) {
|
||||||
|
fprintf(stderr, "ERROR Failed to read header from cache file!\n");
|
||||||
|
return -14;
|
||||||
|
} else if (strncmp(buf, "--- CACHE ENTRY ---\n", 20) != 0) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Cache is invalid (bad header), assuming out of date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check filenames.
|
||||||
|
size_t buf_idx = 0;
|
||||||
|
while(1) {
|
||||||
|
ret = fgetc(cache_fd);
|
||||||
|
if (ret == EOF) {
|
||||||
|
fprintf(
|
||||||
|
stderr, "WARNING Cache is invalid (EOF), assuming out of date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
break;
|
||||||
|
} else if (ret == '\n') {
|
||||||
|
// Got filename in "buf" of size "buf_idx".
|
||||||
|
if (strncmp(buf, "--- BEGIN HTML ---", 18) == 0) {
|
||||||
|
// Got end header instead of filename.
|
||||||
|
break;
|
||||||
|
} else if (buf_idx < buf_size) {
|
||||||
|
buf[buf_idx++] = 0;
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Cache is invalid (too large filename), assuming out of "
|
||||||
|
"date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat file_stat;
|
||||||
|
ret = stat(buf, &file_stat);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Invalid filename cache entry \"%s\" (doesn't exist)! "
|
||||||
|
"Assuming out of date!\n",
|
||||||
|
buf);
|
||||||
|
force_cache_update = 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Invalid filename cache entry \"%s\" (stat errno %d)! "
|
||||||
|
"Assuming out of date!\n",
|
||||||
|
buf,
|
||||||
|
errno);
|
||||||
|
force_cache_update = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache_file_stat.st_mtim.tv_sec < file_stat.st_mtim.tv_sec
|
||||||
|
|| (cache_file_stat.st_mtim.tv_sec == file_stat.st_mtim.tv_sec
|
||||||
|
&& cache_file_stat.st_mtim.tv_nsec
|
||||||
|
< file_stat.st_mtim.tv_nsec)) {
|
||||||
|
// File is newer than cache.
|
||||||
|
force_cache_update = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_idx = 0;
|
||||||
|
} else if (buf_idx < buf_size) {
|
||||||
|
buf[buf_idx++] = (char)ret;
|
||||||
|
} else {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Cache is invalid (too large filename), assuming out of "
|
||||||
|
"date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare modification times.
|
||||||
|
CACHE_FILE_WRITE_CHECK:
|
||||||
|
if (force_cache_update
|
||||||
|
|| cache_file_stat.st_mtim.tv_sec < config_file_stat.st_mtim.tv_sec
|
||||||
|
|| (cache_file_stat.st_mtim.tv_sec == config_file_stat.st_mtim.tv_sec
|
||||||
|
&& cache_file_stat.st_mtim.tv_nsec < config_file_stat.st_mtim.tv_nsec))
|
||||||
|
{
|
||||||
|
// Cache file is out of date.
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *cache_fd = fopen(cache_filename_full, "w");
|
||||||
|
if (fwrite("--- CACHE ENTRY ---\n", 1, 20, cache_fd) != 20) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"ERROR Failed to write to cache file \"%s\"!\n",
|
||||||
|
cache_filename_full);
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
|
SDArchiverLinkedList *used_filenames = NULL;
|
||||||
|
|
||||||
|
size_t generated_html_size = 0;
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *generated_html = c_simple_http_path_to_generated(
|
||||||
|
path, templates, &generated_html_size, &used_filenames);
|
||||||
|
|
||||||
|
if (!generated_html) {
|
||||||
|
fprintf(stderr, "ERROR Failed to generate html for path \"%s\"!\n", path);
|
||||||
|
simple_archiver_helper_cleanup_FILE(&cache_fd);
|
||||||
|
remove(cache_filename_full);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simple_archiver_list_get(
|
||||||
|
used_filenames,
|
||||||
|
c_simple_http_internal_write_filenames_to_cache_file,
|
||||||
|
cache_fd)) {
|
||||||
|
fprintf(stderr, "ERROR Failed to write filenames to cache file!\n");
|
||||||
|
return -6;
|
||||||
|
} else if (fwrite("--- BEGIN HTML ---\n", 1, 19, cache_fd) != 19) {
|
||||||
|
fprintf(stderr, "ERROR Failed to write end of cache file header!\n");
|
||||||
|
return -7;
|
||||||
|
} else if (
|
||||||
|
fwrite(
|
||||||
|
generated_html,
|
||||||
|
1,
|
||||||
|
generated_html_size,
|
||||||
|
cache_fd)
|
||||||
|
!= generated_html_size) {
|
||||||
|
fprintf(stderr, "ERROR Failed to write html to cache file!\n");
|
||||||
|
return -8;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf_out = generated_html;
|
||||||
|
generated_html = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache file is newer.
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *cache_fd = fopen(cache_filename_full, "rb");
|
||||||
|
|
||||||
|
const size_t buf_size = 128;
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *buf = malloc(buf_size);
|
||||||
|
|
||||||
|
// Get first header.
|
||||||
|
if (fread(buf, 1, 20, cache_fd) != 20) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Invalid cache file (read header), assuming out of date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
} else if (strncmp("--- CACHE ENTRY ---\n", buf, 20) != 0) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Invalid cache file (check header), assuming out of date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get filenames end header.
|
||||||
|
uint_fast8_t reached_end_header = 0;
|
||||||
|
size_t buf_idx = 0;
|
||||||
|
while (1) {
|
||||||
|
ret = fgetc(cache_fd);
|
||||||
|
if (ret == EOF) {
|
||||||
|
fprintf(
|
||||||
|
stderr, "WARNING Invalid cache file (EOF), assuming out of date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
} else if (ret == '\n') {
|
||||||
|
if (strncmp("--- BEGIN HTML ---", buf, 18) == 0) {
|
||||||
|
reached_end_header = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf_idx = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf_idx < buf_size) {
|
||||||
|
buf[buf_idx++] = (char)ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reached_end_header) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Invalid cache file (no end header), assuming out of date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remaining bytes in cache_fd is cached html. Fetch it and return it.
|
||||||
|
const long html_start_idx = ftell(cache_fd);
|
||||||
|
if (html_start_idx <= 0) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Failed to get position in cache file, assuming "
|
||||||
|
"invalid/out-of-date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fseek(cache_fd, 0, SEEK_END);
|
||||||
|
if (ret != 0) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Failed to seek in cache file, assuming invalid/out-of-date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
}
|
||||||
|
const long html_end_idx = ftell(cache_fd);
|
||||||
|
if (html_end_idx <= 0) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Failed to get end position in cache file, assuming "
|
||||||
|
"invalid/out-of-date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fseek(cache_fd, html_start_idx, SEEK_SET);
|
||||||
|
if (ret != 0) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Failed to seek in cache file, assuming invalid/out-of-date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t html_size = (size_t)html_end_idx - (size_t)html_start_idx + 1;
|
||||||
|
*buf_out = malloc(html_size);
|
||||||
|
|
||||||
|
if (fread(*buf_out, 1, html_size - 1, cache_fd) != html_size - 1) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING Failed to read html in cache file, assuming "
|
||||||
|
"invalid/out-of-date!\n");
|
||||||
|
force_cache_update = 1;
|
||||||
|
goto CACHE_FILE_WRITE_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*buf_out)[html_size - 1] = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,21 +17,26 @@
|
||||||
#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_HTML_CACHE_H_
|
#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_HTML_CACHE_H_
|
||||||
#define SEODISPARATE_COM_C_SIMPLE_HTTP_HTML_CACHE_H_
|
#define SEODISPARATE_COM_C_SIMPLE_HTTP_HTML_CACHE_H_
|
||||||
|
|
||||||
|
// Local includes.
|
||||||
|
#include "http.h"
|
||||||
|
|
||||||
/// Must be free'd if non-NULL.
|
/// Must be free'd if non-NULL.
|
||||||
char *c_simple_http_path_to_cache_filename(const char *path);
|
char *c_simple_http_path_to_cache_filename(const char *path);
|
||||||
|
|
||||||
/// Must be free'd if non-NULL.
|
/// Must be free'd if non-NULL.
|
||||||
char *c_simple_http_cache_filename_to_path(const char *cache_filename);
|
char *c_simple_http_cache_filename_to_path(const char *cache_filename);
|
||||||
|
|
||||||
/// Given a "path", returns non-zero if the cache is invalidated.
|
/// Given a "path", returns positive-non-zero if the cache is invalidated.
|
||||||
/// "config_filename" is required to check its timestamp. "cache_dir" is
|
/// "config_filename" is required to check its timestamp. "cache_dir" is
|
||||||
/// required to actually get the cache file to check against. "buf_out" will be
|
/// required to actually get the cache file to check against. "buf_out" will be
|
||||||
/// populated if non-NULL, and will either be fetched from the cache or from the
|
/// populated if non-NULL, and will either be fetched from the cache or from the
|
||||||
/// config (using http_template). Note that "buf_out" will point to a c-string.
|
/// config (using http_template). Note that "buf_out" will point to a c-string.
|
||||||
|
/// Returns a negative value on error.
|
||||||
int c_simple_http_cache_path(
|
int c_simple_http_cache_path(
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *config_filename,
|
const char *config_filename,
|
||||||
const char *cache_dir,
|
const char *cache_dir,
|
||||||
|
const C_SIMPLE_HTTP_HTTPTemplates *templates,
|
||||||
char **buf_out);
|
char **buf_out);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
46
src/http.c
46
src/http.c
|
@ -28,6 +28,7 @@
|
||||||
// Local includes
|
// Local includes
|
||||||
#include "http_template.h"
|
#include "http_template.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "html_cache.h"
|
||||||
|
|
||||||
#define REQUEST_TYPE_BUFFER_SIZE 16
|
#define REQUEST_TYPE_BUFFER_SIZE 16
|
||||||
#define REQUEST_PATH_BUFFER_SIZE 256
|
#define REQUEST_PATH_BUFFER_SIZE 256
|
||||||
|
@ -63,7 +64,9 @@ char *c_simple_http_request_response(
|
||||||
uint32_t size,
|
uint32_t size,
|
||||||
const C_SIMPLE_HTTP_HTTPTemplates *templates,
|
const C_SIMPLE_HTTP_HTTPTemplates *templates,
|
||||||
size_t *out_size,
|
size_t *out_size,
|
||||||
enum C_SIMPLE_HTTP_ResponseCode *out_response_code) {
|
enum C_SIMPLE_HTTP_ResponseCode *out_response_code,
|
||||||
|
const char *cache_dir,
|
||||||
|
const char *config_filename) {
|
||||||
if (out_size) {
|
if (out_size) {
|
||||||
*out_size = 0;
|
*out_size = 0;
|
||||||
}
|
}
|
||||||
|
@ -171,11 +174,42 @@ char *c_simple_http_request_response(
|
||||||
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
char *stripped_path = c_simple_http_strip_path(
|
char *stripped_path = c_simple_http_strip_path(
|
||||||
request_path_unescaped, strlen(request_path_unescaped));
|
request_path_unescaped, strlen(request_path_unescaped));
|
||||||
char *generated_buf = c_simple_http_path_to_generated(
|
|
||||||
stripped_path ? stripped_path : request_path_unescaped,
|
char *generated_buf = NULL;
|
||||||
templates,
|
|
||||||
&generated_size,
|
if (cache_dir) {
|
||||||
NULL); // TODO Use the output parameter "filenames list" here.
|
int ret = c_simple_http_cache_path(
|
||||||
|
stripped_path ? stripped_path : request_path_unescaped,
|
||||||
|
config_filename,
|
||||||
|
cache_dir,
|
||||||
|
templates,
|
||||||
|
&generated_buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "ERROR Failed to generate template with cache!\n");
|
||||||
|
if (out_response_code) {
|
||||||
|
if (
|
||||||
|
simple_archiver_hash_map_get(
|
||||||
|
templates->hash_map,
|
||||||
|
stripped_path ? stripped_path : request_path_unescaped,
|
||||||
|
stripped_path
|
||||||
|
? strlen(stripped_path) + 1
|
||||||
|
: strlen(request_path_unescaped) + 1)
|
||||||
|
== NULL) {
|
||||||
|
*out_response_code = C_SIMPLE_HTTP_Response_404_Not_Found;
|
||||||
|
} else {
|
||||||
|
*out_response_code = C_SIMPLE_HTTP_Response_500_Internal_Server_Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
generated_size = strlen(generated_buf);
|
||||||
|
} else {
|
||||||
|
generated_buf = c_simple_http_path_to_generated(
|
||||||
|
stripped_path ? stripped_path : request_path_unescaped,
|
||||||
|
templates,
|
||||||
|
&generated_size,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (!generated_buf || generated_size == 0) {
|
if (!generated_buf || generated_size == 0) {
|
||||||
fprintf(stderr, "ERROR Unable to generate response html for path \"%s\"!\n",
|
fprintf(stderr, "ERROR Unable to generate response html for path \"%s\"!\n",
|
||||||
|
|
|
@ -48,7 +48,9 @@ char *c_simple_http_request_response(
|
||||||
uint32_t size,
|
uint32_t size,
|
||||||
const C_SIMPLE_HTTP_HTTPTemplates *templates,
|
const C_SIMPLE_HTTP_HTTPTemplates *templates,
|
||||||
size_t *out_size,
|
size_t *out_size,
|
||||||
enum C_SIMPLE_HTTP_ResponseCode *out_response_code
|
enum C_SIMPLE_HTTP_ResponseCode *out_response_code,
|
||||||
|
const char *cache_dir,
|
||||||
|
const char *config_filename
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Takes a PATH string and returns a "bare" path.
|
/// Takes a PATH string and returns a "bare" path.
|
||||||
|
|
|
@ -359,7 +359,9 @@ int main(int argc, char **argv) {
|
||||||
(uint32_t)read_ret,
|
(uint32_t)read_ret,
|
||||||
&parsed_config,
|
&parsed_config,
|
||||||
&response_size,
|
&response_size,
|
||||||
&response_code);
|
&response_code,
|
||||||
|
args.cache_dir,
|
||||||
|
args.config_file);
|
||||||
if (response && response_code == C_SIMPLE_HTTP_Response_200_OK) {
|
if (response && response_code == C_SIMPLE_HTTP_Response_200_OK) {
|
||||||
CHECK_ERROR_WRITE(write(connection_fd, "HTTP/1.1 200 OK\n", 16));
|
CHECK_ERROR_WRITE(write(connection_fd, "HTTP/1.1 200 OK\n", 16));
|
||||||
CHECK_ERROR_WRITE(write(connection_fd, "Allow: GET\n", 11));
|
CHECK_ERROR_WRITE(write(connection_fd, "Allow: GET\n", 11));
|
||||||
|
|
191
src/test.c
191
src/test.c
|
@ -4,6 +4,11 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// POSIX includes.
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
// Local includes.
|
// Local includes.
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
@ -593,6 +598,19 @@ int main(void) {
|
||||||
CHECK_TRUE(strcmp(buf, "ABC%ZZ") == 0);
|
CHECK_TRUE(strcmp(buf, "ABC%ZZ") == 0);
|
||||||
free(buf);
|
free(buf);
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
|
|
||||||
|
DIR *dirp = opendir("/tmp/create_dirs_dir");
|
||||||
|
uint_fast8_t dir_exists = dirp ? 1 : 0;
|
||||||
|
closedir(dirp);
|
||||||
|
ASSERT_FALSE(dir_exists);
|
||||||
|
|
||||||
|
int ret = c_simple_http_helper_mkdir_tree("/tmp/create_dirs_dir/dir/");
|
||||||
|
int ret2 = rmdir("/tmp/create_dirs_dir/dir");
|
||||||
|
int ret3 = rmdir("/tmp/create_dirs_dir");
|
||||||
|
|
||||||
|
CHECK_TRUE(ret == 0);
|
||||||
|
CHECK_TRUE(ret2 == 0);
|
||||||
|
CHECK_TRUE(ret3 == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test html_cache.
|
// Test html_cache.
|
||||||
|
@ -655,11 +673,11 @@ int main(void) {
|
||||||
|
|
||||||
ret = c_simple_http_cache_filename_to_path("0x2Fouter0x2Finner");
|
ret = c_simple_http_cache_filename_to_path("0x2Fouter0x2Finner");
|
||||||
ASSERT_TRUE(ret);
|
ASSERT_TRUE(ret);
|
||||||
printf("%s\n", ret);
|
|
||||||
CHECK_TRUE(strcmp(ret, "/outer/inner") == 0);
|
CHECK_TRUE(strcmp(ret, "/outer/inner") == 0);
|
||||||
free(ret);
|
free(ret);
|
||||||
|
|
||||||
ret = c_simple_http_cache_filename_to_path("0x2Fouter0x2Finner0x2F%2F0x2Fmore_inner");
|
ret = c_simple_http_cache_filename_to_path(
|
||||||
|
"0x2Fouter0x2Finner0x2F%2F0x2Fmore_inner");
|
||||||
ASSERT_TRUE(ret);
|
ASSERT_TRUE(ret);
|
||||||
CHECK_TRUE(strcmp(ret, "/outer/inner/%2F/more_inner") == 0);
|
CHECK_TRUE(strcmp(ret, "/outer/inner/%2F/more_inner") == 0);
|
||||||
free(ret);
|
free(ret);
|
||||||
|
@ -669,7 +687,8 @@ int main(void) {
|
||||||
CHECK_TRUE(strcmp(ret, "/outer/inner") == 0);
|
CHECK_TRUE(strcmp(ret, "/outer/inner") == 0);
|
||||||
free(ret);
|
free(ret);
|
||||||
|
|
||||||
ret = c_simple_http_cache_filename_to_path("%2Fouter%2Finner%2F0x2F%2Fmore_inner");
|
ret = c_simple_http_cache_filename_to_path(
|
||||||
|
"%2Fouter%2Finner%2F0x2F%2Fmore_inner");
|
||||||
ASSERT_TRUE(ret);
|
ASSERT_TRUE(ret);
|
||||||
CHECK_TRUE(strcmp(ret, "/outer/inner/0x2F/more_inner") == 0);
|
CHECK_TRUE(strcmp(ret, "/outer/inner/0x2F/more_inner") == 0);
|
||||||
free(ret);
|
free(ret);
|
||||||
|
@ -721,6 +740,172 @@ int main(void) {
|
||||||
ASSERT_TRUE(ret2);
|
ASSERT_TRUE(ret2);
|
||||||
CHECK_TRUE(strcmp(ret2, uri3) == 0);
|
CHECK_TRUE(strcmp(ret2, uri3) == 0);
|
||||||
free(ret2);
|
free(ret2);
|
||||||
|
|
||||||
|
// Set up test config to get template map to test cache.
|
||||||
|
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
|
||||||
|
const char *test_http_template_filename5 =
|
||||||
|
"/tmp/c_simple_http_template_test5.config";
|
||||||
|
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
|
||||||
|
const char *test_http_template_html_filename3 =
|
||||||
|
"/tmp/c_simple_http_template_test3.html";
|
||||||
|
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
|
||||||
|
const char *test_http_template_html_var_filename2 =
|
||||||
|
"/tmp/c_simple_http_template_test_var2.html";
|
||||||
|
|
||||||
|
FILE *test_file = fopen(test_http_template_filename5, "w");
|
||||||
|
ASSERT_TRUE(test_file);
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite(
|
||||||
|
"PATH=/\nHTML_FILE=/tmp/c_simple_http_template_test3.html\n",
|
||||||
|
1,
|
||||||
|
56,
|
||||||
|
test_file)
|
||||||
|
== 56);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite(
|
||||||
|
"VAR_FILE=/tmp/c_simple_http_template_test_var2.html\n",
|
||||||
|
1,
|
||||||
|
52,
|
||||||
|
test_file)
|
||||||
|
== 52);
|
||||||
|
fclose(test_file);
|
||||||
|
|
||||||
|
test_file = fopen(test_http_template_html_filename3, "w");
|
||||||
|
ASSERT_TRUE(test_file);
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite(
|
||||||
|
"<body>{{{VAR_FILE}}}</body>\n",
|
||||||
|
1,
|
||||||
|
28,
|
||||||
|
test_file)
|
||||||
|
== 28);
|
||||||
|
fclose(test_file);
|
||||||
|
|
||||||
|
test_file = fopen(test_http_template_html_var_filename2, "w");
|
||||||
|
ASSERT_TRUE(test_file);
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite(
|
||||||
|
"Some test text.<br>Yep.",
|
||||||
|
1,
|
||||||
|
23,
|
||||||
|
test_file)
|
||||||
|
== 23);
|
||||||
|
fclose(test_file);
|
||||||
|
|
||||||
|
__attribute__((cleanup(c_simple_http_clean_up_parsed_config)))
|
||||||
|
C_SIMPLE_HTTP_ParsedConfig templates =
|
||||||
|
c_simple_http_parse_config(test_http_template_filename5, "PATH", NULL);
|
||||||
|
ASSERT_TRUE(templates.paths);
|
||||||
|
|
||||||
|
// Run cache function. Should return >0 due to new/first cache entry.
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
|
||||||
|
char *buf = NULL;
|
||||||
|
int int_ret = c_simple_http_cache_path(
|
||||||
|
"/",
|
||||||
|
test_http_template_filename5,
|
||||||
|
"/tmp/c_simple_http_cache_dir",
|
||||||
|
&templates,
|
||||||
|
&buf);
|
||||||
|
|
||||||
|
CHECK_TRUE(int_ret > 0);
|
||||||
|
ASSERT_TRUE(buf);
|
||||||
|
CHECK_TRUE(strcmp(buf, "<body>Some test text.<br>Yep.</body>\n") == 0);
|
||||||
|
free(buf);
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
// Check/get size of cache file.
|
||||||
|
FILE *cache_file = fopen("/tmp/c_simple_http_cache_dir/ROOT", "r");
|
||||||
|
uint_fast8_t cache_file_exists = cache_file ? 1 : 0;
|
||||||
|
fseek(cache_file, 0, SEEK_END);
|
||||||
|
const long cache_file_size_0 = ftell(cache_file);
|
||||||
|
fclose(cache_file);
|
||||||
|
ASSERT_TRUE(cache_file_exists);
|
||||||
|
|
||||||
|
// Re-run cache function, checking that it is not invalidated.
|
||||||
|
int_ret = c_simple_http_cache_path(
|
||||||
|
"/",
|
||||||
|
test_http_template_filename5,
|
||||||
|
"/tmp/c_simple_http_cache_dir",
|
||||||
|
&templates,
|
||||||
|
&buf);
|
||||||
|
CHECK_TRUE(int_ret == 0);
|
||||||
|
ASSERT_TRUE(buf);
|
||||||
|
CHECK_TRUE(strcmp(buf, "<body>Some test text.<br>Yep.</body>\n") == 0);
|
||||||
|
free(buf);
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
// Check/get size of cache file.
|
||||||
|
cache_file = fopen("/tmp/c_simple_http_cache_dir/ROOT", "r");
|
||||||
|
cache_file_exists = cache_file ? 1 : 0;
|
||||||
|
fseek(cache_file, 0, SEEK_END);
|
||||||
|
const long cache_file_size_1 = ftell(cache_file);
|
||||||
|
fclose(cache_file);
|
||||||
|
ASSERT_TRUE(cache_file_exists);
|
||||||
|
CHECK_TRUE(cache_file_size_0 == cache_file_size_1);
|
||||||
|
|
||||||
|
// Change a file used by the template for PATH=/ .
|
||||||
|
test_file = fopen(test_http_template_html_var_filename2, "w");
|
||||||
|
ASSERT_TRUE(test_file);
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
fwrite(
|
||||||
|
"Alternate test text.<br>Yep.",
|
||||||
|
1,
|
||||||
|
28,
|
||||||
|
test_file)
|
||||||
|
== 28);
|
||||||
|
fclose(test_file);
|
||||||
|
|
||||||
|
// Re-run cache function, checking that it is invalidated.
|
||||||
|
int_ret = c_simple_http_cache_path(
|
||||||
|
"/",
|
||||||
|
test_http_template_filename5,
|
||||||
|
"/tmp/c_simple_http_cache_dir",
|
||||||
|
&templates,
|
||||||
|
&buf);
|
||||||
|
CHECK_TRUE(int_ret > 0);
|
||||||
|
ASSERT_TRUE(buf);
|
||||||
|
CHECK_TRUE(strcmp(buf, "<body>Alternate test text.<br>Yep.</body>\n") == 0);
|
||||||
|
free(buf);
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
// Get/check size of cache file.
|
||||||
|
cache_file = fopen("/tmp/c_simple_http_cache_dir/ROOT", "r");
|
||||||
|
cache_file_exists = cache_file ? 1 : 0;
|
||||||
|
fseek(cache_file, 0, SEEK_END);
|
||||||
|
const long cache_file_size_2 = ftell(cache_file);
|
||||||
|
fclose(cache_file);
|
||||||
|
ASSERT_TRUE(cache_file_exists);
|
||||||
|
CHECK_TRUE(cache_file_size_0 != cache_file_size_2);
|
||||||
|
|
||||||
|
// Re-run cache function, checking that it is not invalidated.
|
||||||
|
int_ret = c_simple_http_cache_path(
|
||||||
|
"/",
|
||||||
|
test_http_template_filename5,
|
||||||
|
"/tmp/c_simple_http_cache_dir",
|
||||||
|
&templates,
|
||||||
|
&buf);
|
||||||
|
CHECK_TRUE(int_ret == 0);
|
||||||
|
ASSERT_TRUE(buf);
|
||||||
|
CHECK_TRUE(strcmp(buf, "<body>Alternate test text.<br>Yep.</body>\n") == 0);
|
||||||
|
free(buf);
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
// Get/check size of cache file.
|
||||||
|
cache_file = fopen("/tmp/c_simple_http_cache_dir/ROOT", "r");
|
||||||
|
cache_file_exists = cache_file ? 1 : 0;
|
||||||
|
fseek(cache_file, 0, SEEK_END);
|
||||||
|
const long cache_file_size_3 = ftell(cache_file);
|
||||||
|
fclose(cache_file);
|
||||||
|
ASSERT_TRUE(cache_file_exists);
|
||||||
|
CHECK_TRUE(cache_file_size_2 == cache_file_size_3);
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
|
remove("/tmp/c_simple_http_cache_dir/ROOT");
|
||||||
|
rmdir("/tmp/c_simple_http_cache_dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN()
|
RETURN()
|
||||||
|
|
Loading…
Reference in a new issue