diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk
index f8565e5..581ae77 100644
--- a/nbproject/Makefile-Debug.mk
+++ b/nbproject/Makefile-Debug.mk
@@ -42,6 +42,7 @@ OBJECTFILES= \
${OBJECTDIR}/src/http-server.o \
${OBJECTDIR}/src/http.o \
${OBJECTDIR}/src/main.o \
+ ${OBJECTDIR}/src/mime.o \
${OBJECTDIR}/src/socket.o
@@ -59,7 +60,7 @@ FFLAGS=
ASFLAGS=
# Link Libraries and Options
-LDLIBSOPTIONS=
+LDLIBSOPTIONS=-lmagic
# Build Targets
.build-conf: ${BUILD_SUBPROJECTS}
@@ -104,6 +105,11 @@ ${OBJECTDIR}/src/main.o: nbproject/Makefile-${CND_CONF}.mk src/main.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/main.o src/main.c
+${OBJECTDIR}/src/mime.o: nbproject/Makefile-${CND_CONF}.mk src/mime.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/mime.o src/mime.c
+
${OBJECTDIR}/src/socket.o: nbproject/Makefile-${CND_CONF}.mk src/socket.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk
index 05d17da..567d3b9 100644
--- a/nbproject/Makefile-Release.mk
+++ b/nbproject/Makefile-Release.mk
@@ -42,6 +42,7 @@ OBJECTFILES= \
${OBJECTDIR}/src/http-server.o \
${OBJECTDIR}/src/http.o \
${OBJECTDIR}/src/main.o \
+ ${OBJECTDIR}/src/mime.o \
${OBJECTDIR}/src/socket.o
@@ -104,6 +105,11 @@ ${OBJECTDIR}/src/main.o: src/main.c
${RM} "$@.d"
$(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/main.o src/main.c
+${OBJECTDIR}/src/mime.o: src/mime.c
+ ${MKDIR} -p ${OBJECTDIR}/src
+ ${RM} "$@.d"
+ $(COMPILE.c) -O2 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/mime.o src/mime.c
+
${OBJECTDIR}/src/socket.o: src/socket.c
${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d"
diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml
index 35b6d1f..ffa3e5e 100644
--- a/nbproject/configurations.xml
+++ b/nbproject/configurations.xml
@@ -11,6 +11,7 @@
lib/http_parser.h
lib/ini.h
src/main.h
+ src/mime.h
src/socket.h
lib/http_parser.c
lib/ini.c
src/main.c
+ src/mime.c
src/socket.c
3
+
+
+ magic
+
+
-
@@ -104,6 +111,10 @@
-
+ -
+
+ -
+
-
-
@@ -165,6 +176,10 @@
-
+ -
+
+ -
+
-
-
diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml
index 358024c..f3ee2df 100644
--- a/nbproject/private/private.xml
+++ b/nbproject/private/private.xml
@@ -8,6 +8,8 @@
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/src/http-server.h
file:/home/sam/NetBeansProjects/KHttp/src/http.c
file:/home/sam/NetBeansProjects/KHttp/src/http-reader.h
@@ -16,8 +18,10 @@
file:/home/sam/NetBeansProjects/KHttp/content/error.html
file:/home/sam/NetBeansProjects/KHttp/src/main.c
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 5d2da86..ca3c62a 100644
--- a/src/config.c
+++ b/src/config.c
@@ -43,7 +43,7 @@ config_host* config_server_gethost(config_server *config, char *name) {
config_host *defaulthost=NULL;
config_host *host=NULL;
CONFIG_SERVER_FOREACH_HOST(config, host) {
- if (strcasecmp(name, host->hostname) == 0) {
+ if (name != NULL && strcasecmp(name, host->hostname) == 0) {
return host;
}
if (host->default_host == true) {
@@ -123,13 +123,19 @@ static int config_read_ini_cb(void* _config, const char* section, const char* na
host->default_host = false;
}
} else if (MATCH("Host", "serve")) {
- DIR *dir = opendir(value);
+ char* serve_dir = realpath(value, NULL);
+ if (serve_dir == NULL) {
+ warning("Config: host serve directory is invalid", true);
+ return -1;
+ }
+ DIR* dir = opendir(serve_dir);
if (dir == NULL) {
+ free(serve_dir);
warning("Config: host serve directory is invalid", true);
return -1;
}
closedir(dir);
- host->serve_dir = strdup(value);
+ host->serve_dir = serve_dir;
}
}
#undef MATCH
diff --git a/src/config.h b/src/config.h
index ee76bcd..4cd9388 100644
--- a/src/config.h
+++ b/src/config.h
@@ -11,19 +11,18 @@
#ifdef __cplusplus
extern "C" {
#endif
-
#include
#include "main.h"
#define CONFIG_SERVER_FOREACH_HOST(config, elem) \
- elem = config->hosts[0]; \
+ elem = config->hosts != NULL ? config->hosts[0] : NULL; \
for(int i=0; i < config->host_count; elem=config->hosts[i++])
typedef struct config_host {
char *hostname;
bool default_host;
bool enabled;
- char* serve_dir;
+ char *serve_dir;
} config_host;
typedef struct config_server {
diff --git a/src/http-server.c b/src/http-server.c
index ce35a83..614e870 100644
--- a/src/http-server.c
+++ b/src/http-server.c
@@ -1,6 +1,145 @@
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include "http_parser.h"
#include "http.h"
#include "main.h"
+#include "config.h"
+#include "http-server.h"
+#include "mime.h"
+http_response* server_process_request(config_server* config, http_request *request) {
+ http_response* response = NULL;
+ //Determin host
+ char* hostname=NULL;
+ if (request->req->version == HTTP11) {
+ http_header *hostheader = http_header_list_get(request->headers, HEADER_HOST);
+ if (hostheader != NULL) {
+ hostname = strdup(hostheader->content);
+ } else {
+ response = http_response_create_builtin(400, "Host header required");
+ http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ return response;
+ }
+ }
+ //hostname may be null, this indicates that the default host should be used
+ config_host* host_config = config_server_gethost(config, hostname);
+ if (hostname != NULL) {
+ free(hostname);
+ hostname = NULL;
+ }
+ if (host_config == NULL) {
+ //host not found and default not found
+ response = http_response_create_builtin(500, "Server configuration error (host/default not found)");
+ http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ return response;
+ }
+
+ //Validate request method
+ http_request_method acceptable_methods[] = {METHOD_GET, METHOD_HEAD, METHOD_POST};
+ bool valid_method = false;
+ for(int i=0; ireq->method == acceptable_methods[i]) {
+ valid_method = true;
+ break;
+ }
+ }
+ if (valid_method == false) {
+ response = http_response_create_builtin(411, "Method is not valid");
+ http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ return response;
+ }
+
+ //Parse the request uri
+ struct http_parser_url *url = calloc(1, sizeof(struct http_parser_url));
+ 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);
+ return response;
+ }
+
+ //Get the uri/path
+ char* uri = NULL;
+ if ((url->field_set & (1 << UF_PATH)) != 0) {
+ uri = calloc(url->field_data[UF_PATH].len+1, sizeof(char));
+ strncpy(uri, request->req->uri+url->field_data[UF_PATH].off, url->field_data[UF_PATH].len);
+ } else {
+ //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);
+ return response;
+ }
+
+ //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);
+
+ //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");
+ 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;
+ }
+
+ 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");
+ http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ 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;
+ }
+
+ //Open file
+ FILE *file = fopen(filepath_actual, "r");
+ if (file == NULL) {
+ warning("failed to open file for reading", true);
+ response = http_response_create_builtin(404, "File not found");
+ return response;
+ }
+
+ //File is ok and can be served to the client
+ fseek(file, 0, SEEK_END);
+ size_t filesize = ftell(file);
+ rewind(file);
+
+ //Read file into response
+ //TODO: send file directly from the write loop
+ char* buffer = calloc(filesize, sizeof(char));
+ if (fread(buffer, sizeof(char), filesize, file) != filesize) {
+ warning("failed to read file into memory", true);
+ 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);
+ http_response_append_body(response, buffer);
+ }
+ fclose(file);
+ free(buffer);
+ free(filepath_requested);
+ free(filepath_actual);
+
+ return response;
+}
\ No newline at end of file
diff --git a/src/http-server.h b/src/http-server.h
index a2c7f21..b0e3f59 100644
--- a/src/http-server.h
+++ b/src/http-server.h
@@ -14,10 +14,11 @@ extern "C" {
#include "http.h"
#include "main.h"
+#include "config.h"
+
+ http_response* server_process_request(config_server* config, http_request *request);
-
-
#ifdef __cplusplus
}
#endif
diff --git a/src/http.c b/src/http.c
index 719b384..5e8aa83 100644
--- a/src/http.c
+++ b/src/http.c
@@ -428,4 +428,22 @@ char* http_chunks_terminate(http_header_list *footers) {
char* outputstr = utstring_body(output);
free(output);
return outputstr;
+}
+
+http_response_list* http_response_list_new() {
+ http_response_list *list = calloc(1, sizeof(http_response_list));
+ list->responses = NULL;
+ list->count = 0;
+ return list;
+}
+void http_response_list_append(http_response_list *list, http_response* response) {
+ list->responses = realloc(list->responses, (++list->count)*sizeof(http_response*));
+ list->responses[list->count-1] = response;
+}
+void http_response_list_delete(http_response_list *list) {
+ http_response *elem;
+ HTTP_RESPONSE_LIST_FOREACH(list, elem) {
+ http_response_delete(elem);
+ }
+ free(list);
}
\ No newline at end of file
diff --git a/src/http.h b/src/http.h
index cdb39e1..be5d838 100644
--- a/src/http.h
+++ b/src/http.h
@@ -89,6 +89,14 @@ extern "C" {
char* body;
} http_response;
+#define HTTP_RESPONSE_LIST_FOREACH(list, elem) \
+ elem = list->count == 0 ? NULL : list->responses[0]; \
+ for(int i=0; icount; elem=list->responses[++i])
+
+ typedef struct http_response_list {
+ http_response **responses;
+ size_t count;
+ } http_response_list;
char* http_method_getstring(http_request_method method, char* method_other);
http_request_method http_method_fromstring(const char* method);
@@ -124,6 +132,10 @@ extern "C" {
char* http_chunks_write(char* source);
char* http_chunks_terminate(http_header_list *footers);
+
+ http_response_list* http_response_list_new();
+ void http_response_list_append(http_response_list *list, http_response* response);
+ void http_response_list_delete(http_response_list *list);
#ifdef __cplusplus
}
diff --git a/src/main.c b/src/main.c
index 13d0135..a8b2575 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,10 +24,13 @@
#include "http.h"
#include "http-reader.h"
#include "config.h"
+#include "http-server.h"
+#include "mime.h"
int serverfd = 0;
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;
@@ -78,17 +81,23 @@ int main(int argc, char** argv) {
warning(warningmsg, false);
//send 400 back and close connection
http_response *resp400 = http_response_create_builtin(400, "Request was invalid or could not be read");
- char *resp400str = http_response_write(resp400);
- utstring_printf(elem->info->write, "%s", resp400str);
+ http_header_list_add(resp400->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ skt_elem_write_response(elem, resp400, false);
http_response_delete(resp400);
- free(resp400str);
- elem->info->close_afterwrite = true;
+ skt_elem_reset(elem);
}
//Clear read data now that we have processed it
utstring_clear(elem->info->read);
//Process request if received
if (elem->request_complete == true) {
+ http_response *response = server_process_request(config, elem->current_request);
+ if (response == NULL) {
+ response = http_response_create_builtin(500, "Request could not be processed");
+ http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
+ }
+ skt_elem_write_response(elem, response, true);
+ skt_elem_reset(elem);
}
}
}
@@ -129,6 +138,7 @@ int main(int argc, char** argv) {
}
}
+ mime_free();
svr_release(serverfd);
serverfd = 0;
@@ -145,6 +155,30 @@ skt_elem* skt_elem_new(skt_info *info) {
elem->request_complete = false;
return elem;
}
+void skt_elem_reset(skt_elem *elem) {
+ if (elem->current_request != NULL) {
+ http_request_delete(elem->current_request);
+ elem->current_request = NULL;
+ }
+ if (elem->parser_current_header != NULL) {
+ http_header_delete(elem->parser_current_header);
+ }
+ elem->parser_current_header = NULL;
+ elem->parser_header_state = HSTATE_NONE;
+ 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);
+ if (dispose == true) {
+ http_response_delete(response);
+ }
+ 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;
+ }
+}
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);
diff --git a/src/main.h b/src/main.h
index 683530c..afb1f40 100644
--- a/src/main.h
+++ b/src/main.h
@@ -34,6 +34,8 @@ extern "C" {
} skt_elem;
skt_elem* skt_elem_new(skt_info *info);
+ void skt_elem_reset(skt_elem *elem);
+ void skt_elem_write_response(skt_elem *skt, http_response *response, bool dispose);
void skt_elem_delete(skt_elem* elem);
int main(int argc, char** argv);
diff --git a/src/mime.c b/src/mime.c
new file mode 100644
index 0000000..81584d9
--- /dev/null
+++ b/src/mime.c
@@ -0,0 +1,96 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "ut/utlist.h"
+#include "mime.h"
+#include "main.h"
+
+mime_type *mime_list;
+
+int mime_load(const char* file) {
+ FILE *mimetypes = fopen(file == NULL ? MIME_DEFAULT_FILE : file, "r");
+ if (mimetypes == NULL) {
+ return -1;
+ }
+ size_t count = 1024;
+ char* buffer = calloc(count, sizeof(char));
+ ssize_t linelength;
+ while((linelength = getline(&buffer, &count, mimetypes)) >= 0) {
+
+ if (linelength == 0) {
+ continue;
+ }
+ if (buffer[0] == '#' || buffer[0] == '\n') {
+ continue;
+ }
+ char* line = strndup(buffer, linelength);
+ char mime[512], extstr[512];
+ if (sscanf(line, "%511s%*[ \t]%511[a-z0-9 \t]", mime, extstr) == 2) {
+ char* saveptr=NULL;
+ char* ext = strtok_r(extstr, " \t", &saveptr);
+ while(ext != NULL) {
+ mime_type *new = calloc(1, sizeof(mime_type));
+ new->extension = strdup(ext);
+ new->mime = strdup(mime);
+ LL_APPEND(mime_list, new);
+ ext = strtok_r(NULL, " \t", &saveptr);
+ }
+ }
+ free(line);
+ }
+ free(buffer);
+ fclose(mimetypes);
+ count = 0;
+ mime_type *elem;
+ LL_COUNT(mime_list, elem, count);
+ return count;
+}
+void mime_free() {
+ mime_type *elem, *tmp;
+ LL_FOREACH_SAFE(mime_list, elem, tmp) {
+ free(elem->extension);
+ free(elem->mime);
+ free(elem);
+ }
+ mime_list = NULL;
+}
+void mime_print_all() {
+ mime_type *elem;
+ LL_FOREACH(mime_list, elem) {
+ printf("%s\t\t%s\n", elem->mime, elem->extension);
+ }
+}
+
+const char* mime_get_type(const char* filename, const char* fallback) {
+ char *ext = strrchr(filename, '.');
+ if (ext != NULL && strlen(ext) > 1) {
+ ext++; //Skip .
+ mime_type *elem;
+ LL_FOREACH(mime_list, elem) {
+ if (strcmp(ext, elem->extension) == 0) {
+ return elem->mime;
+ }
+ }
+ }
+ return mime_get_type_magic(filename, fallback);
+}
+
+const char* mime_get_type_magic(const char* filename, const char* fallback) {
+ magic_t magic;
+
+ magic = magic_open(MAGIC_MIME_TYPE);
+ magic_load(magic, NULL);
+ magic_compile(magic, NULL);
+
+ const char* mime = magic_file(magic, filename);
+ magic_close(magic);
+
+ if (mime != NULL) {
+ return mime;
+ }
+
+ return fallback;
+}
\ No newline at end of file
diff --git a/src/mime.h b/src/mime.h
new file mode 100644
index 0000000..ada68dd
--- /dev/null
+++ b/src/mime.h
@@ -0,0 +1,39 @@
+/*
+ * File: mime.h
+ * Author: sam
+ *
+ * Created on 29 July 2014, 19:38
+ */
+
+#ifndef MIME_H
+#define MIME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ut/utlist.h"
+
+#define MIME_DEFAULT_FILE "/etc/mime.types"
+
+ typedef struct mime_type {
+ char* mime;
+ char* extension;
+ struct mime_type *next;
+ } mime_type;
+
+ extern mime_type *mime_list;
+
+ int mime_load(const char* file);
+ void mime_free();
+ void mime_print_all();
+
+ const char* mime_get_type(const char* filename, const char* fallback);
+ const char* mime_get_type_magic(const char* filename, const char* fallback);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MIME_H */
+
diff --git a/testreq.txt b/testreq.txt
index d6d2969..6338ec5 100644
--- a/testreq.txt
+++ b/testreq.txt
@@ -1,4 +1,4 @@
-GET /testing123 HTTP/1.1
+GET /index.html 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