Implemented responses
This commit is contained in:
@@ -32,22 +32,28 @@
|
|||||||
#include <responses.h>
|
#include <responses.h>
|
||||||
#include <connections.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;
|
ret->fd = fd;
|
||||||
if (fcntl(ret->fd, F_SETFL, O_NONBLOCK))
|
|
||||||
return 1;
|
|
||||||
ret->progress = RECEIVE_REQUEST;
|
ret->progress = RECEIVE_REQUEST;
|
||||||
|
|
||||||
ret->currLineAlloc = 30;
|
ret->currLineAlloc = 30;
|
||||||
ret->currLineLen = 0;
|
ret->currLineLen = 0;
|
||||||
ret->currLine = malloc(ret->currLineAlloc);
|
ret->currLine = malloc(ret->currLineAlloc);
|
||||||
if (ret->currLine == NULL)
|
if (ret->currLine == NULL) {
|
||||||
return 1;
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ret->allocatedFields = 10;
|
ret->allocatedFields = 10;
|
||||||
ret->fields = malloc(sizeof(char *[2]) * ret->allocatedFields);
|
ret->fields = malloc(sizeof(Field) * ret->allocatedFields);
|
||||||
if (ret->fields == NULL)
|
if (ret->fields == NULL) {
|
||||||
return 1;
|
free(ret->currLine);
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ret->path = NULL;
|
ret->path = NULL;
|
||||||
ret->body = NULL;
|
ret->body = NULL;
|
||||||
@@ -55,7 +61,7 @@ int newConnection(Connection *ret, int fd) {
|
|||||||
//initialized to NULL so that free() doens't fail.
|
//initialized to NULL so that free() doens't fail.
|
||||||
|
|
||||||
ret->next = NULL;
|
ret->next = NULL;
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetConnection(Connection *conn) {
|
void resetConnection(Connection *conn) {
|
||||||
@@ -82,6 +88,8 @@ static int processRequest(Connection *conn) {
|
|||||||
if (line[i] == ' ') {
|
if (line[i] == ' ') {
|
||||||
line[i] = '\0';
|
line[i] = '\0';
|
||||||
conn->type = getType(line);
|
conn->type = getType(line);
|
||||||
|
if (conn->type == INVALID)
|
||||||
|
return 1;
|
||||||
line += i + 1;
|
line += i + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -114,9 +122,11 @@ static int processRequest(Connection *conn) {
|
|||||||
static int processField(Connection *conn) {
|
static int processField(Connection *conn) {
|
||||||
if (conn->currLineLen == 0) {
|
if (conn->currLineLen == 0) {
|
||||||
conn->progress = RECEIVE_BODY;
|
conn->progress = RECEIVE_BODY;
|
||||||
|
conn->bodylen = 0;
|
||||||
for (size_t i = 0; i < conn->fieldCount; i++) {
|
for (size_t i = 0; i < conn->fieldCount; i++) {
|
||||||
if (strcmp(conn->fields[i][0], "Content-Length") == 0)
|
if (strcmp(conn->fields[i].field,
|
||||||
conn->bodylen = atol(conn->fields[i][1]);
|
"Content-Length") == 0)
|
||||||
|
conn->bodylen = atol(conn->fields[i].value);
|
||||||
}
|
}
|
||||||
conn->body = malloc(conn->bodylen + 1);
|
conn->body = malloc(conn->bodylen + 1);
|
||||||
if (conn->body == NULL)
|
if (conn->body == NULL)
|
||||||
@@ -127,7 +137,7 @@ static int processField(Connection *conn) {
|
|||||||
|
|
||||||
if (conn->fieldCount >= conn->allocatedFields) {
|
if (conn->fieldCount >= conn->allocatedFields) {
|
||||||
conn->allocatedFields *= 2;
|
conn->allocatedFields *= 2;
|
||||||
char *(*newfields)[2] = realloc(conn->fields, conn->allocatedFields *
|
Field *newfields = realloc(conn->fields, conn->allocatedFields *
|
||||||
sizeof(char *[2]));
|
sizeof(char *[2]));
|
||||||
if (newfields == NULL)
|
if (newfields == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -140,17 +150,17 @@ static int processField(Connection *conn) {
|
|||||||
if (split == NULL)
|
if (split == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
char *header = malloc(split - line + 1);
|
char *field = malloc(split - line + 1);
|
||||||
memcpy(header, line, split - line);
|
memcpy(field, line, split - line);
|
||||||
header[split - line] = '\0';
|
field[split - line] = '\0';
|
||||||
|
|
||||||
linelen -= split - line + 2;
|
linelen -= split - line + 2;
|
||||||
line += split - line + 2;
|
line += split - line + 2;
|
||||||
char *value = malloc(linelen + 1);
|
char *value = malloc(linelen + 1);
|
||||||
memcpy(value, line, linelen + 1);
|
memcpy(value, line, linelen + 1);
|
||||||
|
|
||||||
conn->fields[conn->fieldCount][0] = header;
|
conn->fields[conn->fieldCount].field = field;
|
||||||
conn->fields[conn->fieldCount][1] = value;
|
conn->fields[conn->fieldCount].value = value;
|
||||||
|
|
||||||
conn->fieldCount++;
|
conn->fieldCount++;
|
||||||
|
|
||||||
@@ -188,9 +198,9 @@ static int processChar(Connection *conn, char c, Sitefile *site) {
|
|||||||
if (conn->receivedBody < conn->bodylen)
|
if (conn->receivedBody < conn->bodylen)
|
||||||
conn->body[conn->receivedBody++] = c;
|
conn->body[conn->receivedBody++] = c;
|
||||||
}
|
}
|
||||||
if (conn->receivedBody >= conn->bodylen) {
|
if (conn->progress == RECEIVE_BODY &&
|
||||||
|
conn->receivedBody >= conn->bodylen)
|
||||||
sendResponse(conn, site);
|
sendResponse(conn, site);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +209,7 @@ int updateConnection(Connection *conn, Sitefile *site) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
ssize_t received = read(conn->fd, buff, sizeof(buff));
|
ssize_t received = read(conn->fd, buff, sizeof(buff));
|
||||||
if (received < 0)
|
if (received < 0)
|
||||||
return 1;
|
return errno != EAGAIN;
|
||||||
if (received == 0)
|
if (received == 0)
|
||||||
break;
|
break;
|
||||||
for (unsigned long i = 0; i < received; i++) {
|
for (unsigned long i = 0; i < received; i++) {
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ typedef enum {
|
|||||||
SEND_RESPONSE,
|
SEND_RESPONSE,
|
||||||
} ConnectionSteps;
|
} ConnectionSteps;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *field;
|
||||||
|
char *value;
|
||||||
|
} Field;
|
||||||
|
|
||||||
typedef struct Connection {
|
typedef struct Connection {
|
||||||
int fd;
|
int fd;
|
||||||
ConnectionSteps progress;
|
ConnectionSteps progress;
|
||||||
@@ -35,7 +40,7 @@ typedef struct Connection {
|
|||||||
char *path;
|
char *path;
|
||||||
//ephemeral
|
//ephemeral
|
||||||
|
|
||||||
char *(*fields)[2];
|
Field *fields;
|
||||||
//pointer to array of 2 pointers, persistent
|
//pointer to array of 2 pointers, persistent
|
||||||
size_t fieldCount;
|
size_t fieldCount;
|
||||||
size_t allocatedFields;
|
size_t allocatedFields;
|
||||||
@@ -57,7 +62,7 @@ typedef struct Connection {
|
|||||||
//Ephemeral fields: Things which are freed and reallocated after each new
|
//Ephemeral fields: Things which are freed and reallocated after each new
|
||||||
//request, path, body
|
//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
|
//returns non-zero on error. creates a new connection bound to fd
|
||||||
void resetConnection(Connection *conn);
|
void resetConnection(Connection *conn);
|
||||||
void freeConnection(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 <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
@@ -75,7 +76,7 @@ int main(int argc, char **argv) {
|
|||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
int opt = 1;
|
int opt = 1;
|
||||||
assert(setsockopt(fd, SOL_SOCKET,
|
assert(setsockopt(fd, SOL_SOCKET,
|
||||||
SO_KEEPALIVE | SO_REUSEPORT,
|
SO_REUSEPORT,
|
||||||
&opt, sizeof(opt)) >= 0);
|
&opt, sizeof(opt)) >= 0);
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
@@ -127,16 +128,19 @@ int main(int argc, char **argv) {
|
|||||||
int newfd = accept(fd, (struct sockaddr *) &addr, &addrlen);
|
int newfd = accept(fd, (struct sockaddr *) &addr, &addrlen);
|
||||||
if (newfd < 0)
|
if (newfd < 0)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
int flags = fcntl(newfd, F_GETFL);
|
||||||
|
if (fcntl(newfd, F_SETFL, flags | O_NONBLOCK))
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
int lowestThread = 0;
|
int lowestThread = 0;
|
||||||
int lowestCount = schedule[0];
|
int lowestCount = pending[0];
|
||||||
for (int i = 1; i < processes - 1; i++) {
|
for (int i = 1; i < processes - 1; i++) {
|
||||||
if (schedule[i] < lowestCount) {
|
if (pending[i] < lowestCount) {
|
||||||
lowestThread = i;
|
lowestThread = i;
|
||||||
lowestCount = schedule[i];
|
lowestCount = pending[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
schedule[0] = lowestThread;
|
|
||||||
schedule[1] = newfd;
|
schedule[1] = newfd;
|
||||||
|
schedule[0] = lowestThread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,29 +16,87 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <responses.h>
|
#include <responses.h>
|
||||||
|
|
||||||
static int sendConn(int fd, char *str) {
|
static int sendConnection(Connection *conn, char *format, ...) {
|
||||||
size_t len = strlen(str);
|
va_list ap;
|
||||||
if (write(fd, str, len))
|
va_start(ap, format);
|
||||||
|
int len = vsnprintf(NULL, 0, format, ap);
|
||||||
|
char *data = malloc(len + 1);
|
||||||
|
if (data == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
va_start(ap, format);
|
||||||
|
|
||||||
|
vsnprintf(data, len + 1, format, ap);
|
||||||
|
write(conn->fd, data, len);
|
||||||
|
free(data);
|
||||||
return 0;
|
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) {
|
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++) {
|
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");
|
sendConnection(conn,
|
||||||
sendConn(conn->fd, "Content-Length: 16\r\n");
|
"HTTP/1.1 404 Not Found\r\n"
|
||||||
sendConn(conn->fd, "\r\n");
|
"Content-Type: text/html; charset=UTF-8\r\n"
|
||||||
sendConn(conn->fd, "<p>Hi there!</p>");
|
"Server: swebs/0.1\r\n"
|
||||||
|
"Content-Length: 24\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"<h1>File not found</h1>\n"
|
||||||
|
);
|
||||||
|
end:
|
||||||
resetConnection(conn);
|
resetConnection(conn);
|
||||||
return 0;
|
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.
|
//list where we add to the end and read from the beginning.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (schedule[0] == id) {
|
if (schedule[0] == id) {
|
||||||
Connection *newconn = malloc(sizeof(Connection));
|
Connection *newconn = newConnection(schedule[1]);
|
||||||
assert(newconn != NULL);
|
assert(newconn != NULL);
|
||||||
assert(newConnection(newconn, schedule[1]) == 0);
|
|
||||||
|
|
||||||
if (last == NULL)
|
if (last == NULL)
|
||||||
connections = newconn;
|
connections = newconn;
|
||||||
@@ -62,17 +61,16 @@ void *runServer(RunnerArgs *args) {
|
|||||||
//pointers which have pointers.
|
//pointers which have pointers.
|
||||||
while (iter != NULL) {
|
while (iter != NULL) {
|
||||||
if (updateConnection(iter, site)) {
|
if (updateConnection(iter, site)) {
|
||||||
if (iter->next == NULL)
|
if (iter == last)
|
||||||
last = prev;
|
last = prev;
|
||||||
|
Connection *old = iter;
|
||||||
|
iter = iter->next;
|
||||||
|
freeConnection(old);
|
||||||
if (prev == NULL)
|
if (prev == NULL)
|
||||||
connections = connections->next;
|
connections = iter;
|
||||||
else
|
else
|
||||||
prev->next = iter->next;
|
prev->next = iter;
|
||||||
Connection *newiter = iter->next;
|
|
||||||
freeConnection(iter);
|
|
||||||
iter = newiter;
|
|
||||||
pending[id]--;
|
pending[id]--;
|
||||||
//TODO: Clean this later
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
prev = iter;
|
prev = iter;
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ static ReturnCode getToken(FILE *file, char **ret) {
|
|||||||
*ret = malloc(allocatedLen);
|
*ret = malloc(allocatedLen);
|
||||||
|
|
||||||
for (len = 0;; len++) {
|
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);
|
int c = fgetc(file);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QUOTED:
|
case QUOTED:
|
||||||
@@ -95,13 +102,6 @@ static ReturnCode getToken(FILE *file, char **ret) {
|
|||||||
goto gotToken;
|
goto gotToken;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (len >= allocatedLen) {
|
|
||||||
allocatedLen *= 2;
|
|
||||||
char *newret = realloc(*ret, allocatedLen);
|
|
||||||
if (newret == NULL)
|
|
||||||
goto error;
|
|
||||||
*ret = newret;
|
|
||||||
}
|
|
||||||
(*ret)[len] = c;
|
(*ret)[len] = c;
|
||||||
}
|
}
|
||||||
gotToken:
|
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