-
Notifications
You must be signed in to change notification settings - Fork 182
/
websocket_server.c
325 lines (285 loc) · 13.4 KB
/
websocket_server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/**
*
* Ulfius Framework example program
*
* This example program implements a websocket
*
* Copyright 2017-2022 Nicolas Mora <[email protected]>
*
* License MIT
*
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <ulfius.h>
#include "u_example.h"
#include "static_compressed_inmemory_website_callback.h"
#define PORT 9275
#define PREFIX_WEBSOCKET "/websocket"
#define PREFIX_STATIC "/static"
#if defined(U_DISABLE_WEBSOCKET)
#error You must build ulfius with websocket support enabled to compile this example, check the install documentation
#else
int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data);
int callback_websocket_echo (const struct _u_request * request, struct _u_response * response, void * user_data);
int callback_websocket_file (const struct _u_request * request, struct _u_response * response, void * user_data);
static char * read_file(const char * filename) {
char * buffer = NULL;
long length;
FILE * f;
if (filename != NULL) {
f = fopen (filename, "rb");
if (f) {
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
buffer = o_malloc ((size_t)(length + 1));
if (buffer != NULL) {
fread (buffer, 1, (size_t)length, f);
buffer[length] = '\0';
}
fclose (f);
}
return buffer;
} else {
return NULL;
}
}
/**
* main function
* open the wbservice on port 9275
*/
int main(int argc, char ** argv) {
int ret;
struct _u_instance instance;
struct _u_compressed_inmemory_website_config file_config;
char * cert_file = NULL, * key_file = NULL;
y_init_logs("websocket_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_example");
if (u_init_compressed_inmemory_website_config(&file_config) == U_OK) {
u_map_put(&file_config.mime_types, ".html", "text/html");
u_map_put(&file_config.mime_types, ".css", "text/css");
u_map_put(&file_config.mime_types, ".js", "application/javascript");
u_map_put(&file_config.mime_types, ".png", "image/png");
u_map_put(&file_config.mime_types, ".jpg", "image/jpeg");
u_map_put(&file_config.mime_types, ".jpeg", "image/jpeg");
u_map_put(&file_config.mime_types, ".ttf", "font/ttf");
u_map_put(&file_config.mime_types, ".woff", "font/woff");
u_map_put(&file_config.mime_types, ".woff2", "font/woff2");
u_map_put(&file_config.mime_types, ".map", "application/octet-stream");
u_map_put(&file_config.mime_types, ".json", "application/json");
u_map_put(&file_config.mime_types, "*", "application/octet-stream");
file_config.files_path = realpath("static", NULL);
file_config.url_prefix = PREFIX_STATIC;
if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort");
return(1);
}
u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*");
// Endpoint list declaration
ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, NULL);
ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/echo", 0, &callback_websocket_echo, NULL);
ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/file", 0, &callback_websocket_file, NULL);
ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_STATIC, "*", 0, &callback_static_compressed_inmemory_website, &file_config);
// Start the framework
if (argc > 3 && 0 == o_strcmp(argv[1], "-https")) {
key_file = read_file(argv[2]);
cert_file = read_file(argv[3]);
if (key_file == NULL || cert_file == NULL) {
printf("Error reading https certificate files\n");
ret = U_ERROR_PARAMS;
} else {
ret = ulfius_start_secure_framework(&instance, key_file, cert_file);
}
o_free(key_file);
o_free(cert_file);
} else {
ret = ulfius_start_framework(&instance);
}
if (ret == U_OK) {
y_log_message(Y_LOG_LEVEL_INFO, "Start framework on port %d %s", instance.port, (argc > 1 && 0 == o_strcmp(argv[1], "-https"))?"https mode":"http mode");
// Wait for the user to press <enter> on the console to quit the application
getchar();
} else {
y_log_message(Y_LOG_LEVEL_ERROR, "Error starting framework");
}
y_log_message(Y_LOG_LEVEL_INFO, "End framework");
ulfius_stop_framework(&instance);
ulfius_clean_instance(&instance);
u_clean_compressed_inmemory_website_config(&file_config);
free(file_config.files_path);
}
y_close_logs();
return 0;
}
/**
* websocket_onclose_callback
* onclose callback function
* Used to clear data after the websocket connection is closed
*/
void websocket_onclose_callback (const struct _u_request * request,
struct _websocket_manager * websocket_manager,
void * websocket_onclose_user_data) {
(void)(request);
(void)(websocket_manager);
if (websocket_onclose_user_data != NULL) {
y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_onclose_user_data is %s", websocket_onclose_user_data);
o_free(websocket_onclose_user_data);
}
}
void websocket_onclose_file_callback (const struct _u_request * request,
struct _websocket_manager * websocket_manager,
void * websocket_onclose_user_data) {
(void)(request);
(void)(websocket_manager);
(void)(websocket_onclose_user_data);
y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_onclose_file_callback");
}
/**
* websocket_manager_callback
* send 5 text messages and 1 ping for 11 seconds, then closes the websocket
*/
void websocket_manager_callback(const struct _u_request * request,
struct _websocket_manager * websocket_manager,
void * websocket_manager_user_data) {
if (websocket_manager_user_data != NULL) {
y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_manager_user_data is %s", websocket_manager_user_data);
}
#ifndef U_DISABLE_WS_MESSAGE_LIST
websocket_manager->keep_messages = U_WEBSOCKET_KEEP_OUTCOMING;
#endif
// Send text message without fragmentation
if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) {
if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen("Message without fragmentation from server"), "Message without fragmentation from server") != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error send message without fragmentation");
}
}
// Send text message with fragmentation for ulfius clients only, browsers seem to dislike fragmented messages
if (o_strncmp(u_map_get(request->map_header, "User-Agent"), U_WEBSOCKET_USER_AGENT, o_strlen(U_WEBSOCKET_USER_AGENT)) == 0 &&
ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) {
if (ulfius_websocket_send_fragmented_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen("Message with fragmentation from server"), "Message with fragmentation from server", 5) != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error send message with fragmentation");
}
}
// Send ping message
if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) {
if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_PING, 0, NULL) != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error send ping message");
}
}
// Send binary message without fragmentation
if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) {
if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_BINARY, o_strlen("Message without fragmentation from server"), "Message without fragmentation from server") != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error send binary message without fragmentation");
}
}
// Send JSON message without fragmentation
#ifndef U_DISABLE_JANSSON
if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) {
json_t * message = json_pack("{ss}", "send", "JSON message without fragmentation");
if (ulfius_websocket_send_json_message(websocket_manager, message) != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error send JSON message without fragmentation");
}
json_decref(message);
}
#endif
y_log_message(Y_LOG_LEVEL_DEBUG, "Closing websocket_manager_callback");
}
void websocket_manager_file_callback(const struct _u_request * request,
struct _websocket_manager * websocket_manager,
void * websocket_manager_user_data) {
(void)(request);
(void)(websocket_manager_user_data);
y_log_message(Y_LOG_LEVEL_DEBUG, "Opening websocket_manager_file_callback");
for (;;) {
sleep(1);
if (websocket_manager == NULL || !websocket_manager->connected) {
break;
}
}
y_log_message(Y_LOG_LEVEL_DEBUG, "Closing websocket_manager_file_callback");
}
/**
* websocket_incoming_message_callback
* Read incoming message and prints it on the console
*/
void websocket_incoming_message_callback (const struct _u_request * request,
struct _websocket_manager * websocket_manager,
const struct _websocket_message * last_message,
void * websocket_incoming_message_user_data) {
(void)(request);
(void)(websocket_manager);
if (websocket_incoming_message_user_data != NULL) {
y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_incoming_message_user_data is %s", websocket_incoming_message_user_data);
}
y_log_message(Y_LOG_LEVEL_DEBUG, "Incoming message, rsv: 0x%02x, opcode: 0x%02x, mask: %d, len: %zu", last_message->rsv, last_message->opcode, last_message->has_mask, last_message->data_len);
if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) {
y_log_message(Y_LOG_LEVEL_DEBUG, "text payload '%.*s'", (int)last_message->data_len, last_message->data);
} else if (last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
y_log_message(Y_LOG_LEVEL_DEBUG, "binary payload");
}
}
void websocket_echo_message_callback (const struct _u_request * request,
struct _websocket_manager * websocket_manager,
const struct _websocket_message * last_message,
void * websocket_incoming_message_user_data) {
(void)(request);
(void)(websocket_incoming_message_user_data);
y_log_message(Y_LOG_LEVEL_DEBUG, "Incoming message, rsv: 0x%02x, opcode: 0x%02x, mask: %d, len: %zu, text payload '%.*s'", last_message->rsv, last_message->opcode, last_message->has_mask, last_message->data_len, last_message->data_len, last_message->data);
if (ulfius_websocket_send_message(websocket_manager, last_message->opcode, last_message->data_len, last_message->data) != U_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_websocket_send_message");
}
}
void websocket_incoming_file_callback (const struct _u_request * request,
struct _websocket_manager * websocket_manager,
const struct _websocket_message * last_message,
void * websocket_incoming_message_user_data) {
(void)(request);
(void)(websocket_incoming_message_user_data);
char * my_message = msprintf("Incoming file %p, rsv: 0x%02x, opcode: 0x%02x, mask: %d, len: %zu", last_message, last_message->rsv, last_message->opcode, last_message->has_mask, last_message->data_len);
y_log_message(Y_LOG_LEVEL_DEBUG, my_message);
ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(my_message), my_message);
o_free(my_message);
}
/**
* Ulfius main callback function that simply calls the websocket manager and closes
*/
int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data) {
char * websocket_user_data = o_strdup("my_user_data");
int ret;
(void)(request);
(void)(user_data);
if ((ret = ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_callback, websocket_user_data, &websocket_incoming_message_callback, websocket_user_data, &websocket_onclose_callback, websocket_user_data)) == U_OK) {
ulfius_add_websocket_deflate_extension(response);
return U_CALLBACK_CONTINUE;
} else {
return U_CALLBACK_ERROR;
}
}
int callback_websocket_echo (const struct _u_request * request, struct _u_response * response, void * user_data) {
char * websocket_user_data = o_strdup("my_user_data");
int ret;
(void)(request);
(void)(user_data);
y_log_message(Y_LOG_LEVEL_DEBUG, "Client connected to echo websocket");
if ((ret = ulfius_set_websocket_response(response, NULL, NULL, NULL, NULL, &websocket_echo_message_callback, websocket_user_data, &websocket_onclose_callback, websocket_user_data)) == U_OK) {
ulfius_add_websocket_deflate_extension(response);
return U_CALLBACK_CONTINUE;
} else {
return U_CALLBACK_ERROR;
}
}
int callback_websocket_file (const struct _u_request * request, struct _u_response * response, void * user_data) {
int ret;
(void)(request);
(void)(user_data);
if ((ret = ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_file_callback, NULL, &websocket_incoming_file_callback, NULL, &websocket_onclose_file_callback, NULL)) == U_OK) {
ulfius_add_websocket_deflate_extension(response);
return U_CALLBACK_CONTINUE;
} else {
return U_CALLBACK_ERROR;
}
}
#endif