From f092d584fad7cca79b6eb0b3412a2546bcfec53d Mon Sep 17 00:00:00 2001 From: overflowerror Date: Mon, 2 Jan 2017 22:31:05 +0100 Subject: [PATCH] split into multible files --- Makefile | 8 +- example.c | 1 + webserver.c | 570 ++-------------------------------------------------- webserver.h | 104 +--------- ws_linear.c | 213 ++++++++++++++++++++ ws_modes.h | 8 + ws_types.h | 74 +++++++ ws_utils.c | 363 +++++++++++++++++++++++++++++++++ ws_utils.h | 17 ++ 9 files changed, 702 insertions(+), 656 deletions(-) create mode 100644 ws_linear.c create mode 100644 ws_modes.h create mode 100644 ws_types.h create mode 100644 ws_utils.c create mode 100644 ws_utils.h diff --git a/Makefile b/Makefile index 2774bde..4d5e1a4 100644 --- a/Makefile +++ b/Makefile @@ -17,14 +17,14 @@ all: example %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -example: example.o webserver.o help.o +example: example.o webserver.o ws_linear.o ws_utils.o help.o $(CC) $(LDFLAGS) -o $@ $^ example.o: example.c webserver.h - help.o: help.c help.h - -webserver.o: webserver.c webserver.h help.h +webserver.o: webserver.c webserver.h ws_types.h ws_modes.h ws_utils.h help.h +ws_utils.o: ws_utils.c ws_utils.h ws_types.h +ws_linear.o: ws_linear.c ws_modes.h ws_types.h webserver.h ws_utils.h clean: rm -f *.o example diff --git a/example.c b/example.c index e2e139e..9821bd8 100644 --- a/example.c +++ b/example.c @@ -1,4 +1,5 @@ #include "webserver.h" +#include "ws_utils.h" #include "help.h" #include #include diff --git a/webserver.c b/webserver.c index 821b883..f705e50 100644 --- a/webserver.c +++ b/webserver.c @@ -1,122 +1,18 @@ #include "webserver.h" +#include "ws_types.h" +#include "ws_utils.h" +#include "ws_modes.h" +#include "help.h" + +#include +#include +#include #include #include #include -#include -#include "help.h" -#include -#include -#include -#include -#include -#include -#include #include - -bool ws_host_match(const char* host, const char* match) { - // only two possible positions for wildcard: begin and end - int lenh = strlen(host); - int lenm = strlen(match); - - if (lenm > lenh) - return false; - - bool wildcard = false; - int wnext = -1; - int found = -1; - - int h = 0, m = 0; - for (; m < lenm && h < lenh; m++, h++) { - if (match[m] == '*') { - wildcard = true; - wnext = m + 1; - found = -1; - // current host char matches automatically - continue; - } - - if (wildcard) { - if (found == -1) { - if (match[m] != host[h]) - m--; - else - found = h; - } else { - if (match[m] != host[h]) { - // found was negated; reset cursor positions - m = wnext; - h = found + 1; - found = -1; - } - } - } else { - if (match[m] != host[h]) - return false; - } - } - // if not whole match string matches, return false - if (m != lenm) - return false; - return true; -} - -bool ws_path_match(const char* path, const char* match) { - // TODO wildcards - int lenp = strlen(path); - int lenm = strlen(match); - - if (lenm > lenp) - return false; - - bool wildcard = false; - int wnext = -1; - int found = -1; - - int p = 0, m = 0; - for (; m < lenm && p < lenp; m++, p++) { - if (match[m] == '*') { - wildcard = true; - wnext = m + 1; - found = -1; - // current host char matches automatically - continue; - } - - if (wildcard) { - if (found < 0) { - if (match[m] != path[p]) - m--; - else - found = p; - } else { - if (match[m] != path[p]) { - // found was negated; reset cursor positions - m = wnext; - p = found + 1; - found = -1; - } - } - - // if directory end - if (path[p] == '/') { - // found is here only true if match[m] = path[p] = '/' - if (found >= 0) { - wildcard = false; - } else { - return false; - } - } - } else { - if (match[m] != path[p]) - return false; - } - } - - // if not whole match string used, return false - if (m != lenm) - return false; - return true; -} +#include +#include handler_t ws_handler_find(webserver_t* server, const char* path, const char* host) { for (int i = 0; i < server->nrhandles; i++) { @@ -135,158 +31,6 @@ handler_t ws_handler_find(webserver_t* server, const char* path, const char* hos return NULL; } -char* ws_host_find(const char** path, headers_t headers) { - char* host = NULL; - for (int i = 0; i < headers.nrfields; i++) { - header_t header = headers.fields[i]; - if (strcmp(header.key, "Host") == 0) { - host = malloc(strlen(header.value) + 1); - memcpy(host, header.value, strlen(header.value) + 1); - return host; - } - } - - if (strlen(*path) <= strlen("http://")) - return host; - if ((*path)[0] == '/') - return host; - - bool setHost = false; - - if (host == NULL) { - setHost = true; - host = malloc(strlen(*path) + 1); - } - int hposition = 0; - - int nrslash = 0; - - size_t len = strlen(*path); - for (size_t i = 0; i < len; i++) { - if (nrslash < 2) { - // http:// - } else if (nrslash < 3) { - if (setHost) { - host[hposition++] = (*path)[i]; - /*if ((*path)[i] == ':') { - host[hposition - 1] = '\0'; - setHost = false; - }*/ - } - } else { - // rebase path - *path = *path + i; - break; - } - - if ((*path)[i] == '/') - nrslash++; - } - if (setHost) { - host[hposition] = '\0'; - host = realloc(host, strlen(host) + 1); - } - - return host; -} - -const char* ws_strm(method_t method) { - switch(method) { - case OPTIONS: - return "OPTIONS"; - case GET: - return "GET"; - case HEAD: - return "HEAD"; - case POST: - return "POST"; - case PUT: - return "PUT"; - case DELETE: - return "DELETE"; - case TRACE: - return "TRACE"; - case CONNECT: - return "CONNECT"; - default: - return "unknown method"; - } -} - -int ws_request_parse(char* buffer, const char** path, method_t* method) { - char lbuffer[8]; - int position = 0; - - int state = 0; - - size_t len = strlen(buffer); - for(size_t i = 0; i < len; i++) { - switch(state) { - case 0: // method - if (buffer[i] != ' ') { - lbuffer[position++] = buffer[i]; - if (position > 7) - return -1; // method name too long - } else { - state++; - lbuffer[position] = '\0'; - if (strcmp(lbuffer, "OPTIONS") == 0) - *method = OPTIONS; - else if (strcmp(lbuffer, "GET") == 0) - *method = GET; - else if (strcmp(lbuffer, "HEAD") == 0) - *method = HEAD; - else if (strcmp(lbuffer, "POST") == 0) - *method = POST; - else if (strcmp(lbuffer, "PUT") == 0) - *method = PUT; - else if (strcmp(lbuffer, "DELETE") == 0) - *method = DELETE; - else if (strcmp(lbuffer, "TRACE") == 0) - *method = TRACE; - else if (strcmp(lbuffer, "CONNECT") == 0) - *method = CONNECT; - else - return -2; // unknown method - } - break; - case 1: // path begin - if (buffer[i] == ' ' || buffer[i] == '\r') - return -3; // malformed; - *path = buffer + i; - state++; - break; - case 2: // path - if (buffer[i] == ' ') { - buffer[i] = '\0'; - position = 0; - state++; - } - break; - case 3: // version - if (buffer[i] != '\r') { - lbuffer[position++] = buffer[i]; - if (position > 7) - return -4; // version too long - } else { - lbuffer[position] = '\0'; - - // we just support HTTP 1.0 and 1.1 - if (!strcmp(lbuffer, "HTTP/1.0")) - return 0; - if (!strcmp(lbuffer, "HTTP/1.1")) - return 0; - - return -5; - } - break; - default: - assert(false); - } - } - return 0; -} - headers_t ws_headers_create(void) { headers_t headers; headers.fields = malloc(0 * sizeof(header_t)); @@ -420,97 +164,6 @@ void ws_log(webserver_t* server, loglevel_t loglevel, const char* format, ...) { fprintf(server->logfile, "\n"); } -const char* ws_code_reason(int code) { - switch(code) { - case 100: - return "Continue"; - case 101: - return "Switching Protocols"; - case 200: - return "OK"; - - case 201: - return "Created"; - case 202: - return "Accepted"; - case 203: - return "Non-Authoritative Information"; - case 204: - return "No Content"; - case 205: - return "Reset Content"; - case 206: - return "Partial Content"; - - case 300: - return "Multible Choices"; - case 301: - return "Moved Permanently"; - case 302: - return "Found"; - case 303: - return "See Other"; - case 304: - return "Not Modified"; - case 305: - return "Use Proxy"; - case 307: - return "Temporary Redirect"; - - case 400: - return "Bad Request"; - case 401: - return "Unauthorized"; - case 402: - return "Payment Required"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 405: - return "Method Not Allowed"; - case 406: - return "Not Acceptable"; - case 407: - return "Proxy Authentication Required"; - case 408: - return "Request Time-out"; - case 409: - return "Conflict"; - case 410: - return "Gone"; - case 411: - return "Length Required"; - case 412: - return "Precondition Failed"; - case 413: - return "Request Entify Too Large"; - case 414: - return "Request-URI Too Large"; - case 415: - return "Unsupported Media Type"; - case 416: - return "Requested range not satisfiable"; - case 417: - return "Expectation Failed"; - - case 500: - return "Internal Server Error"; - case 501: - return "Not Implemented"; - case 502: - return "Bad Gateway"; - case 503: - return "Service Unavailable"; - case 504: - return "Gateway Time-out"; - case 505: - return "HTTP Version not supported"; - default: - return "Unknown extension code"; - } -} - void ws_send(int connfd, int code, headers_t headers, int pipefd) { stream_t connection = (stream_t) fdopen(connfd, "a+"); @@ -547,207 +200,12 @@ void ws_send(int connfd, int code, headers_t headers, int pipefd) { } void ws_simple_status(int connfd, int code) { - ws_send(connfd, code, (headers_t) {.fields = NULL, .nrfields = 0}, -1); + ws_send(connfd, code, (headers_t) { + .fields = NULL, + .nrfields = 0 + }, -1); } -int ws_run_linear(webserver_t* server) { - struct sockaddr_storage peer_addr; - socklen_t peer_addr_len; - int s; - int connfd; - - char* buffer = malloc(BUFFER_SIZE * sizeof(char)); - int buffersize = 0; - int nb = 1; - - char* header = malloc(BUFFER_SIZE * sizeof(char)); - int headersize = 0; - int nhb = 1; - - int headerlines = 0; - - while (true) { - if((connfd = accept(server->sfd, (struct sockaddr*) &peer_addr, &peer_addr_len)) < 0) { - return -1; - } - - char phost[NI_MAXHOST], service[NI_MAXSERV]; - - s = getnameinfo((struct sockaddr *) &peer_addr, - peer_addr_len, phost, NI_MAXHOST, service, - NI_MAXSERV, NI_NUMERICSERV); - - if (s == 0) - ws_log(server, LOG_DEBUG, "Connection from %s:%s", phost, service); - else - ws_log(server, LOG_DEBUG, "getnameinfo: %s", gai_strerror(s)); - - headers_t headers = ws_headers_create(); - char* host = NULL; - method_t method; - const char* path = NULL; - - while(true) { - if (read(connfd, buffer + buffersize++, 1) < 1) { - ws_log(server, LOG_TESTING, "read: %s", strerror(errno)); - // error or disconect - break; - } - - if (buffersize > nb * BUFFER_SIZE - 1) { - nb++; - buffer = realloc(buffer, nb * BUFFER_SIZE * sizeof(char)); - } - - buffer[buffersize] = '\0'; - - if (buffer[buffersize - 1] == '\n') { - ws_log(server, LOG_TESTING, "got line: %i, %s", buffersize, buffer); - - // line end - if (buffersize == 1) { - // TODO 400 - ws_simple_status(connfd, 400); - break; - } - if (buffer[buffersize - 2] != '\r') { - // TODO 400 - ws_simple_status(connfd, 400); - break; - } - // buffer contains the line + \r\n\0 - - if (buffersize == 3 - 1) { // "\r\n" + \0 - ws_log(server, LOG_TESTING, "got last header line"); - - if (headerlines < 1) { - // TODO 400 - ws_simple_status(connfd, 400); - break; - } - - ws_log(server, LOG_TESTING, "find host"); - host = ws_host_find(&path, headers); - ws_log(server, LOG_DEBUG, "uri: %s%s", host, path); - - // find handler - ws_log(server, LOG_TESTING, "find handler"); - handler_t handler = ws_handler_find(server, path, host); - - if (handler == NULL) { - // TODO 404 - ws_log(server, LOG_DEBUG, "handler not found"); - ws_simple_status(connfd, 404); - break; - } - ws_log(server, LOG_TESTING, "found"); - - int pipefd[2]; - - ws_log(server, LOG_TESTING, "create pipe"); - if (pipe(pipefd) < 0) { - return -1; - } - - ws_log(server, LOG_TESTING, "create response header struct"); - headers_t responseHeaders = ws_headers_create(); - ws_log(server, LOG_TESTING, "open request stream"); - stream_t request = (stream_t) fdopen(connfd, "r"); - - if (request == NULL) { - ws_log(server, LOG_DEBUG, "fdopen: %s", strerror(errno)); - } - - //fopen write end of pipe - ws_log(server, LOG_TESTING, "open pipe stream"); - stream_t response = (stream_t) fdopen(pipefd[1], "w"); - - if (response == NULL) { - ws_log(server, LOG_DEBUG, "fdopen: %s", strerror(errno)); - } - - // unbuffer - setbuf(request, NULL); - - // handler - ws_log(server, LOG_DEBUG, "execute handler"); - int code = (*handler)(method, host, path, headers, &responseHeaders, request, response); - ws_log(server, LOG_TESTING, "handler finished with %i", code); - - fclose(response); - - // send - ws_log(server, LOG_TESTING, "sending response"); - ws_send(connfd, code, responseHeaders, pipefd[0]); - - // cleanup - ws_log(server, LOG_DEBUG, "cleanup"); - close(pipefd[0]); - fclose(request); - ws_headers_free(&responseHeaders); - - ws_log(server, LOG_TESTING, "done"); - - break; - } - - ws_log(server, LOG_TESTING, "enlarging header buffer"); - while (headersize + buffersize + 1 > nhb * BUFFER_SIZE) { - nhb++; - header = realloc(header, nhb * BUFFER_SIZE * sizeof(char)); - } - - memcpy(header + headersize, buffer, buffersize + 1); - header[headersize + buffersize] = '\0'; - // new line is now permanently saved in header + headersize - - if (headerlines++ == 0) { - ws_log(server, LOG_TESTING, "parsing first line"); - int e; - if ((e = ws_request_parse(header + headersize, &path, &method)) < 0) { - ws_log(server, LOG_DEBUG, "parsing error: %i", e); - // TODO 400 - ws_simple_status(connfd, 400); - break; - } - ws_log(server, LOG_TESTING, "path: %s", path); - } else { - // convert to header struct - ws_log(server, LOG_TESTING, "convert line to headerstruct"); - ws_headers_convert(&headers, header + headersize); - ws_log(server, LOG_DEBUG, "header: %s: %s", headers.fields[headers.nrfields - 1].key, - headers.fields[headers.nrfields - 1].value); - } - headersize += buffersize + 1; - - ws_log(server, LOG_TESTING, "reset buffer"); - buffer = realloc(buffer, BUFFER_SIZE * sizeof(char)); - nb = 1; - buffersize = 0; - } - } - - header = realloc(header, BUFFER_SIZE * sizeof(char)); - nhb = 1; - headersize = 0; - - headerlines = 0; - - ws_headers_free(&headers); - - if (host != NULL) { - free(host); - host = NULL; - } - - buffer = realloc(buffer, BUFFER_SIZE * sizeof(char)); - nb = 1; - buffersize = 0; - } - - free(buffer); - free(header); -} int ws_run(webserver_t* server) { switch(server->options.mode) { diff --git a/webserver.h b/webserver.h index f77885b..eb1fbde 100644 --- a/webserver.h +++ b/webserver.h @@ -1,6 +1,8 @@ #ifndef WEBSERVER_H #define WEBSERVER_H +#include "ws_types.h" +#include "ws_modes.h" #include #include @@ -9,111 +11,21 @@ #define WS_VERSION "0.1" #define WS_NAME "SERWER" - - -typedef struct header { - const char* key; - const char* value; -} header_t; - -typedef struct headers { - header_t* fields; - int nrfields; -} headers_t; - -typedef FILE* stream_t; - -typedef int loglevel_t; - -#define LOG_TESTING 11 -#define LOG_DEBUG 10 -#define LOG_VERBOSE 5 -#define LOG_WARN 2 -#define LOG_ERROR 0 - -#define ANY NULL - -typedef enum method { - OPTIONS, - GET, - HEAD, - POST, - PUT, - DELETE, - TRACE, - CONNECT -} method_t; - -typedef int(*handler_t)(method_t method, const char* host, const char* path, headers_t requestHeaders, - headers_t* responseHeaders, stream_t request, stream_t response); - -typedef struct handle { - const char* path; - const char* host; - handler_t handler; -} handle_t; - -typedef enum mode { - //PRE_FORKED, - //POST_FORKED, - //STATEFULL, - //THREADED, - LINEAR -} srvmode_t; - -typedef struct srvoptions { - srvmode_t mode; - int timeout; - int maxconnections; - loglevel_t loglevel; -} srvoptions_t; - -typedef struct webserver { - const char* name; - int sfd; - const char* host; - const char* port; - handle_t* handles; - int nrhandles; - FILE* logfile; - srvoptions_t options; -} webserver_t; - -bool ws_host_match(const char*, const char*); -bool ws_path_match(const char*, const char*); - -handler_t ws_handler_find(webserver_t*, const char*, const char*); - -char* ws_host_find(const char**, headers_t); - -const char* ws_strm(method_t); - -int ws_request_parse(char*, const char**, method_t*); - headers_t ws_headers_create(void); - void ws_headers_add(headers_t*, const char*, const char*); - void ws_headers_convert(headers_t*, char*); - void ws_headers_free(headers_t*); -int ws_listen(webserver_t*); - void ws_handle_add(webserver_t*, handle_t); +handler_t ws_handler_find(webserver_t*, const char*, const char*); + +void ws_send(int, int, headers_t, int); +void ws_simple_status(int, int); webserver_t ws_create(const char*, const char*, const char*, FILE*, srvoptions_t); +int ws_listen(webserver_t*); +int ws_run(webserver_t*); void ws_log(webserver_t*, loglevel_t, const char*, ...); -const char* ws_code_reason(int); - -void ws_send(int, int, headers_t, int); - -void ws_simple_status(int, int); - -int ws_run_linear(webserver_t*); - -int ws_run(webserver_t*); - #endif diff --git a/ws_linear.c b/ws_linear.c new file mode 100644 index 0000000..85728b3 --- /dev/null +++ b/ws_linear.c @@ -0,0 +1,213 @@ +#include "webserver.h" +#include "ws_types.h" +#include "ws_modes.h" +#include "ws_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int ws_run_linear(webserver_t* server) { + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; + int s; + int connfd; + + char* buffer = malloc(BUFFER_SIZE * sizeof(char)); + int buffersize = 0; + int nb = 1; + + char* header = malloc(BUFFER_SIZE * sizeof(char)); + int headersize = 0; + int nhb = 1; + + int headerlines = 0; + + while (true) { + if((connfd = accept(server->sfd, (struct sockaddr*) &peer_addr, &peer_addr_len)) < 0) { + return -1; + } + + char phost[NI_MAXHOST], service[NI_MAXSERV]; + + s = getnameinfo((struct sockaddr *) &peer_addr, + peer_addr_len, phost, NI_MAXHOST, service, + NI_MAXSERV, NI_NUMERICSERV); + + if (s == 0) + ws_log(server, LOG_DEBUG, "Connection from %s:%s", phost, service); + else + ws_log(server, LOG_DEBUG, "getnameinfo: %s", gai_strerror(s)); + + headers_t headers = ws_headers_create(); + char* host = NULL; + method_t method; + const char* path = NULL; + + while(true) { + if (read(connfd, buffer + buffersize++, 1) < 1) { + ws_log(server, LOG_TESTING, "read: %s", strerror(errno)); + // error or disconect + break; + } + + if (buffersize > nb * BUFFER_SIZE - 1) { + nb++; + buffer = realloc(buffer, nb * BUFFER_SIZE * sizeof(char)); + } + + buffer[buffersize] = '\0'; + + if (buffer[buffersize - 1] == '\n') { + ws_log(server, LOG_TESTING, "got line: %i, %s", buffersize, buffer); + + // line end + if (buffersize == 1) { + // TODO 400 + ws_simple_status(connfd, 400); + break; + } + if (buffer[buffersize - 2] != '\r') { + // TODO 400 + ws_simple_status(connfd, 400); + break; + } + // buffer contains the line + \r\n\0 + + if (buffersize == 3 - 1) { // "\r\n" + \0 + ws_log(server, LOG_TESTING, "got last header line"); + + if (headerlines < 1) { + // TODO 400 + ws_simple_status(connfd, 400); + break; + } + + ws_log(server, LOG_TESTING, "find host"); + host = ws_host_find(&path, headers); + ws_log(server, LOG_DEBUG, "uri: %s%s", host, path); + + // find handler + ws_log(server, LOG_TESTING, "find handler"); + handler_t handler = ws_handler_find(server, path, host); + + if (handler == NULL) { + // TODO 404 + ws_log(server, LOG_DEBUG, "handler not found"); + ws_simple_status(connfd, 404); + break; + } + ws_log(server, LOG_TESTING, "found"); + + int pipefd[2]; + + ws_log(server, LOG_TESTING, "create pipe"); + if (pipe(pipefd) < 0) { + return -1; + } + + ws_log(server, LOG_TESTING, "create response header struct"); + headers_t responseHeaders = ws_headers_create(); + ws_log(server, LOG_TESTING, "open request stream"); + stream_t request = (stream_t) fdopen(connfd, "r"); + + if (request == NULL) { + ws_log(server, LOG_DEBUG, "fdopen: %s", strerror(errno)); + } + + //fopen write end of pipe + ws_log(server, LOG_TESTING, "open pipe stream"); + stream_t response = (stream_t) fdopen(pipefd[1], "w"); + + if (response == NULL) { + ws_log(server, LOG_DEBUG, "fdopen: %s", strerror(errno)); + } + + // unbuffer + setbuf(request, NULL); + + // handler + ws_log(server, LOG_DEBUG, "execute handler"); + int code = (*handler)(method, host, path, headers, &responseHeaders, request, response); + ws_log(server, LOG_TESTING, "handler finished with %i", code); + + fclose(response); + + // send + ws_log(server, LOG_TESTING, "sending response"); + ws_send(connfd, code, responseHeaders, pipefd[0]); + + // cleanup + ws_log(server, LOG_DEBUG, "cleanup"); + close(pipefd[0]); + fclose(request); + ws_headers_free(&responseHeaders); + + ws_log(server, LOG_TESTING, "done"); + + break; + } + + ws_log(server, LOG_TESTING, "enlarging header buffer"); + while (headersize + buffersize + 1 > nhb * BUFFER_SIZE) { + nhb++; + header = realloc(header, nhb * BUFFER_SIZE * sizeof(char)); + } + + memcpy(header + headersize, buffer, buffersize + 1); + header[headersize + buffersize] = '\0'; + // new line is now permanently saved in header + headersize + + if (headerlines++ == 0) { + ws_log(server, LOG_TESTING, "parsing first line"); + int e; + if ((e = ws_request_parse(header + headersize, &path, &method)) < 0) { + ws_log(server, LOG_DEBUG, "parsing error: %i", e); + // TODO 400 + ws_simple_status(connfd, 400); + break; + } + ws_log(server, LOG_TESTING, "path: %s", path); + } else { + // convert to header struct + ws_log(server, LOG_TESTING, "convert line to headerstruct"); + ws_headers_convert(&headers, header + headersize); + ws_log(server, LOG_DEBUG, "header: %s: %s", headers.fields[headers.nrfields - 1].key, + headers.fields[headers.nrfields - 1].value); + } + headersize += buffersize + 1; + + ws_log(server, LOG_TESTING, "reset buffer"); + buffer = realloc(buffer, BUFFER_SIZE * sizeof(char)); + nb = 1; + buffersize = 0; + } + } + + header = realloc(header, BUFFER_SIZE * sizeof(char)); + nhb = 1; + headersize = 0; + + headerlines = 0; + + ws_headers_free(&headers); + + if (host != NULL) { + free(host); + host = NULL; + } + + buffer = realloc(buffer, BUFFER_SIZE * sizeof(char)); + nb = 1; + buffersize = 0; + } + + free(buffer); + free(header); +} diff --git a/ws_modes.h b/ws_modes.h new file mode 100644 index 0000000..ee66256 --- /dev/null +++ b/ws_modes.h @@ -0,0 +1,8 @@ +#ifndef WS_MODES_H +#define WS_MODES_H + +#include "ws_types.h" + +int ws_run_linear(webserver_t*); + +#endif diff --git a/ws_types.h b/ws_types.h new file mode 100644 index 0000000..d496777 --- /dev/null +++ b/ws_types.h @@ -0,0 +1,74 @@ +#ifndef WS_TYPES_H +#define WS_TYPES_H + +#include + +typedef struct header { + const char* key; + const char* value; +} header_t; + +typedef struct headers { + header_t* fields; + int nrfields; +} headers_t; + +typedef FILE* stream_t; + +typedef int loglevel_t; + +#define LOG_TESTING 11 +#define LOG_DEBUG 10 +#define LOG_VERBOSE 5 +#define LOG_WARN 2 +#define LOG_ERROR 0 + +#define ANY NULL + +typedef enum method { + OPTIONS, + GET, + HEAD, + POST, + PUT, + DELETE, + TRACE, + CONNECT +} method_t; + +typedef int(*handler_t)(method_t method, const char* host, const char* path, headers_t requestHeaders, + headers_t* responseHeaders, stream_t request, stream_t response); + +typedef struct handle { + const char* path; + const char* host; + handler_t handler; +} handle_t; + +typedef enum mode { + //PRE_FORKED, + //POST_FORKED, + //STATEFULL, + //THREADED, + LINEAR +} srvmode_t; + +typedef struct srvoptions { + srvmode_t mode; + int timeout; + int maxconnections; + loglevel_t loglevel; +} srvoptions_t; + +typedef struct webserver { + const char* name; + int sfd; + const char* host; + const char* port; + handle_t* handles; + int nrhandles; + FILE* logfile; + srvoptions_t options; +} webserver_t; + +#endif diff --git a/ws_utils.c b/ws_utils.c new file mode 100644 index 0000000..382a28a --- /dev/null +++ b/ws_utils.c @@ -0,0 +1,363 @@ +#include "webserver.h" +#include "ws_types.h" +#include "ws_utils.h" + +#include +#include +#include + +method_t ws_meth(const char* string) { + if (strcmp(string, "OPTIONS") == 0) + return OPTIONS; + else if (strcmp(string, "GET") == 0) + return GET; + else if (strcmp(string, "HEAD") == 0) + return HEAD; + else if (strcmp(string, "POST") == 0) + return POST; + else if (strcmp(string, "PUT") == 0) + return PUT; + else if (strcmp(string, "DELETE") == 0) + return DELETE; + else if (strcmp(string, "TRACE") == 0) + return TRACE; + else if (strcmp(string, "CONNECT") == 0) + return CONNECT; + else + return -1; // unknown method +} + +const char* ws_strm(method_t method) { + switch(method) { + case OPTIONS: + return "OPTIONS"; + case GET: + return "GET"; + case HEAD: + return "HEAD"; + case POST: + return "POST"; + case PUT: + return "PUT"; + case DELETE: + return "DELETE"; + case TRACE: + return "TRACE"; + case CONNECT: + return "CONNECT"; + default: + return "unknown method"; + } +} + +const char* ws_code_reason(int code) { + switch(code) { + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 200: + return "OK"; + + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + + case 300: + return "Multible Choices"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 307: + return "Temporary Redirect"; + + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Time-out"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Request Entify Too Large"; + case 414: + return "Request-URI Too Large"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Requested range not satisfiable"; + case 417: + return "Expectation Failed"; + + case 500: + return "Internal Server Error"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Time-out"; + case 505: + return "HTTP Version not supported"; + default: + return "Unknown extension code"; + } +} + + +bool ws_host_match(const char* host, const char* match) { + // only two possible positions for wildcard: begin and end + int lenh = strlen(host); + int lenm = strlen(match); + + if (lenm > lenh) + return false; + + bool wildcard = false; + int wnext = -1; + int found = -1; + + int h = 0, m = 0; + for (; m < lenm && h < lenh; m++, h++) { + if (match[m] == '*') { + wildcard = true; + wnext = m + 1; + found = -1; + // current host char matches automatically + continue; + } + + if (wildcard) { + if (found == -1) { + if (match[m] != host[h]) + m--; + else + found = h; + } else { + if (match[m] != host[h]) { + // found was negated; reset cursor positions + m = wnext; + h = found + 1; + found = -1; + } + } + } else { + if (match[m] != host[h]) + return false; + } + } + // if not whole match string matches, return false + if (m != lenm) + return false; + return true; +} + +bool ws_path_match(const char* path, const char* match) { + int lenp = strlen(path); + int lenm = strlen(match); + + if (lenm > lenp) + return false; + + bool wildcard = false; + int wnext = -1; + int found = -1; + + int p = 0, m = 0; + for (; m < lenm && p < lenp; m++, p++) { + if (match[m] == '*') { + wildcard = true; + wnext = m + 1; + found = -1; + // current host char matches automatically + continue; + } + + if (wildcard) { + if (found < 0) { + if (match[m] != path[p]) + m--; + else + found = p; + } else { + if (match[m] != path[p]) { + // found was negated; reset cursor positions + m = wnext; + p = found + 1; + found = -1; + } + } + + // if directory end + if (path[p] == '/') { + // found is here only true if match[m] = path[p] = '/' + if (found >= 0) { + wildcard = false; + } else { + return false; + } + } + } else { + if (match[m] != path[p]) + return false; + } + } + + // if not whole match string used, return false + if (m != lenm) + return false; + return true; +} + +int ws_request_parse(char* buffer, const char** path, method_t* method) { + char lbuffer[9]; + int position = 0; + + int state = 0; + + size_t len = strlen(buffer); + for(size_t i = 0; i < len; i++) { + switch(state) { + case 0: // method + if (buffer[i] != ' ') { + lbuffer[position++] = buffer[i]; + if (position > 7) + return -1; // method name too long + } else { + state++; + lbuffer[position] = '\0'; + *method = ws_meth(lbuffer); + if (*method < 0) + return -2; // unknown method + } + break; + case 1: // path begin + if (buffer[i] == ' ' || buffer[i] == '\r') + return -3; // malformed; + *path = buffer + i; + state++; + break; + case 2: // path + if (buffer[i] == ' ') { + buffer[i] = '\0'; + position = 0; + state++; + } + break; + case 3: // version + if (buffer[i] != '\r') { + lbuffer[position++] = buffer[i]; + if (position > 8) + return -4; // version too long + } else { + lbuffer[position] = '\0'; + + // we just support HTTP 1.0 and 1.1 + if (!strcmp(lbuffer, "HTTP/1.0")) + return 0; + if (!strcmp(lbuffer, "HTTP/1.1")) + return 0; + + return -5; + } + break; + default: + assert(false); + } + } + return 0; +} + + +char* ws_host_find(const char** path, headers_t headers) { + char* host = NULL; + for (int i = 0; i < headers.nrfields; i++) { + header_t header = headers.fields[i]; + if (strcmp(header.key, "Host") == 0) { + host = malloc(strlen(header.value) + 1); + memcpy(host, header.value, strlen(header.value) + 1); + return host; + } + } + + if (strlen(*path) <= strlen("http://")) + return host; + if ((*path)[0] == '/') + return host; + + bool setHost = false; + + if (host == NULL) { + setHost = true; + host = malloc(strlen(*path) + 1); + } + int hposition = 0; + + int nrslash = 0; + + size_t len = strlen(*path); + for (size_t i = 0; i < len; i++) { + if (nrslash < 2) { + // http:// + } else if (nrslash < 3) { + if (setHost) { + host[hposition++] = (*path)[i]; + /*if ((*path)[i] == ':') { + host[hposition - 1] = '\0'; + setHost = false; + }*/ + } + } else { + // rebase path + *path = *path + i; + break; + } + + if ((*path)[i] == '/') + nrslash++; + } + if (setHost) { + host[hposition] = '\0'; + host = realloc(host, strlen(host) + 1); + } + + return host; +} + diff --git a/ws_utils.h b/ws_utils.h new file mode 100644 index 0000000..9a7f887 --- /dev/null +++ b/ws_utils.h @@ -0,0 +1,17 @@ +#ifndef WS_UTILS_H +#define WS_UTILS_H + +#include "ws_types.h" + +method_t ws_meth(const char*); +const char* ws_strm(method_t); +const char* ws_code_reason(int); + +bool ws_host_match(const char*, const char*); +bool ws_path_match(const char*, const char*); + +int ws_request_parse(char*, const char**, method_t*); + +char* ws_host_find(const char**, headers_t); + +#endif