agoo 2.9.0 → 2.10.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.

@@ -75,8 +75,6 @@ agoo_pub_publish(const char *subject, int slen, const char *message, size_t mlen
75
75
 
76
76
  agooPub
77
77
  agoo_pub_write(agooUpgraded up, const char *message, size_t mlen, bool bin) {
78
- // Allocate an extra 16 bytes so the message can be expanded in place if a
79
- // WebSocket write.
80
78
  agooPub p = (agooPub)AGOO_MALLOC(sizeof(struct _agooPub));
81
79
 
82
80
  if (NULL != p) {
@@ -130,4 +128,3 @@ agoo_pub_destroy(agooPub pub) {
130
128
  }
131
129
  AGOO_FREE(pub);
132
130
  }
133
-
@@ -46,6 +46,7 @@ agoo_res_destroy(agooRes res) {
46
46
  agoo_text_release(res->message);
47
47
  }
48
48
  res->next = NULL;
49
+ res->message = NULL;
49
50
  pthread_mutex_lock(&res->con->loop->lock);
50
51
  if (NULL == res->con->loop->res_tail) {
51
52
  res->con->loop->res_head = res;
@@ -58,7 +59,7 @@ agoo_res_destroy(agooRes res) {
58
59
  }
59
60
 
60
61
  void
61
- agoo_res_message_push(agooRes res, agooText t, bool final) {
62
+ agoo_res_message_push(agooRes res, agooText t) {
62
63
  if (NULL != t) {
63
64
  agoo_text_ref(t);
64
65
  }
@@ -73,7 +74,7 @@ agoo_res_message_push(agooRes res, agooText t, bool final) {
73
74
  }
74
75
  end->next = t;
75
76
  }
76
- res->final = final;
77
+ res->final = true;
77
78
  }
78
79
  pthread_mutex_unlock(&res->lock);
79
80
  }
@@ -91,7 +92,21 @@ agoo_res_add_early(agooRes res, agooEarly early) {
91
92
  t = agoo_text_append(t, "\r\n", 2);
92
93
  }
93
94
  t = agoo_text_append(t, "\r\n", 2);
94
- agoo_res_message_push(res, t, false);
95
+
96
+ pthread_mutex_lock(&res->lock);
97
+ if (!res->final) {
98
+ if (NULL == res->message) {
99
+ res->message = t;
100
+ } else {
101
+ agooText end = res->message;
102
+
103
+ for (; NULL != end->next; end = end->next) {
104
+ }
105
+ end->next = t;
106
+ }
107
+ res->final = false;
108
+ }
109
+ pthread_mutex_unlock(&res->lock);
95
110
  }
96
111
 
97
112
  agooText
@@ -113,8 +128,7 @@ agoo_res_message_next(agooRes res) {
113
128
  if (NULL != res->message) {
114
129
  agooText t2 = res->message;
115
130
 
116
- res->message = res->message->next;
117
- // TBD make sure it is not release by the called, change code if it is
131
+ res->message = t2->next;
118
132
  agoo_text_release(t2);
119
133
  }
120
134
  t = res->message;
@@ -28,7 +28,7 @@ typedef struct _agooRes {
28
28
  extern agooRes agoo_res_create(struct _agooCon *con);
29
29
  extern void agoo_res_destroy(agooRes res);
30
30
 
31
- extern void agoo_res_message_push(agooRes res, agooText t, bool final);
31
+ extern void agoo_res_message_push(agooRes res, agooText t);
32
32
  extern void agoo_res_add_early(agooRes res, agooEarly early);
33
33
  extern agooText agoo_res_message_peek(agooRes res);
34
34
  extern agooText agoo_res_message_next(agooRes res);
@@ -12,7 +12,9 @@
12
12
  #include "gqlintro.h"
13
13
  #include "gqlvalue.h"
14
14
  #include "graphql.h"
15
+ #include "pub.h"
15
16
  #include "sdl.h"
17
+ #include "server.h"
16
18
 
17
19
  typedef struct _eval {
18
20
  gqlDoc doc;
@@ -423,6 +425,15 @@ resolve(agooErr err, gqlDoc doc, gqlRef target, gqlField field, gqlSel sel, gqlV
423
425
  }
424
426
  child = rb_funcall(obj, rb_intern(sel->name), 1, rargs);
425
427
  }
428
+ if (GQL_SUBSCRIPTION == doc->op->kind && RUBY_T_STRING == rb_type(child)) {
429
+ gqlValue c;
430
+
431
+ if (NULL == (c = gql_string_create(err, rb_string_value_ptr(&child), RSTRING_LEN(child))) ||
432
+ AGOO_ERR_OK != gql_object_set(err, result, "subject", c)) {
433
+ return err->code;
434
+ }
435
+ return AGOO_ERR_OK;
436
+ }
426
437
  if (NULL != sel->alias) {
427
438
  key = sel->alias;
428
439
  }
@@ -750,6 +761,30 @@ graphql_sdl_dump(VALUE self, VALUE options) {
750
761
  return dump;
751
762
  }
752
763
 
764
+ /* Document-method: publish
765
+ *
766
+ * call-seq: publish(subject, event)
767
+ *
768
+ * Publish an event on the given subject. A subject must be a String while the
769
+ * event must be one of the objects represented by the the GraphQL
770
+ * schema. Generally the subjects are selected to identify which objects are
771
+ * being published and should match the value returned by the subscription
772
+ * methods.
773
+ */
774
+ static VALUE
775
+ graphql_publish(VALUE self, VALUE subject, VALUE event) {
776
+ const char *subj;
777
+ struct _agooErr err = AGOO_ERR_INIT;
778
+
779
+ rb_check_type(subject, T_STRING);
780
+ subj = StringValuePtr(subject);
781
+
782
+ if (AGOO_ERR_OK != agoo_server_gpublish(&err, subj, (gqlRef)event)) {
783
+ rb_raise(rb_eStandardError, "%s", err.msg);
784
+ }
785
+ return Qnil;
786
+ }
787
+
753
788
  /* Document-class: Agoo::Graphql
754
789
  *
755
790
  * The Agoo::GraphQL class provides support for the GraphQL API as defined in
@@ -772,4 +807,6 @@ graphql_init(VALUE mod) {
772
807
  rb_define_module_function(graphql_class, "load_file", graphql_load_file, 1);
773
808
 
774
809
  rb_define_module_function(graphql_class, "sdl_dump", graphql_sdl_dump, 1);
810
+
811
+ rb_define_module_function(graphql_class, "publish", graphql_publish, 2);
775
812
  }
@@ -312,7 +312,7 @@ rescue_error(VALUE x) {
312
312
  message = agoo_text_create(buf, cnt);
313
313
 
314
314
  req->res->close = true;
315
- agoo_res_message_push(req->res, message, true);
315
+ agoo_res_message_push(req->res, message);
316
316
  agoo_queue_wakeup(&agoo_server.con_queue);
317
317
  } else {
318
318
  /*
@@ -340,7 +340,7 @@ handle_base_inner(void *x) {
340
340
  rb_funcall((VALUE)req->hook->handler, on_request_id, 2, rr, rres);
341
341
  }
342
342
  DATA_PTR(rr) = NULL;
343
- agoo_res_message_push(req->res, response_text(rres), true);
343
+ agoo_res_message_push(req->res, response_text(rres));
344
344
  agoo_queue_wakeup(&agoo_server.con_queue);
345
345
 
346
346
  return Qfalse;
@@ -367,10 +367,25 @@ header_cb(VALUE key, VALUE value, agooText *tp) {
367
367
  }
368
368
  }
369
369
  if (0 != strcasecmp("Content-Length", ks)) {
370
- *tp = agoo_text_append(*tp, ks, klen);
371
- *tp = agoo_text_append(*tp, ": ", 2);
372
- *tp = agoo_text_append(*tp, vs, vlen);
373
- *tp = agoo_text_append(*tp, "\r\n", 2);
370
+ char *end = index(vs, '\n');
371
+
372
+ do {
373
+ end = index(vs, '\n');
374
+ *tp = agoo_text_append(*tp, ks, klen);
375
+ *tp = agoo_text_append(*tp, ": ", 2);
376
+ if (NULL == end) {
377
+ if (0 < vlen) {
378
+ *tp = agoo_text_append(*tp, vs, vlen);
379
+ }
380
+ } else {
381
+ if (vs < end) {
382
+ *tp = agoo_text_append(*tp, vs, end - vs);
383
+ }
384
+ vlen -= end - vs + 1;
385
+ vs = end + 1;
386
+ }
387
+ *tp = agoo_text_append(*tp, "\r\n", 2);
388
+ } while (NULL != end && 0 < vlen);
374
389
  }
375
390
  return ST_CONTINUE;
376
391
  }
@@ -515,7 +530,7 @@ handle_rack_inner(void *x) {
515
530
  req->hook = agoo_hook_create(AGOO_NONE, NULL, (void*)handler, PUSH_HOOK, &agoo_server.eval_queue);
516
531
  rupgraded_create(req->res->con, handler, request_env(req, Qnil));
517
532
  t = agoo_sse_upgrade(req, t);
518
- agoo_res_message_push(req->res, t, true);
533
+ agoo_res_message_push(req->res, t);
519
534
  agoo_queue_wakeup(&agoo_server.con_queue);
520
535
  return Qfalse;
521
536
  default:
@@ -546,7 +561,7 @@ handle_rack_inner(void *x) {
546
561
  rb_iterate(rb_each, bv, body_append_cb, (VALUE)&t);
547
562
  }
548
563
  }
549
- agoo_res_message_push(req->res, t, true);
564
+ agoo_res_message_push(req->res, t);
550
565
  agoo_queue_wakeup(&agoo_server.con_queue);
551
566
 
552
567
  return Qfalse;
@@ -572,7 +587,7 @@ handle_wab_inner(void *x) {
572
587
  rb_funcall((VALUE)req->hook->handler, on_request_id, 2, rr, rres);
573
588
  }
574
589
  DATA_PTR(rr) = NULL;
575
- agoo_res_message_push(req->res, response_text(rres), true);
590
+ agoo_res_message_push(req->res, response_text(rres));
576
591
  agoo_queue_wakeup(&agoo_server.con_queue);
577
592
 
578
593
  return Qfalse;
@@ -686,7 +701,7 @@ handle_protected(agooReq req, bool gvi) {
686
701
  agooText message = agoo_text_create(buf, cnt);
687
702
 
688
703
  req->res->close = true;
689
- agoo_res_message_push(req->res, message, true);
704
+ agoo_res_message_push(req->res, message);
690
705
  agoo_queue_wakeup(&agoo_server.con_queue);
691
706
  break;
692
707
  }
@@ -1098,7 +1113,7 @@ domain(VALUE self, VALUE host, VALUE path) {
1098
1113
  volatile VALUE v = rb_funcall(host, rb_intern("inspect"), 0);
1099
1114
  char rx[1024];
1100
1115
 
1101
- if (sizeof(rx) <= RSTRING_LEN(v)) {
1116
+ if (sizeof(rx) <= (size_t)RSTRING_LEN(v)) {
1102
1117
  rb_raise(rb_eArgError, "host Regex limited to %ld characters", sizeof(rx));
1103
1118
  }
1104
1119
  strcpy(rx, rb_string_value_ptr((VALUE*)&v) + 1);
@@ -1318,25 +1318,25 @@ make_sel(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOp op, gqlSel *parentp) {
1318
1318
 
1319
1319
 
1320
1320
  static int
1321
- make_op(agooErr err, agooDoc doc, gqlDoc gdoc) {
1321
+ make_op(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOpKind kind) {
1322
1322
  char name[256];
1323
1323
  const char *start;
1324
- gqlOpKind kind;
1325
1324
  gqlOp op;
1326
1325
  size_t nlen;
1327
1326
 
1328
1327
  agoo_doc_skip_white(doc);
1329
1328
  start = doc->cur;
1330
1329
  agoo_doc_read_token(doc);
1331
- if (doc->cur == start ||
1332
- (5 == (doc->cur - start) && 0 == strncmp(query_str, start, sizeof(query_str) - 1))) {
1333
- kind = GQL_QUERY;
1334
- } else if (8 == (doc->cur - start) && 0 == strncmp(mutation_str, start, sizeof(mutation_str) - 1)) {
1335
- kind = GQL_MUTATION;
1336
- } else if (12 == (doc->cur - start) && 0 == strncmp(subscription_str, start, sizeof(subscription_str) - 1)) {
1337
- kind = GQL_SUBSCRIPTION;
1338
- } else {
1339
- return agoo_doc_err(doc, err, "Invalid operation type");
1330
+ if ( start < doc->cur) {
1331
+ if (5 == (doc->cur - start) && 0 == strncmp(query_str, start, sizeof(query_str) - 1)) {
1332
+ kind = GQL_QUERY;
1333
+ } else if (8 == (doc->cur - start) && 0 == strncmp(mutation_str, start, sizeof(mutation_str) - 1)) {
1334
+ kind = GQL_MUTATION;
1335
+ } else if (12 == (doc->cur - start) && 0 == strncmp(subscription_str, start, sizeof(subscription_str) - 1)) {
1336
+ kind = GQL_SUBSCRIPTION;
1337
+ } else {
1338
+ return agoo_doc_err(doc, err, "Invalid operation type");
1339
+ }
1340
1340
  }
1341
1341
  agoo_doc_skip_white(doc);
1342
1342
  start = doc->cur;
@@ -1597,7 +1597,7 @@ validate_doc(agooErr err, gqlDoc doc) {
1597
1597
  }
1598
1598
 
1599
1599
  gqlDoc
1600
- sdl_parse_doc(agooErr err, const char *str, int len, gqlVar vars) {
1600
+ sdl_parse_doc(agooErr err, const char *str, int len, gqlVar vars, gqlOpKind default_kind) {
1601
1601
  struct _agooDoc doc;
1602
1602
  gqlDoc gdoc = NULL;
1603
1603
 
@@ -1616,7 +1616,7 @@ sdl_parse_doc(agooErr err, const char *str, int len, gqlVar vars) {
1616
1616
  case 'q':
1617
1617
  case 'm':
1618
1618
  case 's':
1619
- if (AGOO_ERR_OK != make_op(err, &doc, gdoc)) {
1619
+ if (AGOO_ERR_OK != make_op(err, &doc, gdoc, default_kind)) {
1620
1620
  gql_doc_destroy(gdoc);
1621
1621
  return NULL;
1622
1622
  }
@@ -4,6 +4,7 @@
4
4
  #define AGOO_SDL_H
5
5
 
6
6
  #include "err.h"
7
+ #include "graphql.h"
7
8
 
8
9
  struct _gqlType;
9
10
  struct _gqlVar;
@@ -12,7 +13,7 @@ struct _gqlValue;
12
13
  extern int sdl_parse(agooErr err, const char *str, int len);
13
14
 
14
15
  // Parse a execution definition.
15
- extern struct _gqlDoc* sdl_parse_doc(agooErr err, const char *str, int len, struct _gqlVar *vars);
16
+ extern struct _gqlDoc* sdl_parse_doc(agooErr err, const char *str, int len, struct _gqlVar *vars, gqlOpKind default_kind);
16
17
 
17
18
  extern gqlVar gql_op_var_create(agooErr err, const char *name, struct _gqlType *type, struct _gqlValue *value);
18
19
 
@@ -12,11 +12,16 @@
12
12
  #include "con.h"
13
13
  #include "domain.h"
14
14
  #include "dtime.h"
15
+ #include "gqlsub.h"
16
+ #include "gqlvalue.h"
17
+ #include "graphql.h"
15
18
  #include "http.h"
16
19
  #include "hook.h"
17
20
  #include "log.h"
18
21
  #include "page.h"
19
22
  #include "pub.h"
23
+ #include "res.h"
24
+ #include "text.h"
20
25
  #include "upgraded.h"
21
26
 
22
27
  #include "server.h"
@@ -34,6 +39,7 @@ agoo_server_setup(agooErr err) {
34
39
  memset(&agoo_server, 0, sizeof(struct _agooServer));
35
40
  pthread_mutex_init(&agoo_server.up_lock, 0);
36
41
  agoo_server.up_list = NULL;
42
+ agoo_server.gsub_list = NULL;
37
43
  agoo_server.max_push_pending = 32;
38
44
 
39
45
  if (AGOO_ERR_OK != agoo_pages_init(err) ||
@@ -336,3 +342,112 @@ agoo_server_publish(struct _agooPub *pub) {
336
342
  }
337
343
  }
338
344
  }
345
+
346
+ void
347
+ agoo_server_add_gsub(gqlSub sub) {
348
+ pthread_mutex_lock(&agoo_server.up_lock);
349
+ sub->next = agoo_server.gsub_list;
350
+ agoo_server.gsub_list = sub;
351
+ pthread_mutex_unlock(&agoo_server.up_lock);
352
+ }
353
+
354
+ void
355
+ agoo_server_del_gsub(gqlSub sub) {
356
+ gqlSub s;
357
+ gqlSub prev = NULL;
358
+
359
+ pthread_mutex_lock(&agoo_server.up_lock);
360
+ for (s = agoo_server.gsub_list; NULL != s; s = s->next) {
361
+ if (s == sub) {
362
+ if (NULL == prev) {
363
+ agoo_server.gsub_list = s->next;
364
+ } else {
365
+ prev->next = s->next;
366
+ }
367
+ }
368
+ prev = s;
369
+ }
370
+ pthread_mutex_unlock(&agoo_server.up_lock);
371
+ }
372
+
373
+ static bool
374
+ subject_check(const char *pattern, const char *subject) {
375
+ for (; '\0' != *pattern && '\0' != *subject; subject++) {
376
+ if (*subject == *pattern) {
377
+ pattern++;
378
+ } else if ('*' == *pattern) {
379
+ for (; '\0' != *subject && '.' != *subject; subject++) {
380
+ }
381
+ if ('\0' == *subject) {
382
+ return true;
383
+ }
384
+ pattern++;
385
+ } else if ('>' == *pattern) {
386
+ return true;
387
+ } else {
388
+ break;
389
+ }
390
+ }
391
+ return '\0' == *pattern && '\0' == *subject;
392
+ }
393
+
394
+ static agooText
395
+ gpub_eval(agooErr err, gqlDoc query, gqlRef event) {
396
+ agooText t = NULL;
397
+ gqlSel sel;
398
+ gqlValue result;
399
+
400
+ if (NULL == query->ops || NULL == query->ops->sels) {
401
+ agoo_err_set(err, AGOO_ERR_TYPE, "subscription not valid");
402
+ return NULL;
403
+ }
404
+ sel = query->ops->sels;
405
+ if (NULL != (result = gql_object_create(err))) {
406
+ struct _gqlField field;
407
+
408
+ memset(&field, 0, sizeof(field));
409
+ field.type = sel->type;
410
+ if (AGOO_ERR_OK != gql_eval_sels(err, query, event, &field, sel->sels, result, 0)) {
411
+ gql_value_destroy(result);
412
+ return NULL;
413
+ }
414
+ if (NULL == (t = agoo_text_allocate(1024))) {
415
+ AGOO_ERR_MEM(err, "Text");
416
+ return NULL;
417
+ }
418
+ t = gql_value_json(t, result, 0, 0);
419
+ gql_value_destroy(result);
420
+ }
421
+ return t;
422
+ }
423
+
424
+ int
425
+ agoo_server_gpublish(agooErr err, const char *subject, gqlRef event) {
426
+ gqlSub sub;
427
+ gqlType type;
428
+
429
+ if (NULL == gql_type_func || NULL == (type = gql_type_func(event))) {
430
+ return agoo_err_set(err, AGOO_ERR_TYPE, "Not able to determine the type for a GraphQL publish.");
431
+ }
432
+ pthread_mutex_lock(&agoo_server.up_lock);
433
+ for (sub = agoo_server.gsub_list; NULL != sub; sub = sub->next) {
434
+ if (subject_check(sub->subject, subject)) {
435
+ agooRes res;
436
+ agooText t;
437
+
438
+ if (NULL == (res = agoo_res_create(sub->con))) {
439
+ AGOO_ERR_MEM(err, "Response");
440
+ break;
441
+ }
442
+ if (NULL == (t = gpub_eval(err, sub->query, event))) {
443
+ break;
444
+ }
445
+ res->con_kind = AGOO_CON_ANY;
446
+ agoo_res_message_push(res, t);
447
+ agoo_con_res_append(sub->con, res);
448
+ }
449
+ }
450
+ pthread_mutex_unlock(&agoo_server.up_lock);
451
+
452
+ return err->code;
453
+ }
@@ -9,6 +9,7 @@
9
9
  #include "atomic.h"
10
10
  #include "bind.h"
11
11
  #include "err.h"
12
+ #include "gqleval.h"
12
13
  #include "hook.h"
13
14
  #include "queue.h"
14
15
 
@@ -17,6 +18,8 @@ struct _agooConLoop;
17
18
  struct _agooPub;
18
19
  struct _agooReq;
19
20
  struct _agooUpgraded;
21
+ struct _gqlSub;
22
+ struct _gqlValue;
20
23
 
21
24
  typedef struct _agooServer {
22
25
  volatile bool inited;
@@ -39,6 +42,7 @@ typedef struct _agooServer {
39
42
  atomic_int con_cnt;
40
43
 
41
44
  struct _agooUpgraded *up_list;
45
+ struct _gqlSub *gsub_list;
42
46
  pthread_mutex_t up_lock;
43
47
  int max_push_pending;
44
48
  void *env_nil_value;
@@ -66,6 +70,10 @@ extern int agoo_server_add_func_hook(agooErr err,
66
70
 
67
71
  extern void agoo_server_publish(struct _agooPub *pub);
68
72
 
73
+ extern void agoo_server_add_gsub(struct _gqlSub *sub);
74
+ extern void agoo_server_del_gsub(struct _gqlSub *sub);
75
+ extern int agoo_server_gpublish(agooErr err, const char *subject, gqlRef event);
76
+
69
77
  extern struct _agooServer agoo_server;
70
78
 
71
79
  extern double agoo_io_loop_ratio;