agoo 1.2.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +11 -9
- data/ext/agoo/agoo.c +45 -0
- data/ext/agoo/base64.c +107 -0
- data/ext/agoo/base64.h +15 -0
- data/ext/agoo/ccache.c +301 -0
- data/ext/agoo/ccache.h +53 -0
- data/ext/agoo/con.c +522 -82
- data/ext/agoo/con.h +7 -5
- data/ext/agoo/debug.c +121 -7
- data/ext/agoo/debug.h +11 -6
- data/ext/agoo/error_stream.c +5 -6
- data/ext/agoo/error_stream.h +1 -1
- data/ext/agoo/extconf.rb +2 -1
- data/ext/agoo/hook.c +4 -4
- data/ext/agoo/hook.h +1 -0
- data/ext/agoo/http.c +2 -2
- data/ext/agoo/http.h +2 -0
- data/ext/agoo/log.c +604 -219
- data/ext/agoo/log.h +20 -7
- data/ext/agoo/page.c +20 -23
- data/ext/agoo/page.h +2 -0
- data/ext/agoo/pub.c +111 -0
- data/ext/agoo/pub.h +40 -0
- data/ext/agoo/queue.c +2 -2
- data/ext/agoo/rack_logger.c +15 -71
- data/ext/agoo/rack_logger.h +1 -1
- data/ext/agoo/request.c +96 -21
- data/ext/agoo/request.h +23 -12
- data/ext/agoo/res.c +5 -2
- data/ext/agoo/res.h +4 -0
- data/ext/agoo/response.c +13 -12
- data/ext/agoo/response.h +1 -2
- data/ext/agoo/server.c +290 -428
- data/ext/agoo/server.h +10 -10
- data/ext/agoo/sha1.c +148 -0
- data/ext/agoo/sha1.h +10 -0
- data/ext/agoo/sse.c +26 -0
- data/ext/agoo/sse.h +12 -0
- data/ext/agoo/sub.c +111 -0
- data/ext/agoo/sub.h +36 -0
- data/ext/agoo/subscription.c +54 -0
- data/ext/agoo/subscription.h +18 -0
- data/ext/agoo/text.c +26 -4
- data/ext/agoo/text.h +2 -0
- data/ext/agoo/types.h +13 -0
- data/ext/agoo/upgraded.c +148 -0
- data/ext/agoo/upgraded.h +13 -0
- data/ext/agoo/websocket.c +248 -0
- data/ext/agoo/websocket.h +27 -0
- data/lib/agoo/version.rb +1 -1
- data/lib/rack/handler/agoo.rb +13 -6
- data/test/base_handler_test.rb +24 -22
- data/test/log_test.rb +146 -199
- data/test/rack_handler_test.rb +19 -20
- data/test/static_test.rb +30 -28
- metadata +23 -7
- data/test/rrr/test.rb +0 -26
- data/test/tests.rb +0 -8
@@ -0,0 +1,18 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef __AGOO_SUBSCRIPTION_H__
|
4
|
+
#define __AGOO_SUBSCRIPTION_H__
|
5
|
+
|
6
|
+
#include <ruby.h>
|
7
|
+
|
8
|
+
typedef struct _Subscription {
|
9
|
+
VALUE self;
|
10
|
+
uint64_t cid;
|
11
|
+
uint64_t id;
|
12
|
+
VALUE handler;
|
13
|
+
} *Subscription;
|
14
|
+
|
15
|
+
extern void subscription_init(VALUE mod);
|
16
|
+
extern VALUE subscription_new(uint64_t cid, uint64_t id, VALUE handler);
|
17
|
+
|
18
|
+
#endif // __AGOO_SUBSCRIPTION_H__
|
data/ext/agoo/text.c
CHANGED
@@ -12,9 +12,10 @@ text_create(const char *str, int len) {
|
|
12
12
|
Text t = (Text)malloc(sizeof(struct _Text) - TEXT_MIN_SIZE + len + 1);
|
13
13
|
|
14
14
|
if (NULL != t) {
|
15
|
-
DEBUG_ALLOC(mem_text)
|
15
|
+
DEBUG_ALLOC(mem_text, t)
|
16
16
|
t->len = len;
|
17
17
|
t->alen = len;
|
18
|
+
t->bin = false;
|
18
19
|
atomic_init(&t->ref_cnt, 0);
|
19
20
|
memcpy(t->text, str, len);
|
20
21
|
t->text[len] = '\0';
|
@@ -27,9 +28,10 @@ text_allocate(int len) {
|
|
27
28
|
Text t = (Text)malloc(sizeof(struct _Text) - TEXT_MIN_SIZE + len + 1);
|
28
29
|
|
29
30
|
if (NULL != t) {
|
30
|
-
DEBUG_ALLOC(mem_text)
|
31
|
+
DEBUG_ALLOC(mem_text, t)
|
31
32
|
t->len = 0;
|
32
33
|
t->alen = len;
|
34
|
+
t->bin = false;
|
33
35
|
atomic_init(&t->ref_cnt, 0);
|
34
36
|
*t->text = '\0';
|
35
37
|
}
|
@@ -44,7 +46,7 @@ text_ref(Text t) {
|
|
44
46
|
void
|
45
47
|
text_release(Text t) {
|
46
48
|
if (1 >= atomic_fetch_sub(&t->ref_cnt, 1)) {
|
47
|
-
DEBUG_FREE(mem_text)
|
49
|
+
DEBUG_FREE(mem_text, t)
|
48
50
|
free(t);
|
49
51
|
}
|
50
52
|
}
|
@@ -61,7 +63,6 @@ text_append(Text t, const char *s, int len) {
|
|
61
63
|
if (NULL == (t = (Text)realloc(t, size))) {
|
62
64
|
return NULL;
|
63
65
|
}
|
64
|
-
DEBUG_ALLOC(mem_text)
|
65
66
|
t->alen = new_len;
|
66
67
|
}
|
67
68
|
memcpy(t->text + t->len, s, len);
|
@@ -70,3 +71,24 @@ text_append(Text t, const char *s, int len) {
|
|
70
71
|
|
71
72
|
return t;
|
72
73
|
}
|
74
|
+
|
75
|
+
Text
|
76
|
+
text_prepend(Text t, const char *s, int len) {
|
77
|
+
if (0 >= len) {
|
78
|
+
len = (int)strlen(s);
|
79
|
+
}
|
80
|
+
if (t->alen <= t->len + len) {
|
81
|
+
long new_len = t->alen + t->alen / 2;
|
82
|
+
size_t size = sizeof(struct _Text) - TEXT_MIN_SIZE + new_len + 1;
|
83
|
+
|
84
|
+
if (NULL == (t = (Text)realloc(t, size))) {
|
85
|
+
return NULL;
|
86
|
+
}
|
87
|
+
t->alen = new_len;
|
88
|
+
}
|
89
|
+
memmove(t->text + len, t->text, t->len + 1);
|
90
|
+
memcpy(t->text, s, len);
|
91
|
+
t->len += len;
|
92
|
+
|
93
|
+
return t;
|
94
|
+
}
|
data/ext/agoo/text.h
CHANGED
@@ -12,6 +12,7 @@ typedef struct _Text {
|
|
12
12
|
long len; // length of valid text
|
13
13
|
long alen; // size of allocated text
|
14
14
|
atomic_int ref_cnt;
|
15
|
+
bool bin;
|
15
16
|
char text[TEXT_MIN_SIZE];
|
16
17
|
} *Text;
|
17
18
|
|
@@ -20,5 +21,6 @@ extern Text text_allocate(int len);
|
|
20
21
|
extern void text_ref(Text t);
|
21
22
|
extern void text_release(Text t);
|
22
23
|
extern Text text_append(Text t, const char *s, int len);
|
24
|
+
extern Text text_prepend(Text t, const char *s, int len);
|
23
25
|
|
24
26
|
#endif /* __AGOO_TEXT_H__ */
|
data/ext/agoo/types.h
CHANGED
@@ -11,8 +11,21 @@ typedef enum {
|
|
11
11
|
OPTIONS = 'O',
|
12
12
|
POST = 'P',
|
13
13
|
PUT = 'U',
|
14
|
+
PATCH = 'T',
|
14
15
|
ALL = 'A',
|
15
16
|
NONE = '\0',
|
17
|
+
|
18
|
+
ON_MSG = 'M', // use for on_message callback
|
19
|
+
ON_BIN = 'B', // use for on_message callback with binary (ASCII8BIT)
|
20
|
+
ON_CLOSE = 'X', // use for on_close callback
|
21
|
+
ON_SHUTDOWN = 'S', // use for on_shotdown callback
|
22
|
+
ON_EMPTY = 'E', // use for on_drained callback
|
16
23
|
} Method;
|
17
24
|
|
25
|
+
typedef enum {
|
26
|
+
CON_HTTP = 'H',
|
27
|
+
CON_WS = 'W',
|
28
|
+
CON_SSE = 'S',
|
29
|
+
} ConKind;
|
30
|
+
|
18
31
|
#endif // __AGOO_TYPES_H__
|
data/ext/agoo/upgraded.c
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
|
5
|
+
#include <ruby/encoding.h>
|
6
|
+
|
7
|
+
#include "pub.h"
|
8
|
+
#include "server.h"
|
9
|
+
#include "upgraded.h"
|
10
|
+
|
11
|
+
static VALUE upgraded_mod = Qundef;
|
12
|
+
|
13
|
+
static ID cid_id = 0;
|
14
|
+
static ID sid_id = 0;
|
15
|
+
static ID on_open_id = 0;
|
16
|
+
static ID to_s_id = 0;
|
17
|
+
|
18
|
+
/* Document-method: write
|
19
|
+
*
|
20
|
+
* call-seq: write(msg)
|
21
|
+
*
|
22
|
+
* Writes a message to the WebSocket or SSe connection.
|
23
|
+
*/
|
24
|
+
static VALUE
|
25
|
+
up_write(VALUE self, VALUE msg) {
|
26
|
+
uint64_t cid = RB_FIX2ULONG(rb_ivar_get(self, cid_id));
|
27
|
+
Pub p;
|
28
|
+
|
29
|
+
if (!the_server.active) {
|
30
|
+
rb_raise(rb_eIOError, "Server shutdown.");
|
31
|
+
}
|
32
|
+
if (0 < the_server.max_push_pending) {
|
33
|
+
int pending = cc_get_pending(&the_server.con_cache, cid);
|
34
|
+
|
35
|
+
if (the_server.max_push_pending <= pending) {
|
36
|
+
rb_raise(rb_eIOError, "Too many writes pending. Try again later.");
|
37
|
+
}
|
38
|
+
}
|
39
|
+
if (T_STRING == rb_type(msg)) {
|
40
|
+
if (RB_ENCODING_IS_ASCII8BIT(msg)) {
|
41
|
+
p = pub_write(cid, StringValuePtr(msg), RSTRING_LEN(msg), true);
|
42
|
+
} else {
|
43
|
+
p = pub_write(cid, StringValuePtr(msg), RSTRING_LEN(msg), false);
|
44
|
+
}
|
45
|
+
} else {
|
46
|
+
volatile VALUE rs = rb_funcall(msg, to_s_id, 0);
|
47
|
+
|
48
|
+
p = pub_write(cid, StringValuePtr(rs), RSTRING_LEN(rs), false);
|
49
|
+
}
|
50
|
+
cc_pending_inc(&the_server.con_cache, cid);
|
51
|
+
queue_push(&the_server.pub_queue, p);
|
52
|
+
|
53
|
+
return Qnil;
|
54
|
+
}
|
55
|
+
|
56
|
+
/* Document-method: subscribe
|
57
|
+
*
|
58
|
+
* call-seq: subscribe(subject)
|
59
|
+
*
|
60
|
+
* Subscribes to messages published on the specified subject. (not implemented
|
61
|
+
* yet)
|
62
|
+
*/
|
63
|
+
static VALUE
|
64
|
+
up_subscribe(VALUE self, VALUE subject) {
|
65
|
+
|
66
|
+
// printf("*** subscribe called\n");
|
67
|
+
|
68
|
+
// increment _sid
|
69
|
+
// create subscription
|
70
|
+
// push pub onto server pub_queue
|
71
|
+
// TBD create subscription object and return it
|
72
|
+
|
73
|
+
return Qnil;
|
74
|
+
}
|
75
|
+
|
76
|
+
/* Document-method: close
|
77
|
+
*
|
78
|
+
* call-seq: close()
|
79
|
+
*
|
80
|
+
* Closes the connections associated with the handler.
|
81
|
+
*/
|
82
|
+
static VALUE
|
83
|
+
up_close(VALUE self) {
|
84
|
+
uint64_t cid = RB_FIX2ULONG(rb_ivar_get(self, cid_id));
|
85
|
+
|
86
|
+
if (!the_server.active) {
|
87
|
+
rb_raise(rb_eIOError, "Server shutdown.");
|
88
|
+
}
|
89
|
+
queue_push(&the_server.pub_queue, pub_close(cid));
|
90
|
+
|
91
|
+
return Qnil;
|
92
|
+
}
|
93
|
+
|
94
|
+
/* Document-method: pending
|
95
|
+
*
|
96
|
+
* call-seq: pending()
|
97
|
+
*
|
98
|
+
* Returns the number of pending WebSocket or SSE writes. If the connection is
|
99
|
+
* closed then -1 is returned.
|
100
|
+
*/
|
101
|
+
static VALUE
|
102
|
+
pending(VALUE self) {
|
103
|
+
uint64_t cid = RB_FIX2ULONG(rb_ivar_get(self, cid_id));
|
104
|
+
|
105
|
+
if (!the_server.active) {
|
106
|
+
rb_raise(rb_eIOError, "Server shutdown.");
|
107
|
+
}
|
108
|
+
return INT2NUM(cc_get_pending(&the_server.con_cache, cid));
|
109
|
+
}
|
110
|
+
|
111
|
+
void
|
112
|
+
upgraded_extend(uint64_t cid, VALUE obj) {
|
113
|
+
if (!the_server.active) {
|
114
|
+
rb_raise(rb_eIOError, "Server shutdown.");
|
115
|
+
}
|
116
|
+
rb_ivar_set(obj, cid_id, ULONG2NUM(cid));
|
117
|
+
rb_ivar_set(obj, sid_id, ULONG2NUM(0)); // used as counter for subscription id
|
118
|
+
rb_extend_object(obj, upgraded_mod);
|
119
|
+
if (rb_respond_to(obj, on_open_id)) {
|
120
|
+
rb_funcall(obj, on_open_id, 0);
|
121
|
+
}
|
122
|
+
cc_set_handler(&the_server.con_cache, cid, obj,
|
123
|
+
rb_respond_to(obj, rb_intern("on_drained")),
|
124
|
+
rb_respond_to(obj, rb_intern("on_close")),
|
125
|
+
rb_respond_to(obj, rb_intern("on_shutdown")),
|
126
|
+
rb_respond_to(obj, rb_intern("on_message")));
|
127
|
+
rb_funcall(obj, rb_intern("to_s"), 0);
|
128
|
+
}
|
129
|
+
|
130
|
+
/* Document-module: Agoo::Upgraded
|
131
|
+
*
|
132
|
+
* Adds methods to a handler of WebSocket and SSe connections.
|
133
|
+
*/
|
134
|
+
void
|
135
|
+
upgraded_init(VALUE mod) {
|
136
|
+
upgraded_mod = rb_define_module_under(mod, "Upgraded");
|
137
|
+
|
138
|
+
rb_define_method(upgraded_mod, "write", up_write, 1);
|
139
|
+
rb_define_method(upgraded_mod, "subscribe", up_subscribe, 1);
|
140
|
+
rb_define_method(upgraded_mod, "close", up_close, 0);
|
141
|
+
rb_define_method(upgraded_mod, "pending", pending, 0);
|
142
|
+
|
143
|
+
cid_id = rb_intern("_cid");
|
144
|
+
sid_id = rb_intern("_sid");
|
145
|
+
on_open_id = rb_intern("on_open");
|
146
|
+
to_s_id = rb_intern("to_s");
|
147
|
+
|
148
|
+
}
|
data/ext/agoo/upgraded.h
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef __AGOO_UPGRADED_H__
|
4
|
+
#define __AGOO_UPGRADED_H__
|
5
|
+
|
6
|
+
#include <stdint.h>
|
7
|
+
|
8
|
+
#include <ruby.h>
|
9
|
+
|
10
|
+
extern void upgraded_init(VALUE mod);
|
11
|
+
extern void upgraded_extend(uint64_t cid, VALUE obj);
|
12
|
+
|
13
|
+
#endif // __AGOO_UPGRADED_H__
|
@@ -0,0 +1,248 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include <stdint.h>
|
4
|
+
|
5
|
+
#include "base64.h"
|
6
|
+
#include "con.h"
|
7
|
+
#include "debug.h"
|
8
|
+
#include "request.h"
|
9
|
+
#include "sha1.h"
|
10
|
+
#include "text.h"
|
11
|
+
#include "websocket.h"
|
12
|
+
|
13
|
+
#define MAX_KEY_LEN 1024
|
14
|
+
|
15
|
+
static const char up_con[] = "Upgrade: websocket\r\nConnection: Upgrade\r\n";
|
16
|
+
static const char ws_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
17
|
+
static const char ws_protocol[] = "Sec-WebSocket-Protocol: ";
|
18
|
+
static const char ws_accept[] = "Sec-WebSocket-Accept: ";
|
19
|
+
|
20
|
+
//static const uint8_t close_msg[] = "\x88\x02\x03\xE8";
|
21
|
+
|
22
|
+
Text
|
23
|
+
ws_add_headers(Req req, Text t) {
|
24
|
+
int klen = 0;
|
25
|
+
const char *key;
|
26
|
+
|
27
|
+
t = text_append(t, up_con, sizeof(up_con) - 1);
|
28
|
+
if (NULL != (key = con_header_value(req->header.start, req->header.len, "Sec-WebSocket-Key", &klen)) &&
|
29
|
+
klen + sizeof(ws_magic) < MAX_KEY_LEN) {
|
30
|
+
char buf[MAX_KEY_LEN];
|
31
|
+
unsigned char sha[32];
|
32
|
+
int len = klen + sizeof(ws_magic) - 1;
|
33
|
+
|
34
|
+
strncpy(buf, key, klen);
|
35
|
+
strcpy(buf + klen, ws_magic);
|
36
|
+
sha1((unsigned char*)buf, len, sha);
|
37
|
+
len = b64_to(sha, SHA1_DIGEST_SIZE, buf);
|
38
|
+
|
39
|
+
t = text_append(t, ws_accept, sizeof(ws_accept) - 1);
|
40
|
+
t = text_append(t, buf, len);
|
41
|
+
t = text_append(t, "\r\n", 2);
|
42
|
+
}
|
43
|
+
if (NULL != (key = con_header_value(req->header.start, req->header.len, "Sec-WebSocket-Protocol", &klen))) {
|
44
|
+
t = text_append(t, ws_protocol, sizeof(ws_protocol) - 1);
|
45
|
+
t = text_append(t, key, klen);
|
46
|
+
t = text_append(t, "\r\n", 2);
|
47
|
+
}
|
48
|
+
return t;
|
49
|
+
}
|
50
|
+
|
51
|
+
Text
|
52
|
+
ws_expand(Text t) {
|
53
|
+
uint8_t buf[16];
|
54
|
+
uint8_t *b = buf;
|
55
|
+
uint8_t opcode = t->bin ? WS_OP_BIN : WS_OP_TEXT;
|
56
|
+
|
57
|
+
*b++ = 0x80 | (uint8_t)opcode;
|
58
|
+
// send unmasked
|
59
|
+
if (125 >= t->len) {
|
60
|
+
*b++ = (uint8_t)t->len;
|
61
|
+
} else if (0xFFFF >= t->len) {
|
62
|
+
*b++ = (uint8_t)0x7E;
|
63
|
+
*b++ = (uint8_t)((t->len >> 8) & 0xFF);
|
64
|
+
*b++ = (uint8_t)(t->len & 0xFF);
|
65
|
+
} else {
|
66
|
+
int i;
|
67
|
+
|
68
|
+
*b++ = (uint8_t)0x7F;
|
69
|
+
for (i = 56; 0 <= i; i -= 8) {
|
70
|
+
*b++ = (uint8_t)((t->len >> i) & 0xFF);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
return text_prepend(t, (const char*)buf, (int)(b - buf));
|
74
|
+
}
|
75
|
+
|
76
|
+
size_t
|
77
|
+
ws_decode(char *buf, size_t mlen) {
|
78
|
+
uint8_t *b = (uint8_t*)buf;
|
79
|
+
bool is_masked;
|
80
|
+
uint8_t *payload;
|
81
|
+
uint64_t plen;
|
82
|
+
|
83
|
+
b++; // op
|
84
|
+
is_masked = 0x80 & *b;
|
85
|
+
plen = 0x7F & *b;
|
86
|
+
b++;
|
87
|
+
if (126 == plen) {
|
88
|
+
plen = *b++;
|
89
|
+
plen = (plen << 8) | *b++;
|
90
|
+
} else if (127 == plen) {
|
91
|
+
plen = *b++;
|
92
|
+
plen = (plen << 8) | *b++;
|
93
|
+
plen = (plen << 8) | *b++;
|
94
|
+
plen = (plen << 8) | *b++;
|
95
|
+
plen = (plen << 8) | *b++;
|
96
|
+
plen = (plen << 8) | *b++;
|
97
|
+
plen = (plen << 8) | *b++;
|
98
|
+
plen = (plen << 8) | *b++;
|
99
|
+
}
|
100
|
+
if (is_masked) {
|
101
|
+
uint8_t mask[4];
|
102
|
+
uint64_t i;
|
103
|
+
|
104
|
+
for (i = 0; i < 4; i++, b++) {
|
105
|
+
mask[i] = *b;
|
106
|
+
}
|
107
|
+
payload = b;
|
108
|
+
for (i = 0; i < plen; i++, b++) {
|
109
|
+
*b = *b ^ mask[i % 4];
|
110
|
+
}
|
111
|
+
} else {
|
112
|
+
payload = b;
|
113
|
+
b += plen;
|
114
|
+
}
|
115
|
+
memmove(buf, payload, plen);
|
116
|
+
buf[plen] = '\0';
|
117
|
+
|
118
|
+
return plen;
|
119
|
+
}
|
120
|
+
|
121
|
+
// if -1 then err, 0 not ready yet, positive is completed length
|
122
|
+
long
|
123
|
+
ws_calc_len(Con c, uint8_t *buf, size_t cnt) {
|
124
|
+
uint8_t *b = buf;
|
125
|
+
bool is_masked;
|
126
|
+
uint64_t plen;
|
127
|
+
|
128
|
+
if (0 == (0x80 & *b)) {
|
129
|
+
log_cat(&error_cat, "FIN must be 1. Websocket continuation not implemented on connection %llu.", c->id);
|
130
|
+
return -1;
|
131
|
+
}
|
132
|
+
b++;
|
133
|
+
is_masked = 0x80 & *b;
|
134
|
+
plen = 0x7F & *b;
|
135
|
+
if (cnt < 3) {
|
136
|
+
return 0; // not read yet
|
137
|
+
}
|
138
|
+
b++;
|
139
|
+
if (126 == plen) {
|
140
|
+
if (cnt < 5) {
|
141
|
+
return 0; // not read yet
|
142
|
+
}
|
143
|
+
plen = *b++;
|
144
|
+
plen = (plen << 8) | *b++;
|
145
|
+
} else if (127 == plen) {
|
146
|
+
if (c->bcnt < 11) {
|
147
|
+
return 0; // not read yet
|
148
|
+
}
|
149
|
+
plen = *b++;
|
150
|
+
plen = (plen << 8) | *b++;
|
151
|
+
plen = (plen << 8) | *b++;
|
152
|
+
plen = (plen << 8) | *b++;
|
153
|
+
plen = (plen << 8) | *b++;
|
154
|
+
plen = (plen << 8) | *b++;
|
155
|
+
plen = (plen << 8) | *b++;
|
156
|
+
plen = (plen << 8) | *b++;
|
157
|
+
}
|
158
|
+
return (long)(b - buf) + (is_masked ? 4 : 0) + plen;
|
159
|
+
}
|
160
|
+
|
161
|
+
// Return true on error otherwise false.
|
162
|
+
bool
|
163
|
+
ws_create_req(Con c, long mlen) {
|
164
|
+
uint8_t op = 0x0F & *c->buf;
|
165
|
+
|
166
|
+
if (NULL == (c->req = request_create(mlen))) {
|
167
|
+
log_cat(&error_cat, "Out of memory attempting to allocate request.");
|
168
|
+
return true;
|
169
|
+
}
|
170
|
+
if (NULL == c->slot || Qnil == c->slot->handler) {
|
171
|
+
return true;
|
172
|
+
}
|
173
|
+
memset(c->req, 0, sizeof(struct _Req));
|
174
|
+
if ((long)c->bcnt <= mlen) {
|
175
|
+
memcpy(c->req->msg, c->buf, c->bcnt);
|
176
|
+
if ((long)c->bcnt < mlen) {
|
177
|
+
memset(c->req->msg + c->bcnt, 0, mlen - c->bcnt);
|
178
|
+
}
|
179
|
+
} else {
|
180
|
+
memcpy(c->req->msg, c->buf, mlen);
|
181
|
+
}
|
182
|
+
c->req->msg[mlen] = '\0';
|
183
|
+
c->req->mlen = mlen;
|
184
|
+
c->req->method = (WS_OP_BIN == op) ? ON_BIN : ON_MSG;
|
185
|
+
c->req->upgrade = UP_NONE;
|
186
|
+
c->req->cid = c->id;
|
187
|
+
c->req->res = NULL;
|
188
|
+
c->req->handler_type = PUSH_HOOK;
|
189
|
+
if (c->slot->on_msg) {
|
190
|
+
c->req->handler = c->slot->handler;
|
191
|
+
} else {
|
192
|
+
c->req->handler = Qnil;
|
193
|
+
}
|
194
|
+
return false;
|
195
|
+
}
|
196
|
+
|
197
|
+
void
|
198
|
+
ws_req_close(Con c) {
|
199
|
+
if (NULL != c->slot && Qnil != c->slot->handler && c->slot->on_close) {
|
200
|
+
Req req = request_create(0);
|
201
|
+
|
202
|
+
req->cid = c->id;
|
203
|
+
req->method = ON_CLOSE;
|
204
|
+
req->handler_type = PUSH_HOOK;
|
205
|
+
req->handler = c->slot->handler;
|
206
|
+
queue_push(&the_server.eval_queue, (void*)req);
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
void
|
211
|
+
ws_ping(Con c) {
|
212
|
+
Res res;
|
213
|
+
|
214
|
+
if (NULL == (res = res_create())) {
|
215
|
+
log_cat(&error_cat, "Memory allocation of response failed on connection %llu.", c->id);
|
216
|
+
} else {
|
217
|
+
DEBUG_ALLOC(mem_res, res)
|
218
|
+
if (NULL == c->res_tail) {
|
219
|
+
c->res_head = res;
|
220
|
+
} else {
|
221
|
+
c->res_tail->next = res;
|
222
|
+
}
|
223
|
+
c->res_tail = res;
|
224
|
+
res->close = false;
|
225
|
+
res->con_kind = CON_WS;
|
226
|
+
res->ping = true;
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
void
|
231
|
+
ws_pong(Con c) {
|
232
|
+
Res res;
|
233
|
+
|
234
|
+
if (NULL == (res = res_create())) {
|
235
|
+
log_cat(&error_cat, "Memory allocation of response failed on connection %llu.", c->id);
|
236
|
+
} else {
|
237
|
+
DEBUG_ALLOC(mem_res, res)
|
238
|
+
if (NULL == c->res_tail) {
|
239
|
+
c->res_head = res;
|
240
|
+
} else {
|
241
|
+
c->res_tail->next = res;
|
242
|
+
}
|
243
|
+
c->res_tail = res;
|
244
|
+
res->close = false;
|
245
|
+
res->con_kind = CON_WS;
|
246
|
+
res->pong = true;
|
247
|
+
}
|
248
|
+
}
|