catalinux / Conn (public) (License: LGPLv2) (since 2016-03-01) (hash sha1)
Net library for easy building ipv4/ipv6 network daemons/clients
List of commits:
Subject Hash Author Date (UTC)
wsdemo added 2baae01f2409496f990b6d673013b66286f0a5af Catalin(ux) M. BOIE 2018-04-02 21:41:40
Various small stuff 0c551268be734dd68a59426315abfc6ef776c60a Catalin(ux) M. BOIE 2018-04-02 21:40:49
More debugging for websocket parsing 66ccde632b280f1aa450610e6b671464b7e56451 Catalin(ux) M. BOIE 2018-04-02 21:39:23
Raise to 4096 to buffer for logging a59dee1fdfe087ff13e4c7012fe29f8da99ad75c Catalin(ux) M. BOIE 2018-04-02 21:38:11
Bump version 9b635cebfffdd6971eefef0ebad395e21684bb8a Catalin(ux) M. BOIE 2018-01-13 18:07:12
Added some more compile flags and websocket1 helper files. fce47295946106956fb70da77244d77ffca09423 Catalin(ux) M. BOIE 2018-01-13 18:03:46
Lots of changes everywhere 91b9113e8f92db07c079c005273683f2e868910c Catalin(ux) M. BOIE 2018-01-12 19:06:04
Require openssl (for websocket) b4b34eb88f38bfd421187da76611cc54476d7309 Catalin(ux) M. BOIE 2017-12-30 23:44:50
Very important fixes 01e33f06a5cdc52fb3795158fd838fcca7055dda Catalin(ux) M. BOIE 2017-12-30 23:39:17
Bump version to 1.0.37 54e8f3bcaf7f7e096c454563039a545a4abb1bf0 Catalin(ux) M. BOIE 2017-12-30 22:13:12
Lots of small fixes 4894de0472e571b8c78294de527bf70089096545 Catalin(ux) M. BOIE 2017-12-30 22:11:09
Checkpoint b6bf45330f046da40d762b6fd10d1bb97bc40036 Catalin(ux) M. BOIE 2017-12-28 00:13:09
Fixed libConn*.so instalation. df1bef9190d30dd11fcd150b41810ce18278b74f Catalin(ux) M. BOIE 2015-04-25 14:43:37
libConn1.so was not included in spec file. dd99625a7fb70d66fa5a91371fb4d2eaf7a4f23d Catalin(ux) M. BOIE 2015-04-25 14:39:32
Small fixes for build system. ab12cbbe149933cbf1b9a9900be1a45d63ee0b29 Catalin(ux) M. BOIE 2015-04-25 14:17:14
Build fixes e7521468199d4da53461fb0c1ffa08e1913c1e9f Catalin(ux) M. BOIE 2015-04-24 19:18:42
Checkpoint before switching to processes not threads de43b387557dde215ab1210838d396e7e7b22c4f Catalin(ux) M. BOIE 2015-01-14 04:13:00
Wpools work now\! f875d6bea1777c3a290bf9bb1aa047f26c935a63 Catalin(ux) M. BOIE 2013-11-13 20:51:13
WIP 1d246f2130d4acf8c267e82051b250a623da6870 Catalin(ux) M. BOIE 2013-10-16 19:57:04
WIP 6387026db3ce7983e610887565a282f4124d4092 Catalin(ux) M. BOIE 2013-10-14 20:06:49
Commit 2baae01f2409496f990b6d673013b66286f0a5af - wsdemo added
Author: Catalin(ux) M. BOIE
Author date (UTC): 2018-04-02 21:41
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2018-04-02 21:41
Parent(s): 0c551268be734dd68a59426315abfc6ef776c60a
Signing key:
Tree: b42555dc4f7c49b3808c700c46959d0ca32d4776
File Lines added Lines deleted
examples/Makefile 4 1
examples/wsdemo.c 722 0
examples/wsdemo.data/index.html 35 0
examples/wsdemo.data/main.css 51 0
examples/wsdemo.data/main.js 18 32
examples/wsdemo.data/wsdemo.nginx 8 8
examples/wsdemo.data/wsdemo.service 2 2
File examples/Makefile changed (mode: 100644) (index 4863db2..6574257)
... ... include ../Makefile.include
3 3 TARGETS := wpool1 wpool2 \ TARGETS := wpool1 wpool2 \
4 4 s c raw udp_s timeout trigger reconnect ntime blackhole_s blackhole_c \ s c raw udp_s timeout trigger reconnect ntime blackhole_s blackhole_c \
5 5 xbind xbind
6 TARGETS := wpool2 ws1 websocket1
6 TARGETS := wpool2 ws1 websocket1 wsdemo
7 7
8 8 all: $(TARGETS) all: $(TARGETS)
9 9 build: $(TARGETS) build: $(TARGETS)
 
... ... ws1: ws1.c $(DEPS)
65 65 websocket1: websocket1.c $(DEPS) websocket1: websocket1.c $(DEPS)
66 66 gcc $(CFLAGS) -I/usr/include/json-c $(INCS) $@.c -o $@ $(LIBS) -ljson-c gcc $(CFLAGS) -I/usr/include/json-c $(INCS) $@.c -o $@ $(LIBS) -ljson-c
67 67
68 wsdemo: wsdemo.c $(DEPS)
69 gcc $(CFLAGS) -I/usr/include/json-c $(INCS) $@.c -o $@ $(LIBS) -ljson-c
70
68 71 ma: ma.c ma: ma.c
69 72 gcc $(CFLAGS) $(INCS) $@.c -o $@ -lpthread gcc $(CFLAGS) $(INCS) $@.c -o $@ -lpthread
70 73
File examples/wsdemo.c added (mode: 100644) (index 0000000..a047725)
1 /*
2 * A websocket server - demo
3 */
4
5 #define _GNU_SOURCE
6
7 #include <errno.h>
8 #include <string.h>
9 #include <strings.h>
10 #include <unistd.h>
11 #include <netinet/in.h>
12 #include <sys/time.h>
13 #include <sys/ioctl.h>
14 #include <time.h>
15 #include <stdarg.h>
16
17 #include <Conn.h>
18 #include <json.h>
19
20 /* Global variables */
21 static unsigned short debug = 10; // recommended 1 for production
22
23 // Private data per client
24 struct priv {
25 unsigned int x, y; // screen size
26
27 unsigned int main_init:1;
28 unsigned int progress_bar1_init:1;
29 unsigned int load1_init:1;
30 unsigned int arc1_init:1;
31 unsigned int band1_init:1;
32
33 unsigned char progress_bar1_percent;
34
35 unsigned char arc1_count;
36
37 };
38
39 /* load is global */
40 static unsigned char load_current;
41 static unsigned char load[100];
42
43 struct Clients
44 {
45 struct Conn *C;
46 struct Clients *next;
47 };
48 static struct Clients *Clients_head, *Clients_tail;
49
50
51 static void push_code(struct Conn *C, const char *code)
52 {
53 const char *s;
54 struct json_object *j;
55
56 j = json_object_new_object();
57 if (!j) {
58 Log(0, "\tCannot alloc memory for json!\n");
59 Conn_close(C);
60 return;
61 }
62
63 json_object_object_add(j, "op", json_object_new_string("push"));
64 json_object_object_add(j, "obj", json_object_new_string("exec"));
65 json_object_object_add(j, "code", json_object_new_string(code));
66 s = json_object_to_json_string(j);
67 Conn_web_ws_enqueue(C, 1, 1, s, strlen(s));
68 json_object_put(j);
69 }
70
71 static void notify(struct Conn *C, const char *format, ...)
72 {
73 char line[512];
74 va_list ap;
75 const char *s;
76 struct json_object *j;
77
78 va_start(ap, format);
79 vsnprintf(line, sizeof(line), format, ap);
80 va_end(ap);
81
82 j = json_object_new_object();
83 if (!j) {
84 Log(0, "\tCannot alloc memory for json!\n");
85 Conn_close(C);
86 return;
87 }
88
89 json_object_object_add(j, "op", json_object_new_string("notify"));
90 json_object_object_add(j, "from", json_object_new_string("wsdemo"));
91 json_object_object_add(j, "msg", json_object_new_string(line));
92 s = json_object_to_json_string(j);
93 Conn_web_ws_enqueue(C, 1, 1, s, strlen(s));
94 json_object_put(j);
95 }
96
97 static void progress_bar1(struct Conn *C)
98 {
99 struct priv *p;
100 char code[256];
101
102 p = Conn_get_private(C);
103 if (!p->progress_bar1_init) {
104 const char *code =
105 "window.wsdemo.progress_bar1_update = function(a) {\n"
106 " d = document.getElementById('progress_bar1');\n"
107 " d.style.width = String(a) + 'px';\n"
108 "}\n";
109 push_code(C, code);
110 p->progress_bar1_init = 1;
111 }
112
113 snprintf(code, sizeof(code),
114 "window.wsdemo.progress_bar1_update(%hhu)",
115 p->progress_bar1_percent);
116 p->progress_bar1_percent = (p->progress_bar1_percent + 1) % 100;
117 push_code(C, code);
118 }
119
120 static void progress_bar2(struct Conn *C)
121 {
122 static unsigned char percent = 0;
123 char code[512];
124
125 snprintf(code, sizeof(code),
126 "d = document.getElementById('progress_bar2')"
127 "; d.style.width = String(%hhu) + 'px'",
128 percent);
129 percent += 5;
130 percent %= 100;
131
132 push_code(C, code);
133 }
134
135 static void load1(struct Conn *C)
136 {
137 struct priv *p;
138 char code[256];
139 unsigned int slots;
140
141 p = Conn_get_private(C);
142
143 if (!p->load1_init) {
144 const char *code =
145 "window.wsdemo.load1_update = function(id, slots, width, space, load) {\n"
146 " var svgns = \"http://www.w3.org/2000/svg\";\n"
147 ""
148 " d = document.getElementById('load1_container');\n"
149 " d.setAttribute('width', slots);\n"
150 ""
151 " d = document.getElementById(id);\n"
152 ""
153 " // Removing first rects if we have too many\n"
154 " while (d.children.length >= slots)\n"
155 " d.removeChild(d.children[0]);\n"
156 ""
157 " // Creating a new rectangle\n"
158 " var rect = document.createElementNS(svgns, 'rect');\n"
159 " rect.setAttributeNS(null, 'y', 90 - load);\n"
160 " rect.setAttributeNS(null, 'height', load);\n"
161 " rect.setAttributeNS(null, 'width', width - space);\n"
162 " rect.setAttributeNS(null, 'fill', 'red');\n"
163 " d.appendChild(rect);\n"
164 ""
165 " // Set correct positions\n"
166 " len = d.children.length;\n"
167 " for (i = 0; i < len; i++) {\n"
168 " d.children[i].setAttributeNS(null, 'x', slots - len + i);\n"
169 " }\n"
170 ""
171 " // Set text\n"
172 " var div = document.getElementById(id + '_text');\n"
173 " div.innerHTML = 'Load: ' + load;\n"
174 "}\n";
175 push_code(C, code);
176 p->load1_init = 1;
177 // TODO: send previous values
178 }
179
180 // Send current value (id, slots, width, space, last_load)
181 slots = p->x - 40 > 100 ? 100 : p->x - 40;
182 snprintf(code, sizeof(code),
183 "window.wsdemo.load1_update('load1', %u, 1, 0.2, %hhu);\n",
184 slots, load[load_current]);
185 push_code(C, code);
186 }
187
188 static void band1(struct Conn *C)
189 {
190 struct priv *p;
191 char code[256];
192 static int up = 30, down = 30;
193 int sign;
194 unsigned int slots;
195
196 p = Conn_get_private(C);
197 if (!p->band1_init) {
198 const char *code =
199 "window.wsdemo.band1_update = function (slots, up, down) {\n"
200 " var svgns = 'http://www.w3.org/2000/svg';\n"
201 ""
202 " d = document.getElementById('band1_container');\n"
203 " console.log('DEBUG: band1_container width=' + slots);\n"
204 " d.setAttribute('width', slots);\n"
205 ""
206 " d = document.getElementById('band1_line');\n"
207 " d.setAttribute('x2', slots);\n"
208 ""
209 " d = document.getElementById('band1_text_up');\n"
210 " d.innerHTML = 'Up: ' + up + 'mbit/s';\n"
211 ""
212 " d = document.getElementById('band1_text_down');\n"
213 " d.innerHTML = 'Down: ' + down + 'mbit/s';\n"
214 ""
215 " d = document.getElementById('band1');\n"
216 " while (d.children.length >= 2 * (slots - 1)) {\n"
217 " d.removeChild(d.children[0]);\n"
218 " d.removeChild(d.children[0]);\n"
219 " }\n"
220 ""
221 " // Creating new lines\n"
222 " for (j = 0; j < 2; j++) {\n"
223 " var line = document.createElementNS(svgns, 'line');\n"
224 " var color = j == 0 ? 'green' : 'red';\n"
225 " var sign = j == 0 ? 1 : -1;\n"
226 " var v = j == 0 ? up : down;\n"
227 " var y1 = j == 0 ? 49 : 51;\n"
228 " var y2 = j == 0 ? 49 - v : 51 + v;\n"
229 " line.setAttributeNS(null, 'y1', y1);\n"
230 " line.setAttributeNS(null, 'y2', y2);\n"
231 " line.setAttributeNS(null, 'stroke', color);\n"
232 " d.appendChild(line);\n"
233 " }\n"
234 ""
235 " // Set correct positions\n"
236 " len = d.children.length;\n"
237 " for (i = 0; i < len; i += 2) {\n"
238 " d.children[i + 0].setAttributeNS(null, 'x1', slots - len / 2 + i / 2);\n"
239 " d.children[i + 0].setAttributeNS(null, 'x2', slots - len / 2 + i / 2);\n"
240 " d.children[i + 1].setAttributeNS(null, 'x1', slots - len / 2 + i / 2);\n"
241 " d.children[i + 1].setAttributeNS(null, 'x2', slots - len / 2 + i / 2);\n"
242 " }\n"
243 "}\n";
244 push_code(C, code);
245 p->band1_init = 1;
246 }
247
248 sign = random() % 2 == 0 ? -1 : 1;
249 up += (random() % 10) * sign;
250 if (up < 0)
251 up = 0;
252 if (up > 40)
253 up = 40;
254
255 sign = random() % 2 == 0 ? -1 : 1;
256 down += (random() % 10) * sign;
257 if (down < 0)
258 down = 0;
259 if (down > 40)
260 down = 40;
261
262 slots = p->x - 40 > 200 ? 200 : p->x - 40;
263 snprintf(code, sizeof(code),
264 "window.wsdemo.band1_update(%u, %u, %u)",
265 slots, up, down);
266 push_code(C, code);
267 }
268
269 static void arc1(struct Conn *C)
270 {
271 struct priv *p;
272 char code[4096];
273 char *names[] = { "Option 1", "Option 2", "Option 3", "Option 4", "Option 5" };
274 char vars[512], *add;
275 unsigned char nr, i;
276
277 p = Conn_get_private(C);
278 if (!p->arc1_init) {
279 snprintf(code, sizeof(code),
280 "window.wsdemo.arc1_update = function(a) {\n"
281 " // compute sum\n"
282 " var sum = 0;\n"
283 ""
284 " for (i = 0; i < a.length; i++)\n"
285 " sum += a[i].value;\n"
286 ""
287 " d = document.getElementById('arc1');\n"
288 " t = document.getElementById('arc1_text');\n"
289 ""
290 " // TODO: we must respect first point to allow user to position the elements!\n"
291 " last_x = 10; last_y = 50; last_angle = Math.PI;\n"
292 " for (i = 0; i < a.length; i++) {\n"
293 " //console.log('i=' + i + ' last_x=' + last_x + ' last_y=' + last_y + ' last_angle=' + last_angle + ' value=' + a[i].value + ' per=' + a[i].value * 100 / sum + '%%');\n"
294 ""
295 " m = d.children[i].pathSegList[0];\n"
296 " m.x = last_x; m.y = last_y;\n"
297 ""
298 " c = d.children[i].pathSegList[1];\n"
299 " angle = a[i].value * 2 * Math.PI / sum;\n"
300 " //console.log('angle[' + i + ']=' + angle);\n"
301 " if (angle > Math.PI) {\n"
302 " c.largeArcFlag = true;\n"
303 " } else {\n"
304 " c.largeArcFlag = false;\n"
305 " }\n"
306 " c.x = (50 - last_x) + 40 * Math.cos(last_angle + angle);\n"
307 " c.y = (50 - last_y) + 40 * Math.sin(last_angle + angle);\n"
308 " //console.log('c.x/y[' + i + ']=' + c.x + '/' + c.y);\n"
309 " last_x += c.x; last_y += c.y; last_angle += angle;\n"
310 " t.children[i].innerHTML = a[i].name + ': ' + parseInt(a[i].value * 100 / sum) + '%%';\n"
311 " }\n"
312 " for (; i < 5; i++) {\n"
313 " c = d.children[i].pathSegList[1];\n"
314 " c.x = 0; c.y = 0;\n"
315 " t.children[i].innerHTML = '';\n"
316 " }\n"
317 "}");
318 push_code(C, code);
319 p->arc1_init = 1;
320 p->arc1_count = 4;
321 }
322
323 p->arc1_count++;
324 if (p->arc1_count < 5)
325 return;
326
327 strcpy(vars, "[ "); add = "";
328 nr = 3 + random() % 3;
329 for (i = 0; i < nr; i++) {
330 char tmp[64];
331 unsigned int v;
332
333 v = 300 + (unsigned int) random() % 10000;
334 snprintf(tmp, sizeof(tmp), "%s{ name:\"%s\", value:%u }",
335 add, names[i], v);
336 strcat(vars, tmp);
337 add = ", ";
338 }
339 strcat(vars, "]");
340
341 snprintf(code, sizeof(code), "window.wsdemo.arc1_update(%s)", vars);
342 push_code(C, code);
343 p->arc1_count = 0;
344 }
345
346 static void trigger(struct Conn *C)
347 {
348 struct priv *p;
349 double avg;
350
351 Log(0, "%llu %s from %s/%d\n", Conn_get_id(C), __func__,
352 Conn_addr_remote(C), Conn_port_remote(C));
353
354 p = Conn_get_private(C);
355 if (p->main_init == 0) {
356 // Here goes some init stuff
357 push_code(C, "window.wsdemo = [];\n"
358 "window.wsdemo.my_resize = function(e) {\n"
359 " var data = {\n"
360 " op: 'resize',\n"
361 " x: window.innerWidth,\n"
362 " y: window.innerHeight\n"
363 " };\n"
364 " ws.send(JSON.stringify(data));\n"
365 "}\n"
366 "window.addEventListener('resize', window.wsdemo.my_resize, true);\n"
367 "window.wsdemo.my_resize();\n");
368 p->main_init = 1;
369 }
370
371 /* This must be global, not per connection */
372 getloadavg(&avg, 1); avg *= 100;
373 load_current = (load_current + 1) % 100;
374 load[++load_current] = avg;
375
376 // We wait for client to send the screen size
377 if (!p->x)
378 return;
379
380 progress_bar1(C);
381 progress_bar2(C);
382 load1(C);
383 band1(C);
384 arc1(C);
385 }
386
387 static const char main_screen[] =
388 "<div class=\"gem\" width=\"120px\">\n"
389 "Progress bar 1 (div)\n"
390 "<div style=\"margin: 3px; height: 10px; width: 100px; background-color: #eeeeee; border: 1px solid\">\n"
391 " <div id=\"progress_bar1\" style=\"height: 10px; width: 0px; background-color: red\"></div>\n"
392 "</div>\n"
393 "</div>\n"
394 "\n"
395 "<div class=\"gem\" width=\"120px\">\n"
396 "Progress bar 2 (div)\n"
397 "<div style=\"margin: 3px; height: 10px; width: 100px; background-color: #eeeeee; border: 1px solid\">\n"
398 " <div id=\"progress_bar2\" style=\"height: 10px; width: 0px; background-color: red\"></div>\n"
399 "</div>\n"
400 "</div>\n"
401 "\n"
402 "<div class=\"gem\" width=\"120px\">\n"
403 "Server load (SVG)\n"
404 "<svg id=\"load1_container\" height=\"102\" width=\"102\" style=\"border: 1px solid green\">\n"
405 " <g id=\"load1\"></g>\n"
406 " <text id=\"load1_text\" x=\"2\" y=\"99\" style=\"font-size: 10px\">Load here</text>\n"
407 "</svg>\n"
408 "<span class=\"note\">Note: the width of the SVG is scaled based on available width.</span>\n"
409 "</div>\n"
410 "\n"
411 "<div class=\"gem\" width=\"420px\">\n"
412 "Bandwidth (SVG)\n"
413 "<svg id=\"band1_container\" height=\"102\" width=\"100\" style=\"border: 1px solid green\">\n"
414 " <g id=\"band1\"></g>\n"
415 " <line id=\"band1_line\" x1=\"0\" x2=\"100\" y1=\"50\" y2=\"50\" style=\"stroke: blue\"/>\n"
416 " <text id=\"band1_text_up\" x=\"1\" y=\"7\" style=\"font-size: 5pt\"></text>\n"
417 " <text id=\"band1_text_down\" x=\"1\" y=\"100\" style=\"font-size: 5pt\"></text>\n"
418 "</svg>\n"
419 "<span class=\"note\">Note: the width of the SVG is scaled based on available width.</span>\n"
420 "</div>\n"
421 "\n"
422 "<div class=\"gem\" width=\"420px\">\n"
423 "Arc 1 (SVG)\n"
424 "<svg height=\"100\" width=\"100\" style=\"border: 1px solid green\">\n"
425 "<defs>\n"
426 " <filter id=\"ds1\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">\n"
427 " <feGaussianBlur result=\"blur\" in=\"SourceAlpha\" stdDeviation=\"2\" />\n"
428 " </filter>\n"
429 "</defs>\n"
430 " <circle r=\"40\" cx=\"50\" cy=\"50\" fill=\"transparent\" stroke=\"black\" stroke-width=\"10\" filter=\"url(#ds1)\"/>\n"
431 " <g id=\"arc1\">\n"
432 " <path d=\"M 10 50 a 40 40 0 0 1 0 0\" fill=\"transparent\" stroke=\"#f00\" stroke-width=\"10\"/>\n"
433 " <path d=\"M 10 50 a 40 40 0 0 1 0 0\" fill=\"transparent\" stroke=\"#00f\" stroke-width=\"10\"\"/>\n"
434 " <path d=\"M 10 50 a 40 40 0 0 1 0 0\" fill=\"transparent\" stroke=\"#f0f\" stroke-width=\"10\"\"/>\n"
435 " <path d=\"M 10 50 a 40 40 0 0 1 0 0\" fill=\"transparent\" stroke=\"#909\" stroke-width=\"10\"\"/>\n"
436 " <path d=\"M 10 50 a 40 40 0 0 1 0 0\" fill=\"transparent\" stroke=\"#770\" stroke-width=\"10\"\"/>\n"
437 " </g>\n"
438 " <g id=\"arc1_text\" style=\"font-size: 4pt\">\n"
439 " <text x=\"30\" y=\"32\" fill=\"#f00\"></text>\n"
440 " <text x=\"30\" y=\"42\" fill=\"#00f\"></text>\n"
441 " <text x=\"30\" y=\"52\" fill=\"#f0f\"></text>\n"
442 " <text x=\"30\" y=\"62\" fill=\"#909\"></text>\n"
443 " <text x=\"30\" y=\"72\" fill=\"#990\"></text>\n"
444 " </g>\n"
445 "</svg>\n"
446 "</div>\n";
447 // A r1 r2 angle largeArcFlag sweepFlag x y
448
449 static void accept_cb(struct Conn *C)
450 {
451 struct Clients *q;
452 struct priv *priv;
453
454 Log(1, "%llu %s from %s/%d\n", Conn_get_id(C), __func__,
455 Conn_addr_remote(C), Conn_port_remote(C));
456
457 priv = malloc(sizeof(struct priv));
458 if (!priv) {
459 Log(0, "\tCannot alloc memory for private area!\n");
460 Conn_close(C);
461 return;
462 }
463 memset(priv, 0, sizeof(struct priv));
464 Conn_set_private(C, priv);
465
466 q = malloc(sizeof(struct Clients));
467 if (!q) {
468 Log(0, "\tCannot alloc memory for Clients!\n");
469 Conn_close(C);
470 return;
471 }
472
473 q->C = C;
474 q->next = NULL;
475
476 if (Clients_head)
477 Clients_tail->next = q;
478 else
479 Clients_head = q;
480 Clients_tail = q;
481
482 /* TODO: Arm a trigger with 1 second interval (not working) */
483 Conn_set_cb(C, CONN_CB_TRIGGER, trigger);
484 Conn_set(C, CONN_PARA_TRIGGER, 1);
485 }
486
487 static void close_cb(struct Conn *C)
488 {
489 struct Clients *q, *p;
490
491 Log(0, "%llu %s Closing...\n", Conn_get_id(C), __func__);
492
493 if (C == Clients_head->C) {
494 p = Clients_head;
495 Clients_head = Clients_head->next;
496 free(p);
497 return;
498 }
499
500 q = Clients_head;
501 while (q->next) {
502 if (q->next->C == C) {
503 p = q->next;
504 q->next = q->next->next;
505 free(p);
506 if (!q->next)
507 Clients_tail = q;
508 break;
509 }
510
511 q = q->next;
512 }
513 }
514
515 static void process_json(struct Conn *C, struct json_object *j)
516 {
517 const char *s_op, *s_answer, *s_text;
518 struct json_object *op, *answer, *text;
519 struct priv *priv;
520
521 Log(10, "%llu %s\n", Conn_get_id(C), __func__);
522
523 priv = Conn_get_private(C);
524
525 Log(10, "json:\n%s\n", json_object_to_json_string_ext(j,
526 JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY));
527
528 json_object_object_get_ex(j, "op", &op);
529 s_op = json_object_get_string(op);
530 if (!s_op) {
531 Log(0, "\tNo s_op! Ignore it!\n");
532 return;
533 }
534 Log(10, "\ts_op=[%s]\n", s_op);
535
536 answer = json_object_new_object();
537 if (!answer) {
538 Log(0, "\tCannot alloc memory for answer!\n");
539 Conn_close(C);
540 return;
541 }
542
543 if (strcmp(s_op, "init") == 0) {
544 json_object_object_get_ex(j, "version", &text);
545 s_text = json_object_get_string(text);
546
547 Log(10, "\tWe have an init command [version=%s]!\n", s_text);
548 json_object_object_add(answer, "op",
549 json_object_new_string("init"));
550 json_object_object_add(answer, "id",
551 json_object_new_int64(Conn_get_id(C)));
552 json_object_object_add(answer, "html",
553 json_object_new_string(main_screen));
554 s_answer = json_object_to_json_string(answer);
555 Conn_web_ws_enqueue(C, 1, 1, s_answer, strlen(s_answer));
556 json_object_put(answer);
557 return;
558 }
559
560 if (strcmp(s_op, "ka") == 0) {
561 Log(10, "\tWe have a ka!\n");
562 json_object_object_add(answer, "op",
563 json_object_new_string("ka"));
564 s_answer = json_object_to_json_string(answer);
565 Conn_web_ws_enqueue(C, 1, 1, s_answer, strlen(s_answer));
566 json_object_put(answer);
567 trigger(C);
568 return;
569 }
570
571 if (strcmp(s_op, "resize") == 0) {
572 json_object_object_get_ex(j, "x", &text);
573 s_text = json_object_get_string(text);
574 priv->x = strtoul(s_text, NULL, 10);
575
576 json_object_object_get_ex(j, "y", &text);
577 s_text = json_object_get_string(text);
578 priv->y = strtoul(s_text, NULL, 10);
579
580 Log(10, "\tWe have a resize event [x=%u y=%u]!\n",
581 priv->x, priv->y);
582
583 notify(C, "Screen resized to %u/%u", priv->x, priv->y);
584 return;
585 }
586
587 Log(0, "\tInvalid operation [%s]!\n", s_op);
588 }
589
590 static void ws1(struct Conn *C)
591 {
592 char *dump, ip[64];
593 int r;
594 struct Conn_web_ws w;
595 struct json_object *json;
596
597 Log(5, "%llu %s\n", Conn_get_id(C), __func__);
598
599 if (!Conn_web_is_ws(C)) {
600 // Last chanse to extract something from header
601 Conn_web_header_lookup(ip, sizeof(ip), C, "X-Original-IP");
602 Log(0, "IP=%s\n", ip);
603
604 Conn_web_ws_negociate(C);
605 return;
606 }
607
608 Log(5, "\tWe already have an established ws connection\n");
609 again:
610 r = Conn_web_ws_parse(&w, C);
611 if (r == -1) {
612 Log(0, "\tProtocol error: %s!\n", Conn_strerror());
613 Conn_close(C);
614 return;
615 }
616 if (r == 0) {
617 Log(0, "\tNot enough data.\n");
618 return;
619 }
620
621 if (debug > 10)
622 Conn_web_ws_log(&w);
623
624 dump = Conn_dump(Conn_ibuf(C), Conn_iqlen(C));
625 Log(5, "\tReceived: %s\n", dump);
626 free(dump);
627
628 json = json_tokener_parse(Conn_ibuf(C));
629 if (!json) {
630 Log(0, "\tCannot parse json!\n");
631 Conn_close(C);
632 return;
633 }
634 process_json(C, json);
635 json_object_put(json);
636
637 Conn_eat(C, w.len);
638 goto again;
639 }
640
641 int main(void)
642 {
643 int ret;
644 struct Conn *C;
645 char *stats;
646
647 setlinebuf(stdout);
648
649 Conn_debug(2, debug);
650
651 ret = Conn_init(0);
652 if (ret == -1) {
653 printf("%s", Conn_strerror());
654 return 1;
655 }
656
657 printf("Trying to register IPv4 socket...\n");
658 C = Conn_alloc();
659 if (!C) {
660 printf("Cannot alloc socket [%s].\n", Conn_strerror());
661 return 1;
662 }
663
664 Conn_set_socket_domain(C, PF_INET);
665 Conn_set_socket_type(C, SOCK_STREAM);
666 Conn_set_socket_bind_addr(C, "0.0.0.0");
667 Conn_set_socket_bind_port(C, 9060);
668
669
670 /* Create a web server attached to C */
671 ret = Conn_web_create(C);
672 if (ret != 0) {
673 printf("Cannot create web server!\n");
674 return 1;
675 }
676
677 /* When you access http://server/ws, you will be redirected to
678 * function ws1 */
679 ret = Conn_web_script(C, "/ws", ws1);
680 if (ret != 0) {
681 printf("Cannot attach script ws1!\n");
682 return 1;
683 }
684
685 ret = Conn_web_path(C, "/", "/x");
686 if (ret != 0) {
687 printf("Cannot attach path /x!\n");
688 return 1;
689 }
690
691 Conn_set_cb(C, CONN_CB_ACCEPT, accept_cb);
692 Conn_set_cb(C, CONN_CB_CLOSE, close_cb);
693
694 ret = Conn_commit(C);
695 if (ret != 0) {
696 printf("Cannot bind on ipv4 socket [%s].\n",
697 Conn_strerror());
698 return 1;
699 }
700
701 Log(12, "Waiting...\n");
702 while (1) {
703 ret = Conn_poll(1000);
704 if (ret == -1) {
705 printf("Error in poll [%s]!\n",
706 Conn_strerror());
707 break;
708 }
709
710 #if 0
711 if (debug >= 9) {
712 stats = Conn_status(0);
713 printf("%s\n", stats);
714 free(stats);
715 }
716 #endif
717 }
718
719 Conn_shutdown();
720
721 return 0;
722 }
File examples/wsdemo.data/index.html added (mode: 100644) (index 0000000..aefebbc)
1 <!DOCTYPE html>
2 <html>
3
4 <head>
5 <title>Websocket demo - wsdemo</title>
6 <meta charset="utf-8" />
7 <meta name="viewport" content="width=device-width, initial-scale=1" />
8 <script type="text/javascript" src="main.js"></script>
9 <link rel="stylesheet" href="main.css">
10 </head>
11
12 <body onLoad="my_start()">
13
14 <!--
15 // This file is part of Conn project
16 // https://rocketgit.com/user/catalinux/Conn
17 // License: LGPLv3+
18 // Copyright: Catalin(ux) M. BOIE
19 -->
20
21 <div id="welcome">
22 Welcome to <a href="https://rocketgit.com/user/catalinux/Conn" target="_new">Conn</a> wsdemo!
23 </div>
24
25 <div id="my_status">
26 Unknown status
27 </div>
28
29 <div id="big">
30 Loading...
31 </div>
32
33 </body>
34
35 </html>
File examples/wsdemo.data/main.css added (mode: 100644) (index 0000000..3492b8f)
1 body {
2 display: flex;
3 flex-direction: column;
4 font-family: sans;
5 font-size: 12px;
6 margin: 0;
7 }
8
9 #welcome {
10 border: 1px solid black;
11 padding: 5px;
12 margin: 2px;
13
14 flex: 1 100%;
15 }
16
17 #my_status {
18 border: 1px solid black;
19 padding: 5px;
20 margin: 2px;
21
22 flex: 1 100%;
23 }
24
25 #big {
26 border: 1px solid black;
27 margin: 2px;
28
29 flex: 1 100%;
30
31 display: flex;
32 flex-direction: row;
33 flex-wrap: wrap;
34 justify-content: flex-start;
35 align-items: stretch;
36 align-content: flex-start;
37 }
38
39 .gem {
40 padding: 5px;
41 border: 1px solid blue;
42 x-width: 150px;
43 margin: 5px;
44
45 display: flex;
46 flex-direction: column;
47 }
48
49 .note {
50 font-size: 4pt;
51 }
File examples/wsdemo.data/main.js copied from file examples/websocket1.data/main.js (similarity 72%) (mode: 100644) (index 5c6ea44..42d6cfa)
3 3 // License: LGPLv3+ // License: LGPLv3+
4 4 // Copyright: Catalin(ux) M. BOIE // Copyright: Catalin(ux) M. BOIE
5 5
6 // TODO: escape html to avoid injecting scripts in chat
7 // TODO: Will the keepalive do its job? Or should I check if I got an answer for
8 // the keepalive message and re-connect if not received?
9
10 6 var my_ws_url = 'ws://' + window.location.host + '/ws'; var my_ws_url = 'ws://' + window.location.host + '/ws';
11 var version = '1'; // version of the javascript code
12 7 var timer, ka; var timer, ka;
13 8 var ws; var ws;
14 var div_status, div_chat, div_msg, div_name;
9 var div_status, div_big;
15 10 var reconnect_time_ms = 3000; var reconnect_time_ms = 3000;
16 var keep_alive_time_ms = 10000;
11 var keep_alive_time_ms = 1000;
17 12 var notify_perm; var notify_perm;
18 13 var last_msg = ''; var last_msg = '';
19 14
20 function set_name()
21 {
22 var data = {
23 op: 'set_name',
24 name: div_name.value
25 };
26 //console.log('set_name...');
27 ws.send(JSON.stringify(data));
28 }
29
30 15 function send_msg() function send_msg()
31 16 { {
32 17 var data = { var data = {
 
... ... function keep_alive()
50 35
51 36 function reconnect() function reconnect()
52 37 { {
53 div_status.innerHTML = 'Connecting...';
38 div_status.innerHTML = 'Status: connecting...';
54 39
55 40 // We do not want the re-connect timer to trigger now, when // We do not want the re-connect timer to trigger now, when
56 41 // the connection is in progress. // the connection is in progress.
 
... ... function reconnect()
67 52 { {
68 53 var data = { var data = {
69 54 op: 'init', op: 'init',
70 version: version
55 version: 1
71 56 }; };
72 57
73 58 console.log('onopen: sending init...'); console.log('onopen: sending init...');
 
... ... function reconnect()
76 61 // Arm keep-alive timer // Arm keep-alive timer
77 62 ka = setInterval(keep_alive, keep_alive_time_ms); ka = setInterval(keep_alive, keep_alive_time_ms);
78 63
79 div_status.innerHTML = 'Connected';
64 div_status.innerHTML = 'Status: connected';
80 65 } }
81 66
82 67 ws.onerror = function(ev) ws.onerror = function(ev)
83 68 { {
84 69 console.log('onerror: ' + ev.reason); console.log('onerror: ' + ev.reason);
85 div_status.innerHTML = 'Error: ' + ev.reason + '!';
70 div_status.innerHTML = 'Status: error: ' + ev.reason + '!';
86 71 } }
87 72
88 // This is called also when an error occurs
89 73 ws.onclose = function(ev) ws.onclose = function(ev)
90 74 { {
91 75 clearInterval(ka); clearInterval(ka);
 
... ... function reconnect()
93 77 console.log('onclose: [' + ev.reason + '] ' + ev.code); console.log('onclose: [' + ev.reason + '] ' + ev.code);
94 78 timer = setInterval(reconnect, reconnect_time_ms); timer = setInterval(reconnect, reconnect_time_ms);
95 79
96 div_status.innerHTML = 'Connection closed: ['
80 div_status.innerHTML = 'Status: connection closed: ['
97 81 + ev.reason + '] ' + ev.code + '!'; + ev.reason + '] ' + ev.code + '!';
98 82 } }
99 83
 
... ... function reconnect()
108 92 //console.log('got a ka answer from server'); //console.log('got a ka answer from server');
109 93 } else if (j.op == 'init') { } else if (j.op == 'init') {
110 94 //console.log('got answer to init command'); //console.log('got answer to init command');
111 div_status.innerHTML = 'OK (id ' + j.id + ')';
112 } else if (j.op == 'msg') {
113 div_chat.innerHTML += j.from + ': ' + j.msg + '<br />\n';
114 div_chat.scrollTop = div_chat.scrollHeight;
115
95 div_status.innerHTML = 'Status: OK (id ' + j.id + ')';
96 div_big.innerHTML = j.html;
97 } else if (j.op == 'push') {
98 if (j.obj == 'exec') {
99 eval(j.code);
100 } else {
101 console.log('Unknown object!');
102 }
103 } else if (j.op == 'notify') {
116 104 // TODO: This only works on Firefox (mobile and desktop) // TODO: This only works on Firefox (mobile and desktop)
117 105 // TODO: It does not work on Chrome 63 mobile // TODO: It does not work on Chrome 63 mobile
118 106 if (notify_perm == 'granted') { if (notify_perm == 'granted') {
 
... ... function msg_key_event(e)
149 137
150 138 function my_start() function my_start()
151 139 { {
152 div_name = document.getElementById('name');
153 140 div_status = document.getElementById('my_status'); div_status = document.getElementById('my_status');
154 div_chat = document.getElementById('chat');
155 div_msg = document.getElementById('msg');
141 div_big = document.getElementById('big');
156 142
157 143 // Ask permission for notifications (if not already granted) // Ask permission for notifications (if not already granted)
158 144 Notification.requestPermission().then(function(r) { notify_perm = r; }); Notification.requestPermission().then(function(r) { notify_perm = r; });
159 145
160 146 // Add a listener for msg div // Add a listener for msg div
161 div_msg.addEventListener('keydown', msg_key_event, false);
147 //div_msg.addEventListener('keydown', msg_key_event, false);
162 148
163 149 reconnect(); reconnect();
164 150 } }
File examples/wsdemo.data/wsdemo.nginx copied from file examples/websocket1.data/websocket1.nginx (similarity 50%) (mode: 100644) (index a74e231..595a30f)
1 upstream websocket1 {
2 server 127.0.0.1:9021;
1 upstream wsdemo {
2 server 127.0.0.1:9060;
3 3 } }
4 4
5 5 server { server {
6 listen 9050;
7 listen [::]:9050;
6 listen 9051;
7 listen [::]:9051;
8 8
9 root /usr/share/websocket1/root;
9 root /usr/share/wsdemo/root;
10 10
11 access_log /var/log/nginx/websocket1b-access.log;
12 error_log /var/log/nginx/websocket1b-error.log;
11 access_log /var/log/nginx/wsdemo-access.log;
12 error_log /var/log/nginx/wsdemo-error.log;
13 13
14 14 location /ws { location /ws {
15 proxy_pass http://websocket1;
15 proxy_pass http://wsdemo;
16 16 proxy_http_version 1.1; proxy_http_version 1.1;
17 17 proxy_set_header X-Original-IP $remote_addr/$remote_port; proxy_set_header X-Original-IP $remote_addr/$remote_port;
18 18 proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
File examples/wsdemo.data/wsdemo.service copied from file examples/websocket1.data/websocket1.service (similarity 64%) (mode: 100644) (index 8c18579..a8e4058)
1 1 [Unit] [Unit]
2 Description=websocket1 service example
2 Description=wsdemo service file
3 3 After=network.target After=network.target
4 4
5 5 [Service] [Service]
6 ExecStart=/usr/bin/websocket1
6 ExecStart=/usr/bin/wsdemo
7 7 KillSignal=9 KillSignal=9
8 8 Restart=on-failure Restart=on-failure
9 9 RestartSec=5s RestartSec=5s
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/catalinux/Conn

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/catalinux/Conn

Clone this repository using git:
git clone git://git.rocketgit.com/user/catalinux/Conn

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main