iodine 0.2.0 → 0.2.1
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/CHANGELOG.md +12 -0
- data/README.md +3 -3
- data/bin/ws-echo +5 -0
- data/ext/iodine/http_response_http1.h +10 -10
- data/ext/iodine/iodine_websocket.c +20 -1
- data/ext/iodine/libserver.c +102 -119
- data/ext/iodine/libsock.c +8 -0
- data/ext/iodine/libsock.h +25 -21
- data/ext/iodine/websockets.c +83 -90
- data/ext/iodine/websockets.h +29 -19
- data/iodine.gemspec +3 -3
- data/lib/iodine/version.rb +1 -1
- metadata +3 -7
data/ext/iodine/libsock.c
CHANGED
@@ -712,6 +712,14 @@ ssize_t sock_send_packet(intptr_t uuid, sock_packet_s *packet) {
|
|
712
712
|
return 0;
|
713
713
|
}
|
714
714
|
|
715
|
+
/**
|
716
|
+
Returns TRUE (non 0) if there is data waiting to be written to the socket in the
|
717
|
+
user-land buffer.
|
718
|
+
*/
|
719
|
+
_Bool sock_packets_pending(intptr_t uuid) {
|
720
|
+
return fd_info && uuid2info(uuid).packet != NULL;
|
721
|
+
}
|
722
|
+
|
715
723
|
/**
|
716
724
|
Use `sock_free_packet` to free unused packets that were checked-out using
|
717
725
|
`sock_checkout_packet`.
|
data/ext/iodine/libsock.h
CHANGED
@@ -35,7 +35,7 @@ User land buffer settings for every packet's pre-alocated memory size (17Kb)
|
|
35
35
|
This information is also useful when implementing read / write hooks.
|
36
36
|
*/
|
37
37
|
#ifndef BUFFER_PACKET_SIZE
|
38
|
-
#define BUFFER_PACKET_SIZE
|
38
|
+
#define BUFFER_PACKET_SIZE \
|
39
39
|
(1024 * 16) /* Use 32 Kb. With sendfile, 16 Kb might be better. */
|
40
40
|
#endif
|
41
41
|
#ifndef BUFFER_FILE_READ_SIZE
|
@@ -118,7 +118,7 @@ client data for "broadcasting" or when an old client task is preparing a
|
|
118
118
|
response in the background while a disconnection and a new connection occur on
|
119
119
|
the same `fd`).
|
120
120
|
*/
|
121
|
-
intptr_t sock_listen(const char*
|
121
|
+
intptr_t sock_listen(const char *address, const char *port);
|
122
122
|
|
123
123
|
/**
|
124
124
|
`sock_accept` accepts a new socket connection from the listening socket
|
@@ -162,7 +162,7 @@ hanging while waiting for a network timeout).
|
|
162
162
|
Use select, poll, `libreact` or other solutions to review the connection state
|
163
163
|
before attempting to write to the socket.
|
164
164
|
*/
|
165
|
-
intptr_t sock_connect(char*
|
165
|
+
intptr_t sock_connect(char *address, char *port);
|
166
166
|
|
167
167
|
/**
|
168
168
|
`sock_open` takes an existing file descriptor `fd` and initializes it's status
|
@@ -225,14 +225,14 @@ read using sock_read (i.e., when using a transport layer, such as TLS).
|
|
225
225
|
Also, some internal buffering will might be used in cases where the transport
|
226
226
|
layer data available is larger then the data requested.
|
227
227
|
*/
|
228
|
-
ssize_t sock_read(intptr_t uuid, void*
|
228
|
+
ssize_t sock_read(intptr_t uuid, void *buf, size_t count);
|
229
229
|
|
230
230
|
typedef struct {
|
231
231
|
/** The fd for sending data. */
|
232
232
|
intptr_t fduuid;
|
233
233
|
/** The data to be sent. This can be either a byte stream or a file pointer
|
234
234
|
* (`FILE *`). */
|
235
|
-
const void*
|
235
|
+
const void *buffer;
|
236
236
|
/** The length (size) of the buffer. irrelevant for file pointers. */
|
237
237
|
size_t length;
|
238
238
|
/** Starting point offset, when the buffer is a file
|
@@ -274,7 +274,7 @@ transferred to the socket's user level buffer.
|
|
274
274
|
**Note** this is actually a specific case of `sock_write2` and this macro
|
275
275
|
actually calls `sock_write2`.
|
276
276
|
*/
|
277
|
-
#define sock_write(uuid, buf, count)
|
277
|
+
#define sock_write(uuid, buf, count) \
|
278
278
|
sock_write2(.fduuid = (uuid), .buffer = (buf), .length = (count))
|
279
279
|
|
280
280
|
/**
|
@@ -292,11 +292,9 @@ the maximum amount of data to be sent.
|
|
292
292
|
|
293
293
|
Returns -1 and closes the file on error. Returns 0 on success.
|
294
294
|
*/
|
295
|
-
__unused static inline ssize_t sock_sendfile(intptr_t uuid,
|
296
|
-
|
297
|
-
|
298
|
-
size_t length) {
|
299
|
-
return sock_write2(.fduuid = uuid, .buffer = (void*)((intptr_t)source_fd),
|
295
|
+
__unused static inline ssize_t sock_sendfile(intptr_t uuid, int source_fd,
|
296
|
+
off_t offset, size_t length) {
|
297
|
+
return sock_write2(.fduuid = uuid, .buffer = (void *)((intptr_t)source_fd),
|
300
298
|
.length = length, .is_fd = 1, .offset = offset);
|
301
299
|
}
|
302
300
|
|
@@ -351,11 +349,11 @@ function.
|
|
351
349
|
*/
|
352
350
|
typedef struct sock_packet_s {
|
353
351
|
ssize_t length;
|
354
|
-
void*
|
352
|
+
void *buffer;
|
355
353
|
/** Metadata about the packet. */
|
356
354
|
struct {
|
357
355
|
/** allows the linking of a number of packets together. */
|
358
|
-
struct sock_packet_s*
|
356
|
+
struct sock_packet_s *next;
|
359
357
|
/** Starting point offset, when the buffer is a file (see
|
360
358
|
* `sock_packet_s.metadata.is_fd`). */
|
361
359
|
off_t offset;
|
@@ -396,7 +394,7 @@ pointer (which can be safely overwritten to point to an external buffer).
|
|
396
394
|
This attached buffer is safely and automatically freed or returned to the memory
|
397
395
|
pool once `sock_send_packet` or `sock_free_packet` are called.
|
398
396
|
*/
|
399
|
-
sock_packet_s*
|
397
|
+
sock_packet_s *sock_checkout_packet(void);
|
400
398
|
/**
|
401
399
|
Attaches a packet to a socket's output buffer and calls `sock_flush` for the
|
402
400
|
socket.
|
@@ -406,7 +404,13 @@ The packet's memory is **always** handled by the `sock_send_packet` function
|
|
406
404
|
|
407
405
|
Returns -1 on error. Returns 0 on success.
|
408
406
|
*/
|
409
|
-
ssize_t sock_send_packet(intptr_t uuid, sock_packet_s*
|
407
|
+
ssize_t sock_send_packet(intptr_t uuid, sock_packet_s *packet);
|
408
|
+
|
409
|
+
/**
|
410
|
+
Returns TRUE (non 0) if there is data waiting to be written to the socket in the
|
411
|
+
user-land buffer.
|
412
|
+
*/
|
413
|
+
_Bool sock_packets_pending(intptr_t uuid);
|
410
414
|
|
411
415
|
/**
|
412
416
|
Use `sock_free_packet` to free unused packets that were checked-out using
|
@@ -415,7 +419,7 @@ Use `sock_free_packet` to free unused packets that were checked-out using
|
|
415
419
|
NEVER use `free`, for any packet checked out using the pool management function
|
416
420
|
`sock_checkout_packet`.
|
417
421
|
*/
|
418
|
-
void sock_free_packet(sock_packet_s*
|
422
|
+
void sock_free_packet(sock_packet_s *packet);
|
419
423
|
|
420
424
|
/* *****************************************************************************
|
421
425
|
TLC - Transport Layer Callback.
|
@@ -429,10 +433,10 @@ replace the default system calls to `recv` and `write`. */
|
|
429
433
|
typedef struct sock_rw_hook_s {
|
430
434
|
/** Implement reading from a file descriptor. Should behave like the file
|
431
435
|
* system `read` call, including the setup or errno to EAGAIN / EWOULDBLOCK.*/
|
432
|
-
ssize_t (*read)(intptr_t fduuid, void*
|
436
|
+
ssize_t (*read)(intptr_t fduuid, void *buf, size_t count);
|
433
437
|
/** Implement writing to a file descriptor. Should behave like the file system
|
434
438
|
* `write` call.*/
|
435
|
-
ssize_t (*write)(intptr_t fduuid, const void*
|
439
|
+
ssize_t (*write)(intptr_t fduuid, const void *buf, size_t count);
|
436
440
|
/** When implemented, this function will be called to flush any data remaining
|
437
441
|
* in the internal buffer.
|
438
442
|
* The function should return the number of bytes remaining in the internal
|
@@ -448,7 +452,7 @@ typedef struct sock_rw_hook_s {
|
|
448
452
|
*
|
449
453
|
* The `on_clear` callback should manage is own thread safety mechanism, if
|
450
454
|
* required. */
|
451
|
-
void (*on_clear)(intptr_t fduuid, struct sock_rw_hook_s*
|
455
|
+
void (*on_clear)(intptr_t fduuid, struct sock_rw_hook_s *rw_hook);
|
452
456
|
} sock_rw_hook_s;
|
453
457
|
|
454
458
|
/* *****************************************************************************
|
@@ -456,10 +460,10 @@ RW hooks implementation
|
|
456
460
|
*/
|
457
461
|
|
458
462
|
/** Gets a socket hook state (a pointer to the struct). */
|
459
|
-
struct sock_rw_hook_s*
|
463
|
+
struct sock_rw_hook_s *sock_rw_hook_get(intptr_t fduuid);
|
460
464
|
|
461
465
|
/** Sets a socket hook state (a pointer to the struct). */
|
462
|
-
int sock_rw_hook_set(intptr_t fduuid, sock_rw_hook_s*
|
466
|
+
int sock_rw_hook_set(intptr_t fduuid, sock_rw_hook_s *rw_hooks);
|
463
467
|
|
464
468
|
/* *****************************************************************************
|
465
469
|
test
|
data/ext/iodine/websockets.c
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
10
10
|
#include <endian.h>
|
11
|
-
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) &&
|
11
|
+
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
|
12
12
|
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
13
13
|
#define __BIG_ENDIAN__
|
14
14
|
#endif
|
@@ -18,21 +18,21 @@
|
|
18
18
|
Buffer management - update to change the way the buffer is handled.
|
19
19
|
*/
|
20
20
|
struct buffer_s {
|
21
|
-
void*
|
21
|
+
void *data;
|
22
22
|
size_t size;
|
23
23
|
};
|
24
24
|
|
25
25
|
#pragma weak create_ws_buffer
|
26
26
|
/** returns a buffer_s struct, with a buffer (at least) `size` long. */
|
27
|
-
struct buffer_s create_ws_buffer(ws_s*
|
27
|
+
struct buffer_s create_ws_buffer(ws_s *owner);
|
28
28
|
|
29
29
|
#pragma weak resize_ws_buffer
|
30
30
|
/** returns a buffer_s struct, with a buffer (at least) `size` long. */
|
31
|
-
struct buffer_s resize_ws_buffer(ws_s*
|
31
|
+
struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s);
|
32
32
|
|
33
33
|
#pragma weak free_ws_buffer
|
34
34
|
/** releases an existing buffer. */
|
35
|
-
void free_ws_buffer(ws_s*
|
35
|
+
void free_ws_buffer(ws_s *owner, struct buffer_s);
|
36
36
|
|
37
37
|
/** Sets the initial buffer size. (16Kb)*/
|
38
38
|
#define WS_INITIAL_BUFFER_SIZE 16384
|
@@ -46,16 +46,16 @@ the code probably wouldn't offer a high performance boost.
|
|
46
46
|
// buffer increments by 4,096 Bytes (4Kb)
|
47
47
|
#define round_up_buffer_size(size) (((size) >> 12) + 1) << 12
|
48
48
|
|
49
|
-
struct buffer_s create_ws_buffer(ws_s*
|
49
|
+
struct buffer_s create_ws_buffer(ws_s *owner) {
|
50
50
|
struct buffer_s buff;
|
51
51
|
buff.size = round_up_buffer_size(WS_INITIAL_BUFFER_SIZE);
|
52
52
|
buff.data = malloc(buff.size);
|
53
53
|
return buff;
|
54
54
|
}
|
55
55
|
|
56
|
-
struct buffer_s resize_ws_buffer(ws_s*
|
56
|
+
struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s buff) {
|
57
57
|
buff.size = round_up_buffer_size(buff.size);
|
58
|
-
void*
|
58
|
+
void *tmp = realloc(buff.data, buff.size);
|
59
59
|
if (!tmp) {
|
60
60
|
free_ws_buffer(owner, buff);
|
61
61
|
buff.size = 0;
|
@@ -63,7 +63,7 @@ struct buffer_s resize_ws_buffer(ws_s* owner, struct buffer_s buff) {
|
|
63
63
|
buff.data = tmp;
|
64
64
|
return buff;
|
65
65
|
}
|
66
|
-
void free_ws_buffer(ws_s*
|
66
|
+
void free_ws_buffer(ws_s *owner, struct buffer_s buff) {
|
67
67
|
if (buff.data)
|
68
68
|
free(buff.data);
|
69
69
|
}
|
@@ -74,8 +74,8 @@ void free_ws_buffer(ws_s* owner, struct buffer_s buff) {
|
|
74
74
|
Create/Destroy the websocket object (prototypes)
|
75
75
|
*/
|
76
76
|
|
77
|
-
static ws_s*
|
78
|
-
static void destroy_ws(ws_s*
|
77
|
+
static ws_s *new_websocket();
|
78
|
+
static void destroy_ws(ws_s *ws);
|
79
79
|
|
80
80
|
/*******************************************************************************
|
81
81
|
The Websocket object (protocol + parser)
|
@@ -86,11 +86,12 @@ struct Websocket {
|
|
86
86
|
/** connection data */
|
87
87
|
intptr_t fd;
|
88
88
|
/** callbacks */
|
89
|
-
void (*on_message)(ws_s*
|
90
|
-
void (*on_shutdown)(ws_s*
|
91
|
-
void (*
|
89
|
+
void (*on_message)(ws_s *ws, char *data, size_t size, uint8_t is_text);
|
90
|
+
void (*on_shutdown)(ws_s *ws);
|
91
|
+
void (*on_ready)(ws_s *ws);
|
92
|
+
void (*on_close)(ws_s *ws);
|
92
93
|
/** Opaque user data. */
|
93
|
-
void*
|
94
|
+
void *udata;
|
94
95
|
/** The maximum websocket message size */
|
95
96
|
size_t max_msg_size;
|
96
97
|
/** message buffer. */
|
@@ -134,16 +135,16 @@ struct Websocket {
|
|
134
135
|
/**
|
135
136
|
The Websocket Protocol Identifying String. Used for the `each` function.
|
136
137
|
*/
|
137
|
-
char*
|
138
|
+
char *WEBSOCKET_ID_STR = "websockets";
|
138
139
|
|
139
140
|
/*******************************************************************************
|
140
141
|
The Websocket Protocol implementation
|
141
142
|
*/
|
142
143
|
|
143
|
-
#define ws_protocol(fd) ((ws_s*)(server_get_protocol(fd)))
|
144
|
+
#define ws_protocol(fd) ((ws_s *)(server_get_protocol(fd)))
|
144
145
|
|
145
|
-
static void ws_ping(intptr_t fd, protocol_s*
|
146
|
-
sock_packet_s*
|
146
|
+
static void ws_ping(intptr_t fd, protocol_s *_ws) {
|
147
|
+
sock_packet_s *packet;
|
147
148
|
while ((packet = sock_checkout_packet()) == NULL)
|
148
149
|
sock_flush_all();
|
149
150
|
*packet = (sock_packet_s){
|
@@ -152,31 +153,29 @@ static void ws_ping(intptr_t fd, protocol_s* _ws) {
|
|
152
153
|
sock_send_packet(fd, packet);
|
153
154
|
}
|
154
155
|
|
155
|
-
static void on_close(protocol_s* _ws)
|
156
|
-
|
156
|
+
static void on_close(protocol_s *_ws) { destroy_ws((ws_s *)_ws); }
|
157
|
+
|
158
|
+
static void on_ready(intptr_t fduuid, protocol_s *ws) {
|
159
|
+
if (ws && ws->service == WEBSOCKET_ID_STR && ((ws_s *)ws)->on_ready)
|
160
|
+
((ws_s *)ws)->on_ready((ws_s *)ws);
|
157
161
|
}
|
158
162
|
|
159
|
-
static void on_open(intptr_t fd, protocol_s*
|
160
|
-
if (callback &&
|
161
|
-
((void (*)(void*))callback)(
|
163
|
+
static void on_open(intptr_t fd, protocol_s *ws, void *callback) {
|
164
|
+
if (callback && ws && ws->service == WEBSOCKET_ID_STR)
|
165
|
+
((void (*)(void *))callback)(ws);
|
162
166
|
}
|
163
167
|
|
164
|
-
static void on_shutdown(intptr_t fd, protocol_s*
|
165
|
-
if (
|
166
|
-
((ws_s*)
|
168
|
+
static void on_shutdown(intptr_t fd, protocol_s *ws) {
|
169
|
+
if (ws && ((ws_s *)ws)->on_shutdown)
|
170
|
+
((ws_s *)ws)->on_shutdown((ws_s *)ws);
|
167
171
|
}
|
168
172
|
|
169
173
|
/* later */
|
170
|
-
static void websocket_write_impl(intptr_t fd,
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
char last,
|
176
|
-
char client);
|
177
|
-
|
178
|
-
static void on_data(intptr_t sockfd, protocol_s* _ws) {
|
179
|
-
#define ws ((ws_s*)_ws)
|
174
|
+
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
175
|
+
char first, char last, char client);
|
176
|
+
|
177
|
+
static void on_data(intptr_t sockfd, protocol_s *_ws) {
|
178
|
+
#define ws ((ws_s *)_ws)
|
180
179
|
if (ws == NULL || ws->protocol.service != WEBSOCKET_ID_STR)
|
181
180
|
return;
|
182
181
|
ssize_t len = 0;
|
@@ -185,10 +184,10 @@ static void on_data(intptr_t sockfd, protocol_s* _ws) {
|
|
185
184
|
ws->parser.pos = 0;
|
186
185
|
while (ws->parser.pos < len) {
|
187
186
|
// collect the frame's head
|
188
|
-
if (!(*(char*)(&ws->parser.head))) {
|
189
|
-
*((char*)(&(ws->parser.head))) = ws->parser.tmp_buffer[ws->parser.pos];
|
187
|
+
if (!(*(char *)(&ws->parser.head))) {
|
188
|
+
*((char *)(&(ws->parser.head))) = ws->parser.tmp_buffer[ws->parser.pos];
|
190
189
|
// save a copy if it's the first head in a fragmented message
|
191
|
-
if (!(*(char*)(&ws->parser.head2))) {
|
190
|
+
if (!(*(char *)(&ws->parser.head2))) {
|
192
191
|
ws->parser.head2 = ws->parser.head;
|
193
192
|
}
|
194
193
|
// advance
|
@@ -198,8 +197,9 @@ static void on_data(intptr_t sockfd, protocol_s* _ws) {
|
|
198
197
|
}
|
199
198
|
|
200
199
|
// save the mask and size information
|
201
|
-
if (!(*(char*)(&ws->parser.sdata))) {
|
202
|
-
*((char*)(&(ws->parser.sdata))) =
|
200
|
+
if (!(*(char *)(&ws->parser.sdata))) {
|
201
|
+
*((char *)(&(ws->parser.sdata))) =
|
202
|
+
ws->parser.tmp_buffer[ws->parser.pos];
|
203
203
|
// set length
|
204
204
|
ws->parser.state.at_len = ws->parser.sdata.size == 127
|
205
205
|
? 7
|
@@ -341,7 +341,7 @@ static void on_data(intptr_t sockfd, protocol_s* _ws) {
|
|
341
341
|
/* text data */
|
342
342
|
} else if (ws->parser.head2.op_code == 2) {
|
343
343
|
/* binary data */
|
344
|
-
} else
|
344
|
+
} else // not a recognized frame, don't act
|
345
345
|
goto reset_parser;
|
346
346
|
// call the on_message callback
|
347
347
|
|
@@ -386,8 +386,9 @@ static void on_data(intptr_t sockfd, protocol_s* _ws) {
|
|
386
386
|
// buffer
|
387
387
|
ws->parser.pos += ws->parser.data_len;
|
388
388
|
// clear the parser
|
389
|
-
*((char*)(&(ws->parser.state))) = *((char*)(&(ws->parser.sdata))) =
|
390
|
-
*((char*)(&(ws->parser.head2))) = *((char*)(&(ws->parser.head))) =
|
389
|
+
*((char *)(&(ws->parser.state))) = *((char *)(&(ws->parser.sdata))) =
|
390
|
+
*((char *)(&(ws->parser.head2))) = *((char *)(&(ws->parser.head))) =
|
391
|
+
0;
|
391
392
|
// // // the above should be the same as... but it isn't
|
392
393
|
// *((uint32_t*)(&(ws->parser.head))) = 0;
|
393
394
|
// set the union size to 0
|
@@ -402,20 +403,21 @@ static void on_data(intptr_t sockfd, protocol_s* _ws) {
|
|
402
403
|
Create/Destroy the websocket object
|
403
404
|
*/
|
404
405
|
|
405
|
-
static ws_s*
|
406
|
+
static ws_s *new_websocket() {
|
406
407
|
// allocate the protocol object (TODO: (maybe) pooling)
|
407
|
-
ws_s*
|
408
|
+
ws_s *ws = calloc(sizeof(*ws), 1);
|
408
409
|
|
409
410
|
// setup the protocol & protocol callbacks
|
410
411
|
ws->protocol.ping = ws_ping;
|
411
412
|
ws->protocol.service = WEBSOCKET_ID_STR;
|
412
413
|
ws->protocol.on_data = on_data;
|
413
414
|
ws->protocol.on_close = on_close;
|
415
|
+
ws->protocol.on_ready = on_ready;
|
414
416
|
ws->protocol.on_shutdown = on_shutdown;
|
415
417
|
// return the object
|
416
418
|
return ws;
|
417
419
|
}
|
418
|
-
static void destroy_ws(ws_s*
|
420
|
+
static void destroy_ws(ws_s *ws) {
|
419
421
|
if (ws->on_close)
|
420
422
|
ws->on_close(ws);
|
421
423
|
free_ws_buffer(ws, ws->buffer);
|
@@ -426,15 +428,11 @@ static void destroy_ws(ws_s* ws) {
|
|
426
428
|
Writing to the Websocket
|
427
429
|
*/
|
428
430
|
|
429
|
-
#define WS_MAX_FRAME_SIZE 65532
|
431
|
+
#define WS_MAX_FRAME_SIZE 65532 // should be less then `unsigned short`
|
430
432
|
|
431
|
-
static void websocket_write_impl(intptr_t fd,
|
432
|
-
void* data,
|
433
|
-
size_t len,
|
433
|
+
static void websocket_write_impl(intptr_t fd, void *data, size_t len,
|
434
434
|
char text, /* TODO: add client masking */
|
435
|
-
char first,
|
436
|
-
char last,
|
437
|
-
char client) {
|
435
|
+
char first, char last, char client) {
|
438
436
|
if (len < 126) {
|
439
437
|
struct {
|
440
438
|
unsigned op_code : 4;
|
@@ -468,9 +466,9 @@ static void websocket_write_impl(intptr_t fd,
|
|
468
466
|
.size = 126,
|
469
467
|
.masked = 0,
|
470
468
|
.length = htons(len)};
|
471
|
-
if (len >> 15) {
|
469
|
+
if (len >> 15) { // if len is larger then 32,767 Bytes.
|
472
470
|
/* head MUST be 4 bytes */
|
473
|
-
void*
|
471
|
+
void *buff = malloc(len + (client ? 8 : 4));
|
474
472
|
memcpy(buff, &head, 4);
|
475
473
|
memcpy(buff + (client ? 8 : 4), data, len);
|
476
474
|
sock_write2(.fduuid = fd, .buffer = buff, .length = len + 4, .move = 1);
|
@@ -487,6 +485,7 @@ static void websocket_write_impl(intptr_t fd,
|
|
487
485
|
websocket_write_impl(fd, data, WS_MAX_FRAME_SIZE, text, first, 0, client);
|
488
486
|
data += WS_MAX_FRAME_SIZE;
|
489
487
|
first = 0;
|
488
|
+
len -= WS_MAX_FRAME_SIZE;
|
490
489
|
}
|
491
490
|
websocket_write_impl(fd, data, len, text, first, 1, client);
|
492
491
|
}
|
@@ -513,7 +512,7 @@ ssize_t websocket_upgrade(websocket_settings_s settings) {
|
|
513
512
|
if (settings.timeout == 0)
|
514
513
|
settings.timeout = 40; /* defaults to 40 seconds */
|
515
514
|
// make sure we have a response object.
|
516
|
-
http_response_s*
|
515
|
+
http_response_s *response = settings.response;
|
517
516
|
if (response == NULL) {
|
518
517
|
/* initialize a default upgrade response */
|
519
518
|
tmp_response = http_response_init(settings.request);
|
@@ -521,7 +520,7 @@ ssize_t websocket_upgrade(websocket_settings_s settings) {
|
|
521
520
|
} else
|
522
521
|
settings.request = response->metadata.request;
|
523
522
|
// allocate the protocol object (TODO: (maybe) pooling)
|
524
|
-
ws_s*
|
523
|
+
ws_s *ws = new_websocket();
|
525
524
|
if (!ws)
|
526
525
|
goto refuse;
|
527
526
|
|
@@ -530,13 +529,14 @@ ssize_t websocket_upgrade(websocket_settings_s settings) {
|
|
530
529
|
// Setup ws callbacks
|
531
530
|
ws->on_close = settings.on_close;
|
532
531
|
ws->on_message = settings.on_message;
|
532
|
+
ws->on_ready = settings.on_ready;
|
533
533
|
ws->on_shutdown = settings.on_shutdown;
|
534
534
|
|
535
535
|
// setup any user data
|
536
536
|
ws->udata = settings.udata;
|
537
537
|
// buffer limits
|
538
538
|
ws->max_msg_size = settings.max_msg_size;
|
539
|
-
const char*
|
539
|
+
const char *recv_str;
|
540
540
|
|
541
541
|
recv_str =
|
542
542
|
http_request_find_header(settings.request, "sec-websocket-version", 21);
|
@@ -596,7 +596,7 @@ cleanup:
|
|
596
596
|
http_response_finish(response);
|
597
597
|
if (response->status == 101) {
|
598
598
|
// update the protocol object, cleanning up the old one
|
599
|
-
server_switch_protocol(ws->fd, (void*)ws);
|
599
|
+
server_switch_protocol(ws->fd, (void *)ws);
|
600
600
|
// we have an active websocket connection - prep the connection buffer
|
601
601
|
ws->buffer = create_ws_buffer(ws);
|
602
602
|
// update the timeout
|
@@ -609,26 +609,22 @@ cleanup:
|
|
609
609
|
destroy_ws(ws);
|
610
610
|
return -1;
|
611
611
|
}
|
612
|
-
#define websocket_upgrade(...)
|
612
|
+
#define websocket_upgrade(...) \
|
613
613
|
websocket_upgrade((websocket_settings_s){__VA_ARGS__})
|
614
614
|
|
615
615
|
/** Returns the opaque user data associated with the websocket. */
|
616
|
-
void*
|
617
|
-
return ws->udata;
|
618
|
-
}
|
616
|
+
void *websocket_get_udata(ws_s *ws) { return ws->udata; }
|
619
617
|
/** Returns the the process specific connection's UUID (see `libsock`). */
|
620
|
-
intptr_t websocket_get_fduuid(ws_s*
|
621
|
-
return ws->fd;
|
622
|
-
}
|
618
|
+
intptr_t websocket_get_fduuid(ws_s *ws) { return ws->fd; }
|
623
619
|
/** Sets the opaque user data associated with the websocket.
|
624
620
|
* Returns the old value, if any. */
|
625
|
-
void*
|
626
|
-
void*
|
621
|
+
void *websocket_set_udata(ws_s *ws, void *udata) {
|
622
|
+
void *old = ws->udata;
|
627
623
|
ws->udata = udata;
|
628
624
|
return old;
|
629
625
|
}
|
630
626
|
/** Writes data to the websocket. Returns -1 on failure (0 on success). */
|
631
|
-
int websocket_write(ws_s*
|
627
|
+
int websocket_write(ws_s *ws, void *data, size_t size, uint8_t is_text) {
|
632
628
|
if (sock_isvalid(ws->fd)) {
|
633
629
|
websocket_write_impl(ws->fd, data, size, is_text, 1, 1,
|
634
630
|
ws->parser.state.client);
|
@@ -637,8 +633,8 @@ int websocket_write(ws_s* ws, void* data, size_t size, uint8_t is_text) {
|
|
637
633
|
return -1;
|
638
634
|
}
|
639
635
|
/** Closes a websocket connection. */
|
640
|
-
void websocket_close(ws_s*
|
641
|
-
sock_packet_s*
|
636
|
+
void websocket_close(ws_s *ws) {
|
637
|
+
sock_packet_s *packet;
|
642
638
|
while ((packet = sock_checkout_packet()) == NULL)
|
643
639
|
sock_flush_all();
|
644
640
|
*packet = (sock_packet_s){
|
@@ -652,9 +648,7 @@ void websocket_close(ws_s* ws) {
|
|
652
648
|
/**
|
653
649
|
Counts the number of websocket connections.
|
654
650
|
*/
|
655
|
-
size_t websocket_count(ws_s*
|
656
|
-
return server_count(WEBSOCKET_ID_STR);
|
657
|
-
}
|
651
|
+
size_t websocket_count(ws_s *ws) { return server_count(WEBSOCKET_ID_STR); }
|
658
652
|
|
659
653
|
/*******************************************************************************
|
660
654
|
Each Implementation
|
@@ -662,20 +656,20 @@ Each Implementation
|
|
662
656
|
|
663
657
|
/** A task container. */
|
664
658
|
struct WSTask {
|
665
|
-
void (*task)(ws_s*, void*);
|
666
|
-
void (*on_finish)(ws_s*, void*);
|
667
|
-
void*
|
659
|
+
void (*task)(ws_s *, void *);
|
660
|
+
void (*on_finish)(ws_s *, void *);
|
661
|
+
void *arg;
|
668
662
|
};
|
669
663
|
/** Performs a task on each websocket connection that shares the same process */
|
670
|
-
static void perform_ws_task(intptr_t fd, protocol_s*
|
671
|
-
struct WSTask*
|
672
|
-
tsk->task((ws_s*)(_ws), tsk->arg);
|
664
|
+
static void perform_ws_task(intptr_t fd, protocol_s *_ws, void *_arg) {
|
665
|
+
struct WSTask *tsk = _arg;
|
666
|
+
tsk->task((ws_s *)(_ws), tsk->arg);
|
673
667
|
}
|
674
668
|
/** clears away a wesbocket task. */
|
675
|
-
static void finish_ws_task(intptr_t fd, protocol_s*
|
676
|
-
struct WSTask*
|
669
|
+
static void finish_ws_task(intptr_t fd, protocol_s *_ws, void *_arg) {
|
670
|
+
struct WSTask *tsk = _arg;
|
677
671
|
if (tsk->on_finish)
|
678
|
-
tsk->on_finish((ws_s*)(_ws), tsk->arg);
|
672
|
+
tsk->on_finish((ws_s *)(_ws), tsk->arg);
|
679
673
|
free(tsk);
|
680
674
|
}
|
681
675
|
|
@@ -683,11 +677,10 @@ static void finish_ws_task(intptr_t fd, protocol_s* _ws, void* _arg) {
|
|
683
677
|
Performs a task on each websocket connection that shares the same process
|
684
678
|
(except the originating `ws_s` connection which is allowed to be NULL).
|
685
679
|
*/
|
686
|
-
void websocket_each(ws_s*
|
687
|
-
void (*task)(ws_s*
|
688
|
-
void* arg
|
689
|
-
|
690
|
-
struct WSTask* tsk = malloc(sizeof(*tsk));
|
680
|
+
void websocket_each(ws_s *ws_originator,
|
681
|
+
void (*task)(ws_s *ws_target, void *arg), void *arg,
|
682
|
+
void (*on_finish)(ws_s *ws_originator, void *arg)) {
|
683
|
+
struct WSTask *tsk = malloc(sizeof(*tsk));
|
691
684
|
tsk->arg = arg;
|
692
685
|
tsk->on_finish = on_finish;
|
693
686
|
tsk->task = task;
|