mirror of
https://github.com/sigmasternchen/Serwer
synced 2025-03-15 23:28:54 +00:00
split into multible files
This commit is contained in:
parent
aa45dd0bcc
commit
f092d584fa
9 changed files with 702 additions and 656 deletions
8
Makefile
8
Makefile
|
@ -17,14 +17,14 @@ all: example
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(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 $@ $^
|
$(CC) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
example.o: example.c webserver.h
|
example.o: example.c webserver.h
|
||||||
|
|
||||||
help.o: help.c help.h
|
help.o: help.c help.h
|
||||||
|
webserver.o: webserver.c webserver.h ws_types.h ws_modes.h ws_utils.h help.h
|
||||||
webserver.o: webserver.c webserver.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:
|
clean:
|
||||||
rm -f *.o example
|
rm -f *.o example
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
|
#include "ws_utils.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
570
webserver.c
570
webserver.c
|
@ -1,122 +1,18 @@
|
||||||
#include "webserver.h"
|
#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/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netdb.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>
|
#include <stdarg.h>
|
||||||
|
#include <unistd.h>
|
||||||
bool ws_host_match(const char* host, const char* match) {
|
#include <time.h>
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
handler_t ws_handler_find(webserver_t* server, const char* path, const char* host) {
|
handler_t ws_handler_find(webserver_t* server, const char* path, const char* host) {
|
||||||
for (int i = 0; i < server->nrhandles; i++) {
|
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;
|
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 ws_headers_create(void) {
|
||||||
headers_t headers;
|
headers_t headers;
|
||||||
headers.fields = malloc(0 * sizeof(header_t));
|
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");
|
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) {
|
void ws_send(int connfd, int code, headers_t headers, int pipefd) {
|
||||||
stream_t connection = (stream_t) fdopen(connfd, "a+");
|
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) {
|
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) {
|
int ws_run(webserver_t* server) {
|
||||||
switch(server->options.mode) {
|
switch(server->options.mode) {
|
||||||
|
|
104
webserver.h
104
webserver.h
|
@ -1,6 +1,8 @@
|
||||||
#ifndef WEBSERVER_H
|
#ifndef WEBSERVER_H
|
||||||
#define WEBSERVER_H
|
#define WEBSERVER_H
|
||||||
|
|
||||||
|
#include "ws_types.h"
|
||||||
|
#include "ws_modes.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
@ -9,111 +11,21 @@
|
||||||
#define WS_VERSION "0.1"
|
#define WS_VERSION "0.1"
|
||||||
#define WS_NAME "SERWER"
|
#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);
|
headers_t ws_headers_create(void);
|
||||||
|
|
||||||
void ws_headers_add(headers_t*, const char*, const char*);
|
void ws_headers_add(headers_t*, const char*, const char*);
|
||||||
|
|
||||||
void ws_headers_convert(headers_t*, char*);
|
void ws_headers_convert(headers_t*, char*);
|
||||||
|
|
||||||
void ws_headers_free(headers_t*);
|
void ws_headers_free(headers_t*);
|
||||||
|
|
||||||
int ws_listen(webserver_t*);
|
|
||||||
|
|
||||||
void ws_handle_add(webserver_t*, handle_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);
|
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*, ...);
|
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
|
#endif
|
||||||
|
|
213
ws_linear.c
Normal file
213
ws_linear.c
Normal 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
8
ws_modes.h
Normal 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
74
ws_types.h
Normal 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
363
ws_utils.c
Normal 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
17
ws_utils.h
Normal 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
|
Loading…
Reference in a new issue