diff --git a/Makefile b/Makefile index 101dd75..b2e5765 100644 --- a/Makefile +++ b/Makefile @@ -5,29 +5,26 @@ LDFLAGS = -lpthread -lrt BIN_NAME = cfloor +OBJS = obj/networking.o obj/linked.o obj/logging.o obj/signals.o obj/headers.o obj/misc.o obj/status.o +DEPS = $(OBJS:%.o=%.d) + all: $(BIN_NAME) -$(BIN_NAME): obj/main.o obj/networking.o obj/linked.o obj/logging.o obj/signals.o obj/headers.o obj/misc.o +$(BIN_NAME): obj/main.o $(OBJS) $(LD) $(LDFLAGS) -o $@ $^ -test: obj/test.o obj/networking.o obj/linked.o obj/logging.o obj/signals.o obj/headers.o obj/misc.o +test: dependencies +test: obj/test.o $(OBJS) $(LD) $(LDFLAGS) -o $@ $^ valgrind: CFLAGS += -static -g valgrind: clean test valgrind --leak-check=yes ./test -obj/main.o: src/networking.h src/linked.h src/logging.h src/signals.h src/misc.h src/headers.h -obj/test.o: src/networking.h src/linked.h src/logging.h src/signals.h src/misc.h src/headers.h -obj/networking.o: src/networking.h src/headers.h src/linked.h src/logging.h src/signals.h -obj/linked.o: src/linked.h -obj/logging.o: src/logging.h -obj/signals.o: src/signals.h -obj/headers.o: src/headers.h src/misc.h -obj/misc.o: src/misc.h +-include $(DEPS) -obj/%.o: src/%.c obj - $(CC) $(CFLAGS) -c -o $@ $< +obj/%.o: src/%.c + $(CC) $(CFLAGS) -MMD -c -o $@ $< obj: @mkdir -p obj @@ -35,5 +32,6 @@ obj: clean: @echo "Cleaning up..." @rm -f obj/*.o + @rm -f obj/*.d @rm -f test @rm -f $(BIN_NAME) diff --git a/src/error.c b/src/error.c deleted file mode 100644 index 64ff8da..0000000 --- a/src/error.c +++ /dev/null @@ -1,7 +0,0 @@ - - -#include "error.h" - -void status500(struct request request, struct response response) { - -} diff --git a/src/error.h b/src/error.h deleted file mode 100644 index 9cb1856..0000000 --- a/src/error.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ERROR_H -#define ERROR_H - -#include "misc.h" - -handler_t status500; - -#endif diff --git a/src/headers.c b/src/headers.c index bb1f1df..87e15e3 100644 --- a/src/headers.c +++ b/src/headers.c @@ -4,6 +4,12 @@ #include "headers.h" #include "misc.h" +struct headers headers_create() { + return (struct headers) { + .number = 0 + }; +} + int headers_find(struct headers* headers, const char* key) { for (int i = 0; i < headers->number; i++) { if (strcmp(headers->headers[i].key, key) == 0) @@ -19,12 +25,26 @@ const char* headers_get(struct headers* headers, const char* key) { return headers->headers[tmp].value; } -int headers_mod(struct headers* headers, char* key, char* value) { +int headers_mod(struct headers* headers, const char* _key, const char* _value) { + char* tmp = strclone(_key); + if (tmp == NULL) { + return HEADERS_ALLOC_ERROR; + } + char* key = tmp; + tmp = strclone(_value); + if (tmp == NULL) { + return HEADERS_ALLOC_ERROR; + } + char* value = tmp; + int index = headers_find(headers, key); if (index < 0) { struct header* tmp = realloc(headers->headers, (headers->number + 1) * sizeof(struct header)); if (tmp == NULL) { + free(key); + free(value); + // we don't need to clean up this connection gets dropped anyway return HEADERS_ALLOC_ERROR; } @@ -109,7 +129,10 @@ int headers_parse(struct headers* headers, const char* _currentHeader, size_t le } } - return headers_mod(headers, key, value); + int tmp = headers_mod(headers, key, value); + free(key); + free(value); + return tmp; } void headers_free(struct headers* headers) { @@ -122,6 +145,14 @@ void headers_free(struct headers* headers) { if (headers->headers != NULL) free(headers->headers); + + headers->number = 0; +} + +void headers_dump(struct headers* headers, FILE* stream) { + for (int i = 0; i < headers->number; i++) { + fprintf(stream, "%s: %s\r\n", headers->headers[i].key, headers->headers[i].value); + } } int headers_metadata(struct metaData* metaData, char* header) { diff --git a/src/headers.h b/src/headers.h index 3393512..ffa5f7a 100644 --- a/src/headers.h +++ b/src/headers.h @@ -1,6 +1,8 @@ #ifndef HEADERS_H #define HEADERS_H +#include + #include "misc.h" #define HEADERS_SUCCESS (0) @@ -18,10 +20,12 @@ struct headers { struct header* headers; }; +struct headers headers_create(); const char* headers_get(struct headers* headers, const char* key); -int headers_mod(struct headers* headers, char* key, char* value); +int headers_mod(struct headers* headers, const char* key, const char* value); int headers_parse(struct headers* headers, const char* currentHeader, size_t length); void headers_free(struct headers* headers); +void headers_dump(struct headers* headers, FILE* stream); int headers_metadata(struct metaData* metaData, char* header); diff --git a/src/logging.h b/src/logging.h index f7c9e73..bbf5f25 100644 --- a/src/logging.h +++ b/src/logging.h @@ -3,6 +3,7 @@ #include #include +#include enum loglevel { DEBUG, VERBOSE, INFO, WARN, ERROR, CRITICAL diff --git a/src/main.c b/src/main.c index 246cb3b..15b95c9 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include "networking.h" #include "logging.h" +#include "headers.h" handler_t handlerGetter(struct metaData metaData, const char* host, struct bind* bind) { return NULL; @@ -13,6 +14,9 @@ int main(int argc, char** argv) { setLogging(stderr, DEBUG, true); setCriticalHandler(NULL); + struct headers headers = headers_create(); + headers_mod(&headers, "Server", "CFloor 0.1"); + struct networkingConfig config = { .maxConnections = 1024, .connectionTimeout = 30000, @@ -25,9 +29,7 @@ int main(int argc, char** argv) { } } }, - .defaultResponse = { - .number = 0 - }, + .defaultHeaders = headers, .getHandler = &handlerGetter }; @@ -36,4 +38,6 @@ int main(int argc, char** argv) { while(true) { sleep(0xffff); } + + headers_free(&headers); } diff --git a/src/misc.c b/src/misc.c index d260593..f7dd150 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1,10 +1,23 @@ #include #include +#include +#include #include #include "misc.h" +const char* getHTTPVersionString(enum httpVersion version) { + switch(version) { + case HTTP10: + return "HTTP/1.0"; + case HTTP11: + return "HTTP/1.1"; + default: + return "UNKNOWN"; + } +} + int startCopyThread(int from, int to, pthread_t* thread) { struct fileCopy* files = malloc(sizeof(struct fileCopy)); if (files < 0) @@ -18,10 +31,12 @@ int startCopyThread(int from, int to, pthread_t* thread) { void* fileCopyThread(void* data) { struct fileCopy* files = (struct fileCopy*) data; - char* c; + char c; - while(read(files->readFd, &c, 1) > 0) + while(read(files->readFd, &c, 1) > 0) { write(files->writeFd, &c, 1); + //printf("copy thread: %c\n", c); + } close(files->readFd); close(files->writeFd); @@ -30,3 +45,11 @@ void* fileCopyThread(void* data) { return NULL; } + +char* strclone(const char* string) { + char* result = malloc(strlen(string) + 1); + if (result == NULL) + return NULL; + strcpy(result, string); + return result; +} diff --git a/src/misc.h b/src/misc.h index 4c6e0e8..9538631 100644 --- a/src/misc.h +++ b/src/misc.h @@ -24,21 +24,28 @@ struct metaData { */ #include "headers.h" +const char* getHTTPVersionString(enum httpVersion version); + +union userData { + int integer; + void* ptr; +}; + struct request { struct metaData metaData; struct headers* headers; int fd; void* _private; + union userData user; }; struct response { - int (*sendHeader)(int statusCode, struct headers headers, struct request* request); + int (*sendHeader)(int statusCode, struct headers* headers, struct request* request); }; typedef void (*handler_t)(struct request request, struct response response); - struct fileCopy { int readFd; int writeFd; @@ -46,4 +53,6 @@ struct fileCopy { int startCopyThread(int from, int to, pthread_t* thread); void* fileCopyThread(void* data); +char* strclone(const char* string); + #endif diff --git a/src/networking.c b/src/networking.c index e36ba57..70cade4 100644 --- a/src/networking.c +++ b/src/networking.c @@ -15,7 +15,7 @@ #include "linked.h" #include "logging.h" #include "signals.h" -#include "error.h" +#include "status.h" struct networkingConfig networkingConfig; @@ -158,14 +158,6 @@ int dumpHeaderBuffer(char* buffer, size_t size, struct connection* connection) { return 0; } -int sendHeader(int statusCode, struct headers headers, struct request* request) { - struct connection* connection = (struct connection*) request->_private; - - // send statusCode and headers - - return connection->threads.responseFd; -} - static inline void stopThread(pthread_t self, pthread_t thread, bool force) { if (pthread_equal(self, thread)) return; @@ -179,20 +171,61 @@ static inline void stopThread(pthread_t self, pthread_t thread, bool force) { void safeEndConnection(struct connection* connection, bool force) { pthread_t self = pthread_self(); + debug("networking: safely shuting down the connection. %d", force); + stopThread(self, connection->threads.request, true); stopThread(self, connection->threads.response, force); stopThread(self, connection->threads.helper[0], force); stopThread(self, connection->threads.helper[1], force); + + close(connection->fd); connection->inUse--; } +int sendHeader(int statusCode, struct headers* headers, struct request* request) { + debug("networking: sending headers"); + + struct headers defaultHeaders = networkingConfig.defaultHeaders; + + for(int i = 0; i < defaultHeaders.number; i++) { + headers_mod(headers, defaultHeaders.headers[i].key, defaultHeaders.headers[i].value); + } + + struct connection* connection = (struct connection*) request->_private; + int fd = connection->threads.responseFd; + + FILE* stream = fdopen(dup(fd), "w"); + if (stream == NULL) { + error("networking: Couldn't send header."); + return -1; + } + + struct statusStrings strings = getStatusStrings(statusCode); + + fprintf(stream, "%s %d %s\r\n", getHTTPVersionString(connection->metaData.httpVersion), statusCode, strings.statusString); + + fprintf(stderr, "%s %d %s\r\n", getHTTPVersionString(connection->metaData.httpVersion), statusCode, strings.statusString); + + headers_dump(headers, stream); + + headers_dump(headers, stderr); + + + fprintf(stream, "\r\n"); + fclose(stream); + + return fd; +} + /* * This thread calls the handler. */ void* responseThread(void* data) { struct connection* connection = (struct connection*) data; + debug("networking: calling response handler"); + connection->threads.handler((struct request) { .metaData = connection->metaData, .headers = &(connection->headers), @@ -201,6 +234,8 @@ void* responseThread(void* data) { }, (struct response) { .sendHeader = sendHeader }); + + debug("networking: response handler returned"); close(connection->threads.requestFd); close(connection->threads._requestFd); @@ -256,7 +291,7 @@ void* requestThread(void* data) { connection->threads._responseFd = response; connection->threads.responseFd = pipefd[1]; - if (startCopyThread(connection->fd, request, &(connection->threads.helper[0])) < 0) { + if (startCopyThread(dup(connection->fd), request, &(connection->threads.helper[0])) < 0) { close(request); close(connection->threads.requestFd); close(response); @@ -268,7 +303,7 @@ void* requestThread(void* data) { connection->inUse--; return NULL; } - if (startCopyThread(response, connection->fd, &(connection->threads.helper[1])) < 0) { + if (startCopyThread(response, dup(connection->fd), &(connection->threads.helper[1])) < 0) { close(request); close(connection->threads.requestFd); close(response); @@ -294,7 +329,11 @@ void* requestThread(void* data) { return NULL; } - // timeout + debug("networking: going to sleep"); + sleep(100); + + error("networking: Timeout of handler."); + error("networking: Aborting"); close(request); close(connection->threads.requestFd); @@ -309,6 +348,7 @@ void* requestThread(void* data) { void startRequestHandler(struct connection* connection) { connection->inUse++; + debug("networking: starting request handler"); if (pthread_create(&(connection->threads.request), NULL, &requestThread, connection) != 0) { error("networking: Couldn't start request thread."); warn("networking: Aborting request."); @@ -483,6 +523,11 @@ void* listenThread(void* _bind) { if (tmp == -1) continue; + + if (setsockopt(tmp, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) { + close(tmp); + continue; + } bindObj->_private.socketFd = tmp; @@ -493,7 +538,7 @@ void* listenThread(void* _bind) { } if (rp == NULL) { - error("networking: Could not bind."); + error("networking: Could not bind: %s", strerror(errno)); warn("networking: Not listening on %s:%s", bindObj->address == NULL ? "0.0.0.0" : bindObj->address, bindObj->port); return NULL; } @@ -551,10 +596,7 @@ void* listenThread(void* _bind) { .path = NULL, .queryString = NULL }; - connection->headers = (struct headers) { - .number = 0, - .headers = NULL - }; + connection->headers = headers_create(); connection->threads = (struct threads) { /* * This is really hacky. pthread_t is no(t always an) integer. diff --git a/src/networking.h b/src/networking.h index 435acd2..e641c64 100644 --- a/src/networking.h +++ b/src/networking.h @@ -74,7 +74,7 @@ struct networkingConfig { struct binds binds; long connectionTimeout; long maxConnections; - struct headers defaultResponse; + struct headers defaultHeaders; handlerGetter_t getHandler; }; diff --git a/src/status.c b/src/status.c new file mode 100644 index 0000000..df4f155 --- /dev/null +++ b/src/status.c @@ -0,0 +1,365 @@ +#include +#include +#include +#include + +#include "status.h" +#include "misc.h" +#include "logging.h" + +struct statusStrings getStatusStrings(int status) { + switch(status) { + case 100: + return (struct statusStrings) { + .statusString = "Continue", + .statusFormat = "" + }; + case 101: + return (struct statusStrings) { + .statusString = "Switching Protocols", + .statusFormat = "" + }; + case 102: + return (struct statusStrings) { + .statusString = "Processing", + .statusFormat = "" + }; + + case 200: + return (struct statusStrings) { + .statusString = "OK", + .statusFormat = "" + }; + case 201: + return (struct statusStrings) { + .statusString = "Created", + .statusFormat = "" + }; + case 202: + return (struct statusStrings) { + .statusString = "Accepted", + .statusFormat = "" + }; + case 203: + return (struct statusStrings) { + .statusString = "Non-Authoritative Information", + .statusFormat = "" + }; + case 204: + return (struct statusStrings) { + .statusString = "No Content", + .statusFormat = "" + }; + case 205: + return (struct statusStrings) { + .statusString = "Reset Content", + .statusFormat = "" + }; + case 206: + return (struct statusStrings) { + .statusString = "Partial Content", + .statusFormat = "" + }; + case 207: + return (struct statusStrings) { + .statusString = "Multi-Status", + .statusFormat = "" + }; + case 208: + return (struct statusStrings) { + .statusString = "Already Reported", + .statusFormat = "" + }; + case 226: + return (struct statusStrings) { + .statusString = "IM Used", + .statusFormat = "" + }; + + case 300: + return (struct statusStrings) { + .statusString = "Multiple Choices", + .statusFormat = "" + }; + case 301: + return (struct statusStrings) { + .statusString = "Moved Permanently", + .statusFormat = "" + }; + case 302: + return (struct statusStrings) { + .statusString = "Found (Moved Temporarily)", + .statusFormat = "" + }; + case 303: + return (struct statusStrings) { + .statusString = "See Other", + .statusFormat = "" + }; + case 304: + return (struct statusStrings) { + .statusString = "Not Modified", + .statusFormat = "" + }; + case 305: + return (struct statusStrings) { + .statusString = "Use Proxy", + .statusFormat = "" + }; + case 306: + return (struct statusStrings) { + .statusString = "(reserved)", + .statusFormat = "" + }; + case 307: + return (struct statusStrings) { + .statusString = "Temporary Redirect", + .statusFormat = "" + }; + case 308: + return (struct statusStrings) { + .statusString = "Permanent Redirect", + .statusFormat = "" + }; + + case 400: + return (struct statusStrings) { + .statusString = "Bad Request", + .statusFormat = "" + }; + case 401: + return (struct statusStrings) { + .statusString = "Unauthorized", + .statusFormat = "" + }; + case 402: + return (struct statusStrings) { + .statusString = "Payment Required", + .statusFormat = "" + }; + case 403: + return (struct statusStrings) { + .statusString = "Forbidden", + .statusFormat = "" + }; + case 404: + return (struct statusStrings) { + .statusString = "Not found", + .statusFormat = "" + }; + case 405: + return (struct statusStrings) { + .statusString = "Method Not Allowed", + .statusFormat = "" + }; + case 406: + return (struct statusStrings) { + .statusString = "Not Acceptable", + .statusFormat = "" + }; + case 407: + return (struct statusStrings) { + .statusString = "Proxy Auhentication Required", + .statusFormat = "" + }; + case 408: + return (struct statusStrings) { + .statusString = "Request Timeout", + .statusFormat = "" + }; + case 409: + return (struct statusStrings) { + .statusString = "Conflict", + .statusFormat = "" + }; + case 410: + return (struct statusStrings) { + .statusString = "Gone", + .statusFormat = "" + }; + case 411: + return (struct statusStrings) { + .statusString = "Length Required", + .statusFormat = "" + }; + case 412: + return (struct statusStrings) { + .statusString = "Precondition Failed", + .statusFormat = "" + }; + case 413: + return (struct statusStrings) { + .statusString = "Request Entity Too Large", + .statusFormat = "" + }; + case 414: + return (struct statusStrings) { + .statusString = "URI Too Long", + .statusFormat = "" + }; + case 415: + return (struct statusStrings) { + .statusString = "Unsupported Media Type", + .statusFormat = "" + }; + case 416: + return (struct statusStrings) { + .statusString = "Requested Range Not Satisfiable", + .statusFormat = "" + }; + case 417: + return (struct statusStrings) { + .statusString = "Expectation Failed", + .statusFormat = "" + }; + case 420: + return (struct statusStrings) { + .statusString = "Policy Not Fulfilled", + .statusFormat = "" + }; + case 421: + return (struct statusStrings) { + .statusString = "Misdirected Request", + .statusFormat = "" + }; + case 422: + return (struct statusStrings) { + .statusString = "Unprocessable Entity", + .statusFormat = "" + }; + case 423: + return (struct statusStrings) { + .statusString = "Locked", + .statusFormat = "" + }; + case 424: + return (struct statusStrings) { + .statusString = "Failed Dependency", + .statusFormat = "" + }; + case 426: + return (struct statusStrings) { + .statusString = "Upgrade Required", + .statusFormat = "" + }; + case 428: + return (struct statusStrings) { + .statusString = "Precondition Required", + .statusFormat = "" + }; + case 429: + return (struct statusStrings) { + .statusString = "Too Many Requests", + .statusFormat = "" + }; + case 431: + return (struct statusStrings) { + .statusString = "Request Header Fields Too Large", + .statusFormat = "" + }; + case 451: + return (struct statusStrings) { + .statusString = "Unavailable For Legal Reasons", + .statusFormat = "" + }; + + case 500: + return (struct statusStrings) { + .statusString = "Internal Server Error", + .statusFormat = "" + }; + case 501: + return (struct statusStrings) { + .statusString = "Not Implemented", + .statusFormat = "" + }; + case 502: + return (struct statusStrings) { + .statusString = "Bad Gateway", + .statusFormat = "" + }; + case 503: + return (struct statusStrings) { + .statusString = "Service Unavailable", + .statusFormat = "" + }; + case 504: + return (struct statusStrings) { + .statusString = "Gateway Timeout", + .statusFormat = "" + }; + case 505: + return (struct statusStrings) { + .statusString = "HTTP Version Not Supported", + .statusFormat = "" + }; + case 506: + return (struct statusStrings) { + .statusString = "Variant Also Negotiates", + .statusFormat = "" + }; + case 507: + return (struct statusStrings) { + .statusString = "Insufficient Storage", + .statusFormat = "" + }; + case 508: + return (struct statusStrings) { + .statusString = "Loop Detected", + .statusFormat = "" + }; + case 509: + return (struct statusStrings) { + .statusString = "Bandwidth Limit Exceeded", + .statusFormat = "" + }; + case 510: + return (struct statusStrings) { + .statusString = "Not Extended", + .statusFormat = "" + }; + case 511: + return (struct statusStrings) { + .statusString = "Notwork Authentication Required", + .statusFormat = "" + }; + + default: + return (struct statusStrings) { + .statusString = "Unknown Status Code", + .statusFormat = "This is pretty bad." + }; + } +} + +void status(struct request request, struct response response, int status) { + struct headers headers = headers_create(); + int fd = response.sendHeader(status, &headers, &request); + headers_free(&headers); + + FILE* stream = fdopen(fd, "w"); + if (stream == NULL) { + error("status: Couldn't get stream from fd: %s", strerror(errno)); + return; + } + + struct statusStrings string = getStatusStrings(status); + + fprintf(stream, "\n"); + fprintf(stream, "\n"); + fprintf(stream, " \n"); + fprintf(stream, " %s\n", string.statusString); + fprintf(stream, " \n"); + fprintf(stream, " \n"); + fprintf(stream, "

%s

\n", string.statusString); + fprintf(stream, string.statusFormat, request.metaData.path); + fprintf(stream, "
\n"); + fprintf(stream, " \n"); + fprintf(stream, "\n"); + + fclose(stream); +} + +void status500(struct request request, struct response response) { + status(request, response, 500); +} diff --git a/src/status.h b/src/status.h new file mode 100644 index 0000000..a1f0361 --- /dev/null +++ b/src/status.h @@ -0,0 +1,15 @@ +#ifndef STATUS_H +#define STATUS_H + +#include "misc.h" + +struct statusStrings { + const char* statusString; + const char* statusFormat; +}; + +struct statusStrings getStatusStrings(int status); + +void status500(struct request request, struct response response); + +#endif