Compare commits

..

2 commits

Author SHA1 Message Date
67f59e1354 Add option --enable-static-dir=<DIR>, use static
Some checks failed
Run Unit Tests / build-and-run-unit-tests (push) Failing after 55s
2024-10-30 14:17:49 +09:00
fe0f87614d Update c_simple_http_get_file(...) in static.c
Add option to not fetch mime-type and default to
"application/octet-stream".
2024-10-30 13:24:36 +09:00
10 changed files with 254 additions and 110 deletions

View file

@ -69,6 +69,8 @@ HTML='''
<h1>Nested inner: further<h1> <h1>Nested inner: further<h1>
{{{VAR}}} {{{VAR}}}
<br> <br>
<img src="/silly.jpg" />
<br>
<a href="/inner">back</a> <a href="/inner">back</a>
</body> </body>
</html> </html>

BIN
example_config/silly.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -43,6 +43,7 @@ void print_usage(void) {
puts(" --enable-reload-config-on-change"); puts(" --enable-reload-config-on-change");
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>");
} }
Args parse_args(int32_t argc, char **argv) { Args parse_args(int32_t argc, char **argv) {
@ -128,6 +129,29 @@ Args parse_args(int32_t argc, char **argv) {
"NOTICE set cache-entry-lifetime to %lu\n", "NOTICE set cache-entry-lifetime to %lu\n",
args.cache_lifespan_seconds); args.cache_lifespan_seconds);
} }
} else if (strncmp(argv[0], "--enable-static-dir=", 20) == 0) {
args.static_dir = argv[0] + 20;
// Check if it actually is an existing directory.
DIR *d = opendir(args.static_dir);
if (d == NULL) {
if (errno == ENOENT) {
fprintf(
stderr,
"ERROR Directory \"%s\" does not exist!\n",
args.static_dir);
exit(1);
} else {
fprintf(
stderr,
"ERROR Failed to open directory \"%s\" (errno %d)!\n",
args.static_dir,
errno);
exit(1);
}
} else {
printf("Directory \"%s\" exists.\n", args.static_dir);
}
closedir(d);
} else { } else {
fprintf(stderr, "ERROR: Invalid args!\n"); fprintf(stderr, "ERROR: Invalid args!\n");
print_usage(); print_usage();

View file

@ -38,6 +38,9 @@ typedef struct Args {
// 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 *cache_dir; const char *cache_dir;
size_t cache_lifespan_seconds; size_t cache_lifespan_seconds;
// 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.
const char *static_dir;
} Args; } Args;
void print_usage(void); void print_usage(void);

View file

@ -65,7 +65,8 @@ char *c_simple_http_request_response(
C_SIMPLE_HTTP_HTTPTemplates *templates, 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 Args *args) { const Args *args,
char **request_path_out) {
if (out_size) { if (out_size) {
*out_size = 0; *out_size = 0;
} }
@ -176,6 +177,11 @@ char *c_simple_http_request_response(
char *generated_buf = NULL; char *generated_buf = NULL;
if (request_path_out) {
*request_path_out =
strdup(stripped_path ? stripped_path : request_path_unescaped);
}
if (args->cache_dir) { if (args->cache_dir) {
int ret = c_simple_http_cache_path( int ret = c_simple_http_cache_path(
stripped_path ? stripped_path : request_path_unescaped, stripped_path ? stripped_path : request_path_unescaped,
@ -212,7 +218,8 @@ char *c_simple_http_request_response(
} }
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,
"WARNING Unable to generate response html for path \"%s\"!\n",
request_path); request_path);
if (out_response_code) { if (out_response_code) {
if ( if (

View file

@ -50,7 +50,8 @@ char *c_simple_http_request_response(
C_SIMPLE_HTTP_HTTPTemplates *templates, 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 Args *args const Args *args,
char **request_path_out
); );
/// Takes a PATH string and returns a "bare" path. /// Takes a PATH string and returns a "bare" path.

View file

@ -42,8 +42,16 @@
#include "constants.h" #include "constants.h"
#include "http.h" #include "http.h"
#include "helpers.h" #include "helpers.h"
#include "static.h"
#define CHECK_ERROR_WRITE(write_expr) \ #define CHECK_ERROR_WRITE(write_expr) \
if (write_expr < 0) { \
close(connection_fd); \
fprintf(stderr, "ERROR Failed to write to connected peer, closing...\n"); \
return 1; \
}
#define CHECK_ERROR_WRITE_CONTINUE(write_expr) \
if (write_expr < 0) { \ if (write_expr < 0) { \
close(connection_fd); \ close(connection_fd); \
fprintf(stderr, "ERROR Failed to write to connected peer, closing...\n"); \ fprintf(stderr, "ERROR Failed to write to connected peer, closing...\n"); \
@ -74,6 +82,31 @@ void c_simple_http_inotify_fd_cleanup(int *fd) {
} }
} }
int c_simple_http_on_error(
enum C_SIMPLE_HTTP_ResponseCode response_code,
int connection_fd
) {
const char *response = c_simple_http_response_code_error_to_response(
response_code);
size_t response_size;
if (response) {
response_size = strlen(response);
CHECK_ERROR_WRITE(write(connection_fd, response, response_size));
} else {
CHECK_ERROR_WRITE(write(
connection_fd, "HTTP/1.1 500 Internal Server Error\n", 35));
CHECK_ERROR_WRITE(write(connection_fd, "Allow: GET\n", 11));
CHECK_ERROR_WRITE(write(connection_fd, "Connection: close\n", 18));
CHECK_ERROR_WRITE(write(
connection_fd, "Content-Type: text/html\n", 24));
CHECK_ERROR_WRITE(write(connection_fd, "Content-Length: 35\n", 19));
CHECK_ERROR_WRITE(write(
connection_fd, "\n<h1>500 Internal Server Error</h1>\n", 36));
}
return 0;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
__attribute__((cleanup(c_simple_http_free_args))) __attribute__((cleanup(c_simple_http_free_args)))
Args args = parse_args(argc, argv); Args args = parse_args(argc, argv);
@ -354,18 +387,23 @@ int main(int argc, char **argv) {
size_t response_size = 0; size_t response_size = 0;
enum C_SIMPLE_HTTP_ResponseCode response_code; enum C_SIMPLE_HTTP_ResponseCode response_code;
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *request_path = NULL;
const char *response = c_simple_http_request_response( const char *response = c_simple_http_request_response(
(const char*)recv_buf, (const char*)recv_buf,
(uint32_t)read_ret, (uint32_t)read_ret,
&parsed_config, &parsed_config,
&response_size, &response_size,
&response_code, &response_code,
&args); &args,
&request_path);
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_CONTINUE(write(
CHECK_ERROR_WRITE(write(connection_fd, "Allow: GET\n", 11)); connection_fd, "HTTP/1.1 200 OK\n", 16));
CHECK_ERROR_WRITE(write(connection_fd, "Connection: close\n", 18)); CHECK_ERROR_WRITE_CONTINUE(write(connection_fd, "Allow: GET\n", 11));
CHECK_ERROR_WRITE(write( CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, "Connection: close\n", 18));
CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, "Content-Type: text/html\n", 24)); connection_fd, "Content-Type: text/html\n", 24));
char content_length_buf[128]; char content_length_buf[128];
size_t content_length_buf_size = 0; size_t content_length_buf_size = 0;
@ -386,28 +424,83 @@ int main(int argc, char **argv) {
continue; continue;
} }
content_length_buf_size += (size_t)written; content_length_buf_size += (size_t)written;
CHECK_ERROR_WRITE(write( CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, content_length_buf, content_length_buf_size)); connection_fd, content_length_buf, content_length_buf_size));
CHECK_ERROR_WRITE(write(connection_fd, "\n", 1)); CHECK_ERROR_WRITE_CONTINUE(write(connection_fd, "\n", 1));
CHECK_ERROR_WRITE(write(connection_fd, response, response_size)); CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, response, response_size));
free((void*)response); free((void*)response);
} else { } else if (
const char *response = c_simple_http_response_code_error_to_response( response_code == C_SIMPLE_HTTP_Response_404_Not_Found
response_code); && args.static_dir) {
if (response) { __attribute__((cleanup(c_simple_http_cleanup_static_file_info)))
response_size = strlen(response); C_SIMPLE_HTTP_StaticFileInfo file_info =
CHECK_ERROR_WRITE(write(connection_fd, response, response_size)); c_simple_http_get_file(args.static_dir, request_path, 0);
if (file_info.result == STATIC_FILE_RESULT_NoXDGMimeAvailable) {
file_info = c_simple_http_get_file(args.static_dir, request_path, 1);
}
if (file_info.result != STATIC_FILE_RESULT_OK
|| !file_info.buf
|| file_info.buf_size == 0
|| !file_info.mime_type) {
if (file_info.result == STATIC_FILE_RESULT_FileError
|| file_info.result == STATIC_FILE_RESULT_InternalError) {
response_code = C_SIMPLE_HTTP_Response_500_Internal_Server_Error;
} else if (file_info.result == STATIC_FILE_RESULT_InvalidParameter) {
response_code = C_SIMPLE_HTTP_Response_400_Bad_Request;
} else {
response_code = C_SIMPLE_HTTP_Response_500_Internal_Server_Error;
}
if (c_simple_http_on_error(response_code, connection_fd)) {
continue;
}
} else { } else {
CHECK_ERROR_WRITE(write( CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, "HTTP/1.1 500 Internal Server Error\n", 35)); connection_fd, "HTTP/1.1 200 OK\n", 16));
CHECK_ERROR_WRITE(write(connection_fd, "Allow: GET\n", 11)); CHECK_ERROR_WRITE_CONTINUE(write(connection_fd, "Allow: GET\n", 11));
CHECK_ERROR_WRITE(write(connection_fd, "Connection: close\n", 18)); CHECK_ERROR_WRITE_CONTINUE(write(
CHECK_ERROR_WRITE(write( connection_fd, "Connection: close\n", 18));
connection_fd, "Content-Type: text/html\n", 24)); uint64_t mime_length = strlen(file_info.mime_type);
CHECK_ERROR_WRITE(write(connection_fd, "Content-Length: 35\n", 19)); __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
CHECK_ERROR_WRITE(write( char *mime_type_buf = malloc(mime_length + 1 + 14 + 1);
connection_fd, "\n<h1>500 Internal Server Error</h1>\n", 36)); snprintf(
mime_type_buf,
mime_length + 1 + 14 + 1,
"Content-Type: %s\n",
file_info.mime_type);
CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, mime_type_buf, mime_length + 1 + 14));
uint64_t content_str_len = 0;
for(uint64_t buf_size_temp = file_info.buf_size;
buf_size_temp > 0;
buf_size_temp /= 10) {
++content_str_len;
}
if (content_str_len == 0) {
content_str_len = 1;
}
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *content_length_buf = malloc(content_str_len + 1 + 16 + 1);
snprintf(
content_length_buf,
content_str_len + 1 + 16 + 1,
"Content-Length: %lu\n",
file_info.buf_size);
CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, content_length_buf, content_str_len + 1 + 16));
CHECK_ERROR_WRITE_CONTINUE(write(connection_fd, "\n", 1));
CHECK_ERROR_WRITE_CONTINUE(write(
connection_fd, file_info.buf, file_info.buf_size));
fprintf(stderr,
"NOTICE Found static file for path \"%s\"\n",
request_path);
}
} else {
if (c_simple_http_on_error(response_code, connection_fd)) {
continue;
} }
} }
close(connection_fd); close(connection_fd);

View file

@ -109,15 +109,15 @@ void c_simple_http_cleanup_static_file_info(
} }
} }
C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(const char *static_dir, C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
const char *path) { const char *static_dir, const char *path, int_fast8_t ignore_mime_type) {
C_SIMPLE_HTTP_StaticFileInfo file_info; C_SIMPLE_HTTP_StaticFileInfo file_info;
memset(&file_info, 0, sizeof(C_SIMPLE_HTTP_StaticFileInfo)); memset(&file_info, 0, sizeof(C_SIMPLE_HTTP_StaticFileInfo));
if (!static_dir || !path) { if (!static_dir || !path) {
file_info.result = STATIC_FILE_RESULT_InvalidParameter; file_info.result = STATIC_FILE_RESULT_InvalidParameter;
return file_info; return file_info;
} else if (!c_simple_http_is_xdg_mime_available()) { } else if (!ignore_mime_type && !c_simple_http_is_xdg_mime_available()) {
file_info.result = STATIC_FILE_RESULT_NoXDGMimeAvailable; file_info.result = STATIC_FILE_RESULT_NoXDGMimeAvailable;
return file_info; return file_info;
} }
@ -202,93 +202,97 @@ C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(const char *static_dir,
simple_archiver_helper_cleanup_FILE(&fd); simple_archiver_helper_cleanup_FILE(&fd);
int from_xdg_mime_pipe[2]; if (ignore_mime_type) {
ret = pipe(from_xdg_mime_pipe); file_info.mime_type = strdup("application/octet-stream");
} else {
int from_xdg_mime_pipe[2];
ret = pipe(from_xdg_mime_pipe);
__attribute__((cleanup(internal_cleanup_file_actions)))
posix_spawn_file_actions_t *actions =
malloc(sizeof(posix_spawn_file_actions_t));
ret = posix_spawn_file_actions_init(actions);
if (ret != 0) {
free(actions);
actions = NULL;
c_simple_http_cleanup_static_file_info(&file_info);
close(from_xdg_mime_pipe[1]);
close(from_xdg_mime_pipe[0]);
file_info.result = STATIC_FILE_RESULT_InternalError;
return file_info;
}
posix_spawn_file_actions_adddup2(actions,
from_xdg_mime_pipe[1],
STDOUT_FILENO);
// Close "read" side of pipe on "xdg-mime"'s side.
posix_spawn_file_actions_addclose(actions, from_xdg_mime_pipe[0]);
buf_size = 256;
buf = malloc(buf_size);
uint64_t buf_idx = 0;
char *path_plus_idx = (char*)path + idx;
pid_t pid;
ret = posix_spawnp(&pid,
"xdg-mime",
actions,
NULL,
(char *const[]){"xdg-mime",
"query",
"filetype",
path_plus_idx,
NULL},
environ);
if (ret != 0) {
c_simple_http_cleanup_static_file_info(&file_info);
close(from_xdg_mime_pipe[1]);
close(from_xdg_mime_pipe[0]);
file_info.result = STATIC_FILE_RESULT_InternalError;
return file_info;
}
__attribute__((cleanup(internal_cleanup_file_actions)))
posix_spawn_file_actions_t *actions =
malloc(sizeof(posix_spawn_file_actions_t));
ret = posix_spawn_file_actions_init(actions);
if (ret != 0) {
free(actions);
actions = NULL;
c_simple_http_cleanup_static_file_info(&file_info);
close(from_xdg_mime_pipe[1]); close(from_xdg_mime_pipe[1]);
close(from_xdg_mime_pipe[0]);
file_info.result = STATIC_FILE_RESULT_InternalError;
return file_info;
}
posix_spawn_file_actions_adddup2(actions, ssize_t ssize_t_ret;
from_xdg_mime_pipe[1], while (1) {
STDOUT_FILENO); ssize_t_ret =
read(from_xdg_mime_pipe[0], buf + buf_idx, buf_size - buf_idx);
// Close "read" side of pipe on "xdg-mime"'s side. if (ssize_t_ret <= 0) {
posix_spawn_file_actions_addclose(actions, from_xdg_mime_pipe[0]); break;
} else {
buf_size = 256; buf_idx += (uint64_t)ssize_t_ret;
buf = malloc(buf_size); if (buf_idx >= buf_size) {
uint64_t buf_idx = 0; buf_size *= 2;
buf = realloc(buf, buf_size);
char *path_plus_idx = (char*)path + idx; if (buf == NULL) {
pid_t pid; c_simple_http_cleanup_static_file_info(&file_info);
ret = posix_spawnp(&pid, close(from_xdg_mime_pipe[0]);
"xdg-mime", file_info.result = STATIC_FILE_RESULT_InternalError;
actions, return file_info;
NULL, }
(char *const[]){"xdg-mime",
"query",
"filetype",
path_plus_idx,
NULL},
environ);
if (ret != 0) {
c_simple_http_cleanup_static_file_info(&file_info);
close(from_xdg_mime_pipe[1]);
close(from_xdg_mime_pipe[0]);
file_info.result = STATIC_FILE_RESULT_InternalError;
return file_info;
}
close(from_xdg_mime_pipe[1]);
ssize_t ssize_t_ret;
while (1) {
ssize_t_ret =
read(from_xdg_mime_pipe[0], buf + buf_idx, buf_size - buf_idx);
if (ssize_t_ret <= 0) {
break;
} else {
buf_idx += (uint64_t)ssize_t_ret;
if (buf_idx >= buf_size) {
buf_size *= 2;
buf = realloc(buf, buf_size);
if (buf == NULL) {
c_simple_http_cleanup_static_file_info(&file_info);
close(from_xdg_mime_pipe[0]);
file_info.result = STATIC_FILE_RESULT_InternalError;
return file_info;
} }
} }
} }
close(from_xdg_mime_pipe[0]);
waitpid(pid, &ret, 0);
if (ret != 0) {
c_simple_http_cleanup_static_file_info(&file_info);
file_info.result = STATIC_FILE_RESULT_InternalError;
return file_info;
}
buf[buf_idx] = 0;
if (buf[buf_idx-1] == '\n') {
buf[buf_idx-1] = 0;
}
file_info.mime_type = buf;
} }
close(from_xdg_mime_pipe[0]);
waitpid(pid, &ret, 0);
if (ret != 0) {
c_simple_http_cleanup_static_file_info(&file_info);
file_info.result = STATIC_FILE_RESULT_InternalError;
return file_info;
}
buf[buf_idx] = 0;
if (buf[buf_idx-1] == '\n') {
buf[buf_idx-1] = 0;
}
file_info.mime_type = buf;
file_info.result = STATIC_FILE_RESULT_OK; file_info.result = STATIC_FILE_RESULT_OK;
return file_info; return file_info;
} }

View file

@ -41,8 +41,10 @@ int_fast8_t c_simple_http_is_xdg_mime_available(void);
void c_simple_http_cleanup_static_file_info( void c_simple_http_cleanup_static_file_info(
C_SIMPLE_HTTP_StaticFileInfo *file_info); C_SIMPLE_HTTP_StaticFileInfo *file_info);
/// If ignore_mime_type is non-zero, then mime information will not be fetched.
/// The mime_type string will therefore default to "application/octet-stream".
C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file( C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
const char *static_dir, const char *path); const char *static_dir, const char *path, int_fast8_t ignore_mime_type);
#endif #endif

View file

@ -984,7 +984,7 @@ int main(int argc, char **argv) {
if (is_xdg_mime_exists) { if (is_xdg_mime_exists) {
CHECK_TRUE(c_simple_http_is_xdg_mime_available()); CHECK_TRUE(c_simple_http_is_xdg_mime_available());
C_SIMPLE_HTTP_StaticFileInfo info = c_simple_http_get_file(".", argv[0]); C_SIMPLE_HTTP_StaticFileInfo info = c_simple_http_get_file(".", argv[0], 0);
CHECK_TRUE(info.buf); CHECK_TRUE(info.buf);
CHECK_TRUE(info.buf_size > 0); CHECK_TRUE(info.buf_size > 0);
CHECK_TRUE(info.mime_type); CHECK_TRUE(info.mime_type);
@ -994,6 +994,14 @@ int main(int argc, char **argv) {
} else { } else {
CHECK_FALSE(c_simple_http_is_xdg_mime_available()); CHECK_FALSE(c_simple_http_is_xdg_mime_available());
} }
C_SIMPLE_HTTP_StaticFileInfo info = c_simple_http_get_file(".", argv[0], 1);
CHECK_TRUE(info.buf);
CHECK_TRUE(info.buf_size > 0);
CHECK_TRUE(info.mime_type);
CHECK_TRUE(info.result == STATIC_FILE_RESULT_OK);
CHECK_STREQ(info.mime_type, "application/octet-stream");
c_simple_http_cleanup_static_file_info(&info);
} }
RETURN() RETURN()