Compare commits

..

43 commits
1.0 ... main

Author SHA1 Message Date
cfa7a065b2 Version 1.5
All checks were successful
Build for Releases / ensure-release-exists (push) Successful in 3s
Build for Releases / push-build-x86_64 (push) Successful in 6s
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2m9s
Build for Releases / push-build-aarch64 (push) Successful in 1m15s
Build for Releases / push-build-x86_64_debian (push) Successful in 34s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m15s
2024-11-25 17:39:04 +09:00
ca5296920c Update third-party submodule SimpleArchiver
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 20s
2024-11-25 17:23:17 +09:00
faa0c457d9 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2m9s
2024-11-25 16:04:44 +09:00
d6edaa3bdd Use proper format constants for size_t/uintX_t etc
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
2024-11-25 16:01:49 +09:00
0ecb8d6801 Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
2024-11-25 14:21:00 +09:00
8052e693e6 Update Changelog.md 2024-11-25 14:20:28 +09:00
a467cceb90 Add separate flag for generate-static overwrite
Previous implementation allowed static files to overwrite existing files
on generate if static-dir was specified and generate-enable-overwrite
was specified. Now, an additional flag
"--generate-static-enable-overwrite" determines if static-dir files
overwrite on generate.
2024-11-25 14:17:32 +09:00
8e58873ea9 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
2024-11-21 21:05:11 +09:00
2d485a3824 Version 1.4
All checks were successful
Build for Releases / ensure-release-exists (push) Successful in 3s
Run Unit Tests / build-and-run-unit-tests (push) Successful in 20s
Build for Releases / push-build-x86_64 (push) Successful in 6s
Build for Releases / push-build-aarch64 (push) Successful in 17s
Build for Releases / push-build-x86_64_debian (push) Successful in 36s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m29s
2024-11-21 14:14:09 +09:00
3fc301cde5 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
2024-11-21 14:06:09 +09:00
82693bfa3e Ensure http_template.c keeps track of _FILE files 2024-11-21 14:05:33 +09:00
e6e9fc16af More testing of nested FOREACH in example_config
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2m10s
2024-11-20 17:12:10 +09:00
fddc43f36b More notes about FOREACH in Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 23s
2024-11-20 16:21:28 +09:00
10bf88ec3e Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 25s
2024-11-20 16:16:25 +09:00
20f413c8d9 Impl. nested FOREACH expressions
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 37s
IF/ELSEIF/ELSE/ENDIF and FOREACH/ENDFOREACH can now be nested in each
other in HTML templates.

TODO: Cleanup/refactorings, properly set up "files_map".
2024-11-20 16:12:58 +09:00
5e2ae5f87b Fix Unit Tests whilst keeping FOREACH working
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 21s
2024-11-19 21:26:42 +09:00
d46a323b15 Impl. FOREACH and NOTFOREACH
Some checks failed
Run Unit Tests / build-and-run-unit-tests (push) Failing after 4s
TODO: Figure out nested FOREACH or abandon that feature.
2024-11-19 20:53:11 +09:00
e9de2682f6 Refactoring related to {{{!IF}}}... 2024-11-19 15:07:40 +09:00
12a1edc897 Allow nested {{{!IF}}}..., fixes
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 20s
2024-11-18 20:10:45 +09:00
63a5eca63c Fix memory leak for array variables
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2m7s
2024-11-18 17:41:26 +09:00
ba1c692e2f Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 23s
2024-11-18 17:10:41 +09:00
8da223461d Impl. extension of template delimeter expressions
Implemented "IF", "ELSEIF", "ELSE", "ENDIF", and "INDEX" for templates.

IF is used like: `{{{!IF Variable==SomeString}}}`.
ELSEIF is used like: `{{{!ELSEIF Variable==AnotherString}}}`.
ELSE is used like: `{{{!ELSE}}}`.
ENDIF is used like: `{{{!ENDIF}}}`.
INDEX is used like: `{{{!INDEX ArrayVar[2]}}}`.
2024-11-18 17:07:07 +09:00
d9d286c53f Refactor template handling
Split off some code into separate function to be updated for support for
more template features.
2024-11-18 13:11:13 +09:00
08769d026e Update third-party submodule SimpleArchiver
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
2024-11-15 17:42:26 +09:00
2c82b82b06 Allow "multi-valued-key-entries" in ParsedConfig
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 25s
This commit begins work on adding more templating features.
2024-11-15 16:28:31 +09:00
35aa5fc5a3 Update Changelog.md, Bump version 1.3
All checks were successful
Build for Releases / ensure-release-exists (push) Successful in 4s
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
Build for Releases / push-build-x86_64 (push) Successful in 6s
Build for Releases / push-build-aarch64 (push) Successful in 1m11s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m25s
Build for Releases / push-build-x86_64_debian (push) Successful in 28s
2024-11-14 10:41:43 +09:00
6e9f0709b4 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 20s
2024-11-12 17:26:33 +09:00
b056a8f99d Add missing NULL check in arg_parse.c
Previous implementation didn't crash because `closedir(...)` is lenient.
2024-11-12 17:25:52 +09:00
1b683f3395 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 21s
2024-11-12 17:18:21 +09:00
33be6c49fd Fix erronous buffer declaration 2024-11-12 17:16:58 +09:00
cdacfc106d Add strip --strip-unneeded to debian builds (ci)
All checks were successful
Build for Releases / ensure-release-exists (push) Successful in 3s
Build for Releases / push-build-x86_64 (push) Successful in 6s
Build for Releases / push-build-aarch64 (push) Successful in 1m12s
Build for Releases / push-build-x86_64_debian (push) Successful in 33s
Build for Releases / push-build-aarch64_debian (push) Successful in 2m5s
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2m0s
2024-11-12 13:49:19 +09:00
600d558b11 Update README.md
All checks were successful
Build for Releases / ensure-release-exists (push) Successful in 4s
Build for Releases / push-build-x86_64 (push) Successful in 9s
Run Unit Tests / build-and-run-unit-tests (push) Successful in 24s
Build for Releases / push-build-aarch64 (push) Successful in 1m12s
Build for Releases / push-build-x86_64_debian (push) Successful in 37s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m32s
2024-11-12 12:06:21 +09:00
b155934701 Update Changelod.md, version 1.2 2024-11-12 12:04:36 +09:00
4784f83234 Impl. copying over "static-dir" files on generate
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 34s
2024-11-11 15:09:12 +09:00
37e0c3a98b Remove unused parameter in function in config.c
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
2024-11-10 21:09:55 +09:00
0bbe280e1f Cleanup by separating "generate" to source files 2024-11-10 21:06:41 +09:00
faa262ba95 Check if fopen() fails during generating html
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 21s
2024-11-10 20:47:56 +09:00
68551de565 Fix typo in impl. of --generate-dir=<DIR>
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 21s
2024-11-10 20:45:37 +09:00
de2d15033e Impl. --generate-dir=<DIR>
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
Also added flag `--generate-enable-overwrite`.

Resolves #11
2024-11-10 20:39:49 +09:00
0d552d2cb0 Version 1.1
All checks were successful
Build for Releases / ensure-release-exists (push) Successful in 1s
Run Unit Tests / build-and-run-unit-tests (push) Successful in 20s
Build for Releases / push-build-x86_64 (push) Successful in 6s
Build for Releases / push-build-aarch64 (push) Successful in 1m8s
Build for Releases / push-build-x86_64_debian (push) Successful in 34s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m9s
2024-11-08 15:20:08 +09:00
e1dde1576a Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 19s
2024-11-08 14:37:54 +09:00
1cb593e155 Allow key/value buffers to be dynamically sized
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 22s
Fixes #10
2024-11-08 14:32:57 +09:00
95aef64618 Refactor config.c to change c-macros to functions
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 1m56s
Resolves #9
2024-11-08 14:00:36 +09:00
25 changed files with 2469 additions and 450 deletions

View file

@ -172,6 +172,8 @@ jobs:
cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release
make -C buildRelease c_simple_http make -C buildRelease c_simple_http
strip --strip-unneeded buildRelease/c_simple_http
popd >&/dev/null popd >&/dev/null
zstd --ultra -20 c_simple_http_clone/buildRelease/c_simple_http -o "${BUILD_ASSET_NAME}" zstd --ultra -20 c_simple_http_clone/buildRelease/c_simple_http -o "${BUILD_ASSET_NAME}"
@ -233,6 +235,8 @@ jobs:
cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release
make -C buildRelease c_simple_http make -C buildRelease c_simple_http
strip --strip-unneeded buildRelease/c_simple_http
popd >&/dev/null popd >&/dev/null
zstd --ultra -20 c_simple_http_clone/buildRelease/c_simple_http -o "${BUILD_ASSET_NAME}" zstd --ultra -20 c_simple_http_clone/buildRelease/c_simple_http -o "${BUILD_ASSET_NAME}"

View file

@ -13,6 +13,7 @@ set(c_simple_http_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/src/helpers.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/helpers.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/html_cache.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/html_cache.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/static.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/static.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/generate.c"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/helpers.c" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/helpers.c"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/linked_list.c" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/linked_list.c"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c"

View file

@ -1,6 +1,81 @@
# Changelog # Changelog
## Latest Changes ## Upcoming Changes
## Version 1.5
Add flag `--generate-static-enable-overwrite`. This flag enables overwriting of
files from static-dir to generate-dir (if static-dir was specified). Previous
implementation relied on `--generate-enable-ovewrite` for this behavior.
Minor refactorings related to `printf` and `uintX_t`/`size_t` types.
## Version 1.4
Implemented "IF", "ELSEIF", "ELSE", "ENDIF", and "INDEX" for templates.
IF is used like: `{{{!IF Variable==SomeString}}}`.
Not equals can also be used: `{{{!IF Variable!=OtherString}}}`.
ELSEIF is used like: `{{{!ELSEIF Variable==AnotherString}}}`.
Not equals can also be used: `{{{!ELSEIF Variable!=AnotherOtherString}}}`.
ELSE is used like: `{{{!ELSE}}}`.
ENDIF is used like: `{{{!ENDIF}}}`.
INDEX is used like: `{{{!INDEX ArrayVar[2]}}}`.
Implemented "FOREACH" and "ENDFOREACH" for templates.
FOREACH is used like:
PATH=/
HTML='''
{{{!FOREACH ArrayVar}}}
{{{ArrayVar}}}
{{{!ENDFOREACH}}}'''
ArrayVar=FirstValue
ArrayVar=SecondValue
ArrayVar=ThirdValue
For multiple variables to expand in FOREACH:
PATH=/
HTML='''
{{{!FOREACH ArrayVar!ArrayVarSecond!ArrayVarThird}}}
{{{ArrayVar}}}
{{{ArrayVarSecond}}}
{{{ArrayVarThird}}}
{{{!ENDFOREACH}}}'''
ArrayVar=FirstVarOnce
ArrayVar=FirstVarTwice
ArrayVarSecond=SecondVarOnce
ArrayVarSecond=SecondVarTwice
ArrayVarThird=ThirdVarOnce
ArrayVarThird=ThirdVarTwice
Implemented nestable "IF" and "FOREACH" expressions in templates. In other
words, there can be `{{{!IF}}}` inside other IF/FOREACH blocks, and vice versa.
## Version 1.3
Fix internal erronous buffer declaration.
Fix internal missing NULL check.
## Version 1.2
Add the `--generate-dir=<DIR>` option, which will generate all html into the
given directory. This requires `--config=<CONFIG_FILE>`.
`--generate-enable-overwrite` is required to overwrite existing files when using
`--generate-dir=<DIR>`.
If `--enable-static-dir=<DIR>` is also specified with generate, then the files
in the given directory will be copied into the directory specified with
`--generate-dir=<DIR>`.
## Version 1.1
Some refactoring of code handling parsing the config file.
Remove buffer-size-limit on config file entries.
## Version 1.0 ## Version 1.0
@ -12,3 +87,7 @@ Features:
- Reload configuration on SIGUSR1 or by listening (enabled by cmd parameter). - Reload configuration on SIGUSR1 or by listening (enabled by cmd parameter).
- Cache served html (enabled by cmd parameter). - Cache served html (enabled by cmd parameter).
- Serve static files from "static-dir" (enabled by cmd parameter). - Serve static files from "static-dir" (enabled by cmd parameter).
<!--
vim: textwidth=80 et sw=2 ts=2 sts=2
-->

View file

@ -44,7 +44,8 @@ HEADERS = \
src/http_template.h \ src/http_template.h \
src/helpers.h \ src/helpers.h \
src/html_cache.h \ src/html_cache.h \
src/static.h src/static.h \
src/generate.h
SOURCES = \ SOURCES = \
src/main.c \ src/main.c \
@ -59,6 +60,7 @@ SOURCES = \
src/helpers.c \ src/helpers.c \
src/html_cache.c \ src/html_cache.c \
src/static.c \ src/static.c \
src/generate.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 \

View file

@ -15,6 +15,9 @@ A simple HTTP/1.1 server written in C.
--enable-cache-dir=<DIR> --enable-cache-dir=<DIR>
--cache-entry-lifetime-seconds=<SECONDS> --cache-entry-lifetime-seconds=<SECONDS>
--enable-static-dir=<DIR> --enable-static-dir=<DIR>
--generate-dir=<DIR>
--generate-enable-overwrite
--generate-static-enable-overwrite
## Changelog ## Changelog

View file

@ -0,0 +1 @@
<b>Each File ONE</b><br>

View file

@ -0,0 +1 @@
<b>Each File TWO</b><br>

View file

@ -0,0 +1 @@
<b>Each File ZERO</b><br>

View file

@ -1,6 +1,6 @@
PATH=/ PATH=/
HTML=''' HTML='''
<html> <html>
<head> <head>
<style> <style>
body { body {
@ -24,14 +24,131 @@ HTML='''
} }
</style> </style>
</head> </head>
<body> <body>
<h1>Test HTML</h1><br> <h2>Test IF/FOREACH Expr</h2>
<h2>{{{Var}}}</h2>
<h3><a href="/inner">To inner.</a></h3> Outer IF<br>
</body> <pre>
{{{!IF ThisValue==true}}}
ThisValue is <b>true</b>.<br>
{{{!FOREACH ArrayValue}}}
{{{!IF ThisOtherValue==true}}}
{{{ArrayValue}}}<br>
ThisOtherValue is <b>true</b>.<br>
{{{Var}}}<br>
{{{!ELSE}}}
{{{ArrayValue}}}<br>
ThisOtherValue is <b>NOT true</b>.<br>
{{{Var}}}<br>
{{{!ENDIF}}}
{{{!ENDFOREACH}}}
{{{!ELSE}}}
ThisValue is <b>NOT true</b>.<br>
{{{!FOREACH ArrayValueSecond}}}
{{{!IF ThisOtherValue==true}}}
{{{ArrayValueSecond}}}<br>
ThisOtherValue is <b>true</b>.<br>
{{{Var}}}<br>
{{{!ELSE}}}
{{{ArrayValueSecond}}}<br>
ThisOtherValue is <b>NOT true</b>.<br>
{{{Var}}}<br>
{{{!ENDIF}}}
{{{!ENDFOREACH}}}
{{{!ENDIF}}}
</pre><br>
Outer FOREACH<br>
<pre>
{{{!FOREACH ArrayValue}}}
{{{ArrayValue}}}<br>
{{{!IF ThisValue==true}}}
ThisValue is <b>true</b>.<br>
{{{!FOREACH ArrayValueSecond}}}
{{{ArrayValueSecond}}}<br>
{{{!IF ThisOtherValue==true}}}
ThisOtherValue is <b>true</b>.<br>
{{{Var}}}<br>
{{{!ELSE}}}
ThisOtherValue is <b>NOT true</b>.<br>
{{{Var}}}<br>
{{{!ENDIF}}}
{{{!ENDFOREACH}}}
{{{!ELSE}}}
ThisValue is <b>NOT true</b>.<br>
{{{!FOREACH ArrayValueSecond}}}
{{{ArrayValueSecond}}}<br>
{{{!IF ThisOtherValue==true}}}
ThisOtherValue is <b>true</b>.<br>
{{{Var}}}<br>
{{{!ELSE}}}
ThisOtherValue is <b>NOT true</b>.<br>
{{{Var}}}<br>
{{{!ENDIF}}}
{{{!ENDFOREACH}}}
{{{!ENDIF}}}
{{{!ENDFOREACH}}}
</pre><br>
Nested FOREACH:<br>
<pre>
{{{!FOREACH ArrayValue}}}
{{{ArrayValue}}}
{{{!FOREACH ArrayValueSecond}}}
{{{ArrayValueSecond}}}
{{{!FOREACH ArrayValueThird}}}
{{{ArrayValueThird}}}
{{{!FOREACH ArrayValueFourth}}}
{{{ArrayValueFourth}}}
{{{!FOREACH Each_FILE}}}
{{{Each_FILE}}}
{{{!ENDFOREACH}}}
{{{!ENDFOREACH}}}
{{{!ENDFOREACH}}}
{{{!ENDFOREACH}}}
{{{!ENDFOREACH}}}
</pre>
<br><h2><a href="/inner">inner</a></h2>
</body>
</html>
''' '''
Var='''Test var value''' Var='''Test var value'''
ThisValue=true
ThisOtherValue=true
ArrayValue=1_IDX_Zero
ArrayValue=1_IDX_One
ArrayValue=1_IDX_Two
ArrayValueSecond=2_IDX_Zero
ArrayValueSecond=2_IDX_One
ArrayValueSecond=2_IDX_Two
ArrayValueThird=3_IDX_Zero
ArrayValueThird=3_IDX_One
ArrayValueThird=3_IDX_Two
ArrayValueFourth=4_IDX_Zero
ArrayValueFourth=4_IDX_One
ArrayValueFourth=4_IDX_Two
EachTestHead='''First Entry Head'''
EachTestMid='''First Entry Mid'''
EachTestTail='''First Entry Tail'''
EachTestHead='''Second Entry Head'''
EachTestMid='''Second Entry Mid'''
EachTestTail='''Second Entry Tail'''
EachTestHead='''Third Entry Head'''
EachTestMid='''Third Entry Mid'''
EachTestTail='''Third Entry Tail'''
Each_FILE='''example_config/each_file_zero.html'''
Each_FILE='''example_config/each_file_one.html'''
Each_FILE='''example_config/each_file_two.html'''
PATH=/inner PATH=/inner
HTML_FILE='''example_config/inner.html''' HTML_FILE='''example_config/inner.html'''

View file

@ -44,6 +44,9 @@ void print_usage(void) {
puts(" --enable-cache-dir=<DIR>"); puts(" --enable-cache-dir=<DIR>");
puts(" --cache-entry-lifetime-seconds=<SECONDS>"); puts(" --cache-entry-lifetime-seconds=<SECONDS>");
puts(" --enable-static-dir=<DIR>"); puts(" --enable-static-dir=<DIR>");
puts(" --generate-dir=<DIR>");
puts(" --generate-enable-overwrite");
puts(" --generate-static-enable-overwrite");
} }
Args parse_args(int32_t argc, char **argv) { Args parse_args(int32_t argc, char **argv) {
@ -126,7 +129,7 @@ Args parse_args(int32_t argc, char **argv) {
exit(1); exit(1);
} else { } else {
printf( printf(
"NOTICE set cache-entry-lifetime to %lu\n", "NOTICE set cache-entry-lifetime to %zu\n",
args.cache_lifespan_seconds); args.cache_lifespan_seconds);
} }
} else if (strncmp(argv[0], "--enable-static-dir=", 20) == 0) { } else if (strncmp(argv[0], "--enable-static-dir=", 20) == 0) {
@ -152,6 +155,43 @@ Args parse_args(int32_t argc, char **argv) {
printf("Directory \"%s\" exists.\n", args.static_dir); printf("Directory \"%s\" exists.\n", args.static_dir);
} }
closedir(d); closedir(d);
} else if (strncmp(argv[0], "--generate-dir=", 15) == 0) {
args.generate_dir = argv[0] + 15;
// Check if it actually is an existing directory.
DIR *d = opendir(args.generate_dir);
if (d == NULL) {
if (errno == ENOENT) {
printf(
"Directory \"%s\" doesn't exist, creating it...\n",
args.generate_dir);
int ret = mkdir(
args.generate_dir,
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret == -1) {
fprintf(
stderr,
"ERROR Failed to create new directory (errno %d)\n",
errno);
exit(1);
}
} else {
fprintf(
stderr,
"ERROR Failed to open directory \"%s\" (errno %d)!\n",
args.generate_dir,
errno);
exit(1);
}
} else {
printf("Directory \"%s\" exists.\n", args.generate_dir);
}
if (d) {
closedir(d);
}
} else if (strcmp(argv[0], "--generate-enable-overwrite") == 0) {
args.flags |= 4;
} else if (strcmp(argv[0], "--generate-static-enable-overwrite") == 0) {
args.flags |= 8;
} else { } else {
fprintf(stderr, "ERROR: Invalid args!\n"); fprintf(stderr, "ERROR: Invalid args!\n");
print_usage(); print_usage();

View file

@ -28,6 +28,8 @@ typedef struct Args {
// xxxx xxx1 - disable peer addr print. // xxxx xxx1 - disable peer addr print.
// xxxx xx0x - disable listen on config file for reloading. // xxxx xx0x - disable listen on config file for reloading.
// xxxx xx1x - enable listen on config file for reloading. // xxxx xx1x - enable listen on config file for reloading.
// xxxx x1xx - enable overwrite on generate.
// xxxx 1xxx - enable overwrite on generate for static dir.
uint16_t flags; uint16_t flags;
uint16_t port; uint16_t port;
// Does not need to be free'd, this should point to a string in argv. // Does not need to be free'd, this should point to a string in argv.
@ -41,6 +43,9 @@ typedef struct Args {
// Non-NULL if static-dir is specified and files in the dir are to be served. // Non-NULL if static-dir is specified and files in the dir are to be served.
// Does not need to be free'd since it points to a string in argv. // Does not need to be free'd since it points to a string in argv.
const char *static_dir; const char *static_dir;
// Non-NULL if generate-dir is specified.
// Does not need to be free'd since it points to a string in argv.
const char *generate_dir;
} Args; } Args;
void print_usage(void); void print_usage(void);

View file

@ -26,215 +26,33 @@
// Local includes // Local includes
#include "constants.h" #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; \
} \
} \
}
#define CHECK_ADD_VALUE() \
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 = 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;
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 { typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredIter {
SDArchiverHashMap *hash_map; SDArchiverHashMap *hash_map;
const char *path; const char *path;
} C_SIMPLE_HTTP_INTERNAL_RequiredIter; } C_SIMPLE_HTTP_INTERNAL_RequiredIter;
typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredCheck {
const SDArchiverHashMap *map_of_paths_and_their_vars;
const SDArchiverLinkedList *required;
} C_SIMPLE_HTTP_INTERNAL_RequiredCheck;
void c_simple_http_cleanup_config_value(
C_SIMPLE_HTTP_ConfigValue *config_value) {
if (config_value) {
if (config_value->next) {
c_simple_http_cleanup_config_value(config_value->next);
}
if(config_value->value) {
free(config_value->value);
}
free(config_value);
}
}
void c_simple_http_cleanup_config_value_void_ptr(void *config_value) {
c_simple_http_cleanup_config_value(config_value);
}
int c_simple_http_required_iter_fn(void *data, void *ud) { int c_simple_http_required_iter_fn(void *data, void *ud) {
C_SIMPLE_HTTP_INTERNAL_RequiredIter *req_iter_struct = ud; C_SIMPLE_HTTP_INTERNAL_RequiredIter *req_iter_struct = ud;
uint32_t data_str_length = (uint32_t)strlen(data) + 1; uint32_t data_str_length = (uint32_t)strlen(data) + 1;
@ -260,10 +78,245 @@ int c_simple_http_required_iter_fn(void *data, void *ud) {
return 0; return 0;
} }
typedef struct C_SIMPLE_HTTP_INTERNAL_RequiredCheck { void c_simple_http_hash_map_wrapper_cleanup_hashmap_fn(void *data) {
const SDArchiverHashMap *map_of_paths_and_their_vars; C_SIMPLE_HTTP_HashMapWrapper *wrapper = data;
const SDArchiverLinkedList *required; simple_archiver_hash_map_free(&wrapper->paths);
} C_SIMPLE_HTTP_INTERNAL_RequiredCheck; 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 internal_single_quote_decrement(uint_fast8_t *single_quote_count,
uint32_t *state,
char **key_buf,
uint32_t *key_capacity,
uint32_t *key_idx,
char **value_buf,
uint32_t *value_capacity,
uint32_t *value_idx) {
for(; (*single_quote_count) > 0; --(*single_quote_count)) {
if (((*state) & 1) == 0) {
(*key_buf)[(*key_idx)++] = '\'';
if (*key_idx >= *key_capacity) {
(*key_capacity) *= 2;
(*key_buf) = realloc(*key_buf, *key_capacity);
}
} else {
(*value_buf)[(*value_idx)++] = '\'';
if ((*value_idx) >= *value_capacity) {
(*value_capacity) *= 2;
(*value_buf) = realloc(*value_buf, *value_capacity);
}
}
}
}
void internal_double_quote_decrement(uint_fast8_t *double_quote_count,
uint32_t *state,
char **key_buf,
uint32_t *key_capacity,
uint32_t *key_idx,
char **value_buf,
uint32_t *value_capacity,
uint32_t *value_idx) {
for(; (*double_quote_count) > 0; --(*double_quote_count)) {
if (((*state) & 1) == 0) {
(*key_buf)[(*key_idx)++] = '"';
if ((*key_idx) >= (*key_capacity)) {
(*key_capacity) *= 2;
(*key_buf) = realloc(*key_buf, *key_capacity);
}
} else {
(*value_buf)[(*value_idx)++] = '"';
if ((*value_idx) >= (*value_capacity)) {
(*value_capacity) *= 2;
(*value_buf) = realloc(*value_buf, *value_capacity);
}
}
}
}
/// Returns non-zero if config should be returned.
int internal_check_add_value(uint32_t *state,
char **key_buf,
uint32_t *key_idx,
char **value_buf,
uint32_t *value_capacity,
uint32_t *value_idx,
C_SIMPLE_HTTP_ParsedConfig *config,
const char *separating_key,
const SDArchiverLinkedList *required_names,
const uint32_t separating_key_size,
SDArchiverLinkedList *paths,
char **current_separating_key_value,
uint32_t *current_separating_key_value_size) {
if ((*value_idx) < (*value_capacity)) {
(*value_buf)[(*value_idx)++] = 0;
} else {
(*value_capacity) *= 2;
(*value_buf) = realloc(*value_buf, *value_capacity);
(*value_buf)[(*value_idx)++] = 0;
}
(*state) &= 0xFFFFFFFE;
/* Check if key is separating_key. */
if (strcmp((*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 1;
}
}
(*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);
C_SIMPLE_HTTP_ConfigValue *config_value =
malloc(sizeof(C_SIMPLE_HTTP_ConfigValue));
config_value->value = malloc(*value_idx);
config_value->next = NULL;
memcpy(config_value->value, *value_buf, (*value_idx));
if (simple_archiver_hash_map_insert(
hash_map,
config_value,
key,
separating_key_size,
c_simple_http_cleanup_config_value_void_ptr,
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(config_value->value);
free(config_value);
return 1;
}
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,
config_value->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 1;
}
simple_archiver_list_add(paths, config_value->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 1;
} 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 1;
}
unsigned char *key = malloc(*key_idx);
memcpy(key, *key_buf, *key_idx);
unsigned char *value = malloc(*value_idx);
memcpy(value, *value_buf, (*value_idx));
// Check if key already exists in wrapped hash-map.
C_SIMPLE_HTTP_ConfigValue *config_value =
simple_archiver_hash_map_get(hash_map_wrapper->paths, key, *key_idx);
if (config_value) {
while(config_value->next) {
config_value = config_value->next;
}
config_value->next = malloc(sizeof(C_SIMPLE_HTTP_ConfigValue));
config_value->next->value = (char*)value;
config_value->next->next = NULL;
free(key);
} else {
config_value = malloc(sizeof(C_SIMPLE_HTTP_ConfigValue));
config_value->value = (char*)value;
config_value->next = NULL;
if (simple_archiver_hash_map_insert(
hash_map_wrapper->paths,
config_value,
key,
*key_idx,
c_simple_http_cleanup_config_value_void_ptr,
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 1;
}
}
}
(*key_idx) = 0;
(*value_idx) = 0;
return 0;
}
void c_simple_http_hash_map_cleanup_helper(SDArchiverHashMap *hash_map) {
simple_archiver_hash_map_free(&hash_map);
}
int c_simple_http_check_required_iter_fn(void *path_void_str, void *ud) { int c_simple_http_check_required_iter_fn(void *path_void_str, void *ud) {
C_SIMPLE_HTTP_INTERNAL_RequiredCheck *req = ud; C_SIMPLE_HTTP_INTERNAL_RequiredCheck *req = ud;
@ -318,8 +371,12 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
c_simple_http_clean_up_parsed_config(&config); c_simple_http_clean_up_parsed_config(&config);
return config; return config;
} }
unsigned char key_buf[C_SIMPLE_HTTP_CONFIG_BUF_SIZE]; __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
unsigned char value_buf[C_SIMPLE_HTTP_CONFIG_BUF_SIZE]; char *key_buf = malloc(C_SIMPLE_HTTP_CONFIG_BUF_SIZE);
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *value_buf = malloc(C_SIMPLE_HTTP_CONFIG_BUF_SIZE);
uint32_t key_capacity = C_SIMPLE_HTTP_CONFIG_BUF_SIZE;
uint32_t value_capacity = C_SIMPLE_HTTP_CONFIG_BUF_SIZE;
uint32_t key_idx = 0; uint32_t key_idx = 0;
uint32_t value_idx = 0; uint32_t value_idx = 0;
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
@ -332,8 +389,8 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
// 01xx - reading value is single quoted // 01xx - reading value is single quoted
// 10xx - reading value is double quoted // 10xx - reading value is double quoted
uint32_t state = 0; uint32_t state = 0;
unsigned char single_quote_count = 0; uint_fast8_t single_quote_count = 0;
unsigned char double_quote_count = 0; uint_fast8_t double_quote_count = 0;
int32_t c; int32_t c;
while (feof(f) == 0) { while (feof(f) == 0) {
@ -342,20 +399,55 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
break; break;
} else if ((state & 0xC) == 0 && (c == ' ' || c == '\t')) { } else if ((state & 0xC) == 0 && (c == ' ' || c == '\t')) {
// Ignore whitespace when not quoted. // Ignore whitespace when not quoted.
SINGLE_QUOTE_DECREMENT(); internal_single_quote_decrement(&single_quote_count,
DOUBLE_QUOTE_DECREMENT(); &state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
internal_double_quote_decrement(&double_quote_count,
&state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
continue; continue;
} else if ((state & 1) == 0 } else if ((state & 1) == 0
&& (state & 0xC) == 0 && (state & 0xC) == 0
&& (c == '\r' || c == '\n')) { && (c == '\r' || c == '\n')) {
// Ignore newlines when parsing for key and when not quoted. // Ignore newlines when parsing for key and when not quoted.
SINGLE_QUOTE_DECREMENT(); internal_single_quote_decrement(&single_quote_count,
DOUBLE_QUOTE_DECREMENT(); &state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
internal_double_quote_decrement(&double_quote_count,
&state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
continue; continue;
} else if ((state & 1) == 1) { } else if ((state & 1) == 1) {
if (c == '\'') { if (c == '\'') {
++single_quote_count; ++single_quote_count;
DOUBLE_QUOTE_DECREMENT(); internal_double_quote_decrement(&double_quote_count,
&state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
if (((state & 0xC) == 0x4 || (state & 0xC) == 0) if (((state & 0xC) == 0x4 || (state & 0xC) == 0)
&& single_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) { && single_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) {
@ -371,7 +463,14 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
continue; continue;
} else if (c == '"') { } else if (c == '"') {
++double_quote_count; ++double_quote_count;
SINGLE_QUOTE_DECREMENT(); internal_single_quote_decrement(&single_quote_count,
&state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
if (((state & 0xC) == 0x8 || (state & 0xC) == 0) if (((state & 0xC) == 0x8 || (state & 0xC) == 0)
&& double_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) { && double_quote_count >= C_SIMPLE_HTTP_QUOTE_COUNT_MAX) {
@ -386,47 +485,68 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
} }
continue; continue;
} else { } else {
SINGLE_QUOTE_DECREMENT(); internal_single_quote_decrement(&single_quote_count,
DOUBLE_QUOTE_DECREMENT(); &state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
internal_double_quote_decrement(&double_quote_count,
&state,
&key_buf,
&key_capacity,
&key_idx,
&value_buf,
&value_capacity,
&value_idx);
} }
} }
if ((state & 1) == 0) { if ((state & 1) == 0) {
if (c != '=') { if (c != '=') {
key_buf[key_idx++] = (unsigned char)c; key_buf[key_idx++] = (char)c;
if (key_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { if (key_idx >= key_capacity) {
fprintf(stderr, key_capacity *= 2;
"ERROR: config file \"key\" is larger than %u bytes!\n", key_buf = realloc(key_buf, key_capacity);
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
c_simple_http_clean_up_parsed_config(&config);
config.hash_map = NULL;
return config;
} }
} else { } else {
if (key_idx < C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { if (key_idx < key_capacity) {
key_buf[key_idx++] = 0; key_buf[key_idx++] = 0;
if (key_idx >= key_capacity) {
key_capacity *= 2;
key_buf = realloc(key_buf, key_capacity);
}
} else { } else {
fprintf(stderr, key_capacity *= 2;
"ERROR: config file \"key\" is larger than %u bytes!\n", key_buf = realloc(key_buf, key_capacity);
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1); key_buf[key_idx++] = 0;
c_simple_http_clean_up_parsed_config(&config);
config.hash_map = NULL;
return config;
} }
state |= 1; state |= 1;
} }
} else if ((state & 1) == 1) { } else if ((state & 1) == 1) {
if ((c != '\n' && c != '\r') || (state & 0xC) != 0) { if ((c != '\n' && c != '\r') || (state & 0xC) != 0) {
value_buf[value_idx++] = (unsigned char)c; value_buf[value_idx++] = (char)c;
if (value_idx >= C_SIMPLE_HTTP_CONFIG_BUF_SIZE) { if (value_idx >= value_capacity) {
fprintf(stderr, value_capacity *= 2;
"ERROR: config file \"value\" is larger than %u bytes!\n", value_buf = realloc(value_buf, value_capacity);
C_SIMPLE_HTTP_CONFIG_BUF_SIZE - 1);
c_simple_http_clean_up_parsed_config(&config);
config.hash_map = NULL;
return config;
} }
} else { } else {
CHECK_ADD_VALUE() if (internal_check_add_value(&state,
&key_buf,
&key_idx,
&value_buf,
&value_capacity,
&value_idx,
&config,
separating_key,
required_names,
separating_key_size,
paths,
&current_separating_key_value,
&current_separating_key_value_size)) {
return config;
}
} }
} }
} }
@ -435,7 +555,21 @@ C_SIMPLE_HTTP_ParsedConfig c_simple_http_parse_config(
if ((state & 0x1) == 1 && (state & 0xC) == 0 && value_idx != 0) { if ((state & 0x1) == 1 && (state & 0xC) == 0 && value_idx != 0) {
// Leftover "value" not added yet. // Leftover "value" not added yet.
CHECK_ADD_VALUE() if (internal_check_add_value(&state,
&key_buf,
&key_idx,
&value_buf,
&value_capacity,
&value_idx,
&config,
separating_key,
required_names,
separating_key_size,
paths,
&current_separating_key_value,
&current_separating_key_value_size)) {
return config;
}
} }
if (!current_separating_key_value) { if (!current_separating_key_value) {

View file

@ -25,6 +25,14 @@ typedef struct C_SIMPLE_HTTP_ParsedConfig {
/// Each entry in this data structure is a hash map where its value for the /// 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 /// key "PATH" is the path it represents. The "key" value should match the
/// mentioned value for "PATH". /// mentioned value for "PATH".
///
/// An example mapping for this structure (based on current example config):
/// KEY: "/", VALUE: HashMapWrapper struct
/// KEY: "/inner", VALUE: HashMapWrapper struct
/// KEY: "/inner/further", VALUE: HashMapWrapper struct
///
/// Each HashMapWrapper struct's hash-map has the following:
/// KEY: VAR_NAME, VALUE: ConfigValue struct
union { union {
SDArchiverHashMap *paths; SDArchiverHashMap *paths;
SDArchiverHashMap *hash_map; SDArchiverHashMap *hash_map;
@ -33,6 +41,15 @@ typedef struct C_SIMPLE_HTTP_ParsedConfig {
typedef C_SIMPLE_HTTP_ParsedConfig C_SIMPLE_HTTP_HashMapWrapper; typedef C_SIMPLE_HTTP_ParsedConfig C_SIMPLE_HTTP_HashMapWrapper;
typedef struct C_SIMPLE_HTTP_ConfigValue {
char *value;
struct C_SIMPLE_HTTP_ConfigValue *next;
} C_SIMPLE_HTTP_ConfigValue;
void c_simple_http_cleanup_config_value(
C_SIMPLE_HTTP_ConfigValue *config_value);
void c_simple_http_cleanup_config_value_void_ptr(void *config_value);
/// 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". A part /// 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 /// of a string can be "quoted" if it is surrounded by three single-quotes or

175
src/generate.c Normal file
View file

@ -0,0 +1,175 @@
// 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 "generate.h"
// Standard library includes.
#include <string.h>
// Linux/Unix includes.
#include <libgen.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
// Local includes.
#include "helpers.h"
#include "http_template.h"
// Third party includes.
#include <SimpleArchiver/src/helpers.h>
#include <SimpleArchiver/src/data_structures/hash_map.h>
#include <SimpleArchiver/src/data_structures/linked_list.h>
int c_simple_http_generate_paths_fn(const void *key,
size_t key_size,
__attribute__((unused)) const void *value,
void *ud) {
const char *path = key;
const ConnectionContext *ctx = ud;
const char *generate_dir = ctx->args->generate_dir;
const unsigned long path_len = key_size - 1;
const unsigned long generate_dir_len = strlen(generate_dir);
__attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *string_parts = simple_archiver_list_init();
// Add generate_dir as first path of paths to join.
c_simple_http_add_string_part(string_parts, generate_dir, 0);
// Ensure next character after generate_dir contains a '/' if generate_dir
// didn't contain one at the end.
if (generate_dir_len > 0 && generate_dir[generate_dir_len - 1] != '/') {
c_simple_http_add_string_part(string_parts, "/", 0);
}
// Append the path.
if (strcmp(path, "/") != 0) {
// Is not root.
uint32_t idx = 0;
while (idx <= path_len && path[idx] == '/') {
++idx;
}
c_simple_http_add_string_part(string_parts, path + idx, 0);
}
// Add the final '/'.
if (path_len > 0 && path[path_len - 1] != '/') {
c_simple_http_add_string_part(string_parts, "/", 0);
}
// Add the ending "index.html".
c_simple_http_add_string_part(string_parts, "index.html", 0);
// Get the combined string.
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *generated_path = c_simple_http_combine_string_parts(string_parts);
if (!generated_path) {
fprintf(stderr, "ERROR Failed to get generated path (path: %s)!\n", path);
return 1;
}
if ((ctx->args->flags & 4) == 0) {
// Overwrite not enabled, check if file already exists.
FILE *fd = fopen(generated_path, "rb");
if (fd) {
fclose(fd);
fprintf(
stderr,
"WARNING Path \"%s\" exists and \"--generate-enable-overwrite\" not "
"specified, skipping!\n",
generated_path);
return 0;
}
}
// Ensure the required dirs exist.
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *generated_path_dup = strdup(generated_path);
uint_fast8_t did_make_generated_path_dir = 0;
char *generated_path_dir = dirname(generated_path_dup);
if (generated_path_dir) {
DIR *fd = opendir(generated_path_dir);
if (!fd) {
if (errno == ENOENT) {
c_simple_http_helper_mkdir_tree(generated_path_dir);
did_make_generated_path_dir = 1;
} else {
fprintf(stderr,
"ERROR opendir on path dirname failed unexpectedly (path: %s)!"
"\n",
path);
return 1;
}
} else {
// Directory already exists.
closedir(fd);
}
} else {
fprintf(stderr,
"ERROR Failed to get dirname of generated path dir (path: %s)"
"!\n",
path);
return 1;
}
// Generate the html.
size_t html_buf_size = 0;
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *html_buf = c_simple_http_path_to_generated(path,
ctx->parsed,
&html_buf_size,
NULL);
if (!html_buf || html_buf_size == 0) {
fprintf(stderr,
"WARNING Failed to generate html for generate (path: %s), "
"skipping!\n",
path);
if (did_make_generated_path_dir) {
if (rmdir(generated_path_dir) == -1) {
fprintf(stderr,
"WARNING rmdir on generated_path_dir failed, errno: %d\n",
errno);
}
}
return 0;
}
// Save the html.
FILE *fd = fopen(generated_path, "wb");
if (!fd) {
fprintf(stderr,
"WARNING Failed to open \"%s\" for writing, skipping!\n",
generated_path);
return 0;
}
unsigned long fwrite_ret = fwrite(html_buf, 1, html_buf_size, fd);
if (fwrite_ret < html_buf_size) {
fclose(fd);
unlink(generated_path);
fprintf(stderr,
"ERROR Unable to write entirely to \"%s\"!\n",
generated_path);
return 1;
} else {
fclose(fd);
}
return 0;
}

30
src/generate.h Normal file
View file

@ -0,0 +1,30 @@
// 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_GENERATE_H_
#define SEODISPARATE_COM_C_SIMPLE_HTTP_GENERATE_H_
#include <stdlib.h>
/// See this function's usage in main.c.
int c_simple_http_generate_paths_fn(const void *key,
size_t key_size,
const void *value,
void *ud);
#endif
// vim: et ts=2 sts=2 sw=2

View file

@ -17,6 +17,7 @@
#include "helpers.h" #include "helpers.h"
// Standard library includes. // Standard library includes.
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -24,7 +25,6 @@
// libc includes. // libc includes.
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <libgen.h> #include <libgen.h>
@ -32,7 +32,9 @@ 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;
*count += part->size - 1; if (part->size > 0) {
*count += part->size - 1;
}
return 0; return 0;
} }
@ -87,9 +89,36 @@ void c_simple_http_add_string_part(
C_SIMPLE_HTTP_String_Part *string_part = C_SIMPLE_HTTP_String_Part *string_part =
malloc(sizeof(C_SIMPLE_HTTP_String_Part)); malloc(sizeof(C_SIMPLE_HTTP_String_Part));
string_part->size = strlen(c_string) + 1; if (c_string) {
string_part->buf = malloc(string_part->size); string_part->size = strlen(c_string) + 1;
memcpy(string_part->buf, c_string, string_part->size); string_part->buf = malloc(string_part->size);
memcpy(string_part->buf, c_string, string_part->size);
} else {
string_part->size = 0;
string_part->buf = NULL;
}
string_part->extra = extra;
simple_archiver_list_add(
list, string_part, c_simple_http_cleanup_string_part);
}
void c_simple_http_add_string_part_sized(
SDArchiverLinkedList *list,
const char *buffer,
size_t size,
uintptr_t extra) {
C_SIMPLE_HTTP_String_Part *string_part =
malloc(sizeof(C_SIMPLE_HTTP_String_Part));
if (buffer && size > 0) {
string_part->size = size;
string_part->buf = malloc(string_part->size - 1);
memcpy(string_part->buf, buffer, string_part->size - 1);
} else {
string_part->size = 0;
string_part->buf = NULL;
}
string_part->extra = extra; string_part->extra = extra;
@ -258,4 +287,73 @@ int c_simple_http_helper_mkdir_tree(const char *path) {
} }
} }
void c_simple_http_cleanup_DIR(DIR **fd) {
if (fd && *fd) {
closedir(*fd);
*fd = NULL;
}
}
char *c_simple_http_FILE_to_c_str(const char *filename, uint64_t *size_out) {
FILE *fd = fopen(filename, "rb");
if (!fd) {
fprintf(stderr, "ERROR Failed to open %s!\n", filename);
return NULL;
} else if (fseek(fd, 0, SEEK_END) != 0) {
fprintf(stderr, "ERROR Failed to seek to end of %s!\n", filename);
fclose(fd);
return NULL;
}
long size = ftell(fd);
if (size < 0) {
fprintf(stderr, "ERROR Failed to get seek pos of end of %s!\n", filename);
fclose(fd);
return NULL;
} else if (size == 0) {
fprintf(stderr, "ERROR Size of file \"%s\" is zero!\n", filename);
fclose(fd);
return NULL;
}
if (size_out) {
*size_out = (uint64_t)size;
}
char *buf = malloc((uint64_t)size + 1);
if (fseek(fd, 0, SEEK_SET) != 0) {
fprintf(stderr, "ERROR Failed to seek to beginning of %s!\n", filename);
free(buf);
fclose(fd);
return NULL;
} else if (fread(buf, 1, (uint64_t)size, fd) != (uint64_t)size) {
fprintf(stderr, "ERROR Failed to read from file %s!\n", filename);
free(buf);
fclose(fd);
return NULL;
}
buf[size] = 0;
fclose(fd);
return buf;
}
size_t c_simple_http_trim_end_whitespace(char *c_str) {
size_t trimmed = 0;
uint64_t idx= strlen(c_str);
for (; idx-- > 0;) {
if (c_str[idx] == ' '
|| c_str[idx] == '\n'
|| c_str[idx] == '\r'
|| c_str[idx] == '\t') {
c_str[idx] = 0;
++trimmed;
} else {
break;
}
}
return trimmed;
}
// vim: et ts=2 sts=2 sw=2 // vim: et ts=2 sts=2 sw=2

View file

@ -21,9 +21,24 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
// libc includes.
#include <time.h>
#include <dirent.h>
// Local includes.
#include "config.h"
#include "arg_parse.h"
// Third-party includes. // Third-party includes.
#include <SimpleArchiver/src/data_structures/linked_list.h> #include <SimpleArchiver/src/data_structures/linked_list.h>
typedef struct ConnectionContext {
char *buf;
const Args *args;
C_SIMPLE_HTTP_ParsedConfig *parsed;
struct timespec current_time;
} ConnectionContext;
typedef struct C_SIMPLE_HTTP_String_Part { typedef struct C_SIMPLE_HTTP_String_Part {
char *buf; char *buf;
size_t size; size_t size;
@ -41,6 +56,16 @@ void c_simple_http_cleanup_string_part(void *data);
void c_simple_http_add_string_part( void c_simple_http_add_string_part(
SDArchiverLinkedList *list, const char *c_string, uintptr_t extra); SDArchiverLinkedList *list, const char *c_string, uintptr_t extra);
/// Puts a malloced instance of String_Part into the list.
/// The given c_string will be copied into a newly malloced buffer.
/// "size" must include NULL if "buffer" is a c_string.
/// If there is no NULL at the end, "size" must be +1 actual size.
void c_simple_http_add_string_part_sized(
SDArchiverLinkedList *list,
const char *buffer,
size_t size,
uintptr_t extra);
/// Combines all String_Parts in the list and returns it as a single buffer. /// Combines all String_Parts in the list and returns it as a single buffer.
char *c_simple_http_combine_string_parts(const SDArchiverLinkedList *list); char *c_simple_http_combine_string_parts(const SDArchiverLinkedList *list);
@ -63,6 +88,15 @@ char *c_simple_http_helper_unescape_uri(const char *uri);
/// Other return values are errors. /// Other return values are errors.
int c_simple_http_helper_mkdir_tree(const char *dirpath); int c_simple_http_helper_mkdir_tree(const char *dirpath);
void c_simple_http_cleanup_DIR(DIR **fd);
/// Must be free'd if non-NULL.
char *c_simple_http_FILE_to_c_str(const char *filename, uint64_t *size_out);
/// Trims by placing NULL bytes in place of whitespace at the end of c_str.
/// Returns number of whitespace trimmed.
size_t c_simple_http_trim_end_whitespace(char *c_str);
#endif #endif
// vim: et ts=2 sts=2 sw=2 // vim: et ts=2 sts=2 sw=2

View file

@ -38,8 +38,12 @@
#include "helpers.h" #include "helpers.h"
#include "http_template.h" #include "http_template.h"
int c_simple_http_internal_write_filenames_to_cache_file(void *data, void *ud) { int c_simple_http_internal_write_filenames_to_cache_file(
char *filename = data; const void *key,
__attribute__((unused)) size_t key_size,
__attribute__((unused)) const void *value,
void *ud) {
const char *filename = key;
FILE *cache_fd = ud; FILE *cache_fd = ud;
const size_t filename_size = strlen(filename); const size_t filename_size = strlen(filename);
@ -436,8 +440,8 @@ CACHE_FILE_WRITE_CHECK:
return -5; return -5;
} }
__attribute__((cleanup(simple_archiver_list_free))) __attribute__((cleanup(simple_archiver_hash_map_free)))
SDArchiverLinkedList *used_filenames = NULL; SDArchiverHashMap *used_filenames = NULL;
size_t generated_html_size = 0; size_t generated_html_size = 0;
@ -452,10 +456,10 @@ CACHE_FILE_WRITE_CHECK:
return -4; return -4;
} }
if (simple_archiver_list_get( if (simple_archiver_hash_map_iter(
used_filenames, used_filenames,
c_simple_http_internal_write_filenames_to_cache_file, c_simple_http_internal_write_filenames_to_cache_file,
cache_fd)) { cache_fd) != 0) {
fprintf(stderr, "ERROR Failed to write filenames to cache file!\n"); fprintf(stderr, "ERROR Failed to write filenames to cache file!\n");
return -6; return -6;
} else if (fwrite("--- BEGIN HTML ---\n", 1, 19, cache_fd) != 19) { } else if (fwrite("--- BEGIN HTML ---\n", 1, 19, cache_fd) != 19) {

File diff suppressed because it is too large Load diff

View file

@ -17,13 +17,16 @@
#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_TEMPLATE_H_ #ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_TEMPLATE_H_
#define SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_TEMPLATE_H_ #define SEODISPARATE_COM_C_SIMPLE_HTTP_HTTP_TEMPLATE_H_
#include "http.h"
// Standard library includes. // Standard library includes.
#include <stddef.h> #include <stddef.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/data_structures/hash_map.h>
// Local includes.
#include "http.h"
// Returns non-NULL on success, which must be free'd after use. Takes a path // Returns non-NULL on success, which must be free'd after use. Takes a path
// string and templates and returns the generated HTML. If "output_buf_size" is // string and templates and returns the generated HTML. If "output_buf_size" is
@ -35,7 +38,7 @@ char *c_simple_http_path_to_generated(
const char *path, const char *path,
const C_SIMPLE_HTTP_HTTPTemplates *templates, const C_SIMPLE_HTTP_HTTPTemplates *templates,
size_t *output_buf_size, size_t *output_buf_size,
SDArchiverLinkedList **files_list_out); SDArchiverHashMap **files_set_out);
#endif #endif

View file

@ -19,6 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h>
// Linux/Unix includes. // Linux/Unix includes.
#include <sys/socket.h> #include <sys/socket.h>
@ -33,13 +34,17 @@
// Third party includes. // Third party includes.
#include <SimpleArchiver/src/helpers.h> #include <SimpleArchiver/src/helpers.h>
#include <SimpleArchiver/src/data_structures/hash_map.h>
#include <SimpleArchiver/src/data_structures/linked_list.h>
// Local includes. // Local includes.
#include "arg_parse.h" #include "arg_parse.h"
#include "big_endian.h" #include "big_endian.h"
#include "config.h" #include "config.h"
#include "http_template.h"
#include "tcp_socket.h" #include "tcp_socket.h"
#include "signal_handling.h" #include "signal_handling.h"
#include "generate.h"
#include "globals.h" #include "globals.h"
#include "constants.h" #include "constants.h"
#include "http.h" #include "http.h"
@ -74,13 +79,6 @@ typedef struct ConnectionItem {
struct in6_addr peer_addr; struct in6_addr peer_addr;
} ConnectionItem; } ConnectionItem;
typedef struct ConnectionContext {
char *buf;
const Args *args;
C_SIMPLE_HTTP_ParsedConfig *parsed;
struct timespec current_time;
} ConnectionContext;
void c_simple_http_cleanup_connection_item(void *data) { void c_simple_http_cleanup_connection_item(void *data) {
ConnectionItem *citem = data; ConnectionItem *citem = data;
if (citem) { if (citem) {
@ -235,7 +233,7 @@ int c_simple_http_manage_connections(void *data, void *ud) {
snprintf( snprintf(
content_length_buf + content_length_buf_size, content_length_buf + content_length_buf_size,
127 - content_length_buf_size, 127 - content_length_buf_size,
"%lu\n%n", "%zu\n%n",
response_size, response_size,
&written); &written);
if (written <= 0) { if (written <= 0) {
@ -307,7 +305,7 @@ int c_simple_http_manage_connections(void *data, void *ud) {
snprintf( snprintf(
content_length_buf, content_length_buf,
content_str_len + 1 + 16 + 1, content_str_len + 1 + 16 + 1,
"Content-Length: %lu\n", "Content-Length: %" PRIu64 "\n",
file_info.buf_size); file_info.buf_size);
CHECK_ERROR_WRITE_NO_FD(write( CHECK_ERROR_WRITE_NO_FD(write(
citem->fd, content_length_buf, content_str_len + 1 + 16)); citem->fd, content_length_buf, content_str_len + 1 + 16));
@ -348,6 +346,34 @@ int main(int argc, char **argv) {
return 5; return 5;
} }
// If generate-dir is specified, the program must stop after generating or
// failure.
if (args.generate_dir) {
ConnectionContext ctx;
ctx.args = &args;
ctx.parsed = &parsed_config;
printf("Generating html files to \"%s\"...\n", args.generate_dir);
if (simple_archiver_hash_map_iter(parsed_config.paths,
c_simple_http_generate_paths_fn,
&ctx)) {
fprintf(stderr, "ERROR during generating!\n");
return 1;
}
puts("Finished generating.");
if (args.static_dir) {
puts("Static dir option specified, copying over static dir entries...");
if (c_simple_http_static_copy_over_dir(args.static_dir,
args.generate_dir,
(args.flags & 8) != 0 ? 1 : 0)
!= 0) {
fprintf(stderr, "ERROR during static-dir-entires copying!\n");
return 1;
}
puts("Finished copying over static-dir files.");
}
return 0;
}
__attribute__((cleanup(cleanup_tcp_socket))) int tcp_socket = __attribute__((cleanup(cleanup_tcp_socket))) int tcp_socket =
create_tcp_socket(args.port); create_tcp_socket(args.port);
if (tcp_socket == -1) { if (tcp_socket == -1) {
@ -360,7 +386,8 @@ int main(int argc, char **argv) {
socklen_t size = sizeof(ipv6_addr); socklen_t size = sizeof(ipv6_addr);
int ret = getsockname(tcp_socket, (struct sockaddr*)&ipv6_addr, &size); int ret = getsockname(tcp_socket, (struct sockaddr*)&ipv6_addr, &size);
if (ret == 0) { if (ret == 0) {
printf("Listening on port: %u\n", u16_be_swap(ipv6_addr.sin6_port)); printf("Listening on port: %" PRIu16 "\n",
u16_be_swap(ipv6_addr.sin6_port));
} else { } else {
fprintf( fprintf(
stderr, stderr,
@ -437,7 +464,7 @@ int main(int argc, char **argv) {
++config_try_reload_attempts; ++config_try_reload_attempts;
fprintf( fprintf(
stderr, stderr,
"Attempting to reload config now (try %u of %u)...\n", "Attempting to reload config now (try %" PRIu32 " of %u)...\n",
config_try_reload_attempts, config_try_reload_attempts,
C_SIMPLE_HTTP_TRY_CONFIG_RELOAD_MAX_ATTEMPTS); C_SIMPLE_HTTP_TRY_CONFIG_RELOAD_MAX_ATTEMPTS);
C_SIMPLE_HTTP_ParsedConfig new_parsed_config = C_SIMPLE_HTTP_ParsedConfig new_parsed_config =
@ -507,7 +534,7 @@ int main(int argc, char **argv) {
} }
} else if (read_ret > 0) { } else if (read_ret > 0) {
#ifndef NDEBUG #ifndef NDEBUG
printf("DEBUG inotify_event->mask: %x\n", inotify_event->mask); printf("DEBUG inotify_event->mask: %" PRIx32 "\n", inotify_event->mask);
#endif #endif
if ((inotify_event->mask & IN_MODIFY) != 0 if ((inotify_event->mask & IN_MODIFY) != 0
|| (inotify_event->mask & IN_CLOSE_WRITE) != 0) { || (inotify_event->mask & IN_CLOSE_WRITE) != 0) {

View file

@ -31,10 +31,17 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <libgen.h>
// Third party includes. // Third party includes.
#include "SimpleArchiver/src/data_structures/linked_list.h"
#include "SimpleArchiver/src/helpers.h" #include "SimpleArchiver/src/helpers.h"
// Local includes.
#include "helpers.h"
char **environ; char **environ;
void internal_fd_cleanup_helper(int *fd) { void internal_fd_cleanup_helper(int *fd) {
@ -180,7 +187,9 @@ C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
if (fd == NULL) { if (fd == NULL) {
fprintf( fprintf(
stderr, "WARNING Failed to open path \"%s\" in static dir!\n", path + idx); stderr,
"WARNING Failed to open path \"%s\" in static dir!\n",
path + idx);
file_info.result = STATIC_FILE_RESULT_404NotFound; file_info.result = STATIC_FILE_RESULT_404NotFound;
return file_info; return file_info;
} }
@ -331,4 +340,161 @@ int c_simple_http_static_validate_path(const char *path) {
return 0; return 0;
} }
int c_simple_http_static_copy_over_dir(const char *from,
const char *to,
uint_fast8_t overwrite_enabled) {
__attribute__((cleanup(c_simple_http_cleanup_DIR)))
DIR *from_fd = opendir(from);
if (!from_fd) {
fprintf(stderr, "ERROR Failed to open directory \"%s\"!\n", from);
return 1;
}
const unsigned long from_len = strlen(from);
const unsigned long to_len = strlen(to);
struct dirent *dir_entry = NULL;
do {
dir_entry = readdir(from_fd);
if (!dir_entry) {
break;
} else if (strcmp(dir_entry->d_name, ".") == 0
|| strcmp(dir_entry->d_name, "..") == 0) {
continue;
} else if (dir_entry->d_type == DT_DIR) {
// Dir entry is a directory.
__attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *string_parts = simple_archiver_list_init();
c_simple_http_add_string_part(string_parts, from, 0);
if (from_len > 0 && from[from_len - 1] != '/') {
c_simple_http_add_string_part(string_parts, "/", 0);
}
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *combined_from = c_simple_http_combine_string_parts(string_parts);
simple_archiver_list_free(&string_parts);
string_parts = simple_archiver_list_init();
c_simple_http_add_string_part(string_parts, to, 0);
if (to_len > 0 && to[to_len - 1] != '/') {
c_simple_http_add_string_part(string_parts, "/", 0);
}
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *combined_to = c_simple_http_combine_string_parts(string_parts);
int ret = c_simple_http_static_copy_over_dir(combined_from,
combined_to,
overwrite_enabled);
if (ret != 0) {
return ret;
}
} else if (dir_entry->d_type == DT_REG) {
// Dir entry is a file.
__attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *string_parts = simple_archiver_list_init();
c_simple_http_add_string_part(string_parts, from, 0);
if (from_len > 0 && from[from_len - 1] != '/') {
c_simple_http_add_string_part(string_parts, "/", 0);
}
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *combined_from = c_simple_http_combine_string_parts(string_parts);
simple_archiver_list_free(&string_parts);
string_parts = simple_archiver_list_init();
c_simple_http_add_string_part(string_parts, to, 0);
if (to_len > 0 && to[to_len - 1] != '/') {
c_simple_http_add_string_part(string_parts, "/", 0);
}
c_simple_http_add_string_part(string_parts, dir_entry->d_name, 0);
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *combined_to = c_simple_http_combine_string_parts(string_parts);
if (!overwrite_enabled) {
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
FILE *fd = fopen(combined_to, "rb");
if (fd) {
fprintf(
stderr,
"WARNING \"%s\" already exists and "
"--generate-static-enable-overwrite not specified, skipping!\n",
combined_to);
continue;
}
}
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *combined_to_dup = strdup(combined_to);
char *combined_to_dirname = dirname(combined_to_dup);
int ret = c_simple_http_helper_mkdir_tree(combined_to_dirname);
if (ret != 0 && ret != 1) {
fprintf(stderr,
"ERROR Failed to create directory \"%s\"!\n",
combined_to_dirname);
return 1;
}
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
FILE *from_file_fd = fopen(combined_from, "rb");
if (!from_file_fd) {
fprintf(stderr,
"ERROR Failed to open file \"%s\" for reading!\n",
combined_from);
return 1;
}
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
FILE *to_file_fd = fopen(combined_to, "wb");
if (!to_file_fd) {
fprintf(stderr,
"ERROR Failed to open file \"%s\" for writing!\n",
combined_to);
return 1;
}
char buf[1024];
size_t fread_ret;
unsigned long fwrite_ret;
while (!feof(from_file_fd)
&& !ferror(from_file_fd)
&& !ferror(to_file_fd)) {
fread_ret = fread(buf, 1, 1024, from_file_fd);
if (fread_ret > 0) {
fwrite_ret = fwrite(buf, 1, fread_ret, to_file_fd);
if (fwrite_ret < fread_ret) {
fprintf(
stderr,
"ERROR Writing to file \"%s\" (not all bytes written)!\n",
combined_to);
return 1;
}
}
}
if (ferror(from_file_fd)) {
fprintf(stderr, "ERROR Reading from file \"%s\"!\n", combined_from);
return 1;
} else if (ferror(to_file_fd)) {
fprintf(stderr, "ERROR Writing to file \"%s\"!\n", combined_to);
return 1;
}
printf("%s -> %s\n", combined_from, combined_to);
} else {
fprintf(stderr,
"WARNING Non-dir and non-file \"%s/%s\", skipping...\n",
from,
dir_entry->d_name);
}
} while (dir_entry != NULL);
return 0;
}
// vim: et ts=2 sts=2 sw=2 // vim: et ts=2 sts=2 sw=2

View file

@ -51,6 +51,11 @@ C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
/// Returns zero if OK. /// Returns zero if OK.
int c_simple_http_static_validate_path(const char *path); int c_simple_http_static_validate_path(const char *path);
/// Copies all files in "from" into "to". Returns non-zero on failure.
int c_simple_http_static_copy_over_dir(const char *from,
const char *to,
uint_fast8_t overwrite_enabled);
#endif #endif
// vim: et ts=2 sts=2 sw=2 // vim: et ts=2 sts=2 sw=2

View file

@ -3,6 +3,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h>
// POSIX includes. // POSIX includes.
#include <unistd.h> #include <unistd.h>
@ -28,7 +29,10 @@ static int32_t checks_passed = 0;
#define RETURN() \ #define RETURN() \
do { \ do { \
fprintf(stderr, "checked %d\npassed %d\n", checks_checked, checks_passed);\ fprintf(stderr, \
"checked %" PRId32 "\npassed %" PRId32 "\n", \
checks_checked, \
checks_passed); \
return checks_checked == checks_passed ? 0 : 1; \ return checks_checked == checks_passed ? 0 : 1; \
} while (0); } while (0);
@ -99,9 +103,13 @@ void test_internal_cleanup_delete_temporary_file(const char **filename) {
} }
} }
int test_internal_check_matching_string_in_list(void *value, void *ud) { int test_internal_check_matching_string_in_list(
if (value && ud) { const void *key,
if (strcmp(value, ud) == 0) { __attribute__((unused)) size_t key_size,
__attribute__((unused)) const void *value,
void *ud) {
if (key && ud) {
if (strcmp(key, ud) == 0) {
return 1; return 1;
} }
} }
@ -173,46 +181,53 @@ int main(int argc, char **argv) {
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);
const char *value = C_SIMPLE_HTTP_ConfigValue *value =
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "PATH", 5); simple_archiver_hash_map_get(first_path_map_wrapper->paths, "PATH", 5);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_STREQ(value, "/"); ASSERT_TRUE(value->value);
ASSERT_STREQ(value->value, "/");
value = value =
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "HTML", 5); simple_archiver_hash_map_get(first_path_map_wrapper->paths, "HTML", 5);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_TRUE(value->value);
// printf("%s\n", value); // printf("%s\n", value);
ASSERT_STREQ(value, " one two three "); ASSERT_STREQ(value->value, " one two three ");
value = value =
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST", 5); simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST", 5);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_TRUE(value->value);
// printf("%s\n", value); // printf("%s\n", value);
ASSERT_STREQ(value, " \"one two \"three "); ASSERT_STREQ(value->value, " \"one two \"three ");
value = value =
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST2", 6); simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST2", 6);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_TRUE(value->value);
// printf("%s\n", value); // printf("%s\n", value);
ASSERT_STREQ(value, "'\"onetwo\"three''"); ASSERT_STREQ(value->value, "'\"onetwo\"three''");
value = value =
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST3", 6); simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST3", 6);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_TRUE(value->value);
// printf("%s\n", value); // printf("%s\n", value);
ASSERT_STREQ(value, " \"one two \"three ''"); ASSERT_STREQ(value->value, " \"one two \"three ''");
value = value =
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST4", 6); simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST4", 6);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_TRUE(value->value);
// printf("%s\n", value); // printf("%s\n", value);
ASSERT_STREQ(value, " \"\"\"one two \"\"\"three "); ASSERT_STREQ(value->value, " \"\"\"one two \"\"\"three ");
value = value =
simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST5", 6); simple_archiver_hash_map_get(first_path_map_wrapper->paths, "TEST5", 6);
ASSERT_TRUE(value); ASSERT_TRUE(value);
ASSERT_TRUE(value->value);
// printf("%s\n", value); // printf("%s\n", value);
ASSERT_STREQ(value, " '''one two '''three "); ASSERT_STREQ(value->value, " '''one two '''three ");
simple_archiver_list_free(&required_names); simple_archiver_list_free(&required_names);
required_names = simple_archiver_list_init(); required_names = simple_archiver_list_init();
@ -271,18 +286,18 @@ int main(int argc, char **argv) {
size_t output_buf_size; size_t output_buf_size;
__attribute__((cleanup(simple_archiver_list_free))) __attribute__((cleanup(simple_archiver_hash_map_free)))
SDArchiverLinkedList *filenames_list = NULL; SDArchiverHashMap *filenames_set = NULL;
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *buf = c_simple_http_path_to_generated( char *buf = c_simple_http_path_to_generated(
"/", &config, &output_buf_size, &filenames_list); "/", &config, &output_buf_size, &filenames_set);
ASSERT_TRUE(buf != NULL); ASSERT_TRUE(buf != NULL);
ASSERT_TRUE(strcmp(buf, "<h1>Test</h1>") == 0); ASSERT_TRUE(strcmp(buf, "<h1>Test</h1>") == 0);
CHECK_TRUE(output_buf_size == 13); CHECK_TRUE(output_buf_size == 13);
CHECK_TRUE(filenames_list->count == 0); CHECK_TRUE(filenames_set->count == 0);
simple_archiver_helper_cleanup_c_string(&buf); simple_archiver_helper_cleanup_c_string(&buf);
simple_archiver_list_free(&filenames_list); simple_archiver_hash_map_free(&filenames_set);
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file))) __attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
const char *test_http_template_filename2 = const char *test_http_template_filename2 =
@ -314,7 +329,7 @@ int main(int argc, char **argv) {
ASSERT_TRUE(config.paths != NULL); ASSERT_TRUE(config.paths != NULL);
buf = c_simple_http_path_to_generated( buf = c_simple_http_path_to_generated(
"/", &config, &output_buf_size, &filenames_list); "/", &config, &output_buf_size, &filenames_set);
ASSERT_TRUE(buf != NULL); ASSERT_TRUE(buf != NULL);
//printf("%s\n", buf); //printf("%s\n", buf);
ASSERT_TRUE( ASSERT_TRUE(
@ -323,9 +338,9 @@ int main(int argc, char **argv) {
"<h1> Some text. </h1><br><h2> More text. </h2>") "<h1> Some text. </h1><br><h2> More text. </h2>")
== 0); == 0);
CHECK_TRUE(output_buf_size == 46); CHECK_TRUE(output_buf_size == 46);
CHECK_TRUE(filenames_list->count == 0); CHECK_TRUE(filenames_set->count == 0);
simple_archiver_helper_cleanup_c_string(&buf); simple_archiver_helper_cleanup_c_string(&buf);
simple_archiver_list_free(&filenames_list); simple_archiver_hash_map_free(&filenames_set);
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file))) __attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
const char *test_http_template_filename3 = const char *test_http_template_filename3 =
@ -380,7 +395,7 @@ int main(int argc, char **argv) {
ASSERT_TRUE(config.paths != NULL); ASSERT_TRUE(config.paths != NULL);
buf = c_simple_http_path_to_generated( buf = c_simple_http_path_to_generated(
"/", &config, &output_buf_size, &filenames_list); "/", &config, &output_buf_size, &filenames_set);
ASSERT_TRUE(buf != NULL); ASSERT_TRUE(buf != NULL);
//printf("%s\n", buf); //printf("%s\n", buf);
ASSERT_TRUE( ASSERT_TRUE(
@ -389,14 +404,14 @@ int main(int argc, char **argv) {
"<h1> testVar text. </h1><br><h2> testVar2 text. </h2>") "<h1> testVar text. </h1><br><h2> testVar2 text. </h2>")
== 0); == 0);
CHECK_TRUE(output_buf_size == 53); CHECK_TRUE(output_buf_size == 53);
CHECK_TRUE(filenames_list->count == 1); CHECK_TRUE(filenames_set->count == 1);
CHECK_TRUE(simple_archiver_list_get( CHECK_TRUE(simple_archiver_hash_map_iter(
filenames_list, filenames_set,
test_internal_check_matching_string_in_list, test_internal_check_matching_string_in_list,
(void*)test_http_template_html_filename) (void*)test_http_template_html_filename)
!= NULL); != 0);
simple_archiver_helper_cleanup_c_string(&buf); simple_archiver_helper_cleanup_c_string(&buf);
simple_archiver_list_free(&filenames_list); simple_archiver_hash_map_free(&filenames_set);
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file))) __attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
const char *test_http_template_filename4 = const char *test_http_template_filename4 =
@ -471,7 +486,7 @@ int main(int argc, char **argv) {
ASSERT_TRUE(config.paths != NULL); ASSERT_TRUE(config.paths != NULL);
buf = c_simple_http_path_to_generated( buf = c_simple_http_path_to_generated(
"/", &config, &output_buf_size, &filenames_list); "/", &config, &output_buf_size, &filenames_set);
ASSERT_TRUE(buf != NULL); ASSERT_TRUE(buf != NULL);
//printf("%s\n", buf); //printf("%s\n", buf);
ASSERT_TRUE( ASSERT_TRUE(
@ -480,17 +495,17 @@ int main(int argc, char **argv) {
"<h1> some test text in test var file. </h1>") "<h1> some test text in test var file. </h1>")
== 0); == 0);
CHECK_TRUE(output_buf_size == 43); CHECK_TRUE(output_buf_size == 43);
CHECK_TRUE(filenames_list->count == 2); CHECK_TRUE(filenames_set->count == 2);
CHECK_TRUE(simple_archiver_list_get( CHECK_TRUE(simple_archiver_hash_map_iter(
filenames_list, filenames_set,
test_internal_check_matching_string_in_list, test_internal_check_matching_string_in_list,
(void*)test_http_template_html_filename2) (void*)test_http_template_html_filename2)
!= NULL); != 0);
CHECK_TRUE(simple_archiver_list_get( CHECK_TRUE(simple_archiver_hash_map_iter(
filenames_list, filenames_set,
test_internal_check_matching_string_in_list, test_internal_check_matching_string_in_list,
(void*)test_http_template_html_var_filename) (void*)test_http_template_html_var_filename)
!= NULL); != 0);
simple_archiver_helper_cleanup_c_string(&buf); simple_archiver_helper_cleanup_c_string(&buf);
} }
@ -613,6 +628,46 @@ int main(int argc, char **argv) {
CHECK_TRUE(ret == 0); CHECK_TRUE(ret == 0);
CHECK_TRUE(ret2 == 0); CHECK_TRUE(ret2 == 0);
CHECK_TRUE(ret3 == 0); CHECK_TRUE(ret3 == 0);
buf = strdup("EndingWithThreeTabs\t\t\t");
CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
CHECK_TRUE(buf[18] == 's');
CHECK_TRUE(buf[19] == 0);
CHECK_TRUE(buf[20] == 0);
CHECK_TRUE(buf[21] == 0);
CHECK_TRUE(buf[22] == 0);
free(buf);
buf = NULL;
buf = strdup("EndingWithThreeSpaces ");
CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
CHECK_TRUE(buf[20] == 's');
CHECK_TRUE(buf[21] == 0);
CHECK_TRUE(buf[22] == 0);
CHECK_TRUE(buf[23] == 0);
CHECK_TRUE(buf[24] == 0);
free(buf);
buf = NULL;
buf = strdup("EndingWithThreeNewlines\n\n\n");
CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
CHECK_TRUE(buf[22] == 's');
CHECK_TRUE(buf[23] == 0);
CHECK_TRUE(buf[24] == 0);
CHECK_TRUE(buf[25] == 0);
CHECK_TRUE(buf[26] == 0);
free(buf);
buf = NULL;
buf = strdup("EndingWithThreeCarraigeReturns\r\r\r");
CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3);
CHECK_TRUE(buf[29] == 's');
CHECK_TRUE(buf[30] == 0);
CHECK_TRUE(buf[31] == 0);
CHECK_TRUE(buf[32] == 0);
CHECK_TRUE(buf[33] == 0);
free(buf);
buf = NULL;
} }
// Test html_cache. // Test html_cache.

@ -1 +1 @@
Subproject commit b256350fbcf170ef3d70e9cf7c6dfa0618e47ef4 Subproject commit 78a36b48dbd3888f56e26ac71115e5f775a74c2c