agoo 2.11.7 → 2.12.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.

@@ -424,6 +424,7 @@ parse_value(agooErr err, agooDoc doc) {
424
424
  if (4 <= doc->end - doc->cur &&
425
425
  'r' == doc->cur[1] && 'u' == doc->cur[2] && 'e' == doc->cur[3]) {
426
426
  value = gql_bool_create(err, true);
427
+ doc->cur += 4;
427
428
  } else {
428
429
  agoo_doc_err(doc, err, "invalid token");
429
430
  }
@@ -432,6 +433,7 @@ parse_value(agooErr err, agooDoc doc) {
432
433
  if (5 <= doc->end - doc->cur &&
433
434
  'a' == doc->cur[1] && 'l' == doc->cur[2] && 's' == doc->cur[3] && 'e' == doc->cur[4]) {
434
435
  value = gql_bool_create(err, false);
436
+ doc->cur += 5;
435
437
  } else {
436
438
  agoo_doc_err(doc, err, "invalid token");
437
439
  }
@@ -440,6 +442,7 @@ parse_value(agooErr err, agooDoc doc) {
440
442
  if (4 <= doc->end - doc->cur &&
441
443
  'u' == doc->cur[1] && 'l' == doc->cur[2] && 'l' == doc->cur[3]) {
442
444
  value = gql_null_create(err);
445
+ doc->cur += 4;
443
446
  } else {
444
447
  agoo_doc_err(doc, err, "invalid token");
445
448
  }
@@ -590,8 +590,7 @@ list_destroy(gqlValue value) {
590
590
 
591
591
  while (NULL != (link = value->members)) {
592
592
  value->members = link->next;
593
- gql_value_destroy(link->value);
594
- AGOO_FREE(link);
593
+ gql_link_destroy(link);
595
594
  }
596
595
  }
597
596
 
@@ -679,9 +678,7 @@ object_destroy(gqlValue value) {
679
678
 
680
679
  while (NULL != (link = value->members)) {
681
680
  value->members = link->next;
682
- gql_value_destroy(link->value);
683
- AGOO_FREE(link->key);
684
- AGOO_FREE(link);
681
+ gql_link_destroy(link);
685
682
  }
686
683
  }
687
684
 
@@ -1572,6 +1569,9 @@ int
1572
1569
  gql_value_convert(agooErr err, gqlValue value, gqlType type) {
1573
1570
  int code = AGOO_ERR_OK;
1574
1571
 
1572
+ if (GQL_NON_NULL == type->kind) {
1573
+ type = type->base;
1574
+ }
1575
1575
  if (type != value->type) {
1576
1576
  switch (type->scalar_kind) {
1577
1577
  case GQL_SCALAR_BOOL:
@@ -517,8 +517,7 @@ gql_type_field(agooErr err,
517
517
  gqlType return_type,
518
518
  gqlValue default_value,
519
519
  const char *desc,
520
- size_t dlen,
521
- bool required) {
520
+ size_t dlen) {
522
521
  gqlField f = (gqlField)AGOO_MALLOC(sizeof(struct _gqlField));
523
522
 
524
523
  if (NULL == f) {
@@ -538,7 +537,6 @@ gql_type_field(agooErr err,
538
537
  f->args = NULL;
539
538
  f->dir = NULL;
540
539
  f->default_value = default_value;
541
- f->required = required;
542
540
  if (NULL == type->fields) {
543
541
  type->fields = f;
544
542
  } else {
@@ -559,8 +557,7 @@ gql_field_arg(agooErr err,
559
557
  gqlType type,
560
558
  const char *desc,
561
559
  size_t dlen,
562
- struct _gqlValue *def_value,
563
- bool required) {
560
+ struct _gqlValue *def_value) {
564
561
  gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
565
562
 
566
563
  if (NULL == a) {
@@ -579,7 +576,6 @@ gql_field_arg(agooErr err,
579
576
  }
580
577
  a->default_value = def_value;
581
578
  a->dir = NULL;
582
- a->required = required;
583
579
  if (NULL == field->args) {
584
580
  field->args = a;
585
581
  } else {
@@ -600,8 +596,7 @@ gql_input_arg(agooErr err,
600
596
  gqlType type,
601
597
  const char *desc,
602
598
  size_t dlen,
603
- struct _gqlValue *def_value,
604
- bool required) {
599
+ struct _gqlValue *def_value) {
605
600
  gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
606
601
 
607
602
  if (NULL == a) {
@@ -620,7 +615,6 @@ gql_input_arg(agooErr err,
620
615
  }
621
616
  a->default_value = def_value;
622
617
  a->dir = NULL;
623
- a->required = required;
624
618
  if (NULL == input->args) {
625
619
  input->args = a;
626
620
  } else {
@@ -801,8 +795,7 @@ gql_dir_arg(agooErr err,
801
795
  gqlType type,
802
796
  const char *desc,
803
797
  size_t dlen,
804
- struct _gqlValue *def_value,
805
- bool required) {
798
+ struct _gqlValue *def_value) {
806
799
 
807
800
  gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
808
801
 
@@ -822,7 +815,6 @@ gql_dir_arg(agooErr err,
822
815
  }
823
816
  a->default_value = def_value;
824
817
  a->dir = NULL;
825
- a->required = required;
826
818
  if (NULL == dir->args) {
827
819
  dir->args = a;
828
820
  } else {
@@ -863,11 +855,11 @@ gql_directive_on(agooErr err, gqlDir d, const char *on, int len) {
863
855
  }
864
856
 
865
857
  gqlType
866
- gql_assure_list(agooErr err, gqlType base, bool not_empty) {
858
+ gql_assure_list(agooErr err, gqlType base) {
867
859
  char name[256];
868
860
  gqlType type;
869
861
 
870
- if ((int)sizeof(name) <= snprintf(name, sizeof(name), "[%s%s]", base->name, not_empty ? "!" : "")) {
862
+ if ((int)sizeof(name) <= snprintf(name, sizeof(name), "[%s]", base->name)) {
871
863
  agoo_err_set(err, AGOO_ERR_ARG, "Name too long");
872
864
  return NULL;
873
865
  }
@@ -876,7 +868,24 @@ gql_assure_list(agooErr err, gqlType base, bool not_empty) {
876
868
  return NULL;
877
869
  }
878
870
  type->base = base;
879
- type->not_empty = not_empty;
871
+ }
872
+ return type;
873
+ }
874
+
875
+ gqlType
876
+ gql_assure_nonnull(agooErr err, gqlType base) {
877
+ char name[256];
878
+ gqlType type;
879
+
880
+ if ((int)sizeof(name) <= snprintf(name, sizeof(name), "%s!", base->name)) {
881
+ agoo_err_set(err, AGOO_ERR_ARG, "Name too long");
882
+ return NULL;
883
+ }
884
+ if (NULL == (type = gql_type_get(name))) {
885
+ if (NULL == (type = type_create(err, GQL_NON_NULL, name, NULL, 0))) {
886
+ return NULL;
887
+ }
888
+ type->base = base;
880
889
  }
881
890
  return type;
882
891
  }
@@ -988,9 +997,6 @@ arg_sdl(agooText text, gqlArg a, bool with_desc, bool same_line, bool last) {
988
997
  if (NULL != a->type) { // should always be true
989
998
  text = agoo_text_append(text, a->type->name, -1);
990
999
  }
991
- if (a->required) {
992
- text = agoo_text_append(text, "!", 1);
993
- }
994
1000
  if (NULL != a->default_value) {
995
1001
  text = agoo_text_append(text, " = ", 3);
996
1002
  text = gql_value_sdl(text, a->default_value, 0, 0);
@@ -1024,9 +1030,6 @@ field_sdl(agooText text, gqlField f, bool with_desc) {
1024
1030
  }
1025
1031
  text = agoo_text_append(text, ": ", 2);
1026
1032
  text = agoo_text_append(text, f->type->name, -1);
1027
- if (f->required) {
1028
- text = agoo_text_append(text, "!", 1);
1029
- }
1030
1033
  if (NULL != f->default_value) {
1031
1034
  text = agoo_text_append(text, " = ", 3);
1032
1035
  text = gql_value_sdl(text, f->default_value, 0, 0);
@@ -1204,7 +1207,7 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1204
1207
  for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1205
1208
  for (s = *bucket; NULL != s; s = s->next) {
1206
1209
  type = s->type;
1207
- if (GQL_LIST == type->kind) {
1210
+ if (GQL_LIST == type->kind || GQL_NON_NULL == type->kind) {
1208
1211
  continue;
1209
1212
  }
1210
1213
  if (!all && type->core) {
@@ -1216,11 +1219,12 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1216
1219
  if (0 < cnt) {
1217
1220
  gqlType types[cnt];
1218
1221
  gqlType *tp = types;
1222
+ long len;
1219
1223
 
1220
1224
  for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1221
1225
  for (s = *bucket; NULL != s; s = s->next) {
1222
1226
  type = s->type;
1223
- if (GQL_LIST == type->kind) {
1227
+ if (GQL_LIST == type->kind || GQL_NON_NULL == type->kind) {
1224
1228
  continue;
1225
1229
  }
1226
1230
  if (!all && type->core) {
@@ -1231,8 +1235,9 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1231
1235
  }
1232
1236
  qsort(types, cnt, sizeof(gqlType), type_cmp);
1233
1237
  for (i = 0, tp = types; i < cnt; i++, tp++) {
1238
+ len = text->len;
1234
1239
  text = gql_type_sdl(text, *tp, with_desc);
1235
- if (i < cnt - 1) {
1240
+ if (i < cnt - 1 && len < text->len) {
1236
1241
  text = agoo_text_append(text, "\n", 1);
1237
1242
  }
1238
1243
  }
@@ -1355,6 +1360,9 @@ gql_doc_create(agooErr err) {
1355
1360
  doc->ops = NULL;
1356
1361
  doc->vars = NULL;
1357
1362
  doc->frags = NULL;
1363
+ doc->req = NULL;
1364
+ doc->ctx = NULL;
1365
+ doc->ctx_free = NULL;
1358
1366
  }
1359
1367
  return doc;
1360
1368
  }
@@ -1474,6 +1482,9 @@ gql_doc_destroy(gqlDoc doc) {
1474
1482
  doc->frags = frag->next;
1475
1483
  gql_frag_destroy(frag);
1476
1484
  }
1485
+ if (NULL != doc->ctx_free) {
1486
+ doc->ctx_free(doc->ctx);
1487
+ }
1477
1488
  AGOO_FREE(doc);
1478
1489
  }
1479
1490
 
@@ -88,7 +88,6 @@ typedef struct _gqlArg {
88
88
  struct _gqlType *type;
89
89
  struct _gqlValue *default_value;
90
90
  struct _gqlDirUse *dir;
91
- bool required;
92
91
  } *gqlArg;
93
92
 
94
93
  typedef struct _gqlField {
@@ -99,7 +98,6 @@ typedef struct _gqlField {
99
98
  gqlArg args;
100
99
  struct _gqlDirUse *dir;
101
100
  struct _gqlValue *default_value;
102
- bool required;
103
101
  } *gqlField;
104
102
 
105
103
  typedef struct _gqlDir {
@@ -133,15 +131,12 @@ typedef struct _gqlType {
133
131
  gqlTypeLink types; // Union
134
132
  gqlEnumVal choices; // Enums
135
133
  gqlArg args; // InputObject
134
+ struct _gqlType *base; // List and NonNull types
136
135
  struct { // scalar
137
136
  agooText (*to_sdl)(agooText text, struct _gqlValue *value, int indent, int depth);
138
137
  agooText (*to_json)(agooText text, struct _gqlValue *value, int indent, int depth);
139
138
  void (*destroy)(struct _gqlValue *value);
140
139
  };
141
- struct { // List types
142
- struct _gqlType *base;
143
- bool not_empty;
144
- };
145
140
  };
146
141
  } *gqlType;
147
142
 
@@ -202,6 +197,9 @@ typedef struct _gqlDoc {
202
197
  gqlFrag frags;
203
198
  gqlOp op; // the op to execute
204
199
  struct _gqlFuncs funcs;
200
+ struct _agooReq *req;
201
+ void *ctx;
202
+ void (*ctx_free)(void*);
205
203
  } *gqlDoc;
206
204
 
207
205
  extern int gql_init(agooErr err);
@@ -222,8 +220,7 @@ extern gqlField gql_type_field(agooErr err,
222
220
  gqlType return_type,
223
221
  struct _gqlValue *default_value,
224
222
  const char *desc,
225
- size_t dlen,
226
- bool required);
223
+ size_t dlen);
227
224
 
228
225
  extern gqlArg gql_field_arg(agooErr err,
229
226
  gqlField field,
@@ -231,8 +228,7 @@ extern gqlArg gql_field_arg(agooErr err,
231
228
  gqlType type,
232
229
  const char *desc,
233
230
  size_t dlen,
234
- struct _gqlValue *def_value,
235
- bool required);
231
+ struct _gqlValue *def_value);
236
232
 
237
233
  extern gqlArg gql_input_arg(agooErr err,
238
234
  gqlType input,
@@ -240,8 +236,7 @@ extern gqlArg gql_input_arg(agooErr err,
240
236
  gqlType type,
241
237
  const char *desc,
242
238
  size_t dlen,
243
- struct _gqlValue *def_value,
244
- bool required);
239
+ struct _gqlValue *def_value);
245
240
 
246
241
  extern gqlType gql_scalar_create(agooErr err, const char *name, const char *desc, size_t dlen);
247
242
 
@@ -252,8 +247,7 @@ extern gqlArg gql_dir_arg(agooErr err,
252
247
  gqlType type,
253
248
  const char *desc,
254
249
  size_t dlen,
255
- struct _gqlValue *def_value,
256
- bool required);
250
+ struct _gqlValue *def_value);
257
251
  extern int gql_directive_on(agooErr err, gqlDir d, const char *on, int len);
258
252
  extern gqlDir gql_directive_get(const char *name);
259
253
 
@@ -266,7 +260,8 @@ extern int gql_union_add(agooErr err, gqlType type, gqlType member);
266
260
  extern gqlType gql_enum_create(agooErr err, const char *name, const char *desc, size_t dlen);
267
261
  extern gqlEnumVal gql_enum_append(agooErr err, gqlType type, const char *value, size_t len, const char *desc, size_t dlen);
268
262
 
269
- extern gqlType gql_assure_list(agooErr err, gqlType base, bool not_empty);
263
+ extern gqlType gql_assure_list(agooErr err, gqlType base);
264
+ extern gqlType gql_assure_nonnull(agooErr err, gqlType base);
270
265
 
271
266
  extern int gql_type_set(agooErr err, gqlType type);
272
267
  extern gqlType gql_type_get(const char *name);
@@ -291,6 +286,7 @@ extern agooText gql_doc_sdl(gqlDoc doc, agooText text);
291
286
  extern void gql_dump_hook(struct _agooReq *req);
292
287
  extern void gql_eval_get_hook(struct _agooReq *req);
293
288
  extern void gql_eval_post_hook(struct _agooReq *req);
289
+ extern void gql_eval_options_hook(struct _agooReq *req);
294
290
 
295
291
  extern int gql_validate(agooErr err);
296
292
 
@@ -13,6 +13,7 @@
13
13
  #include "gqlvalue.h"
14
14
  #include "graphql.h"
15
15
  #include "pub.h"
16
+ #include "request.h"
16
17
  #include "sdl.h"
17
18
  #include "server.h"
18
19
 
@@ -27,13 +28,28 @@ typedef struct _typeClass {
27
28
  const char *classname;
28
29
  } *TypeClass;
29
30
 
31
+ typedef struct _bhArgs {
32
+ agooErr err;
33
+ agooReq req;
34
+ agooText headers;
35
+ } *bhArgs;
36
+
30
37
  static VALUE graphql_class = Qundef;
31
38
  static VALUE vroot = Qnil;
39
+ static VALUE build_headers_func = Qnil;
40
+
41
+ static ID call_id;
32
42
 
33
43
  static TypeClass type_class_map = NULL;
34
44
 
35
45
  static int
36
- make_ruby_use(agooErr err, VALUE root, const char *method, const char *type_name, bool fresh, gqlType schema_type, const char *desc) {
46
+ make_ruby_use(agooErr err,
47
+ VALUE root,
48
+ const char *method,
49
+ const char *type_name,
50
+ bool fresh,
51
+ gqlType schema_type,
52
+ const char *desc) {
37
53
  gqlType type;
38
54
  gqlDirUse use;
39
55
  volatile VALUE v;
@@ -47,7 +63,7 @@ make_ruby_use(agooErr err, VALUE root, const char *method, const char *type_name
47
63
  return agoo_err_set(err, AGOO_ERR_ARG, "Failed to find the '%s' type.", type_name);
48
64
  }
49
65
  if (fresh) {
50
- if (NULL == gql_type_field(err, schema_type, method, type, NULL, desc, 0, false)) {
66
+ if (NULL == gql_type_field(err, schema_type, method, type, NULL, desc, 0)) {
51
67
  return err->code;
52
68
  }
53
69
  }
@@ -225,6 +241,9 @@ coerce(agooErr err, gqlRef ref, gqlType type) {
225
241
  if (Qnil == (VALUE)ref) {
226
242
  return gql_null_create(err);
227
243
  }
244
+ if (NULL != type && GQL_NON_NULL == type->kind) {
245
+ type = type->base;
246
+ }
228
247
  if (NULL == type) {
229
248
  // This is really an error but make a best effort anyway.
230
249
  switch (rb_type((VALUE)ref)) {
@@ -372,12 +391,25 @@ ref_type(gqlRef ref) {
372
391
  return type;
373
392
  }
374
393
 
394
+ static VALUE
395
+ make_plan(gqlSel sel) {
396
+
397
+ // TBD create a plan object that wraps the sel, create children as needed
398
+ // plan is not valid outside of method
399
+ // should be able to create SDL from it
400
+
401
+ return Qnil;
402
+ }
403
+
375
404
  static int
376
405
  resolve(agooErr err, gqlDoc doc, gqlRef target, gqlField field, gqlSel sel, gqlValue result, int depth) {
377
406
  volatile VALUE child;
407
+ volatile VALUE rreq = Qnil;
378
408
  VALUE obj = (VALUE)target;
379
409
  int d2 = depth + 1;
380
410
  const char *key = sel->name;
411
+ ID method;
412
+ int arity;
381
413
 
382
414
  if ('_' == *sel->name && '_' == sel->name[1]) {
383
415
  if (0 == strcmp("__typename", sel->name)) {
@@ -397,8 +429,10 @@ resolve(agooErr err, gqlDoc doc, gqlRef target, gqlField field, gqlSel sel, gqlV
397
429
  return agoo_err_set(err, AGOO_ERR_EVAL, "Not a valid operation on the root object.");
398
430
  }
399
431
  }
400
- if (NULL == sel->args) {
401
- child = rb_funcall(obj, rb_intern(sel->name), 0);
432
+ method = rb_intern(sel->name);
433
+ arity = rb_obj_method_arity(obj, method);
434
+ if (0 == arity) {
435
+ child = rb_funcall(obj, method, 0);
402
436
  } else {
403
437
  volatile VALUE rargs = rb_hash_new();
404
438
  gqlSelArg sa;
@@ -429,7 +463,21 @@ resolve(agooErr err, gqlDoc doc, gqlRef target, gqlField field, gqlSel sel, gqlV
429
463
  }
430
464
  rb_hash_aset(rargs, rb_str_new_cstr(sa->name), gval_to_ruby(v));
431
465
  }
432
- child = rb_funcall(obj, rb_intern(sel->name), 1, rargs);
466
+ if (-1 == arity || 1 == arity) {
467
+ child = rb_funcall(obj, method, 1, rargs);
468
+ } else {
469
+ if (NULL == doc->ctx) {
470
+ rreq = request_wrap(doc->req);
471
+ doc->ctx = (void*)rreq;
472
+ } else {
473
+ rreq = (VALUE)doc->ctx;
474
+ }
475
+ if (-2 == arity || 2 == arity) {
476
+ child = rb_funcall(obj, method, 2, rargs, rreq);
477
+ } else {
478
+ child = rb_funcall(obj, method, 3, rargs, rreq, make_plan(sel));
479
+ }
480
+ }
433
481
  }
434
482
  if (GQL_SUBSCRIPTION == doc->op->kind && RUBY_T_STRING == rb_type(child)) {
435
483
  gqlValue c;
@@ -591,6 +639,7 @@ graphql_schema(VALUE self, VALUE root) {
591
639
  gqlDirUse use;
592
640
  gqlType schema_type;
593
641
  bool fresh = false;
642
+ gqlType string_nn;
594
643
 
595
644
  if (!rb_block_given_p()) {
596
645
  rb_raise(rb_eStandardError, "A block is required.");
@@ -603,7 +652,8 @@ graphql_schema(VALUE self, VALUE root) {
603
652
  printf("*-*-* %s\n", err.msg);
604
653
  exit(2);
605
654
  }
606
- if (NULL == gql_dir_arg(&err, dir, "class", &gql_string_type, NULL, 0, NULL, true)) {
655
+ if (NULL == (string_nn = gql_assure_nonnull(&err, &gql_string_type)) ||
656
+ NULL == gql_dir_arg(&err, dir, "class", string_nn, NULL, 0, NULL)) {
607
657
  printf("*-*-* %s\n", err.msg);
608
658
  exit(3);
609
659
  }
@@ -791,6 +841,105 @@ graphql_publish(VALUE self, VALUE subject, VALUE event) {
791
841
  return Qnil;
792
842
  }
793
843
 
844
+ static VALUE
845
+ rescue_build_header(VALUE x, VALUE ignore) {
846
+ bhArgs args = (bhArgs)x;
847
+ volatile VALUE info = rb_errinfo();
848
+ volatile VALUE msg = rb_funcall(info, rb_intern("message"), 0);
849
+
850
+ agoo_err_set(args->err, AGOO_ERR_EVAL, "%s", rb_string_value_ptr(&msg));
851
+
852
+ return Qnil;
853
+ }
854
+
855
+ static int
856
+ build_headers_cb(VALUE key, VALUE value, VALUE x) {
857
+ bhArgs args = (bhArgs)x;
858
+ const char *ks = rb_string_value_ptr((VALUE*)&key);
859
+ volatile VALUE vs = rb_obj_as_string(value);
860
+ const char *s = rb_string_value_ptr((VALUE*)&vs);
861
+
862
+ if (NULL == (args->headers = gql_add_header(args->err, args->headers, ks, s))) {
863
+ rb_raise(rb_eStandardError, "%s", args->err->msg);
864
+ }
865
+ return ST_CONTINUE;
866
+ }
867
+
868
+ static VALUE
869
+ inner_build_headers(VALUE x) {
870
+ volatile VALUE hh = rb_funcall(build_headers_func, call_id, 1, request_wrap(((bhArgs)x)->req));
871
+
872
+ rb_hash_foreach(hh, build_headers_cb, x);
873
+
874
+ return Qnil;
875
+ }
876
+
877
+ static void*
878
+ protected_build_headers(void *x) {
879
+ return (void*)rb_rescue2(inner_build_headers, (VALUE)x, rescue_build_header, (VALUE)x, rb_eException, 0);
880
+ }
881
+
882
+ static agooText
883
+ build_headers(agooErr err, agooReq req, agooText headers) {
884
+ struct _bhArgs args = {
885
+ .err = err,
886
+ .req = req,
887
+ .headers = headers,
888
+ };
889
+ rb_thread_call_with_gvl(protected_build_headers, &args);
890
+
891
+ return args.headers;
892
+ }
893
+
894
+ /* Document-method: build_headers=
895
+ *
896
+ * call-seq: build_headers=(func)
897
+ *
898
+ * Provide a function to call that builds headers for GraphQL responses. The
899
+ * function should expect a single request and should return a Hash of the
900
+ * headers to add. Content-Type and Content-Length should not be set.
901
+ */
902
+ static VALUE
903
+ graphql_build_headers(VALUE self, VALUE func) {
904
+ gql_build_headers = build_headers;
905
+ build_headers_func = func;
906
+ rb_gc_register_address(&build_headers_func);
907
+
908
+ return Qnil;
909
+ }
910
+
911
+ static int
912
+ headers_cb(VALUE key, VALUE value, VALUE x) {
913
+ agooText *tp = (agooText*)x;
914
+ const char *ks = rb_string_value_ptr((VALUE*)&key);
915
+ volatile VALUE vs = rb_obj_as_string(value);
916
+ const char *s = rb_string_value_ptr((VALUE*)&vs);
917
+ struct _agooErr err = AGOO_ERR_INIT;
918
+
919
+ if (NULL == (*tp = gql_add_header(&err, *tp, ks, s))) {
920
+ rb_raise(rb_eStandardError, "%s", err.msg);
921
+ }
922
+ return ST_CONTINUE;
923
+ }
924
+
925
+
926
+ /* Document-method: headers
927
+ *
928
+ * call-seq: headers(header_hash)
929
+ *
930
+ * Provide a Hash to be used as the headers for GraphQL responses.
931
+ * Content-Type and Content-Length should not be set.
932
+ */
933
+ static VALUE
934
+ graphql_headers(VALUE self, VALUE map) {
935
+ agooText t = agoo_text_allocate(1024);
936
+
937
+ rb_hash_foreach(map, headers_cb, (VALUE)&t);
938
+ gql_headers = t;
939
+
940
+ return Qnil;
941
+ }
942
+
794
943
  /* Document-class: Agoo::Graphql
795
944
  *
796
945
  * The Agoo::GraphQL class provides support for the GraphQL API as defined in
@@ -815,4 +964,9 @@ graphql_init(VALUE mod) {
815
964
  rb_define_module_function(graphql_class, "sdl_dump", graphql_sdl_dump, 1);
816
965
 
817
966
  rb_define_module_function(graphql_class, "publish", graphql_publish, 2);
967
+
968
+ rb_define_module_function(graphql_class, "build_headers=", graphql_build_headers, 1);
969
+ rb_define_module_function(graphql_class, "headers", graphql_headers, 1);
970
+
971
+ call_id = rb_intern("call");
818
972
  }