diff --git a/example_config/example.config b/example_config/example.config index 768af8c..2754095 100644 --- a/example_config/example.config +++ b/example_config/example.config @@ -69,6 +69,8 @@ HTML='''

Nested inner: further

{{{VAR}}}
+ +
back diff --git a/example_config/silly.jpg b/example_config/silly.jpg new file mode 100644 index 0000000..d2c7371 Binary files /dev/null and b/example_config/silly.jpg differ diff --git a/src/arg_parse.c b/src/arg_parse.c index 7801ca2..5fea90a 100644 --- a/src/arg_parse.c +++ b/src/arg_parse.c @@ -43,6 +43,7 @@ void print_usage(void) { puts(" --enable-reload-config-on-change"); puts(" --enable-cache-dir="); puts(" --cache-entry-lifetime-seconds="); + puts(" --enable-static-dir="); } 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", 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 { fprintf(stderr, "ERROR: Invalid args!\n"); print_usage(); diff --git a/src/arg_parse.h b/src/arg_parse.h index 24ba5f7..7595f72 100644 --- a/src/arg_parse.h +++ b/src/arg_parse.h @@ -38,6 +38,9 @@ typedef struct Args { // Does not need to be free'd since it points to a string in argv. const char *cache_dir; 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; void print_usage(void); diff --git a/src/http.c b/src/http.c index 0e688aa..958b012 100644 --- a/src/http.c +++ b/src/http.c @@ -65,7 +65,8 @@ char *c_simple_http_request_response( C_SIMPLE_HTTP_HTTPTemplates *templates, size_t *out_size, enum C_SIMPLE_HTTP_ResponseCode *out_response_code, - const Args *args) { + const Args *args, + char **request_path_out) { if (out_size) { *out_size = 0; } @@ -176,6 +177,11 @@ char *c_simple_http_request_response( char *generated_buf = NULL; + if (request_path_out) { + *request_path_out = + strdup(stripped_path ? stripped_path : request_path_unescaped); + } + if (args->cache_dir) { int ret = c_simple_http_cache_path( stripped_path ? stripped_path : request_path_unescaped, @@ -212,7 +218,8 @@ char *c_simple_http_request_response( } 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); if (out_response_code) { if ( diff --git a/src/http.h b/src/http.h index 7b7a52a..018f065 100644 --- a/src/http.h +++ b/src/http.h @@ -50,7 +50,8 @@ char *c_simple_http_request_response( C_SIMPLE_HTTP_HTTPTemplates *templates, size_t *out_size, 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. diff --git a/src/main.c b/src/main.c index 976f245..92b7b5b 100644 --- a/src/main.c +++ b/src/main.c @@ -42,8 +42,16 @@ #include "constants.h" #include "http.h" #include "helpers.h" +#include "static.h" #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) { \ close(connection_fd); \ 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

500 Internal Server Error

\n", 36)); + } + + return 0; +} + int main(int argc, char **argv) { __attribute__((cleanup(c_simple_http_free_args))) Args args = parse_args(argc, argv); @@ -354,18 +387,23 @@ int main(int argc, char **argv) { size_t response_size = 0; 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*)recv_buf, (uint32_t)read_ret, &parsed_config, &response_size, &response_code, - &args); + &args, + &request_path); if (response && response_code == C_SIMPLE_HTTP_Response_200_OK) { - CHECK_ERROR_WRITE(write(connection_fd, "HTTP/1.1 200 OK\n", 16)); - CHECK_ERROR_WRITE(write(connection_fd, "Allow: GET\n", 11)); - CHECK_ERROR_WRITE(write(connection_fd, "Connection: close\n", 18)); - CHECK_ERROR_WRITE(write( + CHECK_ERROR_WRITE_CONTINUE(write( + connection_fd, "HTTP/1.1 200 OK\n", 16)); + CHECK_ERROR_WRITE_CONTINUE(write(connection_fd, "Allow: GET\n", 11)); + CHECK_ERROR_WRITE_CONTINUE(write( + connection_fd, "Connection: close\n", 18)); + CHECK_ERROR_WRITE_CONTINUE(write( connection_fd, "Content-Type: text/html\n", 24)); char content_length_buf[128]; size_t content_length_buf_size = 0; @@ -386,28 +424,83 @@ int main(int argc, char **argv) { continue; } 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)); - CHECK_ERROR_WRITE(write(connection_fd, "\n", 1)); - CHECK_ERROR_WRITE(write(connection_fd, response, response_size)); + CHECK_ERROR_WRITE_CONTINUE(write(connection_fd, "\n", 1)); + CHECK_ERROR_WRITE_CONTINUE(write( + connection_fd, response, response_size)); free((void*)response); - } else { - const char *response = c_simple_http_response_code_error_to_response( - response_code); - if (response) { - response_size = strlen(response); - CHECK_ERROR_WRITE(write(connection_fd, response, response_size)); + } else if ( + response_code == C_SIMPLE_HTTP_Response_404_Not_Found + && args.static_dir) { + __attribute__((cleanup(c_simple_http_cleanup_static_file_info))) + C_SIMPLE_HTTP_StaticFileInfo file_info = + 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 { - 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

500 Internal Server Error

\n", 36)); + CHECK_ERROR_WRITE_CONTINUE(write( + connection_fd, "HTTP/1.1 200 OK\n", 16)); + CHECK_ERROR_WRITE_CONTINUE(write(connection_fd, "Allow: GET\n", 11)); + CHECK_ERROR_WRITE_CONTINUE(write( + connection_fd, "Connection: close\n", 18)); + uint64_t mime_length = strlen(file_info.mime_type); + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *mime_type_buf = malloc(mime_length + 1 + 14 + 1); + 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);