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.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -9
  3. data/ext/agoo/agoo.c +45 -0
  4. data/ext/agoo/base64.c +107 -0
  5. data/ext/agoo/base64.h +15 -0
  6. data/ext/agoo/ccache.c +301 -0
  7. data/ext/agoo/ccache.h +53 -0
  8. data/ext/agoo/con.c +522 -82
  9. data/ext/agoo/con.h +7 -5
  10. data/ext/agoo/debug.c +121 -7
  11. data/ext/agoo/debug.h +11 -6
  12. data/ext/agoo/error_stream.c +5 -6
  13. data/ext/agoo/error_stream.h +1 -1
  14. data/ext/agoo/extconf.rb +2 -1
  15. data/ext/agoo/hook.c +4 -4
  16. data/ext/agoo/hook.h +1 -0
  17. data/ext/agoo/http.c +2 -2
  18. data/ext/agoo/http.h +2 -0
  19. data/ext/agoo/log.c +604 -219
  20. data/ext/agoo/log.h +20 -7
  21. data/ext/agoo/page.c +20 -23
  22. data/ext/agoo/page.h +2 -0
  23. data/ext/agoo/pub.c +111 -0
  24. data/ext/agoo/pub.h +40 -0
  25. data/ext/agoo/queue.c +2 -2
  26. data/ext/agoo/rack_logger.c +15 -71
  27. data/ext/agoo/rack_logger.h +1 -1
  28. data/ext/agoo/request.c +96 -21
  29. data/ext/agoo/request.h +23 -12
  30. data/ext/agoo/res.c +5 -2
  31. data/ext/agoo/res.h +4 -0
  32. data/ext/agoo/response.c +13 -12
  33. data/ext/agoo/response.h +1 -2
  34. data/ext/agoo/server.c +290 -428
  35. data/ext/agoo/server.h +10 -10
  36. data/ext/agoo/sha1.c +148 -0
  37. data/ext/agoo/sha1.h +10 -0
  38. data/ext/agoo/sse.c +26 -0
  39. data/ext/agoo/sse.h +12 -0
  40. data/ext/agoo/sub.c +111 -0
  41. data/ext/agoo/sub.h +36 -0
  42. data/ext/agoo/subscription.c +54 -0
  43. data/ext/agoo/subscription.h +18 -0
  44. data/ext/agoo/text.c +26 -4
  45. data/ext/agoo/text.h +2 -0
  46. data/ext/agoo/types.h +13 -0
  47. data/ext/agoo/upgraded.c +148 -0
  48. data/ext/agoo/upgraded.h +13 -0
  49. data/ext/agoo/websocket.c +248 -0
  50. data/ext/agoo/websocket.h +27 -0
  51. data/lib/agoo/version.rb +1 -1
  52. data/lib/rack/handler/agoo.rb +13 -6
  53. data/test/base_handler_test.rb +24 -22
  54. data/test/log_test.rb +146 -199
  55. data/test/rack_handler_test.rb +19 -20
  56. data/test/static_test.rb +30 -28
  57. metadata +23 -7
  58. data/test/rrr/test.rb +0 -26
  59. data/test/tests.rb +0 -8
@@ -9,33 +9,30 @@
9
9
 
10
10
  #include <ruby.h>
11
11
 
12
+ #include "ccache.h"
12
13
  #include "hook.h"
13
14
  #include "log.h"
14
15
  #include "page.h"
15
16
  #include "queue.h"
17
+ #include "sub.h"
16
18
 
17
19
  typedef struct _Server {
20
+ volatile bool inited;
18
21
  volatile bool active;
19
- volatile bool ready;
20
22
  int thread_cnt;
23
+ int max_push_pending;
21
24
  int port;
22
25
  bool pedantic;
23
26
  char *root;
24
27
  atomic_int running;
25
28
  pthread_t listen_thread;
26
29
  pthread_t con_thread;
27
- struct _Log log;
28
- struct _LogCat error_cat;
29
- struct _LogCat warn_cat;
30
- struct _LogCat info_cat;
31
- struct _LogCat debug_cat;
32
- struct _LogCat con_cat;
33
- struct _LogCat req_cat;
34
- struct _LogCat resp_cat;
35
- struct _LogCat eval_cat;
36
30
 
37
31
  struct _Queue con_queue;
32
+ struct _Queue pub_queue;
38
33
  struct _Cache pages;
34
+ struct _SubCache sub_cache; // subscription cache
35
+ struct _CCache con_cache; // Only WebSocket and SSE connections
39
36
 
40
37
  Hook hooks;
41
38
  Hook hook404;
@@ -44,6 +41,9 @@ typedef struct _Server {
44
41
  VALUE *eval_threads; // Qnil terminated
45
42
  } *Server;
46
43
 
44
+ extern struct _Server the_server;
45
+
47
46
  extern void server_init(VALUE mod);
47
+ extern void server_shutdown();
48
48
 
49
49
  #endif // __AGOO_SERVER_H__
@@ -0,0 +1,148 @@
1
+ // Taken and modified from the original by Steve Reid which is in the public
2
+ // domain. It has been modified by James H. Brown, Saul Kravitz, Ralph Giles,
3
+ // and now Peter Ohler.
4
+
5
+ #include <stdio.h>
6
+ #include <stdint.h>
7
+ #include <string.h>
8
+
9
+ #include "sha1.h"
10
+
11
+ typedef struct {
12
+ uint32_t h0;
13
+ uint32_t h1;
14
+ uint32_t h2;
15
+ uint32_t h3;
16
+ uint32_t h4;
17
+ uint32_t count[2];
18
+ uint8_t buffer[64];
19
+ } Ctx;
20
+
21
+ // TBD make this stuff ENDIAN independent. Now it is little endian only.
22
+ #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
23
+ #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00)|(rol(block->l[i],8)&0x00FF00FF))
24
+ #define blk(i) (block->l[i&0x0F] = rol(block->l[(i+13)&0x0F]^block->l[(i+8)&0x0F]^block->l[(i+2)&0x0F]^block->l[i&0x0F],1))
25
+
26
+ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
27
+ #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
28
+ #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
29
+ #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
30
+ #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
31
+
32
+ // Transform a 512 bit block.
33
+ static void
34
+ transform(Ctx *ctx, const uint8_t buffer[64]) {
35
+ uint32_t a = ctx->h0;
36
+ uint32_t b = ctx->h1;
37
+ uint32_t c = ctx->h2;
38
+ uint32_t d = ctx->h3;
39
+ uint32_t e = ctx->h4;
40
+
41
+ typedef union {
42
+ uint8_t c[64];
43
+ uint32_t l[16];
44
+ } CHAR64LONG16;
45
+ CHAR64LONG16* block;
46
+
47
+ block = (CHAR64LONG16*)buffer;
48
+
49
+ /* 4 rounds of 20 operations each. Loop unrolled. */
50
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
51
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
52
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
53
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
54
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
55
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
56
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
57
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
58
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
59
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
60
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
61
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
62
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
63
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
64
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
65
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
66
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
67
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
68
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
69
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
70
+
71
+ ctx->h0 += a;
72
+ ctx->h1 += b;
73
+ ctx->h2 += c;
74
+ ctx->h3 += d;
75
+ ctx->h4 += e;
76
+ }
77
+
78
+ static void
79
+ update(Ctx *ctx, const uint8_t* data, const size_t len) {
80
+ size_t i;
81
+ size_t j = (ctx->count[0] >> 3) & 0x3F;
82
+
83
+ if ((ctx->count[0] += len << 3) < (len << 3)) {
84
+ ctx->count[1]++;
85
+ }
86
+ ctx->count[1] += (len >> 29);
87
+ if ((j + len) > 63) {
88
+ i = 64 - j;
89
+ memcpy(ctx->buffer + j, data, i);
90
+ transform(ctx, ctx->buffer);
91
+ for (; i + 63 < len; i += 64) {
92
+ transform(ctx, data + i);
93
+ }
94
+ j = 0;
95
+ } else {
96
+ i = 0;
97
+ }
98
+ memcpy(ctx->buffer + j, data + i, len - i);
99
+ }
100
+
101
+ void
102
+ sha1(const uint8_t *data, size_t len, uint8_t *digest) {
103
+ Ctx ctx;
104
+ uint32_t i;
105
+ uint8_t finalcount[8];
106
+ int shift;
107
+
108
+ ctx.h0 = 0x67452301;
109
+ ctx.h1 = 0xEFCDAB89;
110
+ ctx.h2 = 0x98BADCFE;
111
+ ctx.h3 = 0x10325476;
112
+ ctx.h4 = 0xC3D2E1F0;
113
+ ctx.count[0] = 0;
114
+ ctx.count[1] = 0;
115
+
116
+ update(&ctx, data, len);
117
+
118
+ for (i = 0; i < 8; i++) {
119
+ finalcount[i] = (uint8_t)((ctx.count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 0xFF);
120
+ }
121
+ update(&ctx, (uint8_t*)"\x80", 1);
122
+ while ((ctx.count[0] & 0x000001F8) != 0x000001C0) {
123
+ update(&ctx, (uint8_t*)"", 1);
124
+ }
125
+ update(&ctx, finalcount, 8);
126
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
127
+ shift = (3 - (i & 3)) * 8;
128
+ switch (i >> 2) {
129
+ case 0:
130
+ digest[i] = (uint8_t)((ctx.h0 >> shift) & 0xFF);
131
+ break;
132
+ case 1:
133
+ digest[i] = (uint8_t)((ctx.h1 >> shift) & 0xFF);
134
+ break;
135
+ case 2:
136
+ digest[i] = (uint8_t)((ctx.h2 >> shift) & 0xFF);
137
+ break;
138
+ case 3:
139
+ digest[i] = (uint8_t)((ctx.h3 >> shift) & 0xFF);
140
+ break;
141
+ case 4:
142
+ digest[i] = (uint8_t)((ctx.h4 >> shift) & 0xFF);
143
+ break;
144
+ default:
145
+ break;
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,10 @@
1
+ #ifndef __AGOO_SHA1_H__
2
+ #define __AGOO_SHA1_H__
3
+
4
+ #include <stdint.h>
5
+
6
+ #define SHA1_DIGEST_SIZE 20
7
+
8
+ extern void sha1(const uint8_t *data, size_t len, uint8_t *digest);
9
+
10
+ #endif // __AGOO_SHA1_H__
@@ -0,0 +1,26 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include "request.h"
4
+ #include "sse.h"
5
+ #include "text.h"
6
+
7
+ static const char prefix[] = "event: msg\ndata: ";
8
+ static const char suffix[] = "\n\n";
9
+ static const char up[] = "HTTP/1.1 200 OK\r\n\
10
+ Content-Type: text/event-stream\r\n\
11
+ Cache-Control: no-cache\r\n\
12
+ Connection: keep-alive\r\n\
13
+ \r\n\
14
+ retry: 5\n\n";
15
+
16
+ Text
17
+ sse_upgrade(Req req, Text t) {
18
+ t->len = 0; // reset
19
+ return text_append(t, up, sizeof(up) - 1);
20
+ }
21
+
22
+ Text
23
+ sse_expand(Text t) {
24
+ t = text_prepend(t, prefix, sizeof(prefix) - 1);
25
+ return text_append(t, suffix, sizeof(suffix) - 1);
26
+ }
@@ -0,0 +1,12 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef __AGOO_SSE_H__
4
+ #define __AGOO_SSE_H__
5
+
6
+ struct _Req;
7
+ struct _Text;
8
+
9
+ extern struct _Text* sse_upgrade(struct _Req *req, struct _Text *t);
10
+ extern struct _Text* sse_expand(struct _Text *t);
11
+
12
+ #endif // __AGOO_SSE_H__
@@ -0,0 +1,111 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+
6
+ #include "sub.h"
7
+
8
+ Sub
9
+ sub_new(uint64_t cid, uint64_t id, const char *subject, size_t slen) {
10
+ Sub s = (Sub)malloc(sizeof(struct _Sub) - 8 + slen + 1);
11
+
12
+ if (NULL != s) {
13
+ s->cid = cid;
14
+ s->id = id;
15
+ strncpy(s->subject, subject, slen);
16
+ s->subject[slen] = '\0';
17
+ }
18
+ return s;
19
+ }
20
+
21
+ bool
22
+ sub_match(Sub s, const char *subject, size_t slen) {
23
+ const char *pat = s->subject;
24
+ const char *end = subject + slen;
25
+
26
+ for (; '\0' != *pat && subject < end; subject++) {
27
+ if (*subject == *pat) {
28
+ subject++;
29
+ } else if ('*' == *pat) {
30
+ for (; subject < end && '.' != *subject; subject++) {
31
+ }
32
+ } else if ('>' == *pat) {
33
+ return true;
34
+ } else {
35
+ break;
36
+ }
37
+ }
38
+ return '\0' == *pat && subject == end;
39
+ }
40
+
41
+ void
42
+ sub_init(SubCache sc) {
43
+ memset(sc, 0, sizeof(struct _SubCache));
44
+ }
45
+
46
+ void
47
+ sub_cleanup(SubCache sc) {
48
+ Sub s;
49
+ Sub head;
50
+ Sub *bucket;
51
+ int i;
52
+
53
+ for (i = 0, bucket = sc->buckets; i < SUB_BUCKET_SIZE; i++, bucket++) {
54
+ head = *bucket;
55
+ while (NULL != head) {
56
+ s = head;
57
+ head = head->next;
58
+ free(s);
59
+ }
60
+ }
61
+ }
62
+
63
+ static uint64_t
64
+ calc_hash(uint64_t cid, uint64_t sid) {
65
+ return (SUB_BUCKET_MASK & (cid ^ sid));
66
+ }
67
+
68
+ void
69
+ sub_add(SubCache sc, Sub s) {
70
+ Sub *bucket = sc->buckets + calc_hash(s->cid, s->id);
71
+
72
+ s->next = *bucket;
73
+ *bucket = s;
74
+ }
75
+
76
+ Sub
77
+ sub_get(SubCache sc, uint64_t cid, uint64_t sid) {
78
+ Sub s = *(sc->buckets + calc_hash(cid, sid));
79
+
80
+ for (; NULL != s; s = s->next) {
81
+ if (s->cid == cid && s->id == sid) {
82
+ return s;
83
+ }
84
+ }
85
+ return NULL;
86
+ }
87
+
88
+ void
89
+ sub_del(SubCache sc, uint64_t cid, uint64_t sid) {
90
+ Sub *bucket = sc->buckets + calc_hash(cid, sid);
91
+
92
+ if (NULL != *bucket) {
93
+ Sub s = *bucket;
94
+
95
+ if (s->cid == cid && s->id == sid) {
96
+ *bucket = s->next;
97
+ } else {
98
+ Sub prev = s;
99
+
100
+ for (s = s->next; NULL != s; s = s->next) {
101
+ if (s->cid == cid && s->id == sid) {
102
+ prev->next = s->next;
103
+ free(s);
104
+ break;
105
+ }
106
+ prev = s;
107
+ }
108
+ }
109
+ }
110
+ }
111
+
@@ -0,0 +1,36 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef __AGOO_SUB_H__
4
+ #define __AGOO_SUB_H__
5
+
6
+ #include <stdbool.h>
7
+ #include <stdint.h>
8
+
9
+ #define SUB_BUCKET_SIZE 1024
10
+ #define SUB_BUCKET_MASK 1023
11
+
12
+ struct _Con;
13
+
14
+ typedef struct _Sub {
15
+ struct _Sub *next;
16
+ uint64_t cid;
17
+ struct _Con *con;
18
+ uint64_t id;
19
+ uint64_t key; // hash key
20
+ char subject[8];
21
+ } *Sub;
22
+
23
+ typedef struct _SubCache {
24
+ Sub buckets[SUB_BUCKET_SIZE];
25
+ } *SubCache;
26
+
27
+ extern void sub_init(SubCache sc);
28
+ extern void sub_cleanup(SubCache sc);
29
+
30
+ extern Sub sub_new(uint64_t cid, uint64_t id, const char *subject, size_t slen);
31
+ extern bool sub_match(Sub s, const char *subject, size_t slen);
32
+ extern void sub_add(SubCache sc, Sub s);
33
+ extern Sub sub_get(SubCache sc, uint64_t cid, uint64_t sid);
34
+ extern void sub_del(SubCache sc, uint64_t cid, uint64_t sid);
35
+
36
+ #endif // __AGOO_SUB_H__
@@ -0,0 +1,54 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include "pub.h"
4
+ #include "queue.h"
5
+ #include "server.h"
6
+ #include "subscription.h"
7
+
8
+ static VALUE subscription_class = Qundef;
9
+
10
+ VALUE
11
+ subscription_new(uint64_t cid, uint64_t id, VALUE handler) {
12
+ Subscription s = ALLOC(struct _Subscription);
13
+
14
+ s->cid = cid;
15
+ s->id = id;
16
+ s->handler = handler;
17
+ s->self = Data_Wrap_Struct(subscription_class, NULL, xfree, s);
18
+
19
+ return s->self;
20
+ }
21
+
22
+ /* Document-method: close
23
+ *
24
+ * call-seq: close()
25
+ *
26
+ * Close or unsubscribe a subscription.
27
+ */
28
+ static VALUE
29
+ subscription_close(VALUE self) {
30
+ Subscription s = DATA_PTR(self);
31
+
32
+ if (0 != s->cid && 0 != s->id) {
33
+ Pub p = pub_unsubscribe(s->cid, s->id);
34
+
35
+ queue_push(&the_server.pub_queue, (void*)p);
36
+ queue_wakeup(&the_server.pub_queue);
37
+ s->cid = 0;
38
+ s->id = 0;
39
+ }
40
+ return Qnil;
41
+ }
42
+
43
+ /* Document-class: Agoo::Subscription
44
+ *
45
+ * The handle on a subscriptionscription used to register interest in messages
46
+ * published on a subscriptionject. Published messages are delivered to Javascript
47
+ * clients.
48
+ */
49
+ void
50
+ subscription_init(VALUE mod) {
51
+ subscription_class = rb_define_class_under(mod, "Subscription", rb_cObject);
52
+
53
+ rb_define_method(subscription_class, "close", subscription_close, 0);
54
+ }