agoo 2.5.4 → 2.5.5

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/bin/agoo +3 -1
  4. data/ext/agoo/base64.h +3 -3
  5. data/ext/agoo/bind.c +1 -1
  6. data/ext/agoo/bind.h +3 -3
  7. data/ext/agoo/con.c +4 -2
  8. data/ext/agoo/con.h +3 -3
  9. data/ext/agoo/debug.c +7 -0
  10. data/ext/agoo/debug.h +13 -6
  11. data/ext/agoo/doc.c +159 -0
  12. data/ext/agoo/doc.h +34 -0
  13. data/ext/agoo/dtime.h +3 -3
  14. data/ext/agoo/err.h +3 -3
  15. data/ext/agoo/error_stream.h +3 -3
  16. data/ext/agoo/extconf.rb +1 -1
  17. data/ext/agoo/gqlintro.c +333 -0
  18. data/ext/agoo/gqlintro.h +10 -0
  19. data/ext/agoo/gqlvalue.c +1035 -0
  20. data/ext/agoo/gqlvalue.h +88 -0
  21. data/ext/agoo/graphql.c +1078 -0
  22. data/ext/agoo/graphql.h +198 -0
  23. data/ext/agoo/hook.c +2 -0
  24. data/ext/agoo/hook.h +4 -3
  25. data/ext/agoo/http.c +2 -1
  26. data/ext/agoo/http.h +3 -3
  27. data/ext/agoo/kinds.h +3 -3
  28. data/ext/agoo/log.h +3 -3
  29. data/ext/agoo/log_queue.h +3 -3
  30. data/ext/agoo/method.h +3 -3
  31. data/ext/agoo/page.h +3 -3
  32. data/ext/agoo/pub.h +3 -3
  33. data/ext/agoo/queue.h +3 -3
  34. data/ext/agoo/rack_logger.h +3 -3
  35. data/ext/agoo/req.c +2 -2
  36. data/ext/agoo/req.h +3 -3
  37. data/ext/agoo/request.h +3 -3
  38. data/ext/agoo/res.h +3 -3
  39. data/ext/agoo/response.h +3 -3
  40. data/ext/agoo/rhook.c +2 -2
  41. data/ext/agoo/rhook.h +4 -3
  42. data/ext/agoo/rlog.h +3 -3
  43. data/ext/agoo/rresponse.h +3 -3
  44. data/ext/agoo/rserver.c +64 -0
  45. data/ext/agoo/rserver.h +3 -3
  46. data/ext/agoo/rupgraded.c +1 -1
  47. data/ext/agoo/rupgraded.h +3 -3
  48. data/ext/agoo/sdl.c +334 -0
  49. data/ext/agoo/sdl.h +10 -0
  50. data/ext/agoo/seg.h +3 -3
  51. data/ext/agoo/server.c +3 -1
  52. data/ext/agoo/server.h +5 -4
  53. data/ext/agoo/sha1.h +3 -3
  54. data/ext/agoo/sse.h +3 -3
  55. data/ext/agoo/sub.h +3 -3
  56. data/ext/agoo/subject.h +3 -3
  57. data/ext/agoo/text.h +3 -3
  58. data/ext/agoo/upgraded.h +3 -3
  59. data/ext/agoo/websocket.h +3 -3
  60. data/lib/agoo/version.rb +1 -1
  61. data/lib/rack/handler/agoo.rb +3 -1
  62. metadata +12 -12
  63. data/ext/agoo/foo/agoo.c +0 -109
  64. data/ext/agoo/foo/con.c +0 -1220
  65. data/ext/agoo/foo/con.h +0 -65
  66. data/ext/agoo/foo/page.c +0 -699
  67. data/ext/agoo/foo/pub.c +0 -131
  68. data/ext/agoo/foo/pub.h +0 -40
  69. data/ext/agoo/foo/rserver.c +0 -1016
  70. data/ext/agoo/foo/server.c +0 -303
  71. data/ext/agoo/foo/server.h +0 -67
  72. data/ext/agoo/foo/upgraded.c +0 -182
@@ -0,0 +1,88 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef AGOO_GQLVALUE_H
4
+ #define AGOO_GQLVALUE_H
5
+
6
+ #include <stdbool.h>
7
+ #include <stdint.h>
8
+
9
+ #include "err.h"
10
+ #include "text.h"
11
+
12
+ struct _gqlType;
13
+
14
+ typedef struct _gqlLink {
15
+ struct _gqlLink *next;
16
+ char *key;
17
+ struct _gqlValue *value;
18
+ } *gqlLink;
19
+
20
+ typedef struct _gqlValue {
21
+ struct _gqlType *type;
22
+ union {
23
+ char *url;
24
+ int32_t i;
25
+ int64_t i64;
26
+ double f;
27
+ bool b;
28
+ int64_t time;
29
+ char *str;
30
+ char str16[16];
31
+ struct {
32
+ uint64_t hi;
33
+ uint64_t lo;
34
+ } uuid;
35
+ struct {
36
+ struct _gqlLink *members; // linked list for List and Object types
37
+ struct _gqlType *member_type;
38
+ };
39
+ };
40
+ } *gqlValue;
41
+
42
+ extern int gql_value_init(Err err);
43
+
44
+ extern void gql_value_destroy(gqlValue value);
45
+
46
+ extern gqlValue gql_int_create(Err err, int32_t i);
47
+ extern gqlValue gql_i64_create(Err err, int64_t i);
48
+ extern gqlValue gql_string_create(Err err, const char *str, int len);
49
+ extern gqlValue gql_url_create(Err err, const char *url, int len);
50
+ extern gqlValue gql_bool_create(Err err, bool b);
51
+ extern gqlValue gql_float_create(Err err, double f);
52
+ extern gqlValue gql_time_create(Err err, int64_t t);
53
+ extern gqlValue gql_time_str_create(Err err, const char *str, int len);
54
+ extern gqlValue gql_uuid_create(Err err, uint64_t hi, uint64_t lo);
55
+ extern gqlValue gql_uuid_str_create(Err err, const char *str, int len);
56
+ extern gqlValue gql_null_create(Err err);
57
+ extern gqlValue gql_list_create(Err err, struct _gqlType *itemType);
58
+ extern gqlValue gql_object_create(Err err);
59
+
60
+ extern int gql_list_append(Err err, gqlValue list, gqlValue item);
61
+ extern int gql_list_preend(Err err, gqlValue list, gqlValue item);
62
+ extern int gql_object_set(Err err, gqlValue obj, const char *key, gqlValue item);
63
+
64
+ extern void gql_int_set(gqlValue value, int32_t i);
65
+ extern void gql_i64_set(gqlValue value, int64_t i);
66
+ extern void gql_string_set(gqlValue value, const char *str, int len);
67
+ extern int gql_url_set(Err err, gqlValue value, const char *url, int len);
68
+ extern void gql_bool_set(gqlValue value, bool b);
69
+ extern void gql_float_set(gqlValue value, double f);
70
+ extern void gql_time_set(gqlValue value, int64_t t);
71
+ extern int gql_time_str_set(Err err, gqlValue value, const char *str, int len);
72
+ extern void gql_uuid_set(gqlValue value, uint64_t hi, uint64_t lo);
73
+ extern int gql_uuid_str_set(Err err, gqlValue value, const char *str, int len);
74
+ extern void gql_null_set(gqlValue value);
75
+
76
+ extern Text gql_value_json(Text text, gqlValue value, int indent, int depth);
77
+
78
+ extern struct _gqlType gql_null_type;
79
+ extern struct _gqlType gql_int_type;
80
+ extern struct _gqlType gql_i64_type;
81
+ extern struct _gqlType gql_bool_type;
82
+ extern struct _gqlType gql_float_type;
83
+ extern struct _gqlType gql_time_type;
84
+ extern struct _gqlType gql_uuid_type;
85
+ extern struct _gqlType gql_url_type;
86
+ extern struct _gqlType gql_string_type;
87
+
88
+ #endif // AGOO_GQLVALUE_H
@@ -0,0 +1,1078 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+
7
+ #include "debug.h"
8
+ #include "graphql.h"
9
+ #include "gqlintro.h"
10
+ #include "gqlvalue.h"
11
+ #include "req.h"
12
+ #include "res.h"
13
+
14
+ #define BUCKET_SIZE 64
15
+ #define BUCKET_MASK 63
16
+ #define BAD_NAME (uint64_t)-1
17
+
18
+ typedef struct _Slot {
19
+ struct _Slot *next;
20
+ gqlType type;
21
+ uint64_t hash;
22
+ } *Slot;
23
+
24
+ static Slot buckets[BUCKET_SIZE];
25
+
26
+ static uint8_t name_chars[256] = "\
27
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
28
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
29
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
30
+ \x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x00\x00\x00\x00\x00\x00\
31
+ \x00\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\
32
+ \x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x00\x00\x00\x00\x25\
33
+ \x00\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\
34
+ \x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x00\x00\x00\x00\x00\
35
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
36
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
37
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
38
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
39
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
40
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
41
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
42
+ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
43
+ ";
44
+
45
+ static const char spaces[16] = " ";
46
+
47
+ static gqlType query_type = NULL;
48
+ static gqlType mutation_type = NULL;
49
+ static gqlType subscription_type = NULL;
50
+ static gqlType schema_type = NULL;
51
+ static gqlDir directives = NULL;
52
+
53
+ static uint64_t
54
+ calc_hash(const char *name) {
55
+ uint64_t h = 0;
56
+ const uint8_t *k = (const uint8_t*)name;
57
+ uint64_t x;
58
+
59
+ for (; '\0' != *k; k++) {
60
+ if (0 == (x = name_chars[*k])) {
61
+ return BAD_NAME;
62
+ }
63
+ h = 37 * h + x; // fast, just spread it out
64
+ }
65
+ return h;
66
+ }
67
+
68
+ static Slot*
69
+ get_bucketp(uint64_t h) {
70
+ return buckets + (BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
71
+ }
72
+
73
+ static void
74
+ type_destroy(gqlType type) {
75
+ if (!type->core) {
76
+ free((char*)type->name);
77
+ free((char*)type->desc);
78
+ switch (type->kind) {
79
+ case GQL_OBJECT:
80
+ case GQL_FRAG:
81
+ case GQL_INTERFACE:
82
+ case GQL_INPUT: {
83
+ gqlField f;
84
+ gqlArg a;
85
+
86
+ while (NULL != (f = type->fields)) {
87
+ type->fields = f->next;
88
+ while (NULL != (a = f->args)) {
89
+ f->args = a->next;
90
+ free((char*)a->name);
91
+ free((char*)a->type_name);
92
+ free((char*)a->desc);
93
+ gql_value_destroy(a->default_value);
94
+ DEBUG_FREE(mem_graphql_arg, a);
95
+ free(a);
96
+ }
97
+ free((char*)f->name);
98
+ free((char*)f->desc);
99
+ DEBUG_FREE(mem_graphql_field, f);
100
+ free(f);
101
+ }
102
+ free(type->interfaces);
103
+ break;
104
+ }
105
+ case GQL_UNION: {
106
+ gqlTypeLink link;
107
+
108
+ while (NULL != (link = type->types)) {
109
+ type->types = link->next;
110
+ free(link->name);
111
+ free(link);
112
+ }
113
+ break;
114
+ }
115
+ case GQL_ENUM: {
116
+ gqlStrLink link;
117
+
118
+ while (NULL != (link = type->choices)) {
119
+ type->choices = link->next;
120
+ free(link->str);
121
+ free(link);
122
+ }
123
+ break;
124
+ }
125
+ default:
126
+ break;
127
+ }
128
+ DEBUG_FREE(mem_graphql_type, type);
129
+ free(type);
130
+ }
131
+ }
132
+
133
+ static void
134
+ dir_destroy(gqlDir dir) {
135
+ gqlArg a;
136
+ gqlStrLink link;
137
+
138
+ free((char*)dir->name);
139
+ free((char*)dir->desc);
140
+ while (NULL != (a = dir->args)) {
141
+ dir->args = a->next;
142
+ free((char*)a->name);
143
+ free((char*)a->type_name);
144
+ free((char*)a->desc);
145
+ gql_value_destroy(a->default_value);
146
+ DEBUG_FREE(mem_graphql_arg, a);
147
+ free(a);
148
+ }
149
+ while (NULL != (link = dir->locs)) {
150
+ dir->locs = link->next;
151
+ free(link->str);
152
+ free(link);
153
+ }
154
+ DEBUG_FREE(mem_graphql_directive, dir);
155
+ free(dir);
156
+ }
157
+
158
+ gqlType
159
+ gql_type_get(const char *name) {
160
+ gqlType type = NULL;
161
+ uint64_t h = calc_hash(name);
162
+
163
+ if (0 < h) {
164
+ Slot *bucket = get_bucketp(h);
165
+ Slot s;
166
+
167
+ for (s = *bucket; NULL != s; s = s->next) {
168
+ if (h == s->hash && 0 == strcmp(s->type->name, name)) {
169
+ type = s->type;
170
+ break;
171
+ }
172
+ }
173
+ }
174
+ return type;
175
+ }
176
+
177
+ gqlDir
178
+ gql_directive_get(const char *name) {
179
+ gqlDir dir = directives;
180
+
181
+ for (; NULL != dir; dir = dir->next) {
182
+ if (0 == strcmp(name, dir->name)) {
183
+ break;
184
+ }
185
+ }
186
+ return dir;
187
+ }
188
+
189
+ int
190
+ gql_type_set(Err err, gqlType type) {
191
+ uint64_t h = calc_hash(type->name);
192
+
193
+ if (h <= 0) {
194
+ return err_set(err, ERR_ARG, "%s is not a valid GraphQL type name.", type->name);
195
+ } else {
196
+ Slot *bucket = get_bucketp(h);
197
+ Slot s;
198
+
199
+ for (s = *bucket; NULL != s; s = s->next) {
200
+ if (h == s->hash && 0 == strcmp(s->type->name, type->name)) {
201
+ type_destroy(s->type);
202
+ s->type = type;
203
+ return ERR_OK;
204
+ }
205
+ }
206
+ if (NULL == (s = (Slot)malloc(sizeof(struct _Slot)))) {
207
+ return err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL type.");
208
+ }
209
+ DEBUG_ALLOC(mem_graphql_slot, s);
210
+ s->hash = h;
211
+ s->type = type;
212
+ s->next = *bucket;
213
+ *bucket = s;
214
+ }
215
+ return ERR_OK;
216
+ }
217
+
218
+ static void
219
+ type_remove(gqlType type) {
220
+ uint64_t h = calc_hash(type->name);
221
+
222
+ if (0 < h) {
223
+ Slot *bucket = get_bucketp(h);
224
+ Slot s;
225
+ Slot prev = NULL;
226
+
227
+ for (s = *bucket; NULL != s; s = s->next) {
228
+ if (h == s->hash && 0 == strcmp(s->type->name, type->name)) {
229
+ if (NULL == prev) {
230
+ *bucket = s->next;
231
+ } else {
232
+ prev->next = s->next;
233
+ }
234
+ DEBUG_FREE(mem_graphql_slot, s);
235
+ free(s);
236
+
237
+ break;
238
+ }
239
+ prev = s;
240
+ }
241
+ }
242
+ }
243
+
244
+ int
245
+ gql_init(Err err) {
246
+ memset(buckets, 0, sizeof(buckets));
247
+
248
+ if (ERR_OK != gql_value_init(err) ||
249
+ ERR_OK != gql_intro_init(err)) {
250
+
251
+ return err->code;
252
+ }
253
+ if (
254
+ NULL == (query_type = gql_type_create(err, "Query", "The GraphQL root Query.", -1, false, NULL)) ||
255
+ NULL == (mutation_type = gql_type_create(err, "Mutation", "The GraphQL root Mutation.", -1, false, NULL)) ||
256
+ NULL == (subscription_type = gql_type_create(err, "Subscription", "The GraphQL root Subscription.", -1, false, NULL)) ||
257
+ NULL == (schema_type = gql_type_create(err, "schema", "The GraphQL root Object.", -1, false, NULL)) ||
258
+ NULL == gql_type_field(err, schema_type, "query", query_type, "Root level query.", false, false, false, NULL) ||
259
+ NULL == gql_type_field(err, schema_type, "mutation", mutation_type, "Root level mutation.", false, false, false, NULL) ||
260
+ NULL == gql_type_field(err, schema_type, "subscription", subscription_type, "Root level subscription.", false, false, false, NULL)) {
261
+
262
+ return err->code;
263
+ }
264
+ return ERR_OK;
265
+ }
266
+
267
+ void
268
+ gql_destroy() {
269
+ Slot *sp = buckets;
270
+ Slot s;
271
+ Slot n;
272
+ int i;
273
+ gqlDir dir;
274
+
275
+ for (i = BUCKET_SIZE; 0 < i; i--, sp++) {
276
+ Slot *b = sp;
277
+
278
+ *sp = NULL;
279
+ for (s = *b; NULL != s; s = n) {
280
+ n = s->next;
281
+ DEBUG_FREE(mem_graphql_slot, s);
282
+ gql_type_destroy(s->type);
283
+ free(s);
284
+ }
285
+ *sp = NULL;
286
+ }
287
+ while (NULL != (dir = directives)) {
288
+ directives = dir->next;
289
+ dir_destroy(dir);
290
+ }
291
+ }
292
+
293
+ static gqlType
294
+ type_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
295
+ gqlType type = (gqlType)malloc(sizeof(struct _gqlType));
296
+
297
+ if (NULL == type) {
298
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL Type.");
299
+ } else {
300
+ DEBUG_ALLOC(mem_graphql_type, type);
301
+ type->name = strdup(name);
302
+ if (NULL == desc) {
303
+ type->desc = NULL;
304
+ } else {
305
+ if (0 >= dlen) {
306
+ type->desc = strdup(desc);
307
+ } else {
308
+ type->desc = strndup(desc, dlen);
309
+ }
310
+ }
311
+ type->locked = locked;
312
+ type->core = false;
313
+ type->dir = NULL;
314
+
315
+ if (ERR_OK != gql_type_set(err, type)) {
316
+ gql_type_destroy(type);
317
+ type = NULL;
318
+ }
319
+ }
320
+ return type;
321
+ }
322
+
323
+ Text
324
+ gql_object_to_json(Text text, gqlValue value, int indent, int depth) {
325
+ // TBD
326
+ return text;
327
+ }
328
+
329
+ Text
330
+ gql_object_to_graphql(Text text, gqlValue value, int indent, int depth) {
331
+ // TBD
332
+ return text;
333
+ }
334
+
335
+ gqlType
336
+ gql_type_create(Err err, const char *name, const char *desc, int dlen, bool locked, gqlType *interfaces) {
337
+ gqlType type = type_create(err, name, desc, dlen, locked);
338
+
339
+ if (NULL != type) {
340
+ type->kind = GQL_OBJECT;
341
+ type->to_json = gql_object_to_json;
342
+ type->fields = NULL;
343
+ type->interfaces = NULL;
344
+ if (NULL != interfaces) {
345
+ gqlType *tp = interfaces;
346
+ gqlType *np;
347
+ int cnt = 0;
348
+
349
+ for (; NULL != *tp; tp++) {
350
+ cnt++;
351
+ }
352
+ if (0 < cnt) {
353
+ if (NULL == (type->interfaces = (gqlType*)malloc(sizeof(gqlType) * (cnt + 1)))) {
354
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL type interfaces.");
355
+ free(type);
356
+ return NULL;
357
+ }
358
+ for (np = type->interfaces, tp = interfaces; NULL != *tp; np++, tp++) {
359
+ *np = *tp;
360
+ }
361
+ *np = NULL;
362
+ }
363
+ }
364
+ }
365
+ return type;
366
+ }
367
+
368
+ gqlField
369
+ gql_type_field(Err err,
370
+ gqlType type,
371
+ const char *name,
372
+ gqlType return_type,
373
+ const char *desc,
374
+ bool required,
375
+ bool list,
376
+ bool not_empty,
377
+ gqlResolveFunc resolve) {
378
+ gqlField f = (gqlField)malloc(sizeof(struct _gqlField));
379
+
380
+ if (NULL == f) {
381
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL field.");
382
+ } else {
383
+ DEBUG_ALLOC(mem_graphql_field, f);
384
+ f->next = NULL;
385
+ f->name = strdup(name);
386
+ f->type = return_type;
387
+ if (NULL == desc) {
388
+ f->desc = NULL;
389
+ } else {
390
+ f->desc = strdup(desc);
391
+ }
392
+ f->reason = NULL;
393
+ f->args = NULL;
394
+ f->dir = NULL;
395
+ f->resolve = resolve;
396
+ f->required = required;
397
+ f->list = list;
398
+ f->not_empty = not_empty;
399
+ f->deprecated = false;
400
+ if (NULL == type->fields) {
401
+ type->fields = f;
402
+ } else {
403
+ gqlField fend;
404
+
405
+ for (fend = type->fields; NULL != fend->next; fend = fend->next) {
406
+ }
407
+ fend->next = f;
408
+ }
409
+ }
410
+ return f;
411
+ }
412
+
413
+ gqlArg
414
+ gql_field_arg(Err err,
415
+ gqlField field,
416
+ const char *name,
417
+ gqlType type,
418
+ const char *desc,
419
+ struct _gqlValue *def_value,
420
+ bool required) {
421
+ gqlArg a = (gqlArg)malloc(sizeof(struct _gqlArg));
422
+
423
+ if (NULL == a) {
424
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL field argument.");
425
+ } else {
426
+ DEBUG_ALLOC(mem_graphql_arg, a);
427
+ a->next = NULL;
428
+ a->name = strdup(name);
429
+ a->type = type;
430
+ if (NULL == desc) {
431
+ a->desc = NULL;
432
+ } else {
433
+ a->desc = strdup(desc);
434
+ }
435
+ a->default_value = def_value;
436
+ a->dir = NULL;
437
+ a->required = required;
438
+ if (NULL == field->args) {
439
+ field->args = a;
440
+ } else {
441
+ gqlArg end;
442
+
443
+ for (end = field->args; NULL != end->next; end = end->next) {
444
+ }
445
+ end->next = a;
446
+ }
447
+ }
448
+ return a;
449
+ }
450
+
451
+ Text
452
+ gql_union_to_json(Text text, gqlValue value, int indent, int depth) {
453
+ // TBD
454
+ return text;
455
+ }
456
+
457
+ gqlType
458
+ gql_union_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
459
+ gqlType type = type_create(err, name, desc, dlen, locked);
460
+
461
+ if (NULL != type) {
462
+ type->kind = GQL_UNION;
463
+ type->to_json = gql_union_to_json;
464
+ type->types = NULL;
465
+ }
466
+ return type;
467
+ }
468
+
469
+ int
470
+ gql_union_add(Err err, gqlType type, const char *name, int len) {
471
+ gqlTypeLink link = (gqlTypeLink)malloc(sizeof(gqlTypeLink));
472
+
473
+ if (NULL == link) {
474
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL Union value.");
475
+ }
476
+ if (0 >= len) {
477
+ len = (int)strlen(name);
478
+ }
479
+ link->name = strndup(name, len);
480
+ if (NULL == type->types) {
481
+ link->next = type->types;
482
+ type->types = link;
483
+ } else {
484
+ gqlTypeLink last = type->types;
485
+
486
+ for (; NULL != last->next; last = last->next) {
487
+ }
488
+ link->next = NULL;
489
+ last->next = link;
490
+ }
491
+ return ERR_OK;
492
+ }
493
+
494
+ Text
495
+ gql_enum_to_json(Text text, gqlValue value, int indent, int depth) {
496
+ // TBD
497
+ return text;
498
+ }
499
+
500
+ gqlType
501
+ gql_enum_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
502
+ gqlType type = type_create(err, name, desc, dlen, locked);
503
+
504
+ if (NULL != type) {
505
+ type->kind = GQL_ENUM;
506
+ type->to_json = gql_enum_to_json;
507
+ type->choices = NULL;
508
+ }
509
+ return type;
510
+ }
511
+
512
+ int
513
+ gql_enum_add(Err err, gqlType type, const char *value, int len) {
514
+ gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
515
+
516
+ if (NULL == link) {
517
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL Enum value.");
518
+ }
519
+ if (0 >= len) {
520
+ len = (int)strlen(value);
521
+ }
522
+ link->next = type->choices;
523
+ type->choices = link;
524
+ link->str = strndup(value, len);
525
+
526
+ return ERR_OK;
527
+ }
528
+
529
+ int
530
+ gql_enum_append(Err err, gqlType type, const char *value, int len) {
531
+ gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
532
+
533
+ if (NULL == link) {
534
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL Enum value.");
535
+ }
536
+ if (0 >= len) {
537
+ len = (int)strlen(value);
538
+ }
539
+ link->str = strndup(value, len);
540
+ if (NULL == type->choices) {
541
+ link->next = type->choices;
542
+ type->choices = link;
543
+ } else {
544
+ gqlStrLink last = type->choices;
545
+
546
+ for (; NULL != last->next; last = last->next) {
547
+ }
548
+ link->next = NULL;
549
+ last->next = link;
550
+ }
551
+ return ERR_OK;
552
+ }
553
+
554
+ static Text
555
+ fragment_to_json(Text text, gqlValue value, int indent, int depth) {
556
+ // TBD
557
+ return text;
558
+ }
559
+
560
+ gqlType
561
+ gql_fragment_create(Err err, const char *name, const char *desc, int dlen, bool locked, gqlType on) {
562
+ gqlType type = type_create(err, name, desc, dlen, locked);
563
+
564
+ if (NULL != type) {
565
+ type->kind = GQL_FRAG;
566
+ type->to_json = fragment_to_json;
567
+ type->fields = NULL;
568
+ type->on = on;
569
+ }
570
+ return type;
571
+ }
572
+
573
+ static Text
574
+ input_to_json(Text text, gqlValue value, int indent, int depth) {
575
+ // TBD
576
+ return text;
577
+ }
578
+
579
+ gqlType
580
+ gql_input_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
581
+ gqlType type = type_create(err, name, desc, dlen, locked);
582
+
583
+ if (NULL != type) {
584
+ type->kind = GQL_INPUT;
585
+ type->to_json = input_to_json;
586
+ type->fields = NULL;
587
+ }
588
+ return type;
589
+ }
590
+
591
+ static Text
592
+ interface_to_json(Text text, gqlValue value, int indent, int depth) {
593
+ // TBD
594
+ return text;
595
+ }
596
+
597
+ gqlType
598
+ gql_interface_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
599
+ gqlType type = type_create(err, name, desc, dlen, locked);
600
+
601
+ if (NULL != type) {
602
+ type->kind = GQL_INTERFACE;
603
+ type->to_json = interface_to_json;
604
+ type->fields = NULL;
605
+ }
606
+ return type;
607
+ }
608
+
609
+ static Text
610
+ scalar_to_json(Text text, gqlValue value, int indent, int depth) {
611
+ if (NULL == value->str) {
612
+ text = text_append(text, "null", 4);
613
+ } else {
614
+ text = text_append(text, "\"", 1);
615
+ text = text_append(text, value->str, -1);
616
+ text = text_append(text, "\"", 1);
617
+ }
618
+ return text;
619
+ }
620
+
621
+ // Create a scalar type that will be represented as a string.
622
+ gqlType
623
+ gql_scalar_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
624
+ gqlType type = type_create(err, name, desc, dlen, locked);
625
+
626
+ if (NULL != type) {
627
+ type->kind = GQL_SCALAR;
628
+ type->to_json = scalar_to_json;
629
+ type->coerce = NULL;
630
+ type->destroy = NULL;
631
+ }
632
+ return type;
633
+ }
634
+
635
+ gqlDir
636
+ gql_directive_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
637
+ gqlDir dir;
638
+
639
+ if (NULL == (dir = (gqlDir)malloc(sizeof(struct _gqlDir)))) {
640
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL directive.");
641
+ return NULL;
642
+ }
643
+ DEBUG_ALLOC(mem_graphql_directive, dir);
644
+ dir->next = directives;
645
+ directives = dir;
646
+ dir->name = strdup(name);
647
+ dir->args = NULL;
648
+ dir->locs = NULL;
649
+ dir->locked = locked;
650
+ if (NULL == desc) {
651
+ dir->desc = NULL;
652
+ } else {
653
+ if (0 >= dlen) {
654
+ dir->desc = strdup(desc);
655
+ } else {
656
+ dir->desc = strndup(desc, dlen);
657
+ }
658
+ }
659
+ return dir;
660
+ }
661
+
662
+ gqlArg
663
+ gql_dir_arg(Err err,
664
+ gqlDir dir,
665
+ const char *name,
666
+ const char *type_name,
667
+ const char *desc,
668
+ int dlen,
669
+ struct _gqlValue *def_value,
670
+ bool required) {
671
+
672
+ gqlArg a = (gqlArg)malloc(sizeof(struct _gqlArg));
673
+
674
+ if (NULL == a) {
675
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL directive argument.");
676
+ } else {
677
+ DEBUG_ALLOC(mem_graphql_arg, a);
678
+ a->next = NULL;
679
+ a->name = strdup(name);
680
+ a->type_name = strdup(type_name);
681
+ a->type = NULL;
682
+ if (NULL == desc) {
683
+ a->desc = NULL;
684
+ } else if (0 < dlen) {
685
+ a->desc = strdup(desc);
686
+ } else {
687
+ a->desc = strndup(desc, dlen);
688
+ }
689
+ a->default_value = def_value;
690
+ a->dir = NULL;
691
+ a->required = required;
692
+ if (NULL == dir->args) {
693
+ dir->args = a;
694
+ } else {
695
+ gqlArg end;
696
+
697
+ for (end = dir->args; NULL != end->next; end = end->next) {
698
+ }
699
+ end->next = a;
700
+ }
701
+ }
702
+ return a;
703
+ }
704
+
705
+ int
706
+ gql_directive_on(Err err, gqlDir d, const char *on, int len) {
707
+ gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
708
+ gqlStrLink loc;
709
+
710
+ if (NULL == link) {
711
+ err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL directive location.");
712
+ }
713
+ if (0 >= len) {
714
+ len = (int)strlen(on);
715
+ }
716
+ if (NULL == d->locs) {
717
+ link->next = d->locs;
718
+ d->locs = link;
719
+ } else {
720
+ link->next = NULL;
721
+ for (loc = d->locs; NULL != loc->next; loc = loc->next) {
722
+ }
723
+ loc->next = link;
724
+ }
725
+ link->str = strndup(on, len);
726
+
727
+ return ERR_OK;
728
+ }
729
+
730
+ void
731
+ gql_type_destroy(gqlType type) {
732
+ type_destroy(type);
733
+ type_remove(type);
734
+ }
735
+
736
+ // If negative then there are non-simple-string characters.
737
+ static int
738
+ desc_len(const char *desc) {
739
+ const char *d = desc;
740
+ int special = 1;
741
+
742
+ for (; '\0' != *d; d++) {
743
+ if (*d < ' ' || '"' == *d || '\\' == *d) {
744
+ special = -1;
745
+ }
746
+ }
747
+ return (int)(d - desc) * special;
748
+ }
749
+
750
+ static Text
751
+ desc_sdl(Text text, const char *desc, int indent) {
752
+ if (NULL != desc) {
753
+ int cnt = desc_len(desc);
754
+
755
+ if (0 < indent) {
756
+ text = text_append(text, spaces, indent);
757
+ }
758
+ if (0 <= cnt) {
759
+ text = text_append(text, "\"", 1);
760
+ text = text_append(text, desc, cnt);
761
+ text = text_append(text, "\"\n", 2);
762
+ } else {
763
+ text = text_append(text, "\"\"\"\n", 4);
764
+ if (0 < indent) {
765
+ const char *start = desc;
766
+ const char *d = desc;
767
+
768
+ for (; '\0' != *d; d++) {
769
+ if ('\r' == *d) {
770
+ int len = (int)(d - start);
771
+ d++;
772
+ if ('\n' == *d) {
773
+ d++;
774
+ }
775
+ text = text_append(text, spaces, indent);
776
+ text = text_append(text, start, len);
777
+ text = text_append(text, "\n", 1);
778
+ start = d;
779
+ } else if ('\n' == *d) {
780
+ text = text_append(text, spaces, indent);
781
+ text = text_append(text, start, (int)(d - start));
782
+ text = text_append(text, "\n", 1);
783
+ d++;
784
+ start = d;
785
+ }
786
+ }
787
+ text = text_append(text, spaces, indent);
788
+ text = text_append(text, start, (int)(d - start));
789
+ } else {
790
+ text = text_append(text, desc, -1);
791
+ }
792
+ if (0 < indent) {
793
+ text = text_append(text, "\n", 1);
794
+ text = text_append(text, spaces, indent);
795
+ text = text_append(text, "\"\"\"\n", 4);
796
+ } else {
797
+ text = text_append(text, "\n\"\"\"\n", 5);
798
+ }
799
+ }
800
+ }
801
+ return text;
802
+ }
803
+
804
+ static Text
805
+ arg_sdl(Text text, gqlArg a, bool with_desc, bool last) {
806
+ if (with_desc) {
807
+ text = desc_sdl(text, a->desc, 4);
808
+ }
809
+ text = text_append(text, a->name, -1);
810
+ if (a->required) {
811
+ text = text_append(text, "!: ", 3);
812
+ } else {
813
+ text = text_append(text, ": ", 2);
814
+ }
815
+ text = text_append(text, a->type_name, -1);
816
+ if (NULL != a->default_value) {
817
+ text = text_append(text, " = ", 3);
818
+ text = gql_value_json(text, a->default_value, 0, 0);
819
+ }
820
+ if (!last) {
821
+ text = text_append(text, ", ", 2);
822
+ }
823
+ return text;
824
+ }
825
+
826
+ static Text
827
+ field_sdl(Text text, gqlField f, bool with_desc) {
828
+ if (with_desc) {
829
+ text = desc_sdl(text, f->desc, 2);
830
+ }
831
+ text = text_append(text, " ", 2);
832
+ text = text_append(text, f->name, -1);
833
+ if (NULL != f->args) {
834
+ gqlArg a;
835
+
836
+ text = text_append(text, "(", 1);
837
+ for (a = f->args; NULL != a; a = a->next) {
838
+ text = arg_sdl(text, a, with_desc, NULL == a->next);
839
+ }
840
+ text = text_append(text, ")", 1);
841
+ }
842
+ text = text_append(text, ": ", 2);
843
+ if (f->list) {
844
+ text = text_append(text, "[", 1);
845
+ text = text_append(text, f->type->name, -1);
846
+ if (f->not_empty) {
847
+ text = text_append(text, "!", 1);
848
+ }
849
+ text = text_append(text, "]", 1);
850
+ } else {
851
+ text = text_append(text, f->type->name, -1);
852
+ }
853
+ if (f->required) {
854
+ text = text_append(text, "!", 1);
855
+ }
856
+ text = text_append(text, "\n", 1);
857
+
858
+ return text;
859
+ }
860
+
861
+ Text
862
+ gql_type_sdl(Text text, gqlType type, bool with_desc) {
863
+ if (with_desc) {
864
+ desc_sdl(text, type->desc, 0);
865
+ }
866
+ switch (type->kind) {
867
+ case GQL_OBJECT:
868
+ case GQL_FRAG: {
869
+ gqlField f;
870
+
871
+ text = text_append(text, "type ", 5);
872
+ text = text_append(text, type->name, -1);
873
+ if (NULL != type->interfaces) {
874
+ gqlType *tp = type->interfaces;
875
+
876
+ text = text_append(text, " implements ", 12);
877
+ for (; NULL != *tp; tp++) {
878
+ text = text_append(text, (*tp)->name, -1);
879
+ if (NULL != *(tp + 1)) {
880
+ text = text_append(text, ", ", 2);
881
+ }
882
+ }
883
+ }
884
+ text = text_append(text, " {\n", 3);
885
+ for (f = type->fields; NULL != f; f = f->next) {
886
+ text = field_sdl(text, f, with_desc);
887
+ }
888
+ text = text_append(text, "}\n", 2);
889
+ break;
890
+ }
891
+ case GQL_UNION: {
892
+ gqlTypeLink link;
893
+
894
+ text = text_append(text, "union ", 6);
895
+ text = text_append(text, type->name, -1);
896
+ text = text_append(text, " = ", 3);
897
+ for (link = type->types; NULL != link; link = link->next) {
898
+ text = text_append(text, link->name, -1);
899
+ if (NULL != link->next) {
900
+ text = text_append(text, " | ", 3);
901
+ }
902
+ }
903
+ break;
904
+ }
905
+ case GQL_ENUM: {
906
+ gqlStrLink link;;
907
+
908
+ text = text_append(text, "enum ", 5);
909
+ text = text_append(text, type->name, -1);
910
+ text = text_append(text, " {\n", 3);
911
+ for (link = type->choices; NULL != link; link = link->next) {
912
+ text = text_append(text, " ", 2);
913
+ text = text_append(text, link->str, -1);
914
+ text = text_append(text, "\n", 1);
915
+ }
916
+ text = text_append(text, "}\n", 2);
917
+ break;
918
+ }
919
+ case GQL_SCALAR:
920
+ text = text_append(text, "scalar ", 7);
921
+ text = text_append(text, type->name, -1);
922
+ text = text_append(text, "\n", 1);
923
+ break;
924
+ case GQL_INTERFACE: {
925
+ gqlField f;
926
+
927
+ text = text_append(text, "interface ", 10);
928
+ text = text_append(text, type->name, -1);
929
+ text = text_append(text, " {\n", 3);
930
+
931
+ for (f = type->fields; NULL != f; f = f->next) {
932
+ text = field_sdl(text, f, with_desc);
933
+ }
934
+ text = text_append(text, "}\n", 2);
935
+ break;
936
+ }
937
+ case GQL_INPUT: {
938
+ gqlField f;
939
+
940
+ text = text_append(text, "input ", 6);
941
+ text = text_append(text, type->name, -1);
942
+ text = text_append(text, " {\n", 3);
943
+ for (f = type->fields; NULL != f; f = f->next) {
944
+ text = field_sdl(text, f, with_desc);
945
+ }
946
+ text = text_append(text, "}\n", 2);
947
+ break;
948
+ }
949
+ default:
950
+ break;
951
+ }
952
+ return text;
953
+ }
954
+
955
+ Text
956
+ gql_directive_sdl(Text text, gqlDir d, bool with_desc) {
957
+ gqlStrLink link;
958
+
959
+ if (with_desc) {
960
+ text = desc_sdl(text, d->desc, 0);
961
+ }
962
+ text = text_append(text, "directive @", 11);
963
+ text = text_append(text, d->name, -1);
964
+ if (NULL != d->args) {
965
+ gqlArg a;
966
+
967
+ text = text_append(text, "(", 1);
968
+ for (a = d->args; NULL != a; a = a->next) {
969
+ text = arg_sdl(text, a, with_desc, NULL == a->next);
970
+ }
971
+ text = text_append(text, ")", 1);
972
+ }
973
+ text = text_append(text, " on ", 4);
974
+ for (link = d->locs; NULL != link; link = link->next) {
975
+ text = text_append(text, link->str, -1);
976
+ if (NULL != link->next) {
977
+ text = text_append(text, " | ", 3);
978
+ }
979
+ }
980
+ return text;
981
+ }
982
+
983
+ static int
984
+ type_cmp(const void *v0, const void *v1) {
985
+ gqlType t0 = *(gqlType*)v0;
986
+ gqlType t1 = *(gqlType*)v1;
987
+
988
+ if (t0->kind == t1->kind) {
989
+ if (0 == strcmp("schema", t0->name)) {
990
+ return -1;
991
+ }
992
+ if (0 == strcmp("schema", t1->name)) {
993
+ return 1;
994
+ }
995
+ return strcmp(t0->name, t1->name);
996
+ }
997
+ return t0->kind - t1->kind;
998
+ }
999
+
1000
+ Text
1001
+ gql_schema_sdl(Text text, bool with_desc, bool all) {
1002
+ Slot *bucket;
1003
+ Slot s;
1004
+ gqlType type;
1005
+ gqlDir d;
1006
+ int i;
1007
+ int cnt = 0;
1008
+
1009
+ for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1010
+ for (s = *bucket; NULL != s; s = s->next) {
1011
+ type = s->type;
1012
+ if (!all && type->core) {
1013
+ continue;
1014
+ }
1015
+ cnt++;
1016
+ }
1017
+ }
1018
+ if (0 < cnt) {
1019
+ gqlType types[cnt];
1020
+ gqlType *tp = types;
1021
+
1022
+ for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1023
+ for (s = *bucket; NULL != s; s = s->next) {
1024
+ type = s->type;
1025
+ if (!all && type->core) {
1026
+ continue;
1027
+ }
1028
+ *tp++ = type;
1029
+ }
1030
+ }
1031
+ qsort(types, cnt, sizeof(gqlType), type_cmp);
1032
+ for (i = 0, tp = types; i < cnt; i++, tp++) {
1033
+ text = gql_type_sdl(text, *tp, with_desc);
1034
+ if (i < cnt - 1) {
1035
+ text = text_append(text, "\n", 1);
1036
+ }
1037
+ }
1038
+ }
1039
+ for (d = directives; NULL != d; d = d->next) {
1040
+ text = text_append(text, "\n", 1);
1041
+ text = gql_directive_sdl(text, d, with_desc);
1042
+ text = text_append(text, "\n", 1);
1043
+ }
1044
+ return text;
1045
+ }
1046
+
1047
+ void
1048
+ gql_dump_hook(Req req) {
1049
+ char buf[256];
1050
+ int cnt;
1051
+ Text text = text_allocate(4094);
1052
+ bool all = false;
1053
+ bool with_desc = true;
1054
+ int vlen;
1055
+ const char *s = req_query_value(req, "all", 3, &vlen);
1056
+
1057
+ if (NULL != s && 4 == vlen && 0 == strncasecmp("true", s, 4)) {
1058
+ all = true;
1059
+ }
1060
+ s = req_query_value(req, "with_desc", 9, &vlen);
1061
+
1062
+ if (NULL != s && 5 == vlen && 0 == strncasecmp("false", s, 5)) {
1063
+ with_desc = false;
1064
+ }
1065
+ text = gql_schema_sdl(text, with_desc, all);
1066
+ cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 200 Okay\r\nContent-Type: application/graphql\r\nContent-Length: %ld\r\n\r\n", text->len);
1067
+ text = text_prepend(text, buf, cnt);
1068
+ res_set_message(req->res, text);
1069
+ }
1070
+
1071
+ void
1072
+ gql_eval_hook(Req req) {
1073
+ // TBD detect introspection
1074
+ // start resolving by callout to some global handler as needed
1075
+ // pass target, field, args
1076
+ // return json or gqlValue
1077
+ // for handler, if introspection then handler here else global
1078
+ }