Implemented responses
This commit is contained in:
@@ -32,22 +32,28 @@
|
||||
#include <responses.h>
|
||||
#include <connections.h>
|
||||
|
||||
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++) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
34
src/include/util.h
Normal file
34
src/include/util.h
Normal file
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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
|
||||
14
src/main.c
14
src/main.c
@@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,29 +16,87 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <responses.h>
|
||||
|
||||
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"
|
||||
"<h1>Invalid page</h1>\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, "<p>Hi there!</p>");
|
||||
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"
|
||||
"<h1>File not found</h1>\n"
|
||||
);
|
||||
end:
|
||||
resetConnection(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
16
src/runner.c
16
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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
51
src/util.c
Normal file
51
src/util.c
Normal file
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <util.h>
|
||||
|
||||
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.
|
||||
}
|
||||
Reference in New Issue
Block a user