split into multible files

This commit is contained in:
overflowerror 2017-01-02 22:31:05 +01:00
parent aa45dd0bcc
commit f092d584fa
9 changed files with 702 additions and 656 deletions

View file

@ -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

View file

@ -1,4 +1,5 @@
#include "webserver.h"
#include "ws_utils.h"
#include "help.h"
#include <stdio.h>
#include <stdlib.h>

View file

@ -1,122 +1,18 @@
#include "webserver.h"
#include "ws_types.h"
#include "ws_utils.h"
#include "ws_modes.h"
#include "help.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include "help.h"
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdarg.h>
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 <unistd.h>
#include <time.h>
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) {

View file

@ -1,6 +1,8 @@
#ifndef WEBSERVER_H
#define WEBSERVER_H
#include "ws_types.h"
#include "ws_modes.h"
#include <stdio.h>
#include <stdbool.h>
@ -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

213
ws_linear.c Normal file
View file

@ -0,0 +1,213 @@
#include "webserver.h"
#include "ws_types.h"
#include "ws_modes.h"
#include "ws_utils.h"
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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);
}

8
ws_modes.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef WS_MODES_H
#define WS_MODES_H
#include "ws_types.h"
int ws_run_linear(webserver_t*);
#endif

74
ws_types.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef WS_TYPES_H
#define WS_TYPES_H
#include <stdio.h>
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

363
ws_utils.c Normal file
View file

@ -0,0 +1,363 @@
#include "webserver.h"
#include "ws_types.h"
#include "ws_utils.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
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;
}

17
ws_utils.h Normal file
View file

@ -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