Serwer/ws_utils.c

368 lines
6.8 KiB
C
Raw Normal View History

2017-01-02 21:48:49 +00:00
#include "serwer.h"
2017-01-02 21:31:05 +00:00
#include <assert.h>
#include <string.h>
#include <stdlib.h>
2017-01-02 22:01:06 +00:00
#include <stdbool.h>
2017-01-02 21:31:05 +00:00
2017-01-02 21:36:40 +00:00
method_t ws_method(const char* string) {
2017-01-02 21:31:05 +00:00
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
}
2017-01-02 21:36:40 +00:00
const char* ws_method_string(method_t method) {
2017-01-02 21:31:05 +00:00
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;
}
}
2017-01-03 00:27:24 +00:00
// TODO server:80 and server:80* do not match.
2017-01-02 21:31:05 +00:00
// 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;
}
}
2017-01-03 00:27:24 +00:00
// TODO /hello and /hello* do not match.
2017-01-02 21:31:05 +00:00
// 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';
2017-01-02 21:36:40 +00:00
*method = ws_method(lbuffer);
2017-01-02 21:31:05 +00:00
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;
}