Server is basically working.
Needs a re-design.
This commit is contained in:
1
Makefile
1
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...
|
||||
|
||||
24
content/dirindex.html
Normal file
24
content/dirindex.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{dirname}}</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{dirname}}</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Filesize</td>
|
||||
<td>Last modified</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{index}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<itemPath>src/main.h</itemPath>
|
||||
<itemPath>src/mime.h</itemPath>
|
||||
<itemPath>src/socket.h</itemPath>
|
||||
<itemPath>src/util.h</itemPath>
|
||||
</logicalFolder>
|
||||
<logicalFolder name="ResourceFiles"
|
||||
displayName="Resource Files"
|
||||
@@ -30,6 +31,7 @@
|
||||
<itemPath>src/main.c</itemPath>
|
||||
<itemPath>src/mime.c</itemPath>
|
||||
<itemPath>src/socket.c</itemPath>
|
||||
<itemPath>src/util.c</itemPath>
|
||||
</logicalFolder>
|
||||
<logicalFolder name="TestFiles"
|
||||
displayName="Test Files"
|
||||
@@ -57,11 +59,12 @@
|
||||
</toolsSet>
|
||||
<compileType>
|
||||
<cTool>
|
||||
<architecture>2</architecture>
|
||||
<standard>3</standard>
|
||||
<incDir>
|
||||
<pElem>lib</pElem>
|
||||
</incDir>
|
||||
<commandLine>-O0</commandLine>
|
||||
<commandLine>-O0 -march=native</commandLine>
|
||||
<preprocessorList>
|
||||
<Elem>INI_ALLOW_BOM=0</Elem>
|
||||
<Elem>INI_ALLOW_MULTILINE=0</Elem>
|
||||
@@ -119,26 +122,38 @@
|
||||
</item>
|
||||
<item path="src/socket.h" ex="false" tool="3" flavor2="0">
|
||||
</item>
|
||||
<item path="src/util.c" ex="false" tool="0" flavor2="0">
|
||||
</item>
|
||||
<item path="src/util.h" ex="false" tool="3" flavor2="0">
|
||||
</item>
|
||||
</conf>
|
||||
<conf name="Release" type="1">
|
||||
<toolsSet>
|
||||
<compilerSet>default</compilerSet>
|
||||
<dependencyChecking>true</dependencyChecking>
|
||||
<rebuildPropChanged>false</rebuildPropChanged>
|
||||
<rebuildPropChanged>true</rebuildPropChanged>
|
||||
</toolsSet>
|
||||
<compileType>
|
||||
<cTool>
|
||||
<developmentMode>5</developmentMode>
|
||||
<architecture>2</architecture>
|
||||
<standard>3</standard>
|
||||
<incDir>
|
||||
<pElem>lib</pElem>
|
||||
</incDir>
|
||||
<commandLine>-march=native</commandLine>
|
||||
<preprocessorList>
|
||||
<Elem>INI_ALLOW_BOM=0</Elem>
|
||||
<Elem>INI_ALLOW_MULTILINE=0</Elem>
|
||||
<Elem>_GNU_SOURCE</Elem>
|
||||
</preprocessorList>
|
||||
<warningLevel>3</warningLevel>
|
||||
</cTool>
|
||||
<ccTool>
|
||||
<developmentMode>5</developmentMode>
|
||||
</ccTool>
|
||||
<fortranCompilerTool>
|
||||
<developmentMode>5</developmentMode>
|
||||
</fortranCompilerTool>
|
||||
<asmTool>
|
||||
<developmentMode>5</developmentMode>
|
||||
</asmTool>
|
||||
<linkerTool>
|
||||
<linkerLibItems>
|
||||
<linkerLibLibItem>magic</linkerLibLibItem>
|
||||
</linkerLibItems>
|
||||
</linkerTool>
|
||||
</compileType>
|
||||
<item path="content/error.html" ex="false" tool="3" flavor2="0">
|
||||
</item>
|
||||
@@ -184,6 +199,10 @@
|
||||
</item>
|
||||
<item path="src/socket.h" ex="false" tool="3" flavor2="0">
|
||||
</item>
|
||||
<item path="src/util.c" ex="false" tool="0" flavor2="0">
|
||||
</item>
|
||||
<item path="src/util.h" ex="false" tool="3" flavor2="0">
|
||||
</item>
|
||||
</conf>
|
||||
</confs>
|
||||
</configurationDescriptor>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<runcommandpicklistitem>"${OUTPUT_PATH}"</runcommandpicklistitem>
|
||||
</runcommandpicklist>
|
||||
<runcommand>"${OUTPUT_PATH}"</runcommand>
|
||||
<rundir></rundir>
|
||||
<rundir>/home/sam/NetBeansProjects/KHttp/./dist/Debug/GNU-Linux-x86</rundir>
|
||||
<buildfirst>true</buildfirst>
|
||||
<terminal-type>0</terminal-type>
|
||||
<remove-instrumentation>0</remove-instrumentation>
|
||||
|
||||
@@ -8,19 +8,19 @@
|
||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
|
||||
<group>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/socket.c</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/content/khttpd.ini</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/config.c</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/content/dirindex.html</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/http-server.h</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/http.c</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/http-reader.h</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/lib/ut/utstring.h</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/main.h</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/lib/http_parser.h</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/Makefile</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/content/error.html</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/main.c</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/socket.h</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/http-server.c</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/lib/ini.c</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/http.h</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/http-reader.c</file>
|
||||
<file>file:/home/sam/NetBeansProjects/KHttp/src/config.h</file>
|
||||
</group>
|
||||
</open-files>
|
||||
|
||||
51
src/config.c
51
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; i<host->index_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;i<host->index_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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include <magic.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <time.h>
|
||||
|
||||
#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; i<hconfig->index_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, "<tr><td>%s</td><td>%s</td><td>%s</td></tr>\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);
|
||||
}
|
||||
@@ -12,12 +12,31 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
180
src/main.c
180
src/main.c
@@ -9,11 +9,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
16
src/main.h
16
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
|
||||
|
||||
11
src/mime.c
11
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);
|
||||
|
||||
47
src/socket.c
47
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;
|
||||
}
|
||||
|
||||
|
||||
168
src/util.c
Normal file
168
src/util.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
39
src/util.h
Normal file
39
src/util.h
Normal file
@@ -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 <stdbool.h>
|
||||
|
||||
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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user