Initial commit
This commit is contained in:
244
src/connections.c
Normal file
244
src/connections.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <runner.h>
|
||||
#include <sitefile.h>
|
||||
#include <responses.h>
|
||||
#include <connections.h>
|
||||
|
||||
int newConnection(Connection *ret, int fd) {
|
||||
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;
|
||||
|
||||
ret->allocatedFields = 10;
|
||||
ret->fields = malloc(sizeof(char *[2]) * ret->allocatedFields);
|
||||
if (ret->fields == NULL)
|
||||
return 1;
|
||||
|
||||
ret->path = NULL;
|
||||
ret->body = NULL;
|
||||
//pointers to things that are allocated within functions should be
|
||||
//initialized to NULL so that free() doens't fail.
|
||||
|
||||
ret->next = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void resetConnection(Connection *conn) {
|
||||
conn->type = RECEIVE_REQUEST;
|
||||
conn->fieldCount = 0;
|
||||
free(conn->body);
|
||||
free(conn->path);
|
||||
conn->body = NULL;
|
||||
conn->path = NULL;
|
||||
}
|
||||
|
||||
void freeConnection(Connection *conn) {
|
||||
shutdown(conn->fd, SHUT_RDWR);
|
||||
free(conn->currLine);
|
||||
free(conn->path);
|
||||
free(conn->fields);
|
||||
free(conn->body);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
static int processRequest(Connection *conn) {
|
||||
char *line = conn->currLine;
|
||||
for (int i = 0;; i++) {
|
||||
if (line[i] == ' ') {
|
||||
line[i] = '\0';
|
||||
if (i >= 8)
|
||||
return 1;
|
||||
uint64_t type = 0;
|
||||
for (int j = 0; j < i; j++) {
|
||||
type <<= 8;
|
||||
type |= line[j];
|
||||
}
|
||||
switch (type) {
|
||||
case 0x474554l:
|
||||
conn->type = GET;
|
||||
break;
|
||||
case 0x504f5354l:
|
||||
conn->type = POST;
|
||||
break;
|
||||
case 0x505554l:
|
||||
conn->type = PUT;
|
||||
break;
|
||||
case 0x48454144l:
|
||||
conn->type = HEAD;
|
||||
break;
|
||||
case 0x44454c455445l:
|
||||
conn->type = DELETE;
|
||||
break;
|
||||
case 0x5041544348l:
|
||||
conn->type = PATCH;
|
||||
break;
|
||||
case 0x4f5054494f4e53l:
|
||||
conn->type = OPTIONS;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
//This would actually be far nicer in HolyC of all
|
||||
//languages. I feel like the context immediately
|
||||
//following each magic number is enough.
|
||||
line += i + 1;
|
||||
break;
|
||||
}
|
||||
if (line[i] == '\0') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
for (int i = 0;; i++) {
|
||||
if (line[i] == ' ') {
|
||||
line[i] = '\0';
|
||||
conn->path = malloc(i + 1);
|
||||
if (conn->path == NULL)
|
||||
return 1;
|
||||
memcpy(conn->path, line, i + 1);
|
||||
line += i + 1;
|
||||
break;
|
||||
}
|
||||
if (line[i] == '\0') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (strcmp(line, "HTTP/1.1"))
|
||||
return 1;
|
||||
conn->progress = RECEIVE_HEADER;
|
||||
conn->fieldCount = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int processField(Connection *conn) {
|
||||
if (conn->currLineLen == 0) {
|
||||
conn->progress = RECEIVE_BODY;
|
||||
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]);
|
||||
}
|
||||
conn->body = malloc(conn->bodylen + 1);
|
||||
if (conn->body == NULL)
|
||||
return 1;
|
||||
conn->receivedBody = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (conn->fieldCount >= conn->allocatedFields) {
|
||||
conn->allocatedFields *= 2;
|
||||
char *(*newfields)[2] = realloc(conn->fields, conn->allocatedFields *
|
||||
sizeof(char *[2]));
|
||||
if (newfields == NULL)
|
||||
return 1;
|
||||
conn->fields = newfields;
|
||||
}
|
||||
|
||||
char *line = conn->currLine;
|
||||
char *split = strstr(line, ": ");
|
||||
size_t linelen = conn->currLineLen;
|
||||
if (split == NULL)
|
||||
return 1;
|
||||
|
||||
char *header = malloc(split - line + 1);
|
||||
memcpy(header, line, split - line);
|
||||
header[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->fieldCount++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int processChar(Connection *conn, char c, Sitefile *site) {
|
||||
if (conn->progress < RECEIVE_BODY) {
|
||||
if (conn->currLineLen >= conn->currLineAlloc) {
|
||||
conn->currLineAlloc *= 2;
|
||||
char *newline = realloc(conn->currLine,
|
||||
conn->currLineAlloc);
|
||||
assert(newline != NULL);
|
||||
conn->currLine = newline;
|
||||
}
|
||||
conn->currLine[conn->currLineLen++] = c;
|
||||
if (c == '\n') {
|
||||
if (--conn->currLineLen <= 0)
|
||||
return 1;
|
||||
if (conn->currLine[--conn->currLineLen] != '\r')
|
||||
return 1;
|
||||
conn->currLine[conn->currLineLen] = '\0';
|
||||
if (conn->progress == RECEIVE_REQUEST) {
|
||||
if (processRequest(conn))
|
||||
return 1;
|
||||
}
|
||||
else if (conn->progress == RECEIVE_HEADER) {
|
||||
if (processField(conn))
|
||||
return 1;
|
||||
}
|
||||
conn->currLineLen = 0;
|
||||
}
|
||||
}
|
||||
else if (conn->progress == RECEIVE_BODY) {
|
||||
if (conn->receivedBody < conn->bodylen)
|
||||
conn->body[conn->receivedBody++] = c;
|
||||
}
|
||||
if (conn->receivedBody >= conn->bodylen) {
|
||||
sendResponse(conn, site);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int updateConnection(Connection *conn, Sitefile *site) {
|
||||
char buff[300];
|
||||
for (;;) {
|
||||
ssize_t received = read(conn->fd, buff, sizeof(buff));
|
||||
if (received < 0)
|
||||
return 1;
|
||||
if (received == 0)
|
||||
break;
|
||||
for (unsigned long i = 0; i < received; i++) {
|
||||
if (processChar(conn, buff[i], site))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
78
src/include/connections.h
Normal file
78
src/include/connections.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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_CONNECTIONS
|
||||
#define _HAVE_CONNECTIONS
|
||||
#include <runner.h>
|
||||
#include <sitefile.h>
|
||||
|
||||
typedef enum {
|
||||
RECEIVE_REQUEST,
|
||||
RECEIVE_HEADER,
|
||||
RECEIVE_BODY,
|
||||
SEND_RESPONSE,
|
||||
} ConnectionSteps;
|
||||
|
||||
typedef enum {
|
||||
GET,
|
||||
POST,
|
||||
PUT,
|
||||
HEAD,
|
||||
DELETE,
|
||||
PATCH,
|
||||
OPTIONS,
|
||||
} RequestTypes;
|
||||
|
||||
typedef struct Connection {
|
||||
int fd;
|
||||
ConnectionSteps progress;
|
||||
|
||||
RequestTypes type;
|
||||
char *path;
|
||||
//ephemeral
|
||||
|
||||
char *(*fields)[2];
|
||||
//pointer to array of 2 pointers, persistent
|
||||
size_t fieldCount;
|
||||
size_t allocatedFields;
|
||||
|
||||
char *body;
|
||||
//ephemeral
|
||||
size_t bodylen;
|
||||
size_t receivedBody;
|
||||
|
||||
struct Connection *next;
|
||||
|
||||
char *currLine;
|
||||
//persistent
|
||||
size_t currLineAlloc;
|
||||
size_t currLineLen;
|
||||
} Connection;
|
||||
//The 2 types of fields:
|
||||
//Persistent fields: Things which aren't freed after a reset, currLine, fields
|
||||
//Ephemeral fields: Things which are freed and reallocated after each new
|
||||
//request, path, body
|
||||
|
||||
int newConnection(Connection *ret, int fd);
|
||||
//returns non-zero on error. creates a new connection bound to fd
|
||||
void resetConnection(Connection *conn);
|
||||
void freeConnection(Connection *conn);
|
||||
int updateConnection(Connection *conn, Sitefile *site);
|
||||
//returns non-zero on error.
|
||||
//Generating a new connection and repeatedly calling updateConnection will
|
||||
//handle everything.
|
||||
#endif
|
||||
26
src/include/responses.h
Normal file
26
src/include/responses.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
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_RESPONSES
|
||||
#define _HAVE_RESPONSES
|
||||
#include <sitefile.h>
|
||||
#include <connections.h>
|
||||
|
||||
int sendResponse(Connection *conn, Sitefile *site);
|
||||
//This function assumes that every field in conn has been initialized, and
|
||||
//resets conn for the next request
|
||||
#endif
|
||||
41
src/include/runner.h
Normal file
41
src/include/runner.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
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_RUNNER
|
||||
#define _HAVE_RUNNER
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <sitefile.h>
|
||||
#include <connections.h>
|
||||
|
||||
typedef struct {
|
||||
Sitefile *site;
|
||||
int *pending;
|
||||
//pending[thread id] = the number of connections being handled by that
|
||||
// thread
|
||||
int *schedule;
|
||||
/*
|
||||
* schedule[0] = the thread that should take the next connection (-1 if
|
||||
* there is no connection).
|
||||
* schedule[1] = the fd of the connection to be accepted
|
||||
*/
|
||||
int id;
|
||||
} RunnerArgs;
|
||||
//my least favourite anti pattern
|
||||
|
||||
void *runServer(RunnerArgs *args);
|
||||
#endif
|
||||
32
src/include/sitefile.h
Normal file
32
src/include/sitefile.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
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_SITEFILE
|
||||
#define _HAVE_SITEFILE
|
||||
typedef struct {
|
||||
int argc;
|
||||
char **argv;
|
||||
} SiteCommand;
|
||||
|
||||
typedef struct {
|
||||
int size;
|
||||
SiteCommand *content;
|
||||
} Sitefile;
|
||||
|
||||
Sitefile *parseFile(char *path);
|
||||
void freeSitefile(Sitefile *site);
|
||||
#endif
|
||||
141
src/main.c
Normal file
141
src/main.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <runner.h>
|
||||
#include <sitefile.h>
|
||||
|
||||
FILE *logs;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *logout = "/var/log/swebs.log";
|
||||
char *sitefile = NULL;
|
||||
int processes = 8;
|
||||
uint16_t port = htons(80);
|
||||
int backlog = 100;
|
||||
for (;;) {
|
||||
int c = getopt(argc, argv, "o:j:s:c:p:b:h");
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'o':
|
||||
logout = optarg;
|
||||
break;
|
||||
case 'j':
|
||||
processes = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
sitefile = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
port = htons(atoi(optarg));
|
||||
break;
|
||||
case 'b':
|
||||
backlog = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
printf(
|
||||
"Usage: swebs [options]\n"
|
||||
" -o [out] Set the log file (default: /var/log/swebs.log)\n"
|
||||
" -j [cores] Use that many cores (default: 8)\n"
|
||||
" -s [site file] Use that site file (required)\n"
|
||||
" -p [port] Set the port (default: 443)\n"
|
||||
" -b [backlog] Set the socket backlog (default: 100)\n"
|
||||
);
|
||||
exit(EXIT_SUCCESS);
|
||||
case '?':
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
assert(fd >= 0);
|
||||
int opt = 1;
|
||||
assert(setsockopt(fd, SOL_SOCKET,
|
||||
SO_KEEPALIVE | SO_REUSEPORT,
|
||||
&opt, sizeof(opt)) >= 0);
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = port;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
assert(bind(fd, (struct sockaddr *) &addr, addrlen) >= 0);
|
||||
assert(listen(fd, backlog) >= 0);
|
||||
|
||||
if (sitefile == NULL) {
|
||||
fprintf(stderr, "No sitefile configured\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
logs = fopen(logout, "a");
|
||||
if (logs == NULL) {
|
||||
fprintf(stderr, "Couldn't open logs file %s\n", logout);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
Sitefile *site = parseFile(sitefile);
|
||||
if (site == NULL) {
|
||||
fprintf(stderr, "Invalid sitefile %s\n", sitefile);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int *pending = calloc(processes - 1, sizeof(int));
|
||||
int *schedule = malloc(2 * sizeof(int));
|
||||
if (schedule == NULL)
|
||||
exit(EXIT_FAILURE);
|
||||
schedule[0] = -1;
|
||||
pthread_t *threads = malloc(sizeof(pthread_t) * (processes - 1));
|
||||
if (threads == NULL)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
for (int i = 0; i < processes - 1; i++) {
|
||||
RunnerArgs *args = malloc(sizeof(RunnerArgs));
|
||||
if (args == NULL)
|
||||
exit(EXIT_FAILURE);
|
||||
args->pending = pending;
|
||||
args->schedule = schedule;
|
||||
args->id = i;
|
||||
pthread_create(threads + i, NULL,
|
||||
(void*(*)(void*)) runServer, args);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (schedule[0] == -1) {
|
||||
int newfd = accept(fd, (struct sockaddr *) &addr, &addrlen);
|
||||
if (newfd < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
int lowestThread = 0;
|
||||
int lowestCount = schedule[0];
|
||||
for (int i = 1; i < processes - 1; i++) {
|
||||
if (schedule[i] < lowestCount) {
|
||||
lowestThread = i;
|
||||
lowestCount = schedule[i];
|
||||
}
|
||||
}
|
||||
schedule[0] = lowestThread;
|
||||
schedule[1] = newfd;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/responses.c
Normal file
40
src/responses.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 <stdio.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))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sendResponse(Connection *conn, Sitefile *site) {
|
||||
printf("test\n");
|
||||
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>");
|
||||
resetConnection(conn);
|
||||
return 0;
|
||||
}
|
||||
84
src/runner.c
Normal file
84
src/runner.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <runner.h>
|
||||
#include <sitefile.h>
|
||||
#include <connections.h>
|
||||
|
||||
void *runServer(RunnerArgs *args) {
|
||||
Sitefile *site = args->site;
|
||||
int *pending = args->pending;
|
||||
int *schedule = args->schedule;
|
||||
int id = args->id;
|
||||
|
||||
Connection *connections = NULL;
|
||||
Connection *last = NULL;
|
||||
//Connections are processed in a queue, which is really just a linked
|
||||
//list where we add to the end and read from the beginning.
|
||||
for (;;) {
|
||||
if (schedule[0] == id) {
|
||||
Connection *newconn = malloc(sizeof(Connection));
|
||||
assert(newconn != NULL);
|
||||
assert(newConnection(newconn, schedule[1]) == 0);
|
||||
|
||||
if (last == NULL)
|
||||
connections = newconn;
|
||||
else
|
||||
last->next = newconn;
|
||||
last = newconn;
|
||||
pending[id]++;
|
||||
schedule[0] = -1;
|
||||
}
|
||||
|
||||
Connection *prev = NULL;
|
||||
Connection *iter = connections;
|
||||
//I know of the Linus Thorvalds good taste code thing, it just
|
||||
//gets very confusing very fast to think about pointers to
|
||||
//pointers which have pointers.
|
||||
while (iter != NULL) {
|
||||
if (updateConnection(iter, site)) {
|
||||
if (iter->next == NULL)
|
||||
last = prev;
|
||||
if (prev == NULL)
|
||||
connections = connections->next;
|
||||
else
|
||||
prev->next = iter->next;
|
||||
Connection *newiter = iter->next;
|
||||
freeConnection(iter);
|
||||
iter = newiter;
|
||||
pending[id]--;
|
||||
//TODO: Clean this later
|
||||
}
|
||||
else {
|
||||
prev = iter;
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
198
src/sitefile.c
Normal file
198
src/sitefile.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
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 <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sitefile.h>
|
||||
|
||||
typedef enum {
|
||||
SUCCESS,
|
||||
LINE_END,
|
||||
FILE_END,
|
||||
ERROR,
|
||||
} ReturnCode;
|
||||
//this isn't ideal, but it's necessary to avoid namespace collisions.
|
||||
|
||||
static void freeTokens(int argc, char **argv) {
|
||||
for (int i = 0; i < argc; i++)
|
||||
free(argv[i]);
|
||||
free(argv);
|
||||
}
|
||||
|
||||
static ReturnCode getToken(FILE *file, char **ret) {
|
||||
typedef enum {
|
||||
QUOTED,
|
||||
NONQUOTED,
|
||||
} TokenType;
|
||||
TokenType type;
|
||||
|
||||
for (;;) {
|
||||
int c = fgetc(file);
|
||||
if (c == '\n')
|
||||
return LINE_END;
|
||||
if (c == EOF)
|
||||
return FILE_END;
|
||||
if (c == '#') {
|
||||
while (c != '\n')
|
||||
c = fgetc(file);
|
||||
return LINE_END;
|
||||
}
|
||||
if (!isspace(c)) {
|
||||
if (c == '"')
|
||||
type = QUOTED;
|
||||
else {
|
||||
type = NONQUOTED;
|
||||
ungetc(c, file);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
long allocatedLen = 30;
|
||||
long len;
|
||||
*ret = malloc(allocatedLen);
|
||||
|
||||
for (len = 0;; len++) {
|
||||
int c = fgetc(file);
|
||||
switch (type) {
|
||||
case QUOTED:
|
||||
if (c == '"')
|
||||
goto gotToken;
|
||||
break;
|
||||
case NONQUOTED:
|
||||
if (isspace(c)) {
|
||||
ungetc(c, file);
|
||||
goto gotToken;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case '\\':
|
||||
c = fgetc(file);
|
||||
if (c == EOF)
|
||||
goto error;
|
||||
break;
|
||||
case EOF:
|
||||
if (type == NONQUOTED)
|
||||
goto gotToken;
|
||||
goto error;
|
||||
}
|
||||
(*ret)[len] = c;
|
||||
}
|
||||
gotToken:
|
||||
(*ret)[len] = '\0';
|
||||
return SUCCESS;
|
||||
error:
|
||||
free(*ret);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
static ReturnCode getCommand(FILE *file, int *argcret, char ***argvret) {
|
||||
//THIS FUNCTION WILL NOT RETURN LINE_END
|
||||
if (feof(file))
|
||||
return FILE_END;
|
||||
int argc = 0;
|
||||
int allocatedTokens = 5;
|
||||
char **argv = malloc(allocatedTokens * sizeof(char *));
|
||||
for (;;) {
|
||||
ReturnCode code = getToken(file, argv + argc);
|
||||
|
||||
switch (code) {
|
||||
case ERROR:
|
||||
goto error;
|
||||
case LINE_END:
|
||||
if (argc == 0)
|
||||
continue;
|
||||
//We allow empty lines
|
||||
//fallthrough
|
||||
case FILE_END:
|
||||
if (argc == 0) {
|
||||
free(argv);
|
||||
return FILE_END;
|
||||
}
|
||||
*argcret = argc;
|
||||
*argvret = argv;
|
||||
return SUCCESS;
|
||||
case SUCCESS:
|
||||
argc++;
|
||||
if (argc >= allocatedTokens) {
|
||||
allocatedTokens *= 2;
|
||||
char **newargv = realloc(*argv,
|
||||
allocatedTokens * sizeof(char *));
|
||||
if (newargv == NULL)
|
||||
goto error;
|
||||
argv = newargv;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
error:
|
||||
freeTokens(argc, argv);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
Sitefile *parseFile(char *path) {
|
||||
FILE *file = fopen(path, "r");
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
Sitefile *ret = malloc(sizeof(Sitefile));
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
int allocatedLength = 50;
|
||||
ret->size = 0;
|
||||
ret->content = malloc(allocatedLength * sizeof(SiteCommand));
|
||||
if (ret->content == NULL) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
for (;;) {
|
||||
int argc;
|
||||
char **argv;
|
||||
ReturnCode status = getCommand(file, &argc, &argv);
|
||||
switch (status) {
|
||||
case FILE_END:
|
||||
fclose(file);
|
||||
return ret;
|
||||
case ERROR: case LINE_END:
|
||||
goto error;
|
||||
case SUCCESS:
|
||||
break;
|
||||
}
|
||||
if (ret->size >= allocatedLength) {
|
||||
allocatedLength *= 2;
|
||||
SiteCommand *newcontent = realloc(ret->content,
|
||||
allocatedLength * sizeof(SiteCommand));
|
||||
if (newcontent == NULL)
|
||||
goto error;
|
||||
ret->content = newcontent;
|
||||
}
|
||||
ret->content[ret->size].argc = argc;
|
||||
ret->content[ret->size].argv = argv;
|
||||
}
|
||||
error:
|
||||
freeSitefile(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void freeSitefile(Sitefile *site) {
|
||||
for (long i = 0; i < site->size; i++)
|
||||
freeTokens(site->content[i].argc, site->content[i].argv);
|
||||
free(site->content);
|
||||
free(site);
|
||||
}
|
||||
Reference in New Issue
Block a user