iodine 0.4.19 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/spnlock.inc
CHANGED
@@ -22,7 +22,7 @@ spinlock / sync for tasks
|
|
22
22
|
/* nanosleep seems to be the most effective and efficient reschedule */
|
23
23
|
#define reschedule_thread() \
|
24
24
|
{ \
|
25
|
-
|
25
|
+
const struct timespec tm = {.tv_nsec = 1}; \
|
26
26
|
nanosleep(&tm, NULL); \
|
27
27
|
}
|
28
28
|
#define throttle_thread(micosec) \
|
@@ -44,11 +44,11 @@ typedef volatile unsigned char spn_lock_i;
|
|
44
44
|
|
45
45
|
/* C11 Atomics are defined? */
|
46
46
|
#if defined(__ATOMIC_RELAXED)
|
47
|
-
#define SPN_LOCK_BUILTIN(...) __atomic_exchange_n(__VA_ARGS__,
|
47
|
+
#define SPN_LOCK_BUILTIN(...) __atomic_exchange_n(__VA_ARGS__, __ATOMIC_SEQ_CST)
|
48
48
|
/** An atomic addition operation */
|
49
|
-
#define spn_add(...) __atomic_add_fetch(__VA_ARGS__,
|
49
|
+
#define spn_add(...) __atomic_add_fetch(__VA_ARGS__, __ATOMIC_SEQ_CST)
|
50
50
|
/** An atomic subtraction operation */
|
51
|
-
#define spn_sub(...) __atomic_sub_fetch(__VA_ARGS__,
|
51
|
+
#define spn_sub(...) __atomic_sub_fetch(__VA_ARGS__, __ATOMIC_SEQ_CST)
|
52
52
|
|
53
53
|
/* Select the correct compiler builtin method. */
|
54
54
|
#elif defined(__has_builtin)
|
@@ -90,6 +90,7 @@ static inline __attribute__((unused)) int spn_is_locked(spn_lock_i *lock) {
|
|
90
90
|
__asm__ volatile("" ::: "memory");
|
91
91
|
return *lock;
|
92
92
|
}
|
93
|
+
|
93
94
|
/** Busy waits for the lock. */
|
94
95
|
static inline __attribute__((unused)) void spn_lock(spn_lock_i *lock) {
|
95
96
|
while (spn_trylock(lock)) {
|
@@ -97,4 +98,14 @@ static inline __attribute__((unused)) void spn_lock(spn_lock_i *lock) {
|
|
97
98
|
}
|
98
99
|
}
|
99
100
|
|
101
|
+
#if DEBUG_SPINLOCK
|
102
|
+
/** Busy waits for a lock, reports contention. */
|
103
|
+
#define spn_lock(lock) \
|
104
|
+
while (spn_trylock(lock)) { \
|
105
|
+
fprintf(stderr, "INFO: spinlock spin %s:%d\n", __FILE__, __LINE__); \
|
106
|
+
reschedule_thread(); \
|
107
|
+
}
|
108
|
+
#include <stdio.h>
|
109
|
+
#endif /* DEBUG */
|
110
|
+
|
100
111
|
#endif /* H_SPNLOCK_H */
|
@@ -18,6 +18,9 @@ must be implemented by the including file (the callbacks).
|
|
18
18
|
#include <stdint.h>
|
19
19
|
#include <stdlib.h>
|
20
20
|
#include <string.h>
|
21
|
+
#if DEBUG
|
22
|
+
#include <stdio.h>
|
23
|
+
#endif
|
21
24
|
/* *****************************************************************************
|
22
25
|
API - Message Wrapping
|
23
26
|
***************************************************************************** */
|
@@ -145,20 +148,6 @@ void websocket_xmask(void *msg, uint64_t len, uint32_t mask) {
|
|
145
148
|
if (len > 7) {
|
146
149
|
{ /* XOR any unaligned memory (4 byte alignment) */
|
147
150
|
const uintptr_t offset = 4 - ((uintptr_t)msg & 3);
|
148
|
-
// switch (offset) {
|
149
|
-
// case 3:
|
150
|
-
// ((uint8_t *)msg)[2] ^= ((uint8_t *)(&mask))[2];
|
151
|
-
// /* fallthrough */
|
152
|
-
// case 2:
|
153
|
-
// ((uint8_t *)msg)[1] ^= ((uint8_t *)(&mask))[1];
|
154
|
-
// /* fallthrough */
|
155
|
-
// case 1:
|
156
|
-
// ((uint8_t *)msg)[0] ^= ((uint8_t *)(&mask))[0];
|
157
|
-
// /* rotate mask and move pointer to first 4 byte alignment */
|
158
|
-
// mask = (mask << (offset << 3)) | (mask >> ((4 - offset) << 3));
|
159
|
-
// msg = (void *)((uintptr_t)msg + offset);
|
160
|
-
// len -= offset;
|
161
|
-
// }
|
162
151
|
switch (offset) {
|
163
152
|
case 3:
|
164
153
|
((uint8_t *)msg)[2] ^= ((uint8_t *)(&mask))[2];
|
@@ -396,7 +385,7 @@ static uint64_t websocket_server_wrap(void *target, void *msg, uint64_t len,
|
|
396
385
|
static uint64_t websocket_client_wrap(void *target, void *msg, uint64_t len,
|
397
386
|
unsigned char opcode, unsigned char first,
|
398
387
|
unsigned char last, unsigned char rsv) {
|
399
|
-
uint32_t mask = rand()
|
388
|
+
uint32_t mask = rand() | 0x01020408;
|
400
389
|
((uint8_t *)target)[0] = 0 |
|
401
390
|
/* opcode */ (((first ? opcode : 0) & 15)) |
|
402
391
|
/* rsv */ ((rsv & 7) << 4) |
|
@@ -459,8 +448,9 @@ Message unwrapping
|
|
459
448
|
inline static struct websocket_packet_info_s
|
460
449
|
websocket_buffer_peek(void *buffer, uint64_t len) {
|
461
450
|
if (len < 2) {
|
462
|
-
|
463
|
-
|
451
|
+
const struct websocket_packet_info_s info = {0 /* packet */, 2 /* head */,
|
452
|
+
0 /* masked? */};
|
453
|
+
return info;
|
464
454
|
}
|
465
455
|
const uint8_t mask_f = (((uint8_t *)buffer)[1] >> 7) & 1;
|
466
456
|
const uint8_t mask_l = (mask_f << 2);
|
@@ -503,14 +493,16 @@ static uint64_t websocket_consume(void *buffer, uint64_t len, void *udata,
|
|
503
493
|
/* unmask? */
|
504
494
|
if (info.masked) {
|
505
495
|
/* masked */
|
506
|
-
|
496
|
+
uint32_t mask; // = ((uint32_t *)payload)[-1];
|
507
497
|
((uint8_t *)(&mask))[0] = ((uint8_t *)(payload))[-4];
|
508
498
|
((uint8_t *)(&mask))[1] = ((uint8_t *)(payload))[-3];
|
509
499
|
((uint8_t *)(&mask))[2] = ((uint8_t *)(payload))[-2];
|
510
500
|
((uint8_t *)(&mask))[3] = ((uint8_t *)(payload))[-1];
|
511
501
|
websocket_xmask(payload, info.packet_length, mask);
|
512
|
-
} else if (require_masking) {
|
513
|
-
|
502
|
+
} else if (require_masking && info.packet_length) {
|
503
|
+
#if DEBUG
|
504
|
+
fprintf(stderr, "ERROR: Websocket protocol error - unmasked data.\n");
|
505
|
+
#endif
|
514
506
|
websocket_on_protocol_error(udata);
|
515
507
|
}
|
516
508
|
/* call callback */
|
@@ -543,6 +535,10 @@ static uint64_t websocket_consume(void *buffer, uint64_t len, void *udata,
|
|
543
535
|
websocket_on_protocol_pong(udata, payload, info.packet_length);
|
544
536
|
break;
|
545
537
|
default:
|
538
|
+
#if DEBUG
|
539
|
+
fprintf(stderr, "ERROR: Websocket protocol error - unknown opcode %u\n",
|
540
|
+
(unsigned int)(pos[0] & 15));
|
541
|
+
#endif
|
546
542
|
websocket_on_protocol_error(udata);
|
547
543
|
}
|
548
544
|
/* step forward */
|
data/ext/iodine/websockets.c
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
/*
|
2
|
-
copyright: Boaz
|
2
|
+
copyright: Boaz Segev, 2016-2018
|
3
3
|
license: MIT
|
4
4
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
6
6
|
*/
|
7
7
|
#include "spnlock.inc"
|
8
8
|
|
9
|
-
#include "
|
9
|
+
#include "fio_llist.h"
|
10
10
|
#include "fiobj.h"
|
11
11
|
|
12
|
-
#include "
|
12
|
+
#include "fio_base64.h"
|
13
|
+
#include "fio_sha1.h"
|
14
|
+
#include "http.h"
|
15
|
+
#include "http_internal.h"
|
16
|
+
|
13
17
|
#include "pubsub.h"
|
14
|
-
#include "websockets.h"
|
15
18
|
#include <arpa/inet.h>
|
16
19
|
#include <errno.h>
|
17
20
|
#include <stdio.h>
|
@@ -19,6 +22,8 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
19
22
|
#include <string.h>
|
20
23
|
#include <strings.h>
|
21
24
|
|
25
|
+
#include "fio_mem.h"
|
26
|
+
|
22
27
|
#include "websocket_parser.h"
|
23
28
|
|
24
29
|
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
@@ -98,7 +103,7 @@ static void destroy_ws(ws_s *ws);
|
|
98
103
|
/*******************************************************************************
|
99
104
|
The Websocket object (protocol + parser)
|
100
105
|
*/
|
101
|
-
struct
|
106
|
+
struct ws_s {
|
102
107
|
/** The Websocket protocol */
|
103
108
|
protocol_s protocol;
|
104
109
|
/** connection data */
|
@@ -107,19 +112,20 @@ struct Websocket {
|
|
107
112
|
void (*on_message)(ws_s *ws, char *data, size_t size, uint8_t is_text);
|
108
113
|
void (*on_shutdown)(ws_s *ws);
|
109
114
|
void (*on_ready)(ws_s *ws);
|
110
|
-
void (*
|
115
|
+
void (*on_open)(ws_s *ws);
|
116
|
+
void (*on_close)(intptr_t uuid, void *udata);
|
111
117
|
/** Opaque user data. */
|
112
118
|
void *udata;
|
113
119
|
/** The maximum websocket message size */
|
114
120
|
size_t max_msg_size;
|
115
121
|
/** active pub/sub subscriptions */
|
116
|
-
|
122
|
+
fio_ls_s subscriptions;
|
117
123
|
/** socket buffer. */
|
118
124
|
struct buffer_s buffer;
|
119
125
|
/** data length (how much of the buffer actually used). */
|
120
126
|
size_t length;
|
121
127
|
/** message buffer. */
|
122
|
-
|
128
|
+
FIOBJ msg;
|
123
129
|
/** latest text state. */
|
124
130
|
uint8_t is_text;
|
125
131
|
/** websocket connection type. */
|
@@ -135,28 +141,9 @@ char *WEBSOCKET_ID_STR = "websockets";
|
|
135
141
|
Create/Destroy the websocket subscription objects
|
136
142
|
***************************************************************************** */
|
137
143
|
|
138
|
-
typedef struct {
|
139
|
-
fio_list_s node;
|
140
|
-
pubsub_sub_pt sub;
|
141
|
-
} subscription_s;
|
142
|
-
|
143
|
-
static inline subscription_s *create_subscription(ws_s *ws, pubsub_sub_pt sub) {
|
144
|
-
subscription_s *s = malloc(sizeof(*s));
|
145
|
-
s->sub = sub;
|
146
|
-
fio_list_add(&ws->subscriptions, &s->node);
|
147
|
-
return s;
|
148
|
-
}
|
149
|
-
|
150
|
-
static inline void free_subscription(subscription_s *s) {
|
151
|
-
fio_list_remove(&s->node);
|
152
|
-
free(s);
|
153
|
-
}
|
154
|
-
|
155
144
|
static inline void clear_subscriptions(ws_s *ws) {
|
156
|
-
|
157
|
-
|
158
|
-
pubsub_unsubscribe(s->sub);
|
159
|
-
free_subscription(s);
|
145
|
+
while (fio_ls_any(&ws->subscriptions)) {
|
146
|
+
pubsub_unsubscribe(fio_ls_pop(&ws->subscriptions));
|
160
147
|
}
|
161
148
|
}
|
162
149
|
|
@@ -164,17 +151,17 @@ static inline void clear_subscriptions(ws_s *ws) {
|
|
164
151
|
Callbacks - Required functions for websocket_parser.h
|
165
152
|
***************************************************************************** */
|
166
153
|
|
167
|
-
static void websocket_on_unwrapped(void *
|
154
|
+
static void websocket_on_unwrapped(void *ws_p, void *msg, uint64_t len,
|
168
155
|
char first, char last, char text,
|
169
156
|
unsigned char rsv) {
|
170
|
-
ws_s *ws =
|
157
|
+
ws_s *ws = ws_p;
|
171
158
|
if (last && first) {
|
172
159
|
ws->on_message(ws, msg, len, (uint8_t)text);
|
173
160
|
return;
|
174
161
|
}
|
175
162
|
if (first) {
|
176
163
|
ws->is_text = (uint8_t)text;
|
177
|
-
if (ws->msg ==
|
164
|
+
if (ws->msg == FIOBJ_INVALID)
|
178
165
|
ws->msg = fiobj_str_buf(len);
|
179
166
|
fiobj_str_resize(ws->msg, 0);
|
180
167
|
}
|
@@ -186,23 +173,35 @@ static void websocket_on_unwrapped(void *udata, void *msg, uint64_t len,
|
|
186
173
|
|
187
174
|
(void)rsv;
|
188
175
|
}
|
189
|
-
static void websocket_on_protocol_ping(void *
|
190
|
-
ws_s *ws =
|
191
|
-
|
192
|
-
|
193
|
-
|
176
|
+
static void websocket_on_protocol_ping(void *ws_p, void *msg_, uint64_t len) {
|
177
|
+
ws_s *ws = ws_p;
|
178
|
+
if (msg_) {
|
179
|
+
void *buff = malloc(len + 16);
|
180
|
+
len = (((ws_s *)ws)->is_client
|
181
|
+
? websocket_client_wrap(buff, msg_, len, 10, 1, 1, 0)
|
182
|
+
: websocket_server_wrap(buff, msg_, len, 10, 1, 1, 0));
|
183
|
+
sock_write2(.uuid = ws->fd, .buffer = buff, .length = len);
|
184
|
+
} else {
|
185
|
+
if (((ws_s *)ws)->is_client) {
|
186
|
+
sock_write2(.uuid = ws->fd, .buffer = "\x89\x80mask", .length = 2,
|
187
|
+
.dealloc = SOCK_DEALLOC_NOOP);
|
188
|
+
} else {
|
189
|
+
sock_write2(.uuid = ws->fd, .buffer = "\x89\x00", .length = 2,
|
190
|
+
.dealloc = SOCK_DEALLOC_NOOP);
|
191
|
+
}
|
192
|
+
}
|
194
193
|
}
|
195
|
-
static void websocket_on_protocol_pong(void *
|
194
|
+
static void websocket_on_protocol_pong(void *ws_p, void *msg, uint64_t len) {
|
196
195
|
(void)len;
|
197
196
|
(void)msg;
|
198
|
-
(void)
|
197
|
+
(void)ws_p;
|
199
198
|
}
|
200
|
-
static void websocket_on_protocol_close(void *
|
201
|
-
ws_s *ws =
|
199
|
+
static void websocket_on_protocol_close(void *ws_p) {
|
200
|
+
ws_s *ws = ws_p;
|
202
201
|
sock_close(ws->fd);
|
203
202
|
}
|
204
|
-
static void websocket_on_protocol_error(void *
|
205
|
-
ws_s *ws =
|
203
|
+
static void websocket_on_protocol_error(void *ws_p) {
|
204
|
+
ws_s *ws = ws_p;
|
206
205
|
sock_close(ws->fd);
|
207
206
|
}
|
208
207
|
|
@@ -214,8 +213,13 @@ The Websocket Protocol implementation
|
|
214
213
|
|
215
214
|
static void ws_ping(intptr_t fd, protocol_s *ws) {
|
216
215
|
(void)(ws);
|
217
|
-
|
218
|
-
|
216
|
+
if (((ws_s *)ws)->is_client) {
|
217
|
+
sock_write2(.uuid = fd, .buffer = "\x89\x80MASK", .length = 6,
|
218
|
+
.dealloc = SOCK_DEALLOC_NOOP);
|
219
|
+
} else {
|
220
|
+
sock_write2(.uuid = fd, .buffer = "\x89\x00", .length = 2,
|
221
|
+
.dealloc = SOCK_DEALLOC_NOOP);
|
222
|
+
}
|
219
223
|
}
|
220
224
|
|
221
225
|
static void on_close(intptr_t uuid, protocol_s *_ws) {
|
@@ -229,20 +233,19 @@ static void on_ready(intptr_t fduuid, protocol_s *ws) {
|
|
229
233
|
((ws_s *)ws)->on_ready((ws_s *)ws);
|
230
234
|
}
|
231
235
|
|
232
|
-
// static void on_open(intptr_t fd, protocol_s *ws, void *callback) {
|
233
|
-
// (void)(fd);
|
234
|
-
// if (callback && ws && ws->service == WEBSOCKET_ID_STR)
|
235
|
-
// ((void (*)(void *))callback)(ws);
|
236
|
-
// }
|
237
|
-
|
238
236
|
static void on_shutdown(intptr_t fd, protocol_s *ws) {
|
239
237
|
(void)(fd);
|
240
238
|
if (ws && ((ws_s *)ws)->on_shutdown)
|
241
239
|
((ws_s *)ws)->on_shutdown((ws_s *)ws);
|
240
|
+
if (((ws_s *)ws)->is_client) {
|
241
|
+
sock_write2(.uuid = fd, .buffer = "\x8a\x80MASK", .length = 6,
|
242
|
+
.dealloc = SOCK_DEALLOC_NOOP);
|
243
|
+
} else {
|
244
|
+
sock_write2(.uuid = fd, .buffer = "\x8a\x00", .length = 2,
|
245
|
+
.dealloc = SOCK_DEALLOC_NOOP);
|
246
|
+
}
|
242
247
|
}
|
243
248
|
|
244
|
-
/************** new implementation */
|
245
|
-
|
246
249
|
static void on_data(intptr_t sockfd, protocol_s *ws_) {
|
247
250
|
ws_s *const ws = (ws_s *)ws_;
|
248
251
|
if (ws == NULL || ws->protocol.service != WEBSOCKET_ID_STR)
|
@@ -257,7 +260,7 @@ static void on_data(intptr_t sockfd, protocol_s *ws_) {
|
|
257
260
|
return;
|
258
261
|
}
|
259
262
|
/* test buffer capacity */
|
260
|
-
if (raw_length
|
263
|
+
if (raw_length > ws->buffer.size) {
|
261
264
|
ws->buffer.size = (size_t)raw_length;
|
262
265
|
ws->buffer = resize_ws_buffer(ws, ws->buffer);
|
263
266
|
if (!ws->buffer.data) {
|
@@ -272,10 +275,25 @@ static void on_data(intptr_t sockfd, protocol_s *ws_) {
|
|
272
275
|
if (len <= 0) {
|
273
276
|
return;
|
274
277
|
}
|
275
|
-
ws->length = websocket_consume(ws->buffer.data, ws->length + len, ws,
|
278
|
+
ws->length = websocket_consume(ws->buffer.data, ws->length + len, ws,
|
279
|
+
(~(ws->is_client) & 1));
|
280
|
+
|
281
|
+
facil_force_event(sockfd, FIO_EVENT_ON_DATA);
|
282
|
+
}
|
283
|
+
|
284
|
+
static void on_data_first(intptr_t sockfd, protocol_s *ws_) {
|
285
|
+
ws_s *const ws = (ws_s *)ws_;
|
286
|
+
if (ws->on_open)
|
287
|
+
ws->on_open(ws);
|
288
|
+
ws->protocol.on_data = on_data;
|
289
|
+
|
290
|
+
if (ws->length)
|
291
|
+
ws->length = websocket_consume(ws->buffer.data, ws->length, ws,
|
292
|
+
(~(ws->is_client) & 1));
|
276
293
|
|
277
294
|
facil_force_event(sockfd, FIO_EVENT_ON_DATA);
|
278
295
|
}
|
296
|
+
|
279
297
|
/* later */
|
280
298
|
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
281
299
|
char first, char last, char client);
|
@@ -284,35 +302,86 @@ static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
|
284
302
|
Create/Destroy the websocket object
|
285
303
|
*/
|
286
304
|
|
287
|
-
static ws_s *new_websocket() {
|
305
|
+
static ws_s *new_websocket(intptr_t uuid) {
|
288
306
|
// allocate the protocol object
|
289
307
|
ws_s *ws = malloc(sizeof(*ws));
|
290
308
|
*ws = (ws_s){
|
291
309
|
.protocol.service = WEBSOCKET_ID_STR,
|
292
310
|
.protocol.ping = ws_ping,
|
293
|
-
.protocol.on_data =
|
311
|
+
.protocol.on_data = on_data_first,
|
294
312
|
.protocol.on_close = on_close,
|
295
313
|
.protocol.on_ready = on_ready,
|
296
314
|
.protocol.on_shutdown = on_shutdown,
|
297
|
-
.subscriptions =
|
315
|
+
.subscriptions = FIO_LS_INIT(ws->subscriptions),
|
298
316
|
.is_client = 0,
|
317
|
+
.fd = uuid,
|
299
318
|
};
|
300
319
|
return ws;
|
301
320
|
}
|
302
321
|
static void destroy_ws(ws_s *ws) {
|
303
|
-
clear_subscriptions(ws);
|
304
322
|
if (ws->on_close)
|
305
|
-
ws->on_close(ws);
|
323
|
+
ws->on_close(ws->fd, ws->udata);
|
306
324
|
if (ws->msg)
|
307
325
|
fiobj_free(ws->msg);
|
326
|
+
clear_subscriptions(ws);
|
308
327
|
free_ws_buffer(ws, ws->buffer);
|
309
328
|
free(ws);
|
310
329
|
}
|
311
330
|
|
331
|
+
void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
|
332
|
+
websocket_settings_s *args, void *data, size_t length) {
|
333
|
+
ws_s *ws = new_websocket(uuid);
|
334
|
+
if (!ws) {
|
335
|
+
perror("FATAL ERROR: couldn't allocate Websocket protocol object");
|
336
|
+
exit(errno);
|
337
|
+
}
|
338
|
+
// we have an active websocket connection - prep the connection buffer
|
339
|
+
ws->buffer = create_ws_buffer(ws);
|
340
|
+
// Setup ws callbacks
|
341
|
+
ws->on_open = args->on_open;
|
342
|
+
ws->on_close = args->on_close;
|
343
|
+
ws->on_message = args->on_message;
|
344
|
+
ws->on_ready = args->on_ready;
|
345
|
+
ws->on_shutdown = args->on_shutdown;
|
346
|
+
// setup any user data
|
347
|
+
ws->udata = args->udata;
|
348
|
+
if (http_settings) {
|
349
|
+
// client mode?
|
350
|
+
ws->is_client = http_settings->is_client;
|
351
|
+
// buffer limits
|
352
|
+
ws->max_msg_size = http_settings->ws_max_msg_size;
|
353
|
+
// update the timeout
|
354
|
+
facil_set_timeout(uuid, http_settings->ws_timeout);
|
355
|
+
} else {
|
356
|
+
ws->max_msg_size = (1024 * 256);
|
357
|
+
facil_set_timeout(uuid, 40);
|
358
|
+
}
|
359
|
+
|
360
|
+
if (data && length) {
|
361
|
+
if (length > ws->buffer.size) {
|
362
|
+
ws->buffer.size = length;
|
363
|
+
ws->buffer = resize_ws_buffer(ws, ws->buffer);
|
364
|
+
if (!ws->buffer.data) {
|
365
|
+
// no memory.
|
366
|
+
facil_attach(uuid, (protocol_s *)ws);
|
367
|
+
websocket_close(ws);
|
368
|
+
return;
|
369
|
+
}
|
370
|
+
}
|
371
|
+
memcpy(ws->buffer.data, data, length);
|
372
|
+
ws->length = length;
|
373
|
+
}
|
374
|
+
// update the protocol object, cleanning up the old one
|
375
|
+
facil_attach(uuid, (protocol_s *)ws);
|
376
|
+
// allow the on_open and on_data to take over the control.
|
377
|
+
facil_force_event(uuid, FIO_EVENT_ON_DATA);
|
378
|
+
}
|
379
|
+
|
312
380
|
/*******************************************************************************
|
313
381
|
Writing to the Websocket
|
314
382
|
*/
|
315
|
-
#define WS_MAX_FRAME_SIZE
|
383
|
+
#define WS_MAX_FRAME_SIZE \
|
384
|
+
(FIO_MEMORY_BLOCK_ALLOC_LIMIT - 4096) // should be less then `unsigned short`
|
316
385
|
|
317
386
|
// clang-format off
|
318
387
|
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
@@ -344,25 +413,15 @@ Writing to the Websocket
|
|
344
413
|
(((i)&0xFF000000000000ULL) >> 40) | (((i)&0xFF00000000000000ULL) >> 56))
|
345
414
|
#endif
|
346
415
|
|
347
|
-
static void websocket_write_impl(intptr_t fd, void *data, size_t len,
|
348
|
-
char text, /* TODO: add client masking */
|
416
|
+
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
349
417
|
char first, char last, char client) {
|
350
|
-
if (len
|
351
|
-
|
352
|
-
|
353
|
-
sbuff->len =
|
354
|
-
(client ? websocket_client_wrap(sbuff->buf, data, len, (text ? 1 : 2),
|
355
|
-
first, last, 0)
|
356
|
-
: websocket_server_wrap(sbuff->buf, data, len, (text ? 1 : 2),
|
357
|
-
first, last, 0));
|
358
|
-
sock_buffer_send(fd, sbuff);
|
359
|
-
} else if (len <= WS_MAX_FRAME_SIZE) {
|
360
|
-
void *buff = malloc(len + 16);
|
418
|
+
if (len <= WS_MAX_FRAME_SIZE) {
|
419
|
+
void *buff = fio_malloc(len + 16);
|
361
420
|
len = (client ? websocket_client_wrap(buff, data, len, (text ? 1 : 2),
|
362
421
|
first, last, 0)
|
363
422
|
: websocket_server_wrap(buff, data, len, (text ? 1 : 2),
|
364
423
|
first, last, 0));
|
365
|
-
sock_write2(.uuid = fd, .buffer = buff, .length = len, .
|
424
|
+
sock_write2(.uuid = fd, .buffer = buff, .length = len, .dealloc = fio_free);
|
366
425
|
} else {
|
367
426
|
/* frame fragmentation is better for large data then large frames */
|
368
427
|
while (len > WS_MAX_FRAME_SIZE) {
|
@@ -432,9 +491,8 @@ static const uint8_t utf8d[] = {
|
|
432
491
|
|
433
492
|
static inline uint32_t validate_utf8(uint8_t *str, size_t len) {
|
434
493
|
uint32_t state = 0;
|
435
|
-
uint32_t type;
|
436
494
|
while (len) {
|
437
|
-
type = utf8d[*str];
|
495
|
+
uint32_t type = utf8d[*str];
|
438
496
|
state = utf8d[256 + state * 16 + type];
|
439
497
|
if (state == UTF8_REJECT)
|
440
498
|
return 0;
|
@@ -461,7 +519,9 @@ static void websocket_on_unsubscribe(void *u1, void *u2) {
|
|
461
519
|
free(d);
|
462
520
|
}
|
463
521
|
|
464
|
-
static void
|
522
|
+
static inline void
|
523
|
+
websocket_on_pubsub_message_direct_internal(pubsub_message_s *msg,
|
524
|
+
uint8_t txt) {
|
465
525
|
protocol_s *pr =
|
466
526
|
facil_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_WRITE);
|
467
527
|
if (!pr) {
|
@@ -470,37 +530,36 @@ static void websocket_on_pubsub_message_direct(pubsub_message_s *msg) {
|
|
470
530
|
pubsub_defer(msg);
|
471
531
|
return;
|
472
532
|
}
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
533
|
+
FIOBJ message;
|
534
|
+
fio_cstr_s tmp;
|
535
|
+
if (FIOBJ_TYPE_IS(msg->message, FIOBJ_T_STRING)) {
|
536
|
+
message = fiobj_dup(msg->message);
|
537
|
+
tmp = fiobj_obj2cstr(message);
|
538
|
+
if (txt == 2) {
|
539
|
+
/* unknown text state */
|
540
|
+
txt =
|
541
|
+
(tmp.len >= (2 << 14) ? 0
|
542
|
+
: validate_utf8((uint8_t *)tmp.data, tmp.len));
|
543
|
+
}
|
544
|
+
} else {
|
545
|
+
message = fiobj_obj2json(msg->message, 0);
|
546
|
+
tmp = fiobj_obj2cstr(message);
|
547
|
+
}
|
548
|
+
websocket_write((ws_s *)pr, tmp.data, tmp.len, txt & 1);
|
477
549
|
facil_protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
550
|
+
fiobj_free(message);
|
551
|
+
}
|
552
|
+
|
553
|
+
static void websocket_on_pubsub_message_direct(pubsub_message_s *msg) {
|
554
|
+
websocket_on_pubsub_message_direct_internal(msg, 2);
|
478
555
|
}
|
479
556
|
|
480
557
|
static void websocket_on_pubsub_message_direct_txt(pubsub_message_s *msg) {
|
481
|
-
|
482
|
-
facil_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_WRITE);
|
483
|
-
if (!pr) {
|
484
|
-
if (errno == EBADF)
|
485
|
-
return;
|
486
|
-
pubsub_defer(msg);
|
487
|
-
return;
|
488
|
-
}
|
489
|
-
websocket_write((ws_s *)pr, msg->msg.data, msg->msg.len, 1);
|
490
|
-
facil_protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
558
|
+
websocket_on_pubsub_message_direct_internal(msg, 1);
|
491
559
|
}
|
492
560
|
|
493
561
|
static void websocket_on_pubsub_message_direct_bin(pubsub_message_s *msg) {
|
494
|
-
|
495
|
-
facil_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_WRITE);
|
496
|
-
if (!pr) {
|
497
|
-
if (errno == EBADF)
|
498
|
-
return;
|
499
|
-
pubsub_defer(msg);
|
500
|
-
return;
|
501
|
-
}
|
502
|
-
websocket_write((ws_s *)pr, msg->msg.data, msg->msg.len, 0);
|
503
|
-
facil_protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
562
|
+
websocket_on_pubsub_message_direct_internal(msg, 0);
|
504
563
|
}
|
505
564
|
|
506
565
|
static void websocket_on_pubsub_message(pubsub_message_s *msg) {
|
@@ -517,11 +576,9 @@ static void websocket_on_pubsub_message(pubsub_message_s *msg) {
|
|
517
576
|
if (d->on_message)
|
518
577
|
d->on_message((websocket_pubsub_notification_s){
|
519
578
|
.ws = (ws_s *)pr,
|
520
|
-
.engine = (pubsub_engine_s *)msg->engine,
|
521
579
|
.subscription_id = (intptr_t)msg->subscription,
|
522
|
-
.channel =
|
523
|
-
.
|
524
|
-
.use_pattern = msg->use_pattern,
|
580
|
+
.channel = msg->channel,
|
581
|
+
.message = msg->message,
|
525
582
|
});
|
526
583
|
facil_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
527
584
|
}
|
@@ -531,17 +588,15 @@ static void websocket_on_pubsub_message(pubsub_message_s *msg) {
|
|
531
588
|
*/
|
532
589
|
#undef websocket_subscribe
|
533
590
|
uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
|
591
|
+
if (!args.ws)
|
592
|
+
goto error;
|
534
593
|
websocket_sub_data_s *d = malloc(sizeof(*d));
|
535
594
|
*d = (websocket_sub_data_s){.udata = args.udata,
|
536
595
|
.on_message = args.on_message,
|
537
596
|
.on_unsubscribe = args.on_unsubscribe};
|
597
|
+
|
538
598
|
pubsub_sub_pt sub = pubsub_subscribe(
|
539
|
-
.
|
540
|
-
.channel =
|
541
|
-
{
|
542
|
-
.name = (char *)args.channel.name, .len = args.channel.len,
|
543
|
-
},
|
544
|
-
.use_pattern = args.use_pattern,
|
599
|
+
.channel = args.channel, .use_pattern = args.use_pattern,
|
545
600
|
.on_unsubscribe = websocket_on_unsubscribe,
|
546
601
|
.on_message =
|
547
602
|
(args.on_message
|
@@ -556,8 +611,12 @@ uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
|
|
556
611
|
free(d);
|
557
612
|
return 0;
|
558
613
|
}
|
559
|
-
|
560
|
-
return (uintptr_t)
|
614
|
+
fio_ls_push(&args.ws->subscriptions, sub);
|
615
|
+
return (uintptr_t)args.ws->subscriptions.prev;
|
616
|
+
error:
|
617
|
+
if (args.on_unsubscribe)
|
618
|
+
args.on_unsubscribe(args.udata);
|
619
|
+
return 0;
|
561
620
|
}
|
562
621
|
|
563
622
|
/**
|
@@ -566,12 +625,8 @@ uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
|
|
566
625
|
#undef websocket_find_sub
|
567
626
|
uintptr_t websocket_find_sub(struct websocket_subscribe_s args) {
|
568
627
|
pubsub_sub_pt sub = pubsub_find_sub(
|
569
|
-
.
|
570
|
-
.
|
571
|
-
{
|
572
|
-
.name = (char *)args.channel.name, .len = args.channel.len,
|
573
|
-
},
|
574
|
-
.use_pattern = args.use_pattern,
|
628
|
+
.channel = args.channel, .use_pattern = args.use_pattern,
|
629
|
+
.on_unsubscribe = websocket_on_unsubscribe,
|
575
630
|
.on_message =
|
576
631
|
(args.on_message
|
577
632
|
? websocket_on_pubsub_message
|
@@ -583,10 +638,9 @@ uintptr_t websocket_find_sub(struct websocket_subscribe_s args) {
|
|
583
638
|
.udata1 = (void *)args.ws->fd, .udata2 = args.udata);
|
584
639
|
if (!sub)
|
585
640
|
return 0;
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
return (uintptr_t)s;
|
641
|
+
FIO_LS_FOR(&args.ws->subscriptions, pos) {
|
642
|
+
if (pos->obj == sub)
|
643
|
+
return (uintptr_t)pos;
|
590
644
|
}
|
591
645
|
return 0;
|
592
646
|
}
|
@@ -595,140 +649,15 @@ uintptr_t websocket_find_sub(struct websocket_subscribe_s args) {
|
|
595
649
|
* Unsubscribes from a channel.
|
596
650
|
*/
|
597
651
|
void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id) {
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
pubsub_unsubscribe(s->sub);
|
602
|
-
free_subscription(s);
|
603
|
-
return;
|
604
|
-
}
|
605
|
-
}
|
652
|
+
pubsub_unsubscribe((pubsub_sub_pt)((fio_ls_s *)subscription_id)->obj);
|
653
|
+
fio_ls_remove((fio_ls_s *)subscription_id);
|
654
|
+
(void)ws;
|
606
655
|
}
|
607
656
|
|
608
657
|
/*******************************************************************************
|
609
658
|
The API implementation
|
610
659
|
*/
|
611
660
|
|
612
|
-
static void deferred_on_open(void *func, void *pr) {
|
613
|
-
((void (*)(ws_s *))func)(pr);
|
614
|
-
facil_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
615
|
-
}
|
616
|
-
|
617
|
-
/** The upgrade */
|
618
|
-
#undef websocket_upgrade
|
619
|
-
ssize_t websocket_upgrade(websocket_settings_s settings) {
|
620
|
-
// A static data used for all websocket connections.
|
621
|
-
static char ws_key_accpt_str[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
622
|
-
// require either a request or a response.
|
623
|
-
if (((uintptr_t)settings.request | (uintptr_t)settings.response) ==
|
624
|
-
(uintptr_t)NULL)
|
625
|
-
return -1;
|
626
|
-
if (settings.max_msg_size == 0)
|
627
|
-
settings.max_msg_size = 262144; /** defaults to ~250KB */
|
628
|
-
if (settings.timeout == 0)
|
629
|
-
settings.timeout = 40; /* defaults to 40 seconds */
|
630
|
-
// make sure we have a response object.
|
631
|
-
http_response_s *response = settings.response;
|
632
|
-
if (response == NULL) {
|
633
|
-
/* initialize a default upgrade response */
|
634
|
-
response = http_response_create(settings.request);
|
635
|
-
} else
|
636
|
-
settings.request = response->request;
|
637
|
-
// allocate the protocol object (TODO: (maybe) pooling)
|
638
|
-
ws_s *ws = new_websocket();
|
639
|
-
if (!ws)
|
640
|
-
goto refuse;
|
641
|
-
|
642
|
-
// setup the socket-server data
|
643
|
-
ws->fd = response->request->fd;
|
644
|
-
// Setup ws callbacks
|
645
|
-
ws->on_close = settings.on_close;
|
646
|
-
ws->on_message = settings.on_message;
|
647
|
-
ws->on_ready = settings.on_ready;
|
648
|
-
ws->on_shutdown = settings.on_shutdown;
|
649
|
-
// setup any user data
|
650
|
-
ws->udata = settings.udata;
|
651
|
-
// buffer limits
|
652
|
-
ws->max_msg_size = settings.max_msg_size;
|
653
|
-
const char *recv_str;
|
654
|
-
|
655
|
-
recv_str =
|
656
|
-
http_request_header_find(settings.request, "sec-websocket-version", 21)
|
657
|
-
.value;
|
658
|
-
if (recv_str == NULL || recv_str[0] != '1' || recv_str[1] != '3')
|
659
|
-
goto refuse;
|
660
|
-
|
661
|
-
http_header_s sec_h =
|
662
|
-
http_request_header_find(settings.request, "sec-websocket-key", 17);
|
663
|
-
if (sec_h.value == NULL)
|
664
|
-
goto refuse;
|
665
|
-
|
666
|
-
// websocket extentions (none)
|
667
|
-
|
668
|
-
// the accept Base64 Hash - we need to compute this one and set it
|
669
|
-
// the client's unique string
|
670
|
-
// use the SHA1 methods provided to concat the client string and hash
|
671
|
-
sha1_s sha1;
|
672
|
-
sha1 = bscrypt_sha1_init();
|
673
|
-
bscrypt_sha1_write(&sha1, sec_h.value, sec_h.value_len);
|
674
|
-
bscrypt_sha1_write(&sha1, ws_key_accpt_str, sizeof(ws_key_accpt_str) - 1);
|
675
|
-
// base encode the data
|
676
|
-
char websockets_key[32];
|
677
|
-
int len =
|
678
|
-
bscrypt_base64_encode(websockets_key, bscrypt_sha1_result(&sha1), 20);
|
679
|
-
|
680
|
-
// websocket extentions (none)
|
681
|
-
|
682
|
-
// upgrade taking place, make sure the upgrade headers are valid for the
|
683
|
-
// response.
|
684
|
-
response->status = 101;
|
685
|
-
http_response_write_header(response, .name = "Connection", .name_len = 10,
|
686
|
-
.value = "Upgrade", .value_len = 7);
|
687
|
-
http_response_write_header(response, .name = "Upgrade", .name_len = 7,
|
688
|
-
.value = "websocket", .value_len = 9);
|
689
|
-
http_response_write_header(response, .name = "sec-websocket-version",
|
690
|
-
.name_len = 21, .value = "13", .value_len = 2);
|
691
|
-
// set the string's length and encoding
|
692
|
-
http_response_write_header(response, .name = "Sec-WebSocket-Accept",
|
693
|
-
.name_len = 20, .value = websockets_key,
|
694
|
-
.value_len = len);
|
695
|
-
// // inform about 0 extension support
|
696
|
-
// sec_h = http_request_header_find(settings.request,
|
697
|
-
// "sec-websocket-extensions", 24);
|
698
|
-
// if (recv_str != NULL)
|
699
|
-
// http_response_write_header(response, .name =
|
700
|
-
// "Sec-Websocket-Extensions",
|
701
|
-
// .name_length = 24);
|
702
|
-
|
703
|
-
goto cleanup;
|
704
|
-
refuse:
|
705
|
-
// set the negative response
|
706
|
-
response->status = 400;
|
707
|
-
cleanup:
|
708
|
-
if (response->status == 101) {
|
709
|
-
// update the protocol object, cleanning up the old one
|
710
|
-
facil_attach_locked(ws->fd, (protocol_s *)ws);
|
711
|
-
// send the response
|
712
|
-
http_response_finish(response);
|
713
|
-
// we have an active websocket connection - prep the connection buffer
|
714
|
-
ws->buffer = create_ws_buffer(ws);
|
715
|
-
// update the timeout
|
716
|
-
facil_set_timeout(ws->fd, settings.timeout);
|
717
|
-
// call the on_open callback
|
718
|
-
if (settings.on_open) {
|
719
|
-
defer(deferred_on_open, (void *)settings.on_open, ws);
|
720
|
-
} else {
|
721
|
-
facil_protocol_unlock(&ws->protocol, FIO_PR_LOCK_TASK);
|
722
|
-
}
|
723
|
-
return 0;
|
724
|
-
}
|
725
|
-
http_response_finish(response);
|
726
|
-
destroy_ws(ws);
|
727
|
-
return -1;
|
728
|
-
}
|
729
|
-
#define websocket_upgrade(...) \
|
730
|
-
websocket_upgrade((websocket_settings_s){__VA_ARGS__})
|
731
|
-
|
732
661
|
/** Returns the opaque user data associated with the websocket. */
|
733
662
|
void *websocket_udata(ws_s *ws) { return ws->udata; }
|
734
663
|
/** Returns the the process specific connection's UUID (see `libsock`). */
|
@@ -750,7 +679,7 @@ int websocket_write(ws_s *ws, void *data, size_t size, uint8_t is_text) {
|
|
750
679
|
}
|
751
680
|
/** Closes a websocket connection. */
|
752
681
|
void websocket_close(ws_s *ws) {
|
753
|
-
sock_write2(.uuid = ws->fd, .buffer = "\x88\x00", .length = 2,
|
682
|
+
sock_write2(.uuid = ws->fd, .buffer = "\x88\x00", .length = 2,
|
754
683
|
.dealloc = SOCK_DEALLOC_NOOP);
|
755
684
|
sock_close(ws->fd);
|
756
685
|
return;
|
@@ -799,7 +728,8 @@ Performs a task on each websocket connection that shares the same process
|
|
799
728
|
(except the originating `ws_s` connection which is allowed to be NULL).
|
800
729
|
*/
|
801
730
|
#undef websocket_each
|
802
|
-
void
|
731
|
+
void __attribute__((deprecated))
|
732
|
+
websocket_each(struct websocket_each_args_s args) {
|
803
733
|
struct WSTask *tsk = malloc(sizeof(*tsk));
|
804
734
|
tsk->arg = args.arg;
|
805
735
|
tsk->on_finish = args.on_finish;
|
@@ -850,7 +780,7 @@ static void ws_reduce_or_free_multi_write(void *buff) {
|
|
850
780
|
if (mw->on_finished) {
|
851
781
|
facil_defer(.uuid = mw->origin, .task = ws_mw_defered_on_finish,
|
852
782
|
.arg = mw, .fallback = ws_mw_defered_on_finish_fb,
|
853
|
-
.
|
783
|
+
.type = FIO_PR_LOCK_WRITE);
|
854
784
|
} else
|
855
785
|
free(mw);
|
856
786
|
} else
|
@@ -871,7 +801,7 @@ static void ws_direct_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
|
871
801
|
multi->count += 1;
|
872
802
|
spn_unlock(&multi->lock);
|
873
803
|
sock_write2(.uuid = fd, .buffer = multi->buffer, .length = multi->length,
|
874
|
-
.dealloc = ws_reduce_or_free_multi_write
|
804
|
+
.dealloc = ws_reduce_or_free_multi_write);
|
875
805
|
}
|
876
806
|
|
877
807
|
static void ws_check_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
@@ -883,7 +813,8 @@ static void ws_check_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
|
883
813
|
}
|
884
814
|
|
885
815
|
#undef websocket_write_each
|
886
|
-
int
|
816
|
+
int __attribute__((deprecated))
|
817
|
+
websocket_write_each(struct websocket_write_each_args_s args) {
|
887
818
|
if (!args.data || !args.length)
|
888
819
|
return -1;
|
889
820
|
struct websocket_multi_write *multi =
|