agoo 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

@@ -53,7 +53,7 @@ agoo_malloc(size_t size, const char *file, int line) {
53
53
 
54
54
  if (NULL != ptr) {
55
55
  Rec r = (Rec)malloc(sizeof(struct _rec));
56
-
56
+
57
57
  if (NULL != r) {
58
58
  strcpy(((char*)ptr) + size, mem_pad);
59
59
  r->ptr = ptr;
@@ -79,7 +79,7 @@ agoo_calloc(size_t count, size_t size, const char *file, int line) {
79
79
  size *= count;
80
80
  if (NULL != (ptr = malloc(size + sizeof(mem_pad)))) {
81
81
  Rec r = (Rec)malloc(sizeof(struct _rec));
82
-
82
+
83
83
  if (NULL != r) {
84
84
  memset(ptr, 0, size);
85
85
  strcpy(((char*)ptr) + size, mem_pad);
@@ -103,7 +103,7 @@ void*
103
103
  agoo_realloc(void *orig, size_t size, const char *file, int line) {
104
104
  void *ptr = realloc(orig, size + sizeof(mem_pad));
105
105
  Rec r;
106
-
106
+
107
107
  if (NULL != ptr) {
108
108
  strcpy(((char*)ptr) + size, mem_pad);
109
109
  pthread_mutex_lock(&lock);
@@ -131,7 +131,7 @@ agoo_strdup(const char *str, const char *file, int line) {
131
131
 
132
132
  if (NULL != ptr) {
133
133
  Rec r = (Rec)malloc(sizeof(struct _rec));
134
-
134
+
135
135
  if (NULL != r) {
136
136
  strcpy(ptr, str);
137
137
  strcpy(((char*)ptr) + size, mem_pad);
@@ -158,7 +158,7 @@ agoo_strndup(const char *str, size_t len, const char *file, int line) {
158
158
 
159
159
  if (NULL != ptr) {
160
160
  Rec r = (Rec)malloc(sizeof(struct _rec));
161
-
161
+
162
162
  if (NULL != r) {
163
163
  memcpy(ptr, str, len);
164
164
  ptr[len] = '\0';
@@ -183,7 +183,7 @@ void
183
183
  agoo_mem_check(void *ptr, const char *file, int line) {
184
184
  if (NULL != ptr) {
185
185
  Rec r = NULL;
186
-
186
+
187
187
  pthread_mutex_lock(&lock);
188
188
  for (r = recs; NULL != r; r = r->next) {
189
189
  if (ptr == r->ptr) {
@@ -199,7 +199,7 @@ agoo_mem_check(void *ptr, const char *file, int line) {
199
199
  if (0 != strcmp(mem_pad, pad)) {
200
200
  uint8_t *p;
201
201
  uint8_t *end = (uint8_t*)pad + sizeof(mem_pad);
202
-
202
+
203
203
  printf("Check - Memory at %s:%d (%p) write outside allocated.\n", file, line, ptr);
204
204
  for (p = (uint8_t*)pad; p < end; p++) {
205
205
  if (0x20 < *p && *p < 0x7f) {
@@ -219,7 +219,7 @@ agoo_freed(void *ptr, const char *file, int line) {
219
219
  if (NULL != ptr) {
220
220
  Rec r = NULL;
221
221
  Rec prev = NULL;
222
-
222
+
223
223
  pthread_mutex_lock(&lock);
224
224
  for (r = recs; NULL != r; r = r->next) {
225
225
  if (ptr == r->ptr) {
@@ -241,7 +241,7 @@ agoo_freed(void *ptr, const char *file, int line) {
241
241
  if (0 != strcmp(mem_pad, pad)) {
242
242
  uint8_t *p;
243
243
  uint8_t *end = (uint8_t*)pad + sizeof(mem_pad);
244
-
244
+
245
245
  printf("Memory at %s:%d (%p) write outside allocated.\n", file, line, ptr);
246
246
  for (p = (uint8_t*)pad; p < end; p++) {
247
247
  if (0x20 < *p && *p < 0x7f) {
@@ -3,6 +3,7 @@
3
3
  #ifndef AGOO_DEBUG_H
4
4
  #define AGOO_DEBUG_H
5
5
 
6
+ #include <stdio.h>
6
7
  #include <stdlib.h>
7
8
  #include <string.h>
8
9
 
@@ -30,9 +30,8 @@ error_stream_new() {
30
30
  rb_raise(rb_eNoMemError, "Failed to allocate memory for the error stream.");
31
31
  }
32
32
  es->server = NULL;
33
- if (NULL == (es->text = agoo_text_allocate(1024))) {
34
- rb_raise(rb_eNoMemError, "Failed to allocate memory for the error stream.");
35
- }
33
+ es->text = NULL;
34
+
36
35
  return Data_Wrap_Struct(es_class, NULL, es_free, es);
37
36
  }
38
37
 
@@ -50,6 +49,10 @@ es_puts(VALUE self, VALUE str) {
50
49
  if (NULL == es) {
51
50
  rb_raise(rb_eIOError, "error stream has been closed.");
52
51
  }
52
+ if (NULL == es->text &&
53
+ NULL == (es->text = agoo_text_allocate(1024))) {
54
+ rb_raise(rb_eNoMemError, "Failed to allocate memory for the error stream buffer.");
55
+ }
53
56
  es->text = agoo_text_append(es->text, StringValuePtr(str), (int)RSTRING_LEN(str));
54
57
  es->text = agoo_text_append(es->text, "\n", 1);
55
58
  if (NULL == es->text) {
@@ -72,6 +75,10 @@ es_write(VALUE self, VALUE str) {
72
75
  if (NULL == es) {
73
76
  rb_raise(rb_eIOError, "error stream has been closed.");
74
77
  }
78
+ if (NULL == es->text &&
79
+ NULL == (es->text = agoo_text_allocate(1024))) {
80
+ rb_raise(rb_eNoMemError, "Failed to allocate memory for the error stream buffer.");
81
+ }
75
82
  if (NULL == (es->text = agoo_text_append(es->text, StringValuePtr(str), cnt))) {
76
83
  rb_raise(rb_eNoMemError, "Failed to allocate memory for the error stream puts.");
77
84
  }
@@ -91,9 +98,10 @@ es_flush(VALUE self) {
91
98
  if (NULL == es) {
92
99
  rb_raise(rb_eIOError, "error stream has been closed.");
93
100
  }
94
- agoo_log_cat(&agoo_error_cat, "%s", es->text->text);
95
- es->text->len = 0;
96
-
101
+ if (NULL != es->text) {
102
+ agoo_log_cat(&agoo_error_cat, "%s", es->text->text);
103
+ es->text->len = 0;
104
+ }
97
105
  return self;
98
106
  }
99
107
 
@@ -7,6 +7,7 @@
7
7
  #include "gqleval.h"
8
8
  #include "gqlintro.h"
9
9
  #include "gqljson.h"
10
+ #include "gqlsub.h"
10
11
  #include "gqlvalue.h"
11
12
  #include "graphql.h"
12
13
  #include "http.h"
@@ -15,7 +16,9 @@
15
16
  #include "res.h"
16
17
  #include "sdl.h"
17
18
  #include "sectime.h"
19
+ #include "sse.h"
18
20
  #include "text.h"
21
+ #include "websocket.h"
19
22
 
20
23
  #define MAX_RESOLVE_ARGS 16
21
24
 
@@ -30,6 +33,7 @@ static const char indent_str[] = "indent";
30
33
  static const char json_content_type[] = "application/json";
31
34
  static const char operation_name_str[] = "operationName";
32
35
  static const char query_str[] = "query";
36
+ static const char subscription_str[] = "subscription";
33
37
  static const char variables_str[] = "variables";
34
38
 
35
39
 
@@ -38,7 +42,7 @@ gqlValue (*gql_doc_eval_func)(agooErr err, gqlDoc doc) = NULL;
38
42
  // TBD errors should have message, location, and path
39
43
  static void
40
44
  err_resp(agooRes res, agooErr err, int status) {
41
- char buf[256];
45
+ char buf[1024];
42
46
  int cnt;
43
47
  int64_t now = agoo_now_nano();
44
48
  time_t t = (time_t)(now / 1000000000LL);
@@ -54,11 +58,14 @@ err_resp(agooRes res, agooErr err, int status) {
54
58
  code,
55
59
  at.year, at.mon, at.day, at.hour, at.min, at.sec, frac);
56
60
 
57
- agoo_res_message_push(res, agoo_text_create(buf, cnt), true);
61
+ agoo_res_message_push(res, agoo_text_create(buf, cnt));
58
62
  }
59
63
 
64
+ static char ws_up[] = "HTTP/1.1 101 Switching Protocols\r\n";
65
+
60
66
  static void
61
- value_resp(agooRes res, gqlValue result, int status, int indent) {
67
+ value_resp(agooReq req, gqlValue result, int status, int indent) {
68
+ agooRes res = req->res;
62
69
  char buf[256];
63
70
  struct _agooErr err = AGOO_ERR_INIT;
64
71
  int cnt;
@@ -71,6 +78,27 @@ value_resp(agooRes res, gqlValue result, int status, int indent) {
71
78
  gql_value_destroy(result);
72
79
  return;
73
80
  }
81
+ if (NULL == result) {
82
+ switch (status) {
83
+ case 101:
84
+ if (NULL == (text = agoo_text_append(text, ws_up, sizeof(ws_up) - 1)) ||
85
+ NULL == (text = agoo_ws_add_headers(req, text)) ||
86
+ NULL == (text = agoo_text_append(text, "\r\n", 2))) {
87
+
88
+ agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
89
+ return;
90
+ }
91
+ break;
92
+ case 200:
93
+ text = agoo_sse_upgrade(req, text);
94
+ break;
95
+ default:
96
+ agoo_log_cat(&agoo_error_cat, "Did not expect an HTTP status of %d.", status);
97
+ return;
98
+ }
99
+ agoo_res_message_push(res, text);
100
+ return;
101
+ }
74
102
  if (AGOO_ERR_OK != gql_object_set(&err, msg, "data", result)) {
75
103
  err_resp(res, &err, 500);
76
104
  gql_value_destroy(result);
@@ -84,7 +112,7 @@ value_resp(agooRes res, gqlValue result, int status, int indent) {
84
112
  if (NULL == (text = agoo_text_prepend(text, buf, cnt))) {
85
113
  agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
86
114
  }
87
- agoo_res_message_push(res, text, true);
115
+ agoo_res_message_push(res, text);
88
116
  }
89
117
 
90
118
  gqlValue
@@ -361,13 +389,17 @@ gql_eval_get_hook(agooReq req) {
361
389
  gqlDoc doc;
362
390
  gqlValue result;
363
391
  gqlVar vars = NULL;
392
+ gqlOpKind default_kind = GQL_QUERY;
364
393
 
365
394
  if (NULL != (gq = agoo_req_query_value(req, indent_str, sizeof(indent_str) - 1, &qlen))) {
366
395
  indent = (int)strtol(gq, NULL, 10);
367
396
  }
368
397
  if (NULL == (gq = agoo_req_query_value(req, query_str, sizeof(query_str) - 1, &qlen))) {
369
- err_resp(req->res, &err, 500);
370
- return;
398
+ if (NULL == (gq = agoo_req_query_value(req, subscription_str, sizeof(subscription_str) - 1, &qlen))) {
399
+ err_resp(req->res, &err, 500);
400
+ return;
401
+ }
402
+ default_kind = GQL_SUBSCRIPTION;
371
403
  }
372
404
  op_name = agoo_req_query_value(req, operation_name_str, sizeof(operation_name_str) - 1, &oplen);
373
405
  var_json = agoo_req_query_value(req, variables_str, sizeof(variables_str) - 1, &vlen);
@@ -380,7 +412,7 @@ gql_eval_get_hook(agooReq req) {
380
412
  }
381
413
  // Only call after extracting the variables as it terminates the string with a \0.
382
414
  qlen = agoo_req_query_decode((char*)gq, qlen);
383
- if (NULL == (doc = sdl_parse_doc(&err, gq, qlen, vars))) {
415
+ if (NULL == (doc = sdl_parse_doc(&err, gq, qlen, vars, default_kind))) {
384
416
  err_resp(req->res, &err, 500);
385
417
  return;
386
418
  }
@@ -391,12 +423,55 @@ gql_eval_get_hook(agooReq req) {
391
423
  } else {
392
424
  result = gql_doc_eval_func(&err, doc);
393
425
  }
394
- gql_doc_destroy(doc);
395
426
  if (NULL == result) {
427
+ gql_doc_destroy(doc);
396
428
  err_resp(req->res, &err, 500);
397
429
  return;
398
430
  }
399
- value_resp(req->res, result, 200, indent);
431
+ if (GQL_SUBSCRIPTION == doc->op->kind) {
432
+ int status = 200;
433
+ gqlValue sv;
434
+ const char *subject;
435
+ gqlSub sub;
436
+
437
+ switch (req->res->con_kind) {
438
+ case AGOO_CON_WS:
439
+ status = 101;
440
+ break;
441
+ case AGOO_CON_SSE:
442
+ break;
443
+ default:
444
+ gql_doc_destroy(doc);
445
+ agoo_err_set(&err, AGOO_ERR_NETWORK, "An upgrade to Websockets or SSE is required for a GraphQL subscription.");
446
+ err_resp(req->res, &err, 426);
447
+ return;
448
+ }
449
+ if (NULL == (sv = gql_object_get(result, "subject"))) {
450
+ struct _agooErr e;
451
+
452
+ agoo_err_set(&e, AGOO_ERR_TYPE, "subscription did not return a subject");
453
+ err_resp(req->res, &e, 400);
454
+ gql_doc_destroy(doc);
455
+ return;
456
+ }
457
+ subject = gql_string_get(sv);
458
+
459
+ if (AGOO_CON_WS == req->res->con_kind) {
460
+ status = 101;
461
+ }
462
+ doc->op->kind = GQL_QUERY; // need so eval does the right thing
463
+ if (NULL == (sub = gql_sub_create(&err, req->res->con, subject, doc))) {
464
+ err_resp(req->res, &err, 400);
465
+ return;
466
+ }
467
+ agoo_server_add_gsub(sub);
468
+
469
+ value_resp(req, NULL, status, indent);
470
+
471
+ return;
472
+ }
473
+ gql_doc_destroy(doc);
474
+ value_resp(req, result, 200, indent);
400
475
  }
401
476
 
402
477
  static gqlValue
@@ -429,7 +504,7 @@ eval_post(agooErr err, agooReq req) {
429
504
  return NULL;
430
505
  }
431
506
  if (0 == strncmp(graphql_content_type, s, sizeof(graphql_content_type) - 1)) {
432
- if (NULL == (doc = sdl_parse_doc(err, req->body.start, req->body.len, vars))) {
507
+ if (NULL == (doc = sdl_parse_doc(err, req->body.start, req->body.len, vars, GQL_QUERY))) {
433
508
  return NULL;
434
509
  }
435
510
  } else if (0 == strncmp(json_content_type, s, sizeof(json_content_type) - 1)) {
@@ -474,7 +549,7 @@ eval_post(agooErr err, agooReq req) {
474
549
  }
475
550
  }
476
551
  }
477
- if (NULL == (doc = sdl_parse_doc(err, query, qlen, vars))) {
552
+ if (NULL == (doc = sdl_parse_doc(err, query, qlen, vars, GQL_QUERY))) {
478
553
  goto DONE;
479
554
  }
480
555
  } else {
@@ -491,7 +566,9 @@ eval_post(agooErr err, agooReq req) {
491
566
  } else {
492
567
  result = gql_doc_eval_func(err, doc);
493
568
  }
494
-
569
+ if (GQL_SUBSCRIPTION == doc->op->kind) {
570
+ result = NULL;
571
+ }
495
572
  DONE:
496
573
  gql_doc_destroy(doc);
497
574
  gql_value_destroy(j);
@@ -510,10 +587,12 @@ gql_eval_post_hook(agooReq req) {
510
587
  if (NULL != (s = agoo_req_query_value(req, indent_str, sizeof(indent_str) - 1, &len))) {
511
588
  indent = (int)strtol(s, NULL, 10);
512
589
  }
513
- if (NULL == (result = eval_post(&err, req))) {
590
+ if (NULL == (result = eval_post(&err, req)) && AGOO_ERR_OK != err.code) {
514
591
  err_resp(req->res, &err, 400);
592
+ } else if (NULL == result) {
593
+ value_resp(req, result, 200, indent);
515
594
  } else {
516
- value_resp(req->res, result, 200, indent);
595
+ value_resp(req, result, 200, indent);
517
596
  }
518
597
  }
519
598
 
@@ -0,0 +1,37 @@
1
+ // Copyright (c) 2019, Peter Ohler, All rights reserved.
2
+
3
+ #include "con.h"
4
+ #include "debug.h"
5
+ #include "gqlsub.h"
6
+ #include "graphql.h"
7
+
8
+ gqlSub
9
+ gql_sub_create(agooErr err, agooCon con, const char *subject, struct _gqlDoc *query) {
10
+ gqlSub sub = (gqlSub)AGOO_MALLOC(sizeof(struct _gqlSub));
11
+
12
+ if (NULL == sub) {
13
+ AGOO_ERR_MEM(err, "gqlSub");
14
+ return NULL;
15
+ }
16
+ if (NULL == (sub->subject = AGOO_STRDUP(subject))) {
17
+ AGOO_ERR_MEM(err, "strdup()");
18
+ AGOO_FREE(sub);
19
+ return NULL;
20
+ }
21
+ sub->next = NULL;
22
+ sub->con = con;
23
+ con->gsub = sub;
24
+ sub->query = query;
25
+
26
+ return sub;
27
+
28
+ }
29
+
30
+ void
31
+ gql_sub_destroy(gqlSub sub) {
32
+ AGOO_FREE(sub->subject);
33
+ if (NULL != sub->query) {
34
+ gql_doc_destroy(sub->query);
35
+ }
36
+ AGOO_FREE(sub);
37
+ }
@@ -0,0 +1,24 @@
1
+ // Copyright (c) 2019, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef AGOO_GQL_SUB_H
4
+ #define AGOO_GQL_SUB_H
5
+
6
+ #include <stdbool.h>
7
+ #include <stdlib.h>
8
+
9
+ #include "err.h"
10
+
11
+ struct _agooCon;
12
+ struct _gqlDoc;
13
+
14
+ typedef struct _gqlSub {
15
+ struct _gqlSub *next;
16
+ struct _agooCon *con;
17
+ char *subject;
18
+ struct _gqlDoc *query;
19
+ } *gqlSub;
20
+
21
+ extern gqlSub gql_sub_create(agooErr err, struct _agooCon *con, const char *subject, struct _gqlDoc *query);
22
+ extern void gql_sub_destroy(gqlSub sub);
23
+
24
+ #endif // AGOO_GQL_SUB_H
@@ -993,6 +993,20 @@ gql_object_set(agooErr err, gqlValue obj, const char *key, gqlValue item) {
993
993
  return AGOO_ERR_OK;
994
994
  }
995
995
 
996
+ gqlValue
997
+ gql_object_get(gqlValue obj, const char *key) {
998
+ if (NULL != obj && obj->type == &object_type) {
999
+ gqlLink link = obj->members;
1000
+
1001
+ for (; NULL != link; link = link->next) {
1002
+ if (0 == strcmp(link->key, key)) {
1003
+ return link->value;
1004
+ }
1005
+ }
1006
+ }
1007
+ return NULL;
1008
+ }
1009
+
996
1010
  /// create functions //////////////////////////////////////////////////////////
997
1011
 
998
1012
  static gqlValue
@@ -74,6 +74,7 @@ extern gqlValue gql_object_create(agooErr err);
74
74
  extern int gql_list_append(agooErr err, gqlValue list, gqlValue item);
75
75
  extern int gql_list_preend(agooErr err, gqlValue list, gqlValue item);
76
76
  extern int gql_object_set(agooErr err, gqlValue obj, const char *key, gqlValue item);
77
+ extern gqlValue gql_object_get(gqlValue obj, const char *key);
77
78
 
78
79
  extern void gql_int_set(gqlValue value, int32_t i);
79
80
  extern void gql_i64_set(gqlValue value, int64_t i);
@@ -1556,7 +1556,18 @@ static agooText
1556
1556
  op_sdl(agooText text, gqlOp op) {
1557
1557
  gqlSel sel;
1558
1558
 
1559
- text = agoo_text_append(text, "query", 5);
1559
+ switch (op->kind) {
1560
+ case GQL_MUTATION:
1561
+ text = agoo_text_append(text, "mutation", 8);
1562
+ break;
1563
+ case GQL_SUBSCRIPTION:
1564
+ text = agoo_text_append(text, "subscription", 12);
1565
+ break;
1566
+ case GQL_QUERY:
1567
+ default:
1568
+ text = agoo_text_append(text, "query", 5);
1569
+ break;
1570
+ }
1560
1571
  if (NULL != op->name) {
1561
1572
  text = agoo_text_append(text, " ", 1);
1562
1573
  text = agoo_text_append(text, op->name, -1);
@@ -1647,7 +1658,7 @@ gql_dump_hook(agooReq req) {
1647
1658
  if (NULL == (text = agoo_text_prepend(text, buf, cnt))) {
1648
1659
  agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a GraphQL dump.");
1649
1660
  }
1650
- agoo_res_message_push(req->res, text, true);
1661
+ agoo_res_message_push(req->res, text);
1651
1662
  }
1652
1663
 
1653
1664
  gqlField