Compare commits
5 commits
bce64a14f1
...
67f59e1354
Author | SHA1 | Date | |
---|---|---|---|
67f59e1354 | |||
fe0f87614d | |||
86bfb5aa91 | |||
c952d1e06b | |||
574499252e |
12 changed files with 542 additions and 28 deletions
|
@ -12,6 +12,7 @@ set(c_simple_http_SOURCES
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/http_template.c"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/http_template.c"
|
||||||
"${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}/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"
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -43,7 +43,8 @@ HEADERS = \
|
||||||
src/config.h \
|
src/config.h \
|
||||||
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
|
||||||
|
|
||||||
SOURCES = \
|
SOURCES = \
|
||||||
src/main.c \
|
src/main.c \
|
||||||
|
@ -57,6 +58,7 @@ SOURCES = \
|
||||||
src/http_template.c \
|
src/http_template.c \
|
||||||
src/helpers.c \
|
src/helpers.c \
|
||||||
src/html_cache.c \
|
src/html_cache.c \
|
||||||
|
src/static.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 \
|
||||||
|
|
|
@ -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
BIN
example_config/silly.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
11
src/http.c
11
src/http.c
|
@ -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 (
|
||||||
|
|
|
@ -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.
|
||||||
|
|
137
src/main.c
137
src/main.c
|
@ -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 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 {
|
} else {
|
||||||
const char *response = c_simple_http_response_code_error_to_response(
|
response_code = C_SIMPLE_HTTP_Response_500_Internal_Server_Error;
|
||||||
response_code);
|
}
|
||||||
if (response) {
|
|
||||||
response_size = strlen(response);
|
if (c_simple_http_on_error(response_code, connection_fd)) {
|
||||||
CHECK_ERROR_WRITE(write(connection_fd, response, response_size));
|
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);
|
||||||
|
|
300
src/static.c
Normal file
300
src/static.c
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
// 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 "static.h"
|
||||||
|
|
||||||
|
// Standard library includes.
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// Standard C library includes.
|
||||||
|
#include <spawn.h>
|
||||||
|
|
||||||
|
// Posix includes.
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Third party includes.
|
||||||
|
#include "SimpleArchiver/src/helpers.h"
|
||||||
|
|
||||||
|
char **environ;
|
||||||
|
|
||||||
|
void internal_fd_cleanup_helper(int *fd) {
|
||||||
|
if (fd && *fd >= 0) {
|
||||||
|
close(*fd);
|
||||||
|
*fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void internal_cleanup_file_actions(posix_spawn_file_actions_t **actions) {
|
||||||
|
if (actions && *actions) {
|
||||||
|
posix_spawn_file_actions_destroy(*actions);
|
||||||
|
free(*actions);
|
||||||
|
*actions = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void internal_cleanup_prev_cwd(char **path) {
|
||||||
|
if (path && *path) {
|
||||||
|
int ret = chdir(*path);
|
||||||
|
if (ret != 0) {
|
||||||
|
fprintf(stderr, "WARNING: chdir back to cwd failed! (errno %d)\n", errno);
|
||||||
|
}
|
||||||
|
free(*path);
|
||||||
|
*path = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int_fast8_t c_simple_http_is_xdg_mime_available(void) {
|
||||||
|
__attribute__((cleanup(internal_fd_cleanup_helper)))
|
||||||
|
int dev_null_fd = open("/dev/null", O_WRONLY);
|
||||||
|
|
||||||
|
__attribute__((cleanup(internal_cleanup_file_actions)))
|
||||||
|
posix_spawn_file_actions_t *actions =
|
||||||
|
malloc(sizeof(posix_spawn_file_actions_t));
|
||||||
|
int ret = posix_spawn_file_actions_init(actions);
|
||||||
|
if (ret != 0) {
|
||||||
|
free(actions);
|
||||||
|
actions = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
posix_spawn_file_actions_adddup2(actions, dev_null_fd, STDOUT_FILENO);
|
||||||
|
posix_spawn_file_actions_adddup2(actions, dev_null_fd, STDERR_FILENO);
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
|
ret = posix_spawnp(&pid,
|
||||||
|
"xdg-mime",
|
||||||
|
actions,
|
||||||
|
NULL,
|
||||||
|
(char *const[]){"xdg-mime", "--help", NULL},
|
||||||
|
environ);
|
||||||
|
if (ret != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
waitpid(pid, &ret, 0);
|
||||||
|
|
||||||
|
return (ret == 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_simple_http_cleanup_static_file_info(
|
||||||
|
C_SIMPLE_HTTP_StaticFileInfo *file_info) {
|
||||||
|
if (file_info->buf) {
|
||||||
|
free(file_info->buf);
|
||||||
|
file_info->buf = NULL;
|
||||||
|
}
|
||||||
|
file_info->buf_size = 0;
|
||||||
|
if (file_info->mime_type) {
|
||||||
|
free(file_info->mime_type);
|
||||||
|
file_info->mime_type = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
|
||||||
|
const char *static_dir, const char *path, int_fast8_t ignore_mime_type) {
|
||||||
|
C_SIMPLE_HTTP_StaticFileInfo file_info;
|
||||||
|
memset(&file_info, 0, sizeof(C_SIMPLE_HTTP_StaticFileInfo));
|
||||||
|
|
||||||
|
if (!static_dir || !path) {
|
||||||
|
file_info.result = STATIC_FILE_RESULT_InvalidParameter;
|
||||||
|
return file_info;
|
||||||
|
} else if (!ignore_mime_type && !c_simple_http_is_xdg_mime_available()) {
|
||||||
|
file_info.result = STATIC_FILE_RESULT_NoXDGMimeAvailable;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t buf_size = 128;
|
||||||
|
char *buf = malloc(buf_size);
|
||||||
|
char *ptr;
|
||||||
|
while (1) {
|
||||||
|
ptr = getcwd(buf, buf_size);
|
||||||
|
if (ptr == NULL) {
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
buf_size *= 2;
|
||||||
|
buf = realloc(buf, buf_size);
|
||||||
|
if (buf == NULL) {
|
||||||
|
file_info.result = STATIC_FILE_RESULT_InternalError;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
free(buf);
|
||||||
|
file_info.result = STATIC_FILE_RESULT_InternalError;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((cleanup(internal_cleanup_prev_cwd)))
|
||||||
|
char *prev_cwd = buf;
|
||||||
|
|
||||||
|
int ret = chdir(static_dir);
|
||||||
|
if (ret != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"ERROR: Failed to chdir into \"%s\"! (errno %d)\n",
|
||||||
|
static_dir,
|
||||||
|
errno);
|
||||||
|
file_info.result = STATIC_FILE_RESULT_InternalError;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
|
||||||
|
FILE *fd = NULL;
|
||||||
|
uint64_t idx = 0;
|
||||||
|
if (path[0] == '/') {
|
||||||
|
for(; path[idx] != 0; ++idx) {
|
||||||
|
if (path[idx] != '/') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path[idx] == 0) {
|
||||||
|
fprintf(stderr, "ERROR: Received invalid path \"%s\"!\n", path);
|
||||||
|
file_info.result = STATIC_FILE_RESULT_InvalidParameter;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fd = fopen(path + idx, "rb");
|
||||||
|
|
||||||
|
if (fd == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to open path \"%s\"!\n", path + idx);
|
||||||
|
file_info.result = STATIC_FILE_RESULT_FileError;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(fd, 0, SEEK_END);
|
||||||
|
long long_ret = ftell(fd);
|
||||||
|
if (long_ret < 0) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to seek in path fd \"%s\"!\n", path);
|
||||||
|
file_info.result = STATIC_FILE_RESULT_FileError;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
fseek(fd, 0, SEEK_SET);
|
||||||
|
file_info.buf_size = (uint64_t)long_ret;
|
||||||
|
file_info.buf = malloc(file_info.buf_size);
|
||||||
|
size_t size_t_ret = fread(file_info.buf, 1, file_info.buf_size, fd);
|
||||||
|
if (size_t_ret != file_info.buf_size) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to read path fd \"%s\"!\n", path);
|
||||||
|
free(file_info.buf);
|
||||||
|
file_info.buf_size = 0;
|
||||||
|
file_info.result = STATIC_FILE_RESULT_FileError;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_archiver_helper_cleanup_FILE(&fd);
|
||||||
|
|
||||||
|
if (ignore_mime_type) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_info.result = STATIC_FILE_RESULT_OK;
|
||||||
|
return file_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: et ts=2 sts=2 sw=2
|
51
src/static.h
Normal file
51
src/static.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// 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_STATIC_H_
|
||||||
|
#define SEODISPARATE_COM_C_SIMPLE_HTTP_STATIC_H_
|
||||||
|
|
||||||
|
// Standard library includes.
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum C_SIMPLE_HTTP_StaticFileResult {
|
||||||
|
STATIC_FILE_RESULT_OK,
|
||||||
|
STATIC_FILE_RESULT_FileError,
|
||||||
|
STATIC_FILE_RESULT_InvalidParameter,
|
||||||
|
STATIC_FILE_RESULT_NoXDGMimeAvailable,
|
||||||
|
STATIC_FILE_RESULT_InternalError
|
||||||
|
} C_SIMPLE_HTTP_StaticFileResult;
|
||||||
|
|
||||||
|
typedef struct C_SIMPLE_HTTP_StaticFileInfo {
|
||||||
|
char *buf;
|
||||||
|
uint64_t buf_size;
|
||||||
|
char *mime_type;
|
||||||
|
C_SIMPLE_HTTP_StaticFileResult result;
|
||||||
|
} C_SIMPLE_HTTP_StaticFileInfo;
|
||||||
|
|
||||||
|
/// Returns non-zero if "xdg_mime" is available.
|
||||||
|
int_fast8_t c_simple_http_is_xdg_mime_available(void);
|
||||||
|
|
||||||
|
void c_simple_http_cleanup_static_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(
|
||||||
|
const char *static_dir, const char *path, int_fast8_t ignore_mime_type);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// vim: et ts=2 sts=2 sw=2
|
32
src/test.c
32
src/test.c
|
@ -16,6 +16,7 @@
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "html_cache.h"
|
#include "html_cache.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
#include "static.h"
|
||||||
|
|
||||||
// Third party includes.
|
// Third party includes.
|
||||||
#include <SimpleArchiver/src/helpers.h>
|
#include <SimpleArchiver/src/helpers.h>
|
||||||
|
@ -107,7 +108,7 @@ int test_internal_check_matching_string_in_list(void *value, void *ud) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(int argc, char **argv) {
|
||||||
// Test config.
|
// Test config.
|
||||||
{
|
{
|
||||||
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
|
__attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
|
||||||
|
@ -974,6 +975,35 @@ int main(void) {
|
||||||
rmdir("/tmp/c_simple_http_cache_dir");
|
rmdir("/tmp/c_simple_http_cache_dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test static.
|
||||||
|
{
|
||||||
|
FILE *fd = fopen("/usr/bin/xdg-mime", "rb");
|
||||||
|
int_fast8_t is_xdg_mime_exists = fd ? 1 : 0;
|
||||||
|
fclose(fd);
|
||||||
|
|
||||||
|
if (is_xdg_mime_exists) {
|
||||||
|
CHECK_TRUE(c_simple_http_is_xdg_mime_available());
|
||||||
|
|
||||||
|
C_SIMPLE_HTTP_StaticFileInfo info = c_simple_http_get_file(".", argv[0], 0);
|
||||||
|
CHECK_TRUE(info.buf);
|
||||||
|
CHECK_TRUE(info.buf_size > 0);
|
||||||
|
CHECK_TRUE(info.mime_type);
|
||||||
|
CHECK_TRUE(info.result == STATIC_FILE_RESULT_OK);
|
||||||
|
printf("unit test mime type is: %s\n", info.mime_type);
|
||||||
|
c_simple_http_cleanup_static_file_info(&info);
|
||||||
|
} else {
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue