aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2019-05-17 19:41:14 +0200
committercbdev <cb@cbcdn.com>2019-05-17 19:41:14 +0200
commitd28b5eb2960e80d80e967a3f01ce70e1fa457808 (patch)
treef19f22d441f9e66c9c17476477707e65d5e425cc
parent10874eb2195d3e6ceed8e8a76aaf4455e41c8683 (diff)
downloadwebsocksy-d28b5eb2960e80d80e967a3f01ce70e1fa457808.tar.gz
websocksy-d28b5eb2960e80d80e967a3f01ce70e1fa457808.tar.bz2
websocksy-d28b5eb2960e80d80e967a3f01ce70e1fa457808.zip
Split out websocket handling, HTTP header processing
-rw-r--r--index.html2
-rw-r--r--makefile3
-rw-r--r--websocksy.c142
-rw-r--r--websocksy.h15
-rw-r--r--ws_proto.c213
5 files changed, 236 insertions, 139 deletions
diff --git a/index.html b/index.html
index c0af329..233ee69 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
<head>
<script type="text/javascript">
function init(){
- var webSocket = new WebSocket("ws://localhost:8001/");
+ var webSocket = new WebSocket("ws://localhost:8001/foo/bar");
}
</script>
</head>
diff --git a/makefile b/makefile
index a7a0b45..90716a0 100644
--- a/makefile
+++ b/makefile
@@ -2,7 +2,8 @@ CFLAGS=-g -Wall -Wpedantic
all: websocksy
-websocksy: websocksy.c websocksy.h
+websocksy: websocksy.c websocksy.h ws_proto.c
+ $(CC) $(CFLAGS) $< -o $@
clean:
rm websocksy
diff --git a/websocksy.c b/websocksy.c
index cc5b9e0..f193ae6 100644
--- a/websocksy.c
+++ b/websocksy.c
@@ -10,6 +10,7 @@
#include <fcntl.h>
#include "websocksy.h"
+#include "ws_proto.c"
/* TODO
* - TLS
@@ -17,9 +18,6 @@
* - Prevent http overrun
*/
-static size_t socks = 0;
-static websocket* sock = NULL;
-
static volatile sig_atomic_t shutdown_requested = 0;
void signal_handler(int signum){
@@ -121,122 +119,14 @@ int network_socket(char* host, char* port, int socktype, int listener){
return fd;
}
-int ws_close(websocket* ws){
- size_t p;
-
- if(ws->fd >= 0){
- close(ws->fd);
- ws->fd = -1;
- }
-
- for(p = 0; p < ws->protocols; p++){
- if(ws->protocol[p].fd >= 0){
- close(ws->protocol[p].fd);
- ws->protocol[p].fd = -1;
- free(ws->protocol[p].name);
- ws->protocol[p].name = NULL;
- }
- }
-
- ws->read_buffer_offset = 0;
-
- return 0;
-}
-
-int ws_accept(int listen_fd){
- size_t n = 0;
-
- websocket ws = {
- .fd = accept(listen_fd, NULL, NULL)
- };
-
- //try to find a slot to occupy
- for(n = 0; n < socks; n++){
- if(sock[n].fd == -1){
- break;
- }
- }
-
- //none found, need to extend
- if(n == socks){
- sock = realloc(sock, (socks + 1) * sizeof(websocket));
- if(!sock){
- close(ws.fd);
- fprintf(stderr, "Failed to allocate memory\n");
- shutdown_requested = 1;
- return 1;
- }
- socks++;
- }
-
- sock[n] = ws;
-
- return 0;
-}
-
-int ws_data(websocket* ws){
- ssize_t bytes_read, u, bytes_left = sizeof(ws->read_buffer) - ws->read_buffer_offset;
-
- bytes_read = recv(ws->fd, ws->read_buffer + ws->read_buffer_offset, bytes_left - 1, 0);
- if(bytes_read < 0){
- fprintf(stderr, "Failed to receive from websocket: %s\n", strerror(errno));
- ws_close(ws);
- return 0;
- }
- else if(bytes_read == 0){
- //client closed connection
- ws_close(ws);
- return 0;
- }
-
- //terminate new data
- ws->read_buffer[ws->read_buffer_offset + bytes_read] = 0;
-
- switch(ws->state){
- case ws_new:
- case ws_http:
- //scan for newline, handle line
- for(u = 0; u < bytes_read - 1; u++){
- if(!strncmp((char*) ws->read_buffer + ws->read_buffer_offset + u, "\r\n", 2)){
- //terminate line
- ws->read_buffer[ws->read_buffer_offset + u] = 0;
-
- fprintf(stderr, "Line: %s\n", ws->read_buffer);
-
- //remove from buffer
- bytes_read -= (u + 2);
- memmove(ws->read_buffer, ws->read_buffer + ws->read_buffer_offset + u + 2, bytes_read);
- ws->read_buffer_offset = 0;
-
- //restart from the beginning
- u = -1;
- }
- }
- break;
- //case ws_rfc6455:
- //TODO parse websocket encap, forward to peer
- }
-
- //update read buffer offset
- ws->read_buffer_offset = bytes_read;
-
- //disconnect spammy clients
- if(sizeof(ws->read_buffer) - ws->read_buffer_offset < 2){
- fprintf(stderr, "Disconnecting misbehaving client\n");
- ws_close(ws);
- return 0;
- }
- return 0;
-}
-
-int ws_peer_data(websocket* ws, size_t proto){
+int ws_peer_data(websocket* ws){
//TODO
return -1;
}
int main(int argc, char** argv){
fd_set read_fds;
- size_t n, p;
+ size_t n;
int listen_fd = -1, status, max_fd;
if(args_parse(argc - 1, argv + 1)){
@@ -263,13 +153,11 @@ int main(int argc, char** argv){
if(max_fd < sock[n].fd){
max_fd = sock[n].fd;
}
-
- for(p = 0; p < sock[n].protocols; p++){
- if(sock[n].protocol[p].fd >= 0){
- FD_SET(sock[n].protocol[p].fd, &read_fds);
- if(max_fd < sock[n].protocol[p].fd){
- max_fd = sock[n].protocol[p].fd;
- }
+
+ if(sock[n].peer >= 0){
+ FD_SET(sock[n].peer, &read_fds);
+ if(max_fd < sock[n].peer){
+ max_fd = sock[n].peer;
}
}
}
@@ -301,11 +189,9 @@ int main(int argc, char** argv){
}
}
- for(p = 0; p < sock[n].protocols; p++){
- if(sock[n].protocol[p].fd >= 0 && FD_ISSET(sock[n].protocol[p].fd, &read_fds)){
- if(ws_peer_data(sock + n, p)){
- break;
- }
+ if(sock[n].peer >= 0 && FD_ISSET(sock[n].peer, &read_fds)){
+ if(ws_peer_data(sock + n)){
+ break;
}
}
}
@@ -314,11 +200,7 @@ int main(int argc, char** argv){
}
//cleanup
- for(n = 0; n < socks; n++){
- ws_close(sock + n);
- }
- free(sock);
- socks = 0;
+ ws_cleanup();
close(listen_fd);
return 0;
}
diff --git a/websocksy.h b/websocksy.h
index 0fc9538..794b4ed 100644
--- a/websocksy.h
+++ b/websocksy.h
@@ -21,17 +21,18 @@ struct {
.backend.name = "internal"
};
-typedef struct {
- char* name;
- int fd;
-} ws_proto;
-
typedef struct /*_web_socket*/ {
+ int fd;
+ int peer;
uint8_t read_buffer[WS_MAX_LINE];
size_t read_buffer_offset;
ws_state state;
- int fd;
+ char* request_path;
+ unsigned websocket_version;
+ char* socket_key;
+ unsigned want_upgrade;
+
size_t protocols;
- ws_proto* protocol;
+ char** protocol;
} websocket;
diff --git a/ws_proto.c b/ws_proto.c
new file mode 100644
index 0000000..b9f92e9
--- /dev/null
+++ b/ws_proto.c
@@ -0,0 +1,213 @@
+#include <ctype.h>
+
+static size_t socks = 0;
+static websocket* sock = NULL;
+
+int ws_close(websocket* ws){
+ size_t p;
+
+ if(ws->fd >= 0){
+ close(ws->fd);
+ ws->fd = -1;
+ }
+
+ if(ws->peer >= 0){
+ close(ws->peer);
+ ws->peer = -1;
+ }
+
+ for(p = 0; p < ws->protocols; p++){
+ free(ws->protocol[p]);
+ }
+ ws->protocols = 0;
+ ws->protocol = NULL;
+
+ ws->read_buffer_offset = 0;
+
+ free(ws->request_path);
+ ws->request_path = NULL;
+
+ free(ws->socket_key);
+ ws->socket_key = NULL;
+
+ ws->websocket_version = 0;
+ ws->want_upgrade = 0;
+
+ return 0;
+}
+
+int ws_accept(int listen_fd){
+ size_t n = 0;
+
+ websocket ws = {
+ .fd = accept(listen_fd, NULL, NULL),
+ .peer = -1
+ };
+
+ //try to find a slot to occupy
+ for(n = 0; n < socks; n++){
+ if(sock[n].fd == -1){
+ break;
+ }
+ }
+
+ //none found, need to extend
+ if(n == socks){
+ sock = realloc(sock, (socks + 1) * sizeof(websocket));
+ if(!sock){
+ close(ws.fd);
+ fprintf(stderr, "Failed to allocate memory\n");
+ return 1;
+ }
+ socks++;
+ }
+
+ sock[n] = ws;
+
+ return 0;
+}
+
+int ws_handle_new(websocket* ws){
+ size_t u;
+ char* path, *proto;
+
+ if(!strncmp((char*) ws->read_buffer, "GET ", 4)){
+ path = (char*) ws->read_buffer + 4;
+ for(u = 0; path[u] && !isspace(path[u]); u++){
+ }
+ path[u] = 0;
+ proto = path + u + 1;
+ }
+ //TODO handle other methods
+ else{
+ fprintf(stderr, "Unknown HTTP method in request\n");
+ return 1;
+ }
+
+ if(strncmp(proto, "HTTP/", 5)){
+ fprintf(stderr, "Malformed HTTP initiation\n");
+ return 1;
+ }
+
+ ws->state = ws_http;
+ ws->request_path = strdup(path);
+ return 0;
+}
+
+int ws_upgrade_http(websocket* ws){
+ if(ws->websocket_version == 13
+ && ws->want_upgrade){
+
+ return 0;
+ }
+ return 1;
+}
+
+int ws_handle_http(websocket* ws){
+ char* header, *value;
+ if(!ws->read_buffer[0]){
+ return ws_upgrade_http(ws);
+ }
+ else if(isspace(ws->read_buffer[0])){
+ //i hate header folding
+ //TODO disconnect client
+ return 1;
+ }
+ else{
+ header = (char*) ws->read_buffer;
+ value = strchr(header, ':');
+ if(!value){
+ //TODO disconnect
+ return 1;
+ }
+ *value = 0;
+ value++;
+ for(; isspace(*value); value++){
+ }
+ }
+
+ if(!strcmp(header, "Sec-WebSocket-Version")){
+ ws->websocket_version = strtoul(value, NULL, 10);
+ }
+ else if(!strcmp(header, "Sec-WebSocket-Key")){
+ ws->socket_key = strdup(value);
+ }
+ else if(!strcmp(header, "Upgrade") && !strcmp(value, "websocket")){
+ ws->want_upgrade = 1;
+ }
+ //TODO parse websocket protocol offers
+ return 0;
+}
+
+int ws_data(websocket* ws){
+ ssize_t bytes_read, u, bytes_left = sizeof(ws->read_buffer) - ws->read_buffer_offset;
+ int rv = 0;
+
+ bytes_read = recv(ws->fd, ws->read_buffer + ws->read_buffer_offset, bytes_left - 1, 0);
+ if(bytes_read < 0){
+ fprintf(stderr, "Failed to receive from websocket: %s\n", strerror(errno));
+ ws_close(ws);
+ return 0;
+ }
+ else if(bytes_read == 0){
+ //client closed connection
+ ws_close(ws);
+ return 0;
+ }
+
+ //terminate new data
+ ws->read_buffer[ws->read_buffer_offset + bytes_read] = 0;
+
+ switch(ws->state){
+ case ws_new:
+ case ws_http:
+ //scan for newline, handle line
+ for(u = 0; u < bytes_read - 1; u++){
+ if(!strncmp((char*) ws->read_buffer + ws->read_buffer_offset + u, "\r\n", 2)){
+ //terminate line
+ ws->read_buffer[ws->read_buffer_offset + u] = 0;
+
+ if(ws->state == ws_new){
+ rv |= ws_handle_new(ws);
+ }
+ else{
+ rv |= ws_handle_http(ws);
+ }
+ //TODO handle rv
+
+ //remove from buffer
+ bytes_read -= (u + 2);
+ memmove(ws->read_buffer, ws->read_buffer + ws->read_buffer_offset + u + 2, bytes_read);
+ ws->read_buffer_offset = 0;
+
+ //restart from the beginning
+ u = -1;
+ }
+ }
+ break;
+ //case ws_rfc6455:
+ //TODO parse websocket encap, forward to peer
+ }
+
+ //update read buffer offset
+ ws->read_buffer_offset = bytes_read;
+
+ //disconnect spammy clients
+ if(sizeof(ws->read_buffer) - ws->read_buffer_offset < 2){
+ fprintf(stderr, "Disconnecting misbehaving client\n");
+ ws_close(ws);
+ return 0;
+ }
+ return rv;
+}
+
+void ws_cleanup(){
+ size_t n;
+ for(n = 0; n < socks; n++){
+ ws_close(sock + n);
+ }
+
+ free(sock);
+ sock = NULL;
+ socks = 0;
+}