Skip to content

Commit a8233d9

Browse files
committed
Basic plugin loading
1 parent 4c6833d commit a8233d9

File tree

8 files changed

+165
-25
lines changed

8 files changed

+165
-25
lines changed

builtins.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "websocksy.h"
44
#include "builtins.h"
5+
#include "plugin.h"
56

67
#define UTF8_BYTE(a) ((a & 0xC0) == 0x80)
78

@@ -47,6 +48,13 @@ uint64_t backend_defaultpeer_configure(char* key, char* value){
4748
free(default_peer_proto);
4849
default_peer_proto = strdup(value);
4950
}
51+
else if(!strcmp(key, "framing")){
52+
default_peer.framing = plugin_framing(value);
53+
}
54+
else if(!strcmp(key, "framing-config")){
55+
free(default_peer.framing_config);
56+
default_peer.framing_config = strdup(value);
57+
}
5058
return 1;
5159
}
5260

@@ -61,6 +69,7 @@ ws_peer_info backend_defaultpeer_query(char* endpoint, size_t protocols, char**
6169
ws_peer_info peer = default_peer;
6270
peer.host = (default_peer.host) ? strdup(default_peer.host) : NULL;
6371
peer.port = (default_peer.port) ? strdup(default_peer.port) : NULL;
72+
peer.framing_config = (default_peer.framing_config) ? strdup(default_peer.framing_config) : NULL;
6473

6574
//if none set, announce none
6675
peer.protocol = protocols;
@@ -90,6 +99,8 @@ void backend_defaultpeer_cleanup(){
9099
default_peer.host = NULL;
91100
free(default_peer.port);
92101
default_peer.port = NULL;
102+
free(default_peer.framing_config);
103+
default_peer.framing_config = NULL;
93104
free(default_peer_proto);
94105
default_peer_proto = NULL;
95106
}
@@ -98,7 +109,7 @@ void backend_defaultpeer_cleanup(){
98109
* The `auto` stream framing function forwards all incoming data from the peer immediately,
99110
* with the text frame type if the data is valid UTF8 and the binary frame type otherwise.
100111
*/
101-
int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){
112+
int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){
102113
size_t p;
103114
uint8_t valid = 1;
104115
for(p = 0; p < length; p++){
@@ -150,16 +161,16 @@ int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operatio
150161
/*
151162
* The `binary` peer stream framing function forwards all incoming data from the peer immediately as binary frames
152163
*/
153-
int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){
164+
int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){
154165
return length;
155166
}
156167

157-
int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){
168+
int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){
158169
//TODO implement separator framer
159170
return length;
160171
}
161172

162-
int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config){
173+
int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config){
163174
//TODO implement separator framer
164175
return length;
165176
}

builtins.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ws_peer_info backend_defaultpeer_query(char* endpoint, size_t protocols, char**
55
void backend_defaultpeer_cleanup();
66

77
/* Built-in framing functions */
8-
int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config);
9-
int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config);
10-
int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config);
11-
int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config);
8+
int64_t framing_auto(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config);
9+
int64_t framing_binary(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config);
10+
int64_t framing_separator(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config);
11+
int64_t framing_newline(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config);

makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
CFLAGS=-g -Wall -Wpedantic
1+
PLUGINPATH=plugins/
2+
3+
CFLAGS=-g -Wall -Wpedantic -DPLUGINS=\"$(PLUGINPATH)\"
24
LDLIBS=-lnettle
35

4-
OBJECTS=builtins.o network.o websocket.o
6+
OBJECTS=builtins.o network.o websocket.o plugin.o
57

68
all: websocksy
79

plugin.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#include <string.h>
2+
#include <stdio.h>
3+
4+
#include "websocksy.h"
5+
#include "plugin.h"
6+
7+
static size_t framing_functions = 0;
8+
static ws_framing* framing_function = NULL;
9+
static char** framing_function_name = NULL;
10+
11+
int plugin_framing_load(char* path){
12+
//TODO load plugins
13+
return 1;
14+
}
15+
16+
int plugin_backend_load(char* backend_requested, ws_backend* backend){
17+
//TODO load backend
18+
return 1;
19+
}
20+
21+
int plugin_register_framing(char* name, ws_framing func){
22+
size_t u;
23+
24+
for(u = 0; u < framing_functions; u++){
25+
if(!strcmp(framing_function_name[u], name)){
26+
fprintf(stderr, "Replacing framing %s\n", name);
27+
break;
28+
}
29+
}
30+
31+
if(u == framing_functions){
32+
framing_function = realloc(framing_function, (framing_functions + 1) * sizeof(ws_framing));
33+
framing_function_name = realloc(framing_function_name, (framing_functions + 1) * sizeof(char*));
34+
if(!framing_function || !framing_function_name){
35+
fprintf(stderr, "Failed to allocate memory for framing function\n");
36+
return 1;
37+
}
38+
39+
framing_function_name[u] = strdup(name);
40+
framing_functions++;
41+
}
42+
43+
framing_function[u] = func;
44+
return 0;
45+
}
46+
47+
ws_framing plugin_framing(char* name){
48+
size_t u;
49+
50+
if(!name){
51+
return plugin_framing("auto");
52+
}
53+
54+
for(u = 0; u < framing_functions; u++){
55+
if(!strcmp(framing_function_name[u], name)){
56+
return framing_function[u];
57+
}
58+
}
59+
60+
//if unknown framing, return the default
61+
return plugin_framing("auto");
62+
}
63+
64+
void plugin_cleanup(){
65+
size_t u;
66+
//TODO dlclose all plugins
67+
68+
for(u = 0; u < framing_functions; u++){
69+
free(framing_function_name[u]);
70+
}
71+
free(framing_function);
72+
framing_function = NULL;
73+
free(framing_function_name);
74+
framing_function_name = 0;
75+
framing_function = NULL;
76+
framing_functions = 0;
77+
}

plugin.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* Shared object handling */
2+
int plugin_framing_load(char* path);
3+
int plugin_backend_load(char* backend_requested, ws_backend* backend);
4+
5+
/* Framing function registry */
6+
int plugin_register_framing(char* name, ws_framing func);
7+
ws_framing plugin_framing(char* name);
8+
9+
/* Module management */
10+
void plugin_cleanup();

websocket.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,17 @@ int ws_close(websocket* ws, ws_close_reason code, char* reason){
2626
};
2727

2828
if(ws->state == ws_open && reason){
29-
//TODO send close frame
29+
//send close frame
30+
//FIXME this should prepend the status code to the reason
31+
ws_send_frame(ws, ws_frame_close, (uint8_t*) reason, strlen(reason));
32+
}
33+
else if(ws->state == ws_http
34+
&& code == ws_close_http
35+
&& reason){
36+
//send http response
37+
network_send_str(ws->ws_fd, "HTTP/1.1 ");
38+
network_send_str(ws->ws_fd, reason);
39+
network_send_str(ws->ws_fd, "\r\n\r\n");
3040
}
3141
ws->state = ws_closed;
3242

@@ -349,17 +359,17 @@ static size_t ws_frame(websocket* ws){
349359
//TODO handle fragmentation
350360
//TODO handle control frames within fragmented frames
351361

352-
fprintf(stderr, "Incoming websocket data: %s %s OP %02X LEN %u %lu\n",
362+
/*fprintf(stderr, "Incoming websocket data: %s %s OP %02X LEN %u %lu\n",
353363
WS_GET_FIN(ws->read_buffer[0]) ? "FIN" : "CONT",
354364
WS_GET_MASK(ws->read_buffer[1]) ? "MASK" : "CLEAR",
355365
WS_GET_OP(ws->read_buffer[0]),
356366
WS_GET_LEN(ws->read_buffer[1]),
357-
payload_length);
367+
payload_length);*/
358368

359369
//handle data
360370
switch(WS_GET_OP(ws->read_buffer[0])){
361371
case ws_frame_text:
362-
fprintf(stderr, "Text payload: %.*s\n", (int) payload_length, (char*) payload);
372+
//fprintf(stderr, "Text payload: %.*s\n", (int) payload_length, (char*) payload);
363373
case ws_frame_binary:
364374
//forward to peer
365375
if(ws->peer_fd >= 0){
@@ -373,12 +383,13 @@ static size_t ws_frame(websocket* ws){
373383
ws_close(ws, ws_close_normal, "Client requested termination");
374384
break;
375385
case ws_frame_ping:
376-
break;
377-
case ws_frame_pong:
378386
if(ws_send_frame(ws, ws_frame_pong, payload, payload_length)){
379-
ws_close(ws, ws_close_unexpected, "Failed to send ping");
387+
ws_close(ws, ws_close_unexpected, "Failed to send pong");
380388
}
381389
break;
390+
case ws_frame_pong:
391+
//TODO keep-alive pings
392+
break;
382393
default:
383394
//unknown frame type received
384395
fprintf(stderr, "Unknown WebSocket opcode %02X in frame\n", WS_GET_OP(ws->read_buffer[0]));
@@ -390,7 +401,7 @@ static size_t ws_frame(websocket* ws){
390401
}
391402

392403
int ws_send_frame(websocket* ws, ws_operation opcode, uint8_t* data, size_t len){
393-
fprintf(stderr, "Peer -> WS %lu bytes\n", len);
404+
fprintf(stderr, "Peer -> WS %lu bytes (%02X)\n", len, opcode);
394405
uint8_t frame_header[WS_FRAME_HEADER_LEN];
395406
size_t header_bytes = 2;
396407
uint16_t* payload_len16 = (uint16_t*) (frame_header + 2);

websocksy.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
#include "builtins.h"
1515
#include "network.h"
1616
#include "websocket.h"
17+
#include "plugin.h"
1718

1819
#define DEFAULT_HOST "::"
1920
#define DEFAULT_PORT "8001"
2021

2122
/* TODO
2223
* - TLS
2324
* - framing function discovery / registry
25+
* - WS p2p
2426
*/
2527

2628
/* Main loop condition, to be set from signal handler */
@@ -203,7 +205,18 @@ static int args_parse(int argc, char** argv){
203205
config.host = argv[u + 1];
204206
break;
205207
case 'b':
206-
//TODO load peer discovery plugin
208+
//clean up the previously registered backend
209+
if(config.backend.cleanup){
210+
config.backend.cleanup();
211+
}
212+
//load the backend plugin
213+
if(plugin_backend_load(argv[u + 1], &(config.backend))){
214+
return 1;
215+
}
216+
if(config.backend.init() != WEBSOCKSY_API_VERSION){
217+
fprintf(stderr, "Loaded backend %s was built for a different API version\n", argv[u + 1]);
218+
return 1;
219+
}
207220
break;
208221
case 'c':
209222
if(!strchr(argv[u + 1], '=')){
@@ -286,17 +299,32 @@ int main(int argc, char** argv){
286299
size_t n;
287300
int listen_fd = -1, status, max_fd;
288301

289-
//parse command line arguments
290-
if(args_parse(argc - 1, argv + 1)){
291-
exit(usage(argv[0]));
302+
//register default framing functions before parsing arguments, as they may be assigned within a backend configuration
303+
if(plugin_register_framing("auto", framing_auto)
304+
|| plugin_register_framing("binary", framing_binary)
305+
|| plugin_register_framing("separator", framing_separator)
306+
|| plugin_register_framing("newline", framing_newline)){
307+
fprintf(stderr, "Failed to initialize builtins\n");
308+
exit(EXIT_FAILURE);
292309
}
293310

294-
//initialize the selected peer discovery backend
311+
//load plugin framing functions
312+
if(plugin_framing_load(PLUGINS)){
313+
fprintf(stderr, "Failed to load plugins\n");
314+
exit(EXIT_FAILURE);
315+
}
316+
317+
//initialize the default backend
295318
if(config.backend.init() != WEBSOCKSY_API_VERSION){
296-
fprintf(stderr, "The selected backend was built for another API version than the core\n");
319+
fprintf(stderr, "Failed to initialize builtin backend\n");
297320
exit(EXIT_FAILURE);
298321
}
299322

323+
//parse command line arguments
324+
if(args_parse(argc - 1, argv + 1)){
325+
exit(usage(argv[0]));
326+
}
327+
300328
//open listening socket
301329
listen_fd = network_socket(config.host, config.port, SOCK_STREAM, 1);
302330
if(listen_fd < 0){
@@ -371,6 +399,7 @@ int main(int argc, char** argv){
371399
config.backend.cleanup();
372400
}
373401
client_cleanup();
402+
plugin_cleanup();
374403
close(listen_fd);
375404
return 0;
376405
}

websocksy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ typedef struct /*_ws_http_header*/ {
8585
* NULL `data` pointer as an indication the any allocation within `framing_data` is to be freed.
8686
* The return value is the number of bytes to be sent to the peer.
8787
*/
88-
typedef int64_t (*ws_framing)(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, char* config);
88+
typedef int64_t (*ws_framing)(uint8_t* data, size_t length, size_t last_read, ws_operation* opcode, void** framing_data, const char* config);
8989

9090
/* Peer connection modes */
9191
typedef enum {

0 commit comments

Comments
 (0)