diff --git a/Makefile b/Makefile
index 812f445..15784b5 100644
--- a/Makefile
+++ b/Makefile
@@ -62,6 +62,7 @@ build: .build-post
# clean
clean: .clean-post
+ -rm --recursive ${CND_ARTIFACT_DIR_${CONF}}/
.clean-pre:
# Add your pre 'clean' code here...
diff --git a/content/dirindex.html b/content/dirindex.html
new file mode 100644
index 0000000..7859534
--- /dev/null
+++ b/content/dirindex.html
@@ -0,0 +1,24 @@
+
+
+
+ {{dirname}}
+
+
+
+
+ {{dirname}}
+
+
+
+ | Name |
+ Filesize |
+ Last modified |
+
+
+
+ {{index}}
+
+
+
+
+
diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk
index 581ae77..1d674ab 100644
--- a/nbproject/Makefile-Debug.mk
+++ b/nbproject/Makefile-Debug.mk
@@ -43,11 +43,12 @@ OBJECTFILES= \
${OBJECTDIR}/src/http.o \
${OBJECTDIR}/src/main.o \
${OBJECTDIR}/src/mime.o \
- ${OBJECTDIR}/src/socket.o
+ ${OBJECTDIR}/src/socket.o \
+ ${OBJECTDIR}/src/util.o
# C Compiler Flags
-CFLAGS=-O0
+CFLAGS=-m64 -O0 -march=native
# CC Compiler Flags
CCFLAGS=
@@ -115,6 +116,11 @@ ${OBJECTDIR}/src/socket.o: nbproject/Makefile-${CND_CONF}.mk src/socket.c
${RM} "$@.d"
$(COMPILE.c) -g -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/socket.o src/socket.c
+${OBJECTDIR}/src/util.o: nbproject/Makefile-${CND_CONF}.mk src/util.c
+ ${MKDIR} -p ${OBJECTDIR}/src
+ ${RM} "$@.d"
+ $(COMPILE.c) -g -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/util.o src/util.c
+
# Subprojects
.build-subprojects:
diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk
index 567d3b9..4a7c552 100644
--- a/nbproject/Makefile-Release.mk
+++ b/nbproject/Makefile-Release.mk
@@ -43,11 +43,12 @@ OBJECTFILES= \
${OBJECTDIR}/src/http.o \
${OBJECTDIR}/src/main.o \
${OBJECTDIR}/src/mime.o \
- ${OBJECTDIR}/src/socket.o
+ ${OBJECTDIR}/src/socket.o \
+ ${OBJECTDIR}/src/util.o
# C Compiler Flags
-CFLAGS=
+CFLAGS=-m64 -march=native
# CC Compiler Flags
CCFLAGS=
@@ -60,7 +61,7 @@ FFLAGS=
ASFLAGS=
# Link Libraries and Options
-LDLIBSOPTIONS=
+LDLIBSOPTIONS=-lmagic
# Build Targets
.build-conf: ${BUILD_SUBPROJECTS}
@@ -70,50 +71,55 @@ ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/khttp: ${OBJECTFILES}
${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}
${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/khttp ${OBJECTFILES} ${LDLIBSOPTIONS}
-${OBJECTDIR}/lib/http_parser.o: lib/http_parser.c
+${OBJECTDIR}/lib/http_parser.o: nbproject/Makefile-${CND_CONF}.mk lib/http_parser.c
${MKDIR} -p ${OBJECTDIR}/lib
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/lib/http_parser.o lib/http_parser.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/lib/http_parser.o lib/http_parser.c
-${OBJECTDIR}/lib/ini.o: lib/ini.c
+${OBJECTDIR}/lib/ini.o: nbproject/Makefile-${CND_CONF}.mk lib/ini.c
${MKDIR} -p ${OBJECTDIR}/lib
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/lib/ini.o lib/ini.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/lib/ini.o lib/ini.c
-${OBJECTDIR}/src/config.o: src/config.c
+${OBJECTDIR}/src/config.o: nbproject/Makefile-${CND_CONF}.mk src/config.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/config.o src/config.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/config.o src/config.c
-${OBJECTDIR}/src/http-reader.o: src/http-reader.c
+${OBJECTDIR}/src/http-reader.o: nbproject/Makefile-${CND_CONF}.mk src/http-reader.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-reader.o src/http-reader.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-reader.o src/http-reader.c
-${OBJECTDIR}/src/http-server.o: src/http-server.c
+${OBJECTDIR}/src/http-server.o: nbproject/Makefile-${CND_CONF}.mk src/http-server.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-server.o src/http-server.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-server.o src/http-server.c
-${OBJECTDIR}/src/http.o: src/http.c
+${OBJECTDIR}/src/http.o: nbproject/Makefile-${CND_CONF}.mk src/http.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http.o src/http.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http.o src/http.c
-${OBJECTDIR}/src/main.o: src/main.c
+${OBJECTDIR}/src/main.o: nbproject/Makefile-${CND_CONF}.mk src/main.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/main.o src/main.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/main.o src/main.c
-${OBJECTDIR}/src/mime.o: src/mime.c
+${OBJECTDIR}/src/mime.o: nbproject/Makefile-${CND_CONF}.mk src/mime.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/mime.o src/mime.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/mime.o src/mime.c
-${OBJECTDIR}/src/socket.o: src/socket.c
+${OBJECTDIR}/src/socket.o: nbproject/Makefile-${CND_CONF}.mk src/socket.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
- $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/socket.o src/socket.c
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/socket.o src/socket.c
+
+${OBJECTDIR}/src/util.o: nbproject/Makefile-${CND_CONF}.mk src/util.c
+ ${MKDIR} -p ${OBJECTDIR}/src
+ ${RM} "$@.d"
+ $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/util.o src/util.c
# Subprojects
.build-subprojects:
diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml
index ffa3e5e..a6563b1 100644
--- a/nbproject/configurations.xml
+++ b/nbproject/configurations.xml
@@ -13,6 +13,7 @@
src/main.h
src/mime.h
src/socket.h
+ src/util.h
src/main.c
src/mime.c
src/socket.c
+ src/util.c
+ 2
3
lib
- -O0
+ -O0 -march=native
INI_ALLOW_BOM=0
INI_ALLOW_MULTILINE=0
@@ -119,26 +122,38 @@
-
+ -
+
+ -
+
default
true
- false
+ true
5
+ 2
+ 3
+
+ lib
+
+ -march=native
+
+ INI_ALLOW_BOM=0
+ INI_ALLOW_MULTILINE=0
+ _GNU_SOURCE
+
+ 3
-
- 5
-
-
- 5
-
-
- 5
-
+
+
+ magic
+
+
-
@@ -184,6 +199,10 @@
-
+ -
+
+ -
+
diff --git a/nbproject/private/configurations.xml b/nbproject/private/configurations.xml
index cb9d4e5..3897824 100644
--- a/nbproject/private/configurations.xml
+++ b/nbproject/private/configurations.xml
@@ -62,7 +62,7 @@
"${OUTPUT_PATH}"
"${OUTPUT_PATH}"
-
+ /home/sam/NetBeansProjects/KHttp/./dist/Debug/GNU-Linux-x86
true
0
0
diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml
index f3ee2df..d97b1b3 100644
--- a/nbproject/private/private.xml
+++ b/nbproject/private/private.xml
@@ -8,19 +8,19 @@
file:/home/sam/NetBeansProjects/KHttp/src/socket.c
- file:/home/sam/NetBeansProjects/KHttp/content/khttpd.ini
file:/home/sam/NetBeansProjects/KHttp/src/config.c
+ file:/home/sam/NetBeansProjects/KHttp/content/dirindex.html
file:/home/sam/NetBeansProjects/KHttp/src/http-server.h
file:/home/sam/NetBeansProjects/KHttp/src/http.c
- file:/home/sam/NetBeansProjects/KHttp/src/http-reader.h
+ file:/home/sam/NetBeansProjects/KHttp/lib/ut/utstring.h
file:/home/sam/NetBeansProjects/KHttp/src/main.h
+ file:/home/sam/NetBeansProjects/KHttp/lib/http_parser.h
file:/home/sam/NetBeansProjects/KHttp/Makefile
- file:/home/sam/NetBeansProjects/KHttp/content/error.html
file:/home/sam/NetBeansProjects/KHttp/src/main.c
+ file:/home/sam/NetBeansProjects/KHttp/src/socket.h
file:/home/sam/NetBeansProjects/KHttp/src/http-server.c
file:/home/sam/NetBeansProjects/KHttp/lib/ini.c
file:/home/sam/NetBeansProjects/KHttp/src/http.h
- file:/home/sam/NetBeansProjects/KHttp/src/http-reader.c
file:/home/sam/NetBeansProjects/KHttp/src/config.h
diff --git a/src/config.c b/src/config.c
index ca3c62a..8235acb 100644
--- a/src/config.c
+++ b/src/config.c
@@ -20,7 +20,7 @@ config_server* config_server_new() {
config->servername = calloc(128, sizeof(char));
if (gethostname(config->servername, 128) < 0) {
- warning("failed to get server hostname", true);
+ warning(true, "failed to get server hostname");
free(config->servername);
config->servername = strdup(default_servername);
}
@@ -69,12 +69,20 @@ config_host* config_host_new() {
host->enabled = true;
host->hostname = NULL;
host->serve_dir = NULL;
+ host->dir_listings = true;
+ host->index_files = calloc(1, sizeof(char*));
+ host->index_files[0] = strdup("index.html");
+ host->index_files_count = 1;
return host;
}
void config_host_delete(config_host *host) {
if (host->hostname != NULL) free(host->hostname);
if (host->serve_dir != NULL) free(host->serve_dir);
+ for(int i=0; iindex_files_count; i++) {
+ free(host->index_files[i]);
+ }
+ free(host->index_files);
free(host);
}
@@ -92,18 +100,21 @@ static int config_read_ini_cb(void* _config, const char* section, const char* na
return -1;
} else if (name != NULL) {
if (MATCH("Server", "name")) {
- config->servername = strdup(value);
+ config->servername = realloc(config->servername, strlen(value)+1);
+ strcpy(config->servername, value);
} else if (MATCH("Server", "admin")) {
- config->administrator = strdup(value);
+ config->administrator = realloc(config->administrator, strlen(value)+1);
+ strcpy(config->administrator, value);
} else if (MATCH("Server", "listen")) {
errno = 0;
config->listen_port = (uint16_t)strtol(value, NULL, 10);
if (errno != 0) {
- warning("Config: Invalid port number for [Server]listen", true);
+ warning(true, "Config: Invalid port number for [Server]listen");
}
return -1;
} else if (MATCH("Host", "name")) {
- host->hostname = strdup(value);
+ host->hostname = realloc(host->hostname, strlen(value)+1);
+ strcpy(host->hostname, value);
} else if (MATCH("Host", "enabled")) {
if (strcasecmp(value, "yes") == 0) {
host->enabled = true;
@@ -125,17 +136,43 @@ static int config_read_ini_cb(void* _config, const char* section, const char* na
} else if (MATCH("Host", "serve")) {
char* serve_dir = realpath(value, NULL);
if (serve_dir == NULL) {
- warning("Config: host serve directory is invalid", true);
+ warning(true, "Config: host serve directory is invalid");
return -1;
}
DIR* dir = opendir(serve_dir);
if (dir == NULL) {
free(serve_dir);
- warning("Config: host serve directory is invalid", true);
+ warning(true, "Config: host serve directory is invalid");
return -1;
}
closedir(dir);
+ if (host->serve_dir != NULL) free(host->serve_dir);
host->serve_dir = serve_dir;
+ } else if (MATCH("Host", "index_files")) {
+ for(int i=0;iindex_files_count;i++) {
+ free(host->index_files[i]);
+ }
+ free(host->index_files);
+ host->index_files = NULL;
+ host->index_files_count = 0;
+
+ char* savepos=NULL;
+ char* value_cpy = strdup(value);
+ char* file = strtok_r(value_cpy, ",", &savepos);
+ while(file != NULL) {
+ host->index_files = realloc(host->index_files, sizeof(char*)*(host->index_files_count+1));
+ host->index_files[host->index_files_count++] = strdup(str_trimwhitespace(file));
+ file = strtok_r(NULL, ",", &savepos);
+ }
+ free(value_cpy);
+ } else if (MATCH("Host", "dir_listings")) {
+ if (strcasecmp(value, "yes") == 0) {
+ host->dir_listings = true;
+ } else if (strcasecmp(value, "no") == 0) {
+ host->dir_listings = false;
+ } else {
+ warning(false, "Unexpected value for [Host]dir_listings");
+ }
}
}
#undef MATCH
diff --git a/src/config.h b/src/config.h
index 4cd9388..034f224 100644
--- a/src/config.h
+++ b/src/config.h
@@ -23,6 +23,9 @@ extern "C" {
bool default_host;
bool enabled;
char *serve_dir;
+ char**index_files;
+ size_t index_files_count;
+ bool dir_listings;
} config_host;
typedef struct config_server {
diff --git a/src/http-server.c b/src/http-server.c
index fa12f02..fc4f6a6 100644
--- a/src/http-server.c
+++ b/src/http-server.c
@@ -5,6 +5,9 @@
#include
#include
#include
+#include
+#include
+#include
#include "http_parser.h"
#include "http.h"
@@ -60,6 +63,7 @@ http_response* server_process_request(config_server* config, http_request *reque
if (http_parser_parse_url(request->req->uri, strlen(request->req->uri), false, url) < 0) {
response = http_response_create_builtin(400, "Invalid request");
http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ free(url);
return response;
}
@@ -72,52 +76,52 @@ http_response* server_process_request(config_server* config, http_request *reque
//Not found (for some reason)
response = http_response_create_builtin(400, "URI is required or was invalid");
http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ free(url);
return response;
}
+ free(url);
//Build actual path to file requested
- char* filepath_requested = NULL;
- size_t totallen = strlen(host_config->serve_dir) + strlen(uri) + 2;
- filepath_requested = calloc(totallen, sizeof(char));
- strcat(filepath_requested, host_config->serve_dir);
- strcat(filepath_requested, "/");
- strcat(filepath_requested, uri);
+ char* filepath = NULL;
+ size_t totallen = strlen(host_config->serve_dir) + strlen(uri) + 1;
+ filepath = calloc(totallen, sizeof(char));
+ strcat(filepath, host_config->serve_dir);
+ strcat(filepath, uri);
+ free(uri);
- //Check that the file exists
- char* filepath_actual = realpath(filepath_requested, NULL);
- if (filepath_actual == NULL) {
- warning("realpath: not found/error", true);
- response = http_response_create_builtin(404, "File not found");
- http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
- return response;
- }
- //Check that the file is within the server directory of the host
- if (strncmp(host_config->serve_dir, filepath_actual, strlen(host_config->serve_dir)) != 0) {
- //file is outside the server directory :S
- response = http_response_create_builtin(400, "URI is required or was invalid");
- http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
- return response;
- }
+ server_file_result *file_result = server_determine_file(host_config, filepath);
+ free(filepath);
+ filepath=NULL;
- struct stat *pathstat = calloc(1, sizeof(struct stat));
- if (stat(filepath_actual, pathstat) < 0) {
- warning("stat failed", true);
- response = http_response_create_builtin(400, "URI is required or was invalid");
+ if (file_result->error != false) {
+ response = http_response_create_builtin(file_result->error_code, file_result->error_text);
http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ server_file_result_delete(file_result);
return response;
}
- //Is is a directory?
- if (S_ISDIR(pathstat->st_mode) != 0) {
- response = http_response_create_builtin(200, "Directory listing not implemented");
- return response;
+ FILE *file = NULL;
+ if (file_result->dir == true) {
+ file = server_generate_directory_index(host_config, file_result->path);
+ if (file == NULL) {
+ response = http_response_create_builtin(500, "Failed to generate directory index");
+ http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ server_file_result_delete(file_result);
+ return response;
+ }
+ } else {
+ filepath = strdup(file_result->path);
}
+ server_file_result_delete(file_result);
//Open file
- FILE *file = fopen(filepath_actual, "r");
if (file == NULL) {
- warning("failed to open file for reading", true);
+ file = fopen(filepath, "rb");
+ }
+ if (file == NULL) {
+ warning(true, "failed to open file for reading");
response = http_response_create_builtin(404, "File not found");
http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ free(filepath);
return response;
}
@@ -128,20 +132,23 @@ http_response* server_process_request(config_server* config, http_request *reque
//Read file into response
//TODO: send file directly from the write loop
- char* buffer = calloc(filesize, sizeof(char));
+ char* buffer = calloc(filesize+1, sizeof(char));
if (fread(buffer, sizeof(char), filesize, file) != filesize) {
- warning("failed to read file into memory", true);
+ warning(true, "failed to read file into memory");
response = http_response_create_builtin(500, "Could not read file");
} else {
response = http_response_new(http_response_line_new(200));
response->resp->version = request->req->version;
- http_header_list_add(response->headers, http_header_new(HEADER_CONTENT_TYPE, mime_get_type(filepath_actual, DEFAULT_CONTENT_TYPE)), false);
+ const char* mime_type = "text/html";
+ if (filepath != NULL) {
+ mime_type = mime_get_type(filepath, DEFAULT_CONTENT_TYPE);
+ }
+ http_header_list_add(response->headers, http_header_new(HEADER_CONTENT_TYPE, mime_type), false);
http_response_append_body(response, buffer);
}
fclose(file);
free(buffer);
- free(filepath_requested);
- free(filepath_actual);
+ free(filepath);
//Check to see if client requested the connection be closed
http_header* request_connection = http_header_list_get(request->headers, HEADER_CONNECTION);
@@ -150,4 +157,187 @@ http_response* server_process_request(config_server* config, http_request *reque
}
return response;
+}
+
+server_file_result* server_determine_file(config_host* hconfig, const char* requested_path) {
+ server_file_result *result = server_file_result_new();
+ //Check that the file exists
+ char* filepath_actual = realpath(requested_path, NULL);
+ if (filepath_actual == NULL) {
+ server_file_result_seterror(result, 404, "File not found");
+ return result;
+ }
+ //Check that the file is within the server directory of the host
+ if (strncmp(hconfig->serve_dir, filepath_actual, strlen(hconfig->serve_dir)) != 0) {
+ //file is outside the server directory :S
+ server_file_result_seterror(result, 404, "File not found");
+ free(filepath_actual);
+ return result;
+ }
+
+ struct stat *pathstat = calloc(1, sizeof(struct stat));
+ if (stat(filepath_actual, pathstat) < 0) {
+ server_file_result_seterror(result, 404, "File not found");
+ free(filepath_actual);
+ free(pathstat);
+ return result;
+ }
+ //If directory
+ if (S_ISDIR(pathstat->st_mode) != 0) {
+ char* dir_index = server_find_directory_index(hconfig, filepath_actual);
+ if (hconfig->index_files_count > 0 && dir_index != NULL) {
+ //Use the directory index
+ result->path = dir_index;
+ } else if (hconfig->dir_listings == true) {
+ //Show directory listing
+ result->dir = true;
+ result->path = strdup(filepath_actual);
+ } else {
+ server_file_result_seterror(result, 403, "No index file was found and directory indexes are disabled");
+ }
+ } else if (S_ISREG(pathstat->st_mode) != 0) {
+ result->path = strdup(filepath_actual);
+ }
+ free(pathstat);
+ free(filepath_actual);
+ return result;
+}
+char* server_find_directory_index(config_host *hconfig, char* path) {
+ DIR *dir = opendir(path);
+ if (dir == NULL) {
+ return NULL;
+ }
+ struct dirent *entry;
+ while((entry = readdir(dir)) != NULL) {
+ for(int i=0; iindex_files_count; i++) {
+ if (strcasecmp(entry->d_name, hconfig->index_files[i]) == 0) {
+ char* dirindex = calloc(strlen(path)+strlen(entry->d_name)+2, sizeof(char));
+ strcat(dirindex, path);
+ strcat(dirindex, "/");
+ strcat(dirindex, entry->d_name);
+ closedir(dir);
+ return dirindex;
+ }
+ }
+ }
+ closedir(dir);
+ return NULL;
+}
+FILE * server_generate_directory_index(config_host *hconfig, const char* dirpath) {
+ DIR *dir = opendir(dirpath);
+ if (dir == NULL) {
+ return NULL;
+ }
+
+ UT_string *index = NULL;
+ utstring_new(index);
+
+ struct dirent *entry;
+ struct stat *filestat = calloc(1, sizeof(struct stat));
+ uint32_t count=0;
+ while((entry = readdir(dir)) != NULL) {
+ if (count++ > 1024*8) break;
+ char *filepath = calloc(strlen(dirpath)+strlen(entry->d_name)+2, sizeof(char));
+ strcat(filepath, dirpath);
+ strcat(filepath, "/");
+ strcat(filepath, entry->d_name);
+
+ if (stat(filepath, filestat) < 0) {
+ continue;
+ }
+
+ char* filesize = NULL;
+ if (S_ISDIR(filestat->st_mode)) {
+ filesize = strdup("[DIR]");
+ } else if (S_ISREG(filestat->st_mode)) {
+ filesize = server_get_filesize(filepath);
+ } else {
+ continue;
+ }
+ char* uri = strdup(filepath);
+ uri = str_replace(uri, hconfig->serve_dir, "");
+ if (uri[0] = '/') {
+ memmove(uri, uri+1, strlen(uri+1)+1);
+ uri = realloc(uri, strlen(uri)+1);
+ }
+
+ time_t file_mtime = (time_t)filestat->st_mtim.tv_sec;
+ char* file_mod_time = ctime(&file_mtime);
+
+ utstring_printf(index, "| %s | %s | %s |
\r\n", uri,
+ (filesize!=NULL)?filesize:"N/A",
+ (file_mod_time!=NULL)?file_mod_time:"N/A");
+ free(filepath);
+ free(filesize);
+ free(uri);
+ }
+ closedir(dir);
+ free(filestat);
+ char *dirname = strdup(dirpath);
+ dirname = str_replace(dirname, hconfig->serve_dir, "/");
+
+ file_map *dirindex_map = file_map_new("dirindex.html");
+ if (dirindex_map == NULL) {
+ utstring_free(index);
+ free(dirname);
+ return NULL;
+ }
+ char* dirindex = strdup(dirindex_map->map);
+ file_map_delete(dirindex_map);
+
+ dirindex = str_replace(dirindex, "{{dirname}}", dirname);
+ free(dirname);
+ dirindex = str_replace(dirindex, "{{index}}", utstring_body(index));
+ utstring_free(index);
+
+ FILE *file = tmpfile();
+ fwrite(dirindex, sizeof(char), strlen(dirindex), file);
+ free(dirindex);
+
+ return file;
+}
+char* server_get_filesize(const char* filename) {
+ FILE *file = fopen(filename, "rb");
+ if (file == NULL) {
+ return NULL;
+ }
+ fseek(file, 0L, SEEK_END);
+ long int size = ftell(file);
+ fclose(file);
+
+ char* sizenames[] = {"b", "kb", "mb", "gb", "pb"};
+ int size_i = 0;
+
+ while(size > 1024) {
+ if (size_i > (sizeof(sizenames) / sizeof(char*))) {
+ break;
+ }
+ size_i++;
+ size /= 1024;
+ }
+ char* sizestr = calloc(100, sizeof(char));
+ snprintf(sizestr, 100, "%li%s", size, sizenames[size_i]);
+ sizestr = realloc(sizestr, sizeof(char)*(strlen(sizestr)+1));
+ return sizestr;
+}
+
+server_file_result* server_file_result_new() {
+ server_file_result *result = calloc(1, sizeof(server_file_result));
+ result->error = false;
+ result->error_code = 0;
+ result->error_text = NULL;
+ result->dir = false;
+ result->path = NULL;
+
+ return result;
+}
+void server_file_result_seterror(server_file_result *result, uint16_t code, const char* message) {
+ result->error = true;
+ result->error_code = code;
+ result->error_text = strdup(message);
+}
+void server_file_result_delete(server_file_result *result) {
+ free(result->error_text);
+ free(result->path);
+ free(result);
}
\ No newline at end of file
diff --git a/src/http-server.h b/src/http-server.h
index b0e3f59..6ba5d1f 100644
--- a/src/http-server.h
+++ b/src/http-server.h
@@ -12,12 +12,31 @@
extern "C" {
#endif
+#include
+#include
+#include
#include "http.h"
#include "main.h"
#include "config.h"
- http_response* server_process_request(config_server* config, http_request *request);
+ typedef struct server_file_result {
+ bool error;
+ uint16_t error_code;
+ char* error_text;
+ bool dir;
+ DIR *dirent;
+ char* path;
+ } server_file_result;
+ http_response* server_process_request(config_server* config, http_request *request);
+ server_file_result* server_determine_file(config_host* hconfig, const char* requested_path);
+ char* server_find_directory_index(config_host *hconfig, char* path);
+ FILE * server_generate_directory_index(config_host *hconfig, const char* dirpath);
+ char* server_get_filesize(const char* filename);
+
+ server_file_result* server_file_result_new();
+ void server_file_result_seterror(server_file_result *result, uint16_t code, const char* message);
+ void server_file_result_delete(server_file_result *result);
#ifdef __cplusplus
}
diff --git a/src/http.h b/src/http.h
index 70aea64..8d862fd 100644
--- a/src/http.h
+++ b/src/http.h
@@ -29,7 +29,7 @@ extern "C" {
#define HEADER_IF_MODIFIED_SINCE "If-Modified-Since"
#define HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since"
-#define FORMAT_HEADER_DATE "%a, %e %h %Y %T %Z"
+#define FORMAT_HEADER_DATE "%a, %d %h %Y %T %Z"
#define DEFAULT_CONTENT_TYPE "text/plain"
#define HTTP_CHUNK_MAXSIZE 1024*16
diff --git a/src/main.c b/src/main.c
index be33ceb..3129636 100644
--- a/src/main.c
+++ b/src/main.c
@@ -9,11 +9,12 @@
#include
#include
#include
-#include
#include
#include
#include
#include
+#include
+#include
#include "http_parser.h"
@@ -28,29 +29,39 @@
#include "mime.h"
int serverfd = 0;
+volatile static bool stop = false;
+
+static void signal_int(int signum) {
+ fprintf(stderr, "Terminating...\n");
+ stop = true;
+}
int main(int argc, char** argv) {
+
mime_load(NULL);
config_server *config = config_server_new();
if (config_read_ini("khttpd.ini", config) < 0) {
- return 1;
+ fatal("Could not read config");
}
+ signal(SIGINT, signal_int);
+
skt_elem *connections = NULL;
serverfd = svr_create();
svr_listen(serverfd, config->listen_port);
while(1) {
- uint32_t counter;
+ uint32_t connections_open;
skt_elem *elem, *tmp;
//Accept new connections
- LL_COUNT(connections, elem, counter);
- while(counter < 100 && svr_canaccept(serverfd)) {
+ LL_COUNT(connections, elem, connections_open);
+ while(connections_open < 100 && svr_canaccept(serverfd)) {
skt_info *info = svr_accept(serverfd);
if (info != NULL) {
- LL_APPEND(connections, skt_elem_new(info));
+ skt_elem *elem = skt_elem_new(info);
+ LL_APPEND(connections, elem);
}
}
@@ -78,7 +89,7 @@ int main(int argc, char** argv) {
"error parsing request (%s: %s). closing connection",
http_errno_name(elem->parser->http_errno),
http_errno_description(elem->parser->http_errno));
- warning(warningmsg, false);
+ warning(false, warningmsg);
//send 400 back and close connection
http_response *resp400 = http_response_create_builtin(400, "Request was invalid or could not be read");
http_header_list_add(resp400->headers, http_header_new(HEADER_CONNECTION, "close"), false);
@@ -125,7 +136,7 @@ int main(int argc, char** argv) {
if (elem->info->close_afterwrite && utstring_len(elem->info->write) == 0) {
elem->info->close = true;
}
- if (elem->info->close == true) {
+ if (elem->info->close == true || stop == true) {
skt_close(elem->info);
}
}
@@ -136,9 +147,13 @@ int main(int argc, char** argv) {
skt_elem_delete(elem);
}
}
+ if (stop == true) {
+ break;
+ }
}
mime_free();
+ config_server_delete(config);
svr_release(serverfd);
serverfd = 0;
@@ -168,13 +183,20 @@ void skt_elem_reset(skt_elem *elem) {
elem->request_complete = false;
}
void skt_elem_write_response(skt_elem *elem, http_response *response, bool dispose) {
- char *response_str = http_response_write(response);
- utstring_printf(elem->info->write, "%s", response_str);
- free(response_str);
http_header* connection_header = http_header_list_get(response->headers, HEADER_CONNECTION);
if (connection_header != NULL && strcasecmp(connection_header->content, "close") == 0) {
elem->info->close_afterwrite = true;
}
+ if (connection_header == NULL) {
+ if (response->resp->version == HTTP11) {
+ http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "Keep-Alive"), true);
+ } else if (response->resp->version == HTTP10) {
+ elem->info->close_afterwrite = true;
+ }
+ }
+ char *response_str = http_response_write(response);
+ utstring_printf(elem->info->write, "%s", response_str);
+ free(response_str);
if (dispose == true) {
http_response_delete(response);
}
@@ -182,136 +204,10 @@ void skt_elem_write_response(skt_elem *elem, http_response *response, bool dispo
void skt_elem_delete(skt_elem* elem) {
if (elem->info!=NULL) skt_delete(elem->info);
if (elem->current_request!=NULL) http_request_delete(elem->current_request);
+ if (elem->parser!= NULL) {
+ elem->parser->data = NULL;
+ free(elem->parser);
+ }
+
free(elem);
}
-
-void fatal(char* msg) {
- fprintf(stderr, "\n");
- perror(msg);
- exit(EXIT_FAILURE);
-}
-void warning(char* msg, bool showPError) {
- char warning[1024];
- memset(&warning, 0, 1024*sizeof(char));
- snprintf(warning, 1024, "Warning: %s", msg);
-
- if (showPError == true) {
- perror(warning);
- } else {
- fprintf(stderr, "%s\n", warning);
- }
-}
-void info(char* msg, ...) {
- va_list va;
- va_start(va, msg);
- vfprintf(stdout, msg, va);
- fputc('\n', stdout);
- va_end(va);
-}
-
-char** str_splitlines(char *str, size_t *line_count) {
- char **result;
- *line_count = 0;
- char *tmp = str;
-
- while(*tmp) {
- if (*tmp == '\n') {
- (*line_count)++;
- }
- tmp++;
- }
- if (*line_count == 0) {
- result = calloc(1, sizeof(char*));
- result[0] = calloc(strlen(str), sizeof(char));
- strcpy(result[0], str);
- return result;
- }
- result = calloc(*line_count, sizeof(char*));
- if (result == NULL) {
- fatal("calloc failed");
- }
-
- size_t i=0, linelen = 0;
- char *line = strtok(str, "\n");
- while(line) {
- linelen = strlen(line);
- result[i] = calloc(linelen+1, sizeof(char));
- if (result[i] == NULL) {
- fatal("calloc failed");
- }
- strcpy(result[i], line);
- if (result[i][linelen-1] == '\r') {
- result[i][linelen-1] = '\0';
- result[i] = realloc(result[i], linelen);
- }
- line = strtok(NULL, "\n");
- i++;
- }
-
- return result;
-}
-
-file_map* file_map_new(const char* filename) {
-
- int fd = open(filename, O_RDONLY);
- if (fd < 0) {
- warning("Failed to open file for memory mapping", true);
- return NULL;
- }
- size_t size = lseek(fd, 0L, SEEK_END);
- void* map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (map == MAP_FAILED) {
- warning("Failed to mmap file", true);
- close(fd);
- return NULL;
- }
- close(fd);
-
- file_map* filemap = calloc(1, sizeof(file_map));
- filemap->map = (char*)map;
- filemap->size = size;
- return filemap;
-}
-void file_map_delete(file_map* file) {
- if (munmap((void*)file->map, file->size) < 0) {
- warning("failed to unmap file", true);
- }
- free(file);
-}
-
-char* str_replace(char *haystack, const char *search, const char *replacement) {
-
- size_t haystacklen = strlen(haystack);
- size_t searchlen = strlen(search);
- size_t replacementlen = strlen(replacement);
-
- char* result = haystack;
-
- if (searchlen > haystacklen || searchlen == 0) {
- return result;
- }
- if (strstr(replacement, search) != NULL) {
- warning("str_replace: replacement should not contain the search criteria", false);
- }
- int count = 0;
- while(count++ < 1000) {
- char* pos = strstr(result, search);
- if (pos == NULL) {
- break;
- }
- uint32_t start = (pos - result) / sizeof(char);
- uint32_t end = start + searchlen;
-
- size_t resultlen = strlen(result);
- size_t newlen = resultlen + replacementlen - searchlen;
-
- char* newstr = calloc(newlen+1, sizeof(char));
- strncpy(newstr, result, start);
- strcat(newstr, replacement);
- strcat(newstr, pos+(searchlen*sizeof(char)));
-
- free(result);
- result = newstr;
- }
- return result;
-}
\ No newline at end of file
diff --git a/src/main.h b/src/main.h
index 7608e24..3dca40e 100644
--- a/src/main.h
+++ b/src/main.h
@@ -18,11 +18,7 @@ extern "C" {
#include "http_parser.h"
#include "socket.h"
#include "http.h"
-
- typedef struct file_map {
- char* map;
- size_t size;
- } file_map;
+#include "util.h"
typedef enum skt_elem_hstate {HSTATE_NONE, HSTATE_VALUE, HSTATE_FIELD} skt_elem_hstate;
typedef struct skt_elem {
@@ -42,15 +38,7 @@ extern "C" {
int main(int argc, char** argv);
- void fatal(char* msg);
- void warning(char* msg, bool showPError);
- void info(char* msg, ...);
-
- char** str_splitlines(char *str, size_t *line_count);
- char* str_replace(char *str, const char *search, const char *replacement);
-
- file_map* file_map_new(const char* filename);
- void file_map_delete(file_map* map);
+
#ifdef __cplusplus
diff --git a/src/mime.c b/src/mime.c
index 81584d9..5c80a83 100644
--- a/src/mime.c
+++ b/src/mime.c
@@ -8,13 +8,14 @@
#include "mime.h"
#include "main.h"
-mime_type *mime_list;
+mime_type *mime_list = NULL;
int mime_load(const char* file) {
FILE *mimetypes = fopen(file == NULL ? MIME_DEFAULT_FILE : file, "r");
if (mimetypes == NULL) {
return -1;
}
+ mime_type *new_list = NULL;
size_t count = 1024;
char* buffer = calloc(count, sizeof(char));
ssize_t linelength;
@@ -35,7 +36,7 @@ int mime_load(const char* file) {
mime_type *new = calloc(1, sizeof(mime_type));
new->extension = strdup(ext);
new->mime = strdup(mime);
- LL_APPEND(mime_list, new);
+ LL_APPEND(new_list, new);
ext = strtok_r(NULL, " \t", &saveptr);
}
}
@@ -43,6 +44,12 @@ int mime_load(const char* file) {
}
free(buffer);
fclose(mimetypes);
+
+ if (mime_list != NULL) {
+ mime_free();
+ }
+ mime_list = new_list;
+
count = 0;
mime_type *elem;
LL_COUNT(mime_list, elem, count);
diff --git a/src/socket.c b/src/socket.c
index 0939802..f167950 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -17,7 +17,7 @@
u_int64_t skt_nextid() {
static u_int64_t id = 0;
- return id++;
+ return __atomic_fetch_add(&id, 1, __ATOMIC_SEQ_CST);
}
skt_info* skt_new(int fd) {
skt_info* skt = calloc(1, sizeof(skt_info));
@@ -29,7 +29,6 @@ skt_info* skt_new(int fd) {
skt->close = false;
skt->close_afterwrite = false;
skt->closed = false;
-
return skt;
}
void skt_delete(skt_info* skt) {
@@ -42,7 +41,7 @@ void skt_delete(skt_info* skt) {
bool skt_canread(skt_info* skt) {
int len = 0;
if (ioctl(skt->fd, FIONREAD, &len) < 0) {
- warning("ioctl failed", true);
+ warning(true, "ioctl failed");
return false;
}
return len > 0;
@@ -52,9 +51,11 @@ uint32_t skt_read(skt_info* skt) {
memset(buffer, 0, 1024);
int result = read(skt->fd, &buffer,1023);
- if (result < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
- warning("read error", true);
- skt->close = true;
+ if (result < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ warning(true, "read error");
+ skt->close = true;
+ }
return 0;
}
skt->last_act = time(NULL);
@@ -67,17 +68,19 @@ uint32_t skt_write(skt_info* skt) {
}
int result = write(skt->fd, utstring_body(skt->write), utstring_len(skt->write));
- if (result < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
- warning("write error", true);
- skt->close = true;
+ if (result < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ warning(true, "write error");
+ skt->close = true;
+ }
return 0;
}
+
skt->last_act = time(NULL);
if (result == utstring_len(skt->write)) {
- utstring_free(skt->write);
- utstring_new(skt->write);
- return 0;
+ utstring_clear(skt->write);
+ return result;
}
//remove first x chars
char* newstr = calloc(utstring_len(skt->write) - result + 1, sizeof(char));
@@ -85,8 +88,7 @@ uint32_t skt_write(skt_info* skt) {
char* writeBody = utstring_body(skt->write);
strcpy(newstr, writeBody + (sizeof(char) * result));
- utstring_free(skt->write);
- utstring_new(skt->write);
+ utstring_clear(skt->write);
utstring_printf(skt->write, "%s", newstr);
free(newstr);
return result; //bytes written
@@ -97,7 +99,7 @@ void skt_close(skt_info* skt) {
}
info("[#%lu %s] Closed", skt->id, skt_clientaddr(skt));
if (close(skt->fd) < 0) {
- warning("error closing socket", true);
+ warning(true, "error closing socket");
}
skt->closed = true;
}
@@ -122,20 +124,18 @@ void svr_listen(int fd, uint16_t port) {
server_address.sin_port = htons(port);
if (bind(fd, (struct sockaddr*)&server_address, sizeof server_address) < 0) {
- warning("failed to bind", true);
close(fd);
- exit(EXIT_FAILURE);
+ fatal("Failed to bind to socket");
}
if (listen(fd, 16) < 0) {
- warning("failed to listen", true);
close(fd);
- exit(EXIT_FAILURE);
+ fatal("Could not set socket to listen mode");
}
info("Listening on port %u", port);
}
void svr_release(int fd) {
if (close(fd) < 0) {
- warning("could not close socket", true);
+ warning(true, "could not close socket");
}
}
bool svr_canaccept(int fd) {
@@ -145,12 +145,15 @@ bool svr_canaccept(int fd) {
pfd[0].events = POLLIN;
if (poll(pfd, 1, 50/*ms*/) < 0) {
- warning("poll failed", true);
+ warning(true, "poll failed");
+ free(pfd);
return false;
}
if ((pfd[0].revents & POLLIN) == POLLIN) {
+ free(pfd);
return true;
}
+ free(pfd);
return false;
}
skt_info* svr_accept(int fd) {
@@ -160,7 +163,7 @@ skt_info* svr_accept(int fd) {
socklen_t clientaddr_len = (socklen_t)sizeof(struct sockaddr_in);
clientfd = accept(fd, (struct sockaddr*)clientaddr, &clientaddr_len);
if (clientfd < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
- warning("error accepting connection", true);
+ warning(true, "error accepting connection");
return NULL;
}
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..b468558
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,168 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+void fatal(char* msg) {
+ fprintf(stderr, "\n");
+ perror(msg);
+ exit(EXIT_FAILURE);
+}
+void warning(bool showPError, char* msg, ...) {
+ char warning[128] = {0};
+ va_list va;
+ va_start(va, msg);
+ vsnprintf(warning, 128, msg, va);
+ va_end(va);
+
+ if (showPError == true) {
+ perror(warning);
+ } else {
+ fprintf(stderr, "%s\n", warning);
+ }
+}
+void info(char* msg, ...) {
+ va_list va;
+ va_start(va, msg);
+ vfprintf(stdout, msg, va);
+ fputc('\n', stdout);
+ va_end(va);
+}
+
+char** str_splitlines(char *str, size_t *line_count) {
+ char **result;
+ *line_count = 0;
+ char *tmp = str;
+
+ while(*tmp) {
+ if (*tmp == '\n') {
+ (*line_count)++;
+ }
+ tmp++;
+ }
+ if (*line_count == 0) {
+ result = calloc(1, sizeof(char*));
+ result[0] = calloc(strlen(str), sizeof(char));
+ strcpy(result[0], str);
+ return result;
+ }
+ result = calloc(*line_count, sizeof(char*));
+ if (result == NULL) {
+ fatal("calloc failed");
+ }
+
+ size_t i=0, linelen = 0;
+ char *line = strtok(str, "\n");
+ while(line) {
+ linelen = strlen(line);
+ result[i] = calloc(linelen+1, sizeof(char));
+ if (result[i] == NULL) {
+ fatal("calloc failed");
+ }
+ strcpy(result[i], line);
+ if (result[i][linelen-1] == '\r') {
+ result[i][linelen-1] = '\0';
+ result[i] = realloc(result[i], linelen);
+ }
+ line = strtok(NULL, "\n");
+ i++;
+ }
+
+ return result;
+}
+/* str_trimwhitespace
+ * Credit to https://stackoverflow.com/a/122721/428
+ */
+char* str_trimwhitespace(char *str)
+{
+ char *end;
+
+ // Trim leading space
+ while(isspace(*str)) str++;
+
+ if(*str == 0) // All spaces?
+ return str;
+
+ // Trim trailing space
+ end = str + strlen(str) - 1;
+ while(end > str && isspace(*end)) end--;
+
+ // Write new null terminator
+ *(end+1) = 0;
+
+ return str;
+}
+
+file_map* file_map_new(const char* filename) {
+
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ warning(true, "Failed to open file for memory mapping");
+ return NULL;
+ }
+ size_t size = lseek(fd, 0L, SEEK_END);
+ void* map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ warning(true, "Failed to mmap file");
+ close(fd);
+ return NULL;
+ }
+ close(fd);
+
+ file_map* filemap = calloc(1, sizeof(file_map));
+ filemap->map = (char*)map;
+ filemap->size = size;
+ return filemap;
+}
+void file_map_delete(file_map* file) {
+ if (munmap((void*)file->map, file->size) < 0) {
+ warning(true, "failed to unmap file");
+ }
+ free(file);
+}
+
+char* str_replace(char *haystack, const char *search, const char *replacement) {
+
+ size_t haystacklen = strlen(haystack);
+ size_t searchlen = strlen(search);
+ size_t replacementlen = strlen(replacement);
+
+ char* result = haystack;
+
+ if (searchlen > haystacklen || searchlen == 0) {
+ return result;
+ }
+ if (strstr(replacement, search) != NULL) {
+ warning(false, "str_replace: replacement should not contain the search criteria");
+ }
+ int count = 0;
+ while(count++ < 1000) {
+ char* pos = strstr(result, search);
+ if (pos == NULL) {
+ break;
+ }
+ uint32_t start = (pos - result) / sizeof(char);
+ uint32_t end = start + searchlen;
+
+ size_t resultlen = strlen(result);
+ size_t newlen = resultlen + replacementlen - searchlen;
+
+ char* newstr = calloc(newlen+1, sizeof(char));
+ strncpy(newstr, result, start);
+ strcat(newstr, replacement);
+ strcat(newstr, pos+(searchlen*sizeof(char)));
+
+ free(result);
+ result = newstr;
+ }
+ return result;
+}
\ No newline at end of file
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..135c31e
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,39 @@
+/*
+ * File: util.h
+ * Author: sam
+ *
+ * Created on 03 August 2014, 13:52
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+ typedef struct file_map {
+ char* map;
+ size_t size;
+ } file_map;
+
+ void fatal(char* msg);
+ void warning(bool showPError, char* msg, ...);
+ void info(char* msg, ...);
+
+ char* str_trimwhitespace(char *str);
+ char** str_splitlines(char *str, size_t *line_count);
+ char* str_replace(char *str, const char *search, const char *replacement);
+
+ file_map* file_map_new(const char* filename);
+ void file_map_delete(file_map* map);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UTIL_H */
+
diff --git a/testreq.txt b/testreq.txt
index 663ef9c..7b7f32c 100644
--- a/testreq.txt
+++ b/testreq.txt
@@ -1,4 +1,4 @@
-GET /index.html HTTP/1.1
+GET / HTTP/1.1
Host: example.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36