diff --git a/src/connections.c b/src/connections.c index 7386264..12ebdd0 100644 --- a/src/connections.c +++ b/src/connections.c @@ -32,22 +32,28 @@ #include #include -int newConnection(Connection *ret, int fd) { +Connection *newConnection(int fd) { + Connection *ret = malloc(sizeof(Connection)); + if (ret == NULL) + return NULL; ret->fd = fd; - if (fcntl(ret->fd, F_SETFL, O_NONBLOCK)) - return 1; ret->progress = RECEIVE_REQUEST; ret->currLineAlloc = 30; ret->currLineLen = 0; ret->currLine = malloc(ret->currLineAlloc); - if (ret->currLine == NULL) - return 1; + if (ret->currLine == NULL) { + free(ret); + return NULL; + } ret->allocatedFields = 10; - ret->fields = malloc(sizeof(char *[2]) * ret->allocatedFields); - if (ret->fields == NULL) - return 1; + ret->fields = malloc(sizeof(Field) * ret->allocatedFields); + if (ret->fields == NULL) { + free(ret->currLine); + free(ret); + return NULL; + } ret->path = NULL; ret->body = NULL; @@ -55,7 +61,7 @@ int newConnection(Connection *ret, int fd) { //initialized to NULL so that free() doens't fail. ret->next = NULL; - return 0; + return ret; } void resetConnection(Connection *conn) { @@ -82,6 +88,8 @@ static int processRequest(Connection *conn) { if (line[i] == ' ') { line[i] = '\0'; conn->type = getType(line); + if (conn->type == INVALID) + return 1; line += i + 1; break; } @@ -114,9 +122,11 @@ static int processRequest(Connection *conn) { static int processField(Connection *conn) { if (conn->currLineLen == 0) { conn->progress = RECEIVE_BODY; + conn->bodylen = 0; for (size_t i = 0; i < conn->fieldCount; i++) { - if (strcmp(conn->fields[i][0], "Content-Length") == 0) - conn->bodylen = atol(conn->fields[i][1]); + if (strcmp(conn->fields[i].field, + "Content-Length") == 0) + conn->bodylen = atol(conn->fields[i].value); } conn->body = malloc(conn->bodylen + 1); if (conn->body == NULL) @@ -127,7 +137,7 @@ static int processField(Connection *conn) { if (conn->fieldCount >= conn->allocatedFields) { conn->allocatedFields *= 2; - char *(*newfields)[2] = realloc(conn->fields, conn->allocatedFields * + Field *newfields = realloc(conn->fields, conn->allocatedFields * sizeof(char *[2])); if (newfields == NULL) return 1; @@ -140,17 +150,17 @@ static int processField(Connection *conn) { if (split == NULL) return 1; - char *header = malloc(split - line + 1); - memcpy(header, line, split - line); - header[split - line] = '\0'; + char *field = malloc(split - line + 1); + memcpy(field, line, split - line); + field[split - line] = '\0'; linelen -= split - line + 2; line += split - line + 2; char *value = malloc(linelen + 1); memcpy(value, line, linelen + 1); - conn->fields[conn->fieldCount][0] = header; - conn->fields[conn->fieldCount][1] = value; + conn->fields[conn->fieldCount].field = field; + conn->fields[conn->fieldCount].value = value; conn->fieldCount++; @@ -188,9 +198,9 @@ static int processChar(Connection *conn, char c, Sitefile *site) { if (conn->receivedBody < conn->bodylen) conn->body[conn->receivedBody++] = c; } - if (conn->receivedBody >= conn->bodylen) { + if (conn->progress == RECEIVE_BODY && + conn->receivedBody >= conn->bodylen) sendResponse(conn, site); - } return 0; } @@ -199,7 +209,7 @@ int updateConnection(Connection *conn, Sitefile *site) { for (;;) { ssize_t received = read(conn->fd, buff, sizeof(buff)); if (received < 0) - return 1; + return errno != EAGAIN; if (received == 0) break; for (unsigned long i = 0; i < received; i++) { diff --git a/src/include/connections.h b/src/include/connections.h index 0a8d693..4e057b1 100644 --- a/src/include/connections.h +++ b/src/include/connections.h @@ -27,6 +27,11 @@ typedef enum { SEND_RESPONSE, } ConnectionSteps; +typedef struct { + char *field; + char *value; +} Field; + typedef struct Connection { int fd; ConnectionSteps progress; @@ -35,7 +40,7 @@ typedef struct Connection { char *path; //ephemeral - char *(*fields)[2]; + Field *fields; //pointer to array of 2 pointers, persistent size_t fieldCount; size_t allocatedFields; @@ -57,7 +62,7 @@ typedef struct Connection { //Ephemeral fields: Things which are freed and reallocated after each new //request, path, body -int newConnection(Connection *ret, int fd); +Connection *newConnection(int fd); //returns non-zero on error. creates a new connection bound to fd void resetConnection(Connection *conn); void freeConnection(Connection *conn); diff --git a/src/include/util.h b/src/include/util.h new file mode 100644 index 0000000..d634187 --- /dev/null +++ b/src/include/util.h @@ -0,0 +1,34 @@ +/* + swebs - a simple web server + Copyright (C) 2022 Nate Choe + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef _HAVE_UTIL +#define _HAVE_UTIL +typedef enum { + GET, + POST, + PUT, + HEAD, + DELETE, + PATCH, + OPTIONS, + INVALID, + //this indicates an invalid type of request, there is no request called + //INVALID in HTTP/1.1. +} RequestType; + +RequestType getType(char *str); +#endif diff --git a/src/main.c b/src/main.c index 3247703..d59b795 100644 --- a/src/main.c +++ b/src/main.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -75,7 +76,7 @@ int main(int argc, char **argv) { assert(fd >= 0); int opt = 1; assert(setsockopt(fd, SOL_SOCKET, - SO_KEEPALIVE | SO_REUSEPORT, + SO_REUSEPORT, &opt, sizeof(opt)) >= 0); struct sockaddr_in addr; addr.sin_family = AF_INET; @@ -127,16 +128,19 @@ int main(int argc, char **argv) { int newfd = accept(fd, (struct sockaddr *) &addr, &addrlen); if (newfd < 0) exit(EXIT_FAILURE); + int flags = fcntl(newfd, F_GETFL); + if (fcntl(newfd, F_SETFL, flags | O_NONBLOCK)) + exit(EXIT_FAILURE); int lowestThread = 0; - int lowestCount = schedule[0]; + int lowestCount = pending[0]; for (int i = 1; i < processes - 1; i++) { - if (schedule[i] < lowestCount) { + if (pending[i] < lowestCount) { lowestThread = i; - lowestCount = schedule[i]; + lowestCount = pending[i]; } } - schedule[0] = lowestThread; schedule[1] = newfd; + schedule[0] = lowestThread; } } } diff --git a/src/responses.c b/src/responses.c index 72533ee..cfde26e 100644 --- a/src/responses.c +++ b/src/responses.c @@ -16,29 +16,87 @@ along with this program. If not, see . */ #include +#include +#include #include #include #include -static int sendConn(int fd, char *str) { - size_t len = strlen(str); - if (write(fd, str, len)) +static int sendConnection(Connection *conn, char *format, ...) { + va_list ap; + va_start(ap, format); + int len = vsnprintf(NULL, 0, format, ap); + char *data = malloc(len + 1); + if (data == NULL) return 1; + + va_end(ap); + va_start(ap, format); + + vsnprintf(data, len + 1, format, ap); + write(conn->fd, data, len); + free(data); return 0; } +static void readResponse(Connection *conn, char *path) { + FILE *file = fopen(path, "r"); + if (file == NULL) { + sendConnection(conn, + "HTTP/1.1 403 Forbidden\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Server: swebs/0.1\r\n" + "Content-Length: 21\r\n" + "\r\n" + "

Invalid page

\n" + ); + return; + } + fseek(file, 0, SEEK_END); + long len = ftell(file); + char *data = malloc(len); + if (data == NULL) + return; + fseek(file, 0, SEEK_SET); + fread(data, 1, len, file); + fclose(file); + sendConnection(conn, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Server: swebs/0.1\r\n" + "Content-Length: %ld\r\n" + "\r\n", len + ); + write(conn->fd, data, len); + free(data); +} + int sendResponse(Connection *conn, Sitefile *site) { - printf("test %d\n", site->size); + if (conn->path == NULL) + return 1; for (int i = 0; i < site->size; i++) { - printf("%s %s\n", site->content[i].path, site->content[i].arg); + if (site->content[i].respondto != conn->type) + continue; + if (strcmp(conn->path, site->content[i].path) == 0) { + switch (site->content[i].command) { + case READ: + readResponse(conn, site->content[i].arg); + goto end; + default: + return 1; + } + } } - - sendConn(conn->fd, "HTTP/1.1 200 OK\r\n"); - sendConn(conn->fd, "Content-Type: text/html\r\n"); - sendConn(conn->fd, "Content-Length: 16\r\n"); - sendConn(conn->fd, "\r\n"); - sendConn(conn->fd, "

Hi there!

"); + sendConnection(conn, + "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Server: swebs/0.1\r\n" + "Content-Length: 24\r\n" + "\r\n" + "

File not found

\n" + ); +end: resetConnection(conn); return 0; } diff --git a/src/runner.c b/src/runner.c index 5c74a92..da07454 100644 --- a/src/runner.c +++ b/src/runner.c @@ -42,9 +42,8 @@ void *runServer(RunnerArgs *args) { //list where we add to the end and read from the beginning. for (;;) { if (schedule[0] == id) { - Connection *newconn = malloc(sizeof(Connection)); + Connection *newconn = newConnection(schedule[1]); assert(newconn != NULL); - assert(newConnection(newconn, schedule[1]) == 0); if (last == NULL) connections = newconn; @@ -62,17 +61,16 @@ void *runServer(RunnerArgs *args) { //pointers which have pointers. while (iter != NULL) { if (updateConnection(iter, site)) { - if (iter->next == NULL) + if (iter == last) last = prev; + Connection *old = iter; + iter = iter->next; + freeConnection(old); if (prev == NULL) - connections = connections->next; + connections = iter; else - prev->next = iter->next; - Connection *newiter = iter->next; - freeConnection(iter); - iter = newiter; + prev->next = iter; pending[id]--; - //TODO: Clean this later } else { prev = iter; diff --git a/src/sitefile.c b/src/sitefile.c index 8c43af8..1248cf9 100644 --- a/src/sitefile.c +++ b/src/sitefile.c @@ -71,6 +71,13 @@ static ReturnCode getToken(FILE *file, char **ret) { *ret = malloc(allocatedLen); for (len = 0;; len++) { + if (len >= allocatedLen) { + allocatedLen *= 2; + char *newret = realloc(*ret, allocatedLen); + if (newret == NULL) + goto error; + *ret = newret; + } int c = fgetc(file); switch (type) { case QUOTED: @@ -95,13 +102,6 @@ static ReturnCode getToken(FILE *file, char **ret) { goto gotToken; goto error; } - if (len >= allocatedLen) { - allocatedLen *= 2; - char *newret = realloc(*ret, allocatedLen); - if (newret == NULL) - goto error; - *ret = newret; - } (*ret)[len] = c; } gotToken: diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..2a881ae --- /dev/null +++ b/src/util.c @@ -0,0 +1,51 @@ +/* + swebs - a simple web server + Copyright (C) 2022 Nate Choe + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include + +#include + +RequestType getType(char *str) { + if (strlen(str) >= 8) + return INVALID; + uint64_t type = 0; + for (int i = 0; str[i]; i++) { + type <<= 8; + type |= str[i]; + } + switch (type) { + case 0x474554l: + return GET; + case 0x504f5354l: + return POST; + case 0x505554l: + return PUT; + case 0x48454144l: + return HEAD; + case 0x44454c455445l: + return DELETE; + case 0x5041544348l: + return PATCH; + case 0x4f5054494f4e53l: + return OPTIONS; + default: + return INVALID; + } + //This would actually be far nicer in HolyC of all languages. I feel + //like the context immediately following each magic number is enough. +}