agoo 2.11.7 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06273c8f4d9d00c382ed4d0abec44bc54b24a1063cb7cc15796137153a169206
4
- data.tar.gz: ba1e9f93e1cbd39e2777ce5a0d9e61498f5718c3742ad001d28490eec848e838
3
+ metadata.gz: df42da153e97e018d4984fa75be78ac86a1c8b97aed6b48d62ec7fe5047d7099
4
+ data.tar.gz: e83a55eed4781bbb0ab08df41203509df6ec45d06817b87c9d61d25bca8a294a
5
5
  SHA512:
6
- metadata.gz: ccf05a2fed955f9e44461a6557c7d3232280c0fd316d523bbbb97cfc9942ef8407a4f31e92b4ba888d3d895578d85753d12e2966aeb08d8725a4de6f5c74eb2f
7
- data.tar.gz: 3af05ec64f0af3ab57bbb0c95652d8050ca6c73d0cf39777cfeff8e5c0ac2cc71ddc783b4d0fecc5d675e865616fe8dee7c145268359e51e5bc7864f00428733
6
+ metadata.gz: ac6e8d86a9b545d93c9ee033950422292b4ab2f740ba692f2c24940e928c42466b895dfae6ce5263040f2b53613a6c1241f5dfe5c0be21961f1ee885970ec220
7
+ data.tar.gz: 9dd8414c88948198bd1a22d5aac236ef8d5bf4251020b7fcfc4b94837d8a48433aa6774ba9e13df1dc36dc1e29f1697dfcc2c21e8ef1a9dc6897d223b72eb946
@@ -2,6 +2,54 @@
2
2
 
3
3
  All changes to the Agoo gem are documented here. Releases follow semantic versioning.
4
4
 
5
+ ## [2.13.0] - 2020-07-05
6
+
7
+ ### Added
8
+
9
+ - Agoo::Server.use() added. It works similar to the Rack use() function.
10
+
11
+ ### Fixed
12
+
13
+ - Header checks are now case insensitive to comply with RFC 7230.
14
+
15
+ ## [2.12.3] - 2020-03-28
16
+
17
+ rb_rescue2 termination
18
+
19
+ ### Fixed
20
+
21
+ - rb_rescue2 must be terminated by a (VALUE)0 and not simply 0.
22
+
23
+ ## [2.12.2] - 2020-03-22
24
+
25
+ Stub generator
26
+
27
+ ### Added
28
+
29
+ - GraphQL stub generator to generate Ruby class file corresponding to GraphQL schema type. The generator is `bin/agoo_stubs`.
30
+
31
+ ## [2.12.1] - 2020-03-17
32
+
33
+ Check SO_REUSEPORT
34
+
35
+ ### Fixed
36
+
37
+ - Verifiy SO_REUSEPORT is define for older OS versions.
38
+
39
+ ## [2.12.0] - 2020-01-19
40
+
41
+ Request GraphQL access and GraphiQL support
42
+
43
+ ### Added
44
+
45
+ - Optional addition of request information is now available in GraphQL resolve functions.
46
+
47
+ - Headers for GraphQL responses can now be set.
48
+
49
+ - Fixed incorrect values in introspection responses.
50
+
51
+ - Fixed JSON parser bug with null, true, and false.
52
+
5
53
  ## [2.11.7] - 2020-01-02
6
54
 
7
55
  Ruby 2.7.0 cleanup
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ while (index = ARGV.index('-I'))
4
+ _, path = ARGV.slice!(index, 2)
5
+ $LOAD_PATH << path
6
+ end
7
+
8
+ require 'optparse'
9
+
10
+ require 'agoo'
11
+
12
+ usage = %{
13
+ Usage: agoo_stubs [options] <graphql_schema>
14
+
15
+ version #{Agoo::VERSION}
16
+
17
+ Generates Ruby files that match the types in the GraphQL schema file specified.
18
+
19
+ Example:
20
+
21
+ agoo_stubs --module Data --dir lib schema.graphql
22
+
23
+ }
24
+
25
+ @verbose = false
26
+ @single = nil
27
+ @dir = nil
28
+ @mod = nil
29
+
30
+ @opts = OptionParser.new(usage)
31
+ @opts.on('-h', '--help', 'show this display') { puts @opts.help; Process.exit!(0) }
32
+ @opts.on('-v', '--verbose', 'verbose output') { @verbose = true }
33
+ @opts.on('-s', '--single PATH', String, 'path to file for all stubs') { |s| @single = s }
34
+ @opts.on('-d', '--dir PATH', String, 'directory to write files into') { |d| @dir = d }
35
+ @opts.on('-m', '--module STRING', String, 'module for the stub classes') { |m| @mod = m }
36
+
37
+ @files = @opts.parse(ARGV)
38
+
39
+ if @files.empty?
40
+ puts "A schema file must be specified."
41
+ puts @opts.help
42
+ Process.exit!(0)
43
+ elsif 1 < @files.size
44
+ puts "Only one schema file can be specified."
45
+ puts @opts.help
46
+ Process.exit!(0)
47
+ end
48
+
49
+ Agoo::GraphQL.schema(nil) {
50
+ Agoo::GraphQL.load_file(@files[0])
51
+ }
52
+
53
+ def write_type_stub(f, t, depth)
54
+ indent = ' ' * depth
55
+ f.puts "#{indent}# #{t.description}" unless t.description.nil?
56
+ f.puts "#{indent}class #{t.name}"
57
+ t.fields.each { |field|
58
+ next unless field.args.nil?
59
+ f.puts "#{indent} # #{field.description}" unless field.description.nil?
60
+ f.puts "#{indent} attr_accessor :#{field.name}"
61
+ }
62
+ f.puts
63
+ f.puts "#{indent} def initialize"
64
+ f.puts "#{indent} end"
65
+ t.fields.each { |field|
66
+ next if field.args.nil?
67
+ f.puts
68
+ f.puts "#{indent} # #{field.description}" unless field.description.nil?
69
+ f.puts "#{indent} # args: #{field.args.map { |a| a.name }.join(', ')}"
70
+ f.puts "#{indent} def #{field.name}(args={})"
71
+ f.puts "#{indent} end"
72
+ }
73
+ f.puts "#{indent}end"
74
+ end
75
+
76
+ def calc_filepath(t)
77
+ path = @dir
78
+ path += '/' + @mod.split('::').map { |m| m.downcase }.join('/') unless @mod.nil?
79
+ path + '/' + t.name.downcase + '.rb'
80
+ end
81
+
82
+ def assure_parent_dir(path)
83
+ dirs = path.split('/')[0..-2]
84
+ dirs.each_index { |i|
85
+ path = dirs[0..i].join('/')
86
+ Dir.mkdir(path) unless Dir.exist?(path)
87
+ }
88
+ end
89
+
90
+ def write_type_stub_file(t)
91
+ path = calc_filepath(t)
92
+ assure_parent_dir(path)
93
+ File.open(path, 'w') { |f|
94
+ f.puts "# #{t.name} class for schema file #{@files[0]}"
95
+ f.puts
96
+ depth = 0
97
+ unless @mod.nil?
98
+ @mod.split('::').each { |m|
99
+ f.puts("#{' ' * depth}module #{m}")
100
+ depth += 1
101
+ }
102
+ f.puts
103
+ end
104
+ write_type_stub(f, t, depth)
105
+ f.puts
106
+ unless @mod.nil?
107
+ @mod.split('::').size.times {
108
+ depth -= 1
109
+ f.puts("#{' ' * depth}end")
110
+ }
111
+ end
112
+ }
113
+ end
114
+
115
+ @out = nil
116
+
117
+ if @dir.nil?
118
+ if @single.nil?
119
+ @out = STDOUT
120
+ else @single
121
+ @out = File.new(@single, 'w')
122
+ end
123
+ end
124
+
125
+ unless @out.nil?
126
+ @out.puts "# Classes for schema file #{@files[0]}"
127
+ @out.puts
128
+ depth = 0
129
+ unless @mod.nil?
130
+ @mod.split('::').each { |m|
131
+ @out.puts("#{' ' * depth}module #{m}")
132
+ depth += 1
133
+ }
134
+ @out.puts
135
+ end
136
+ Agoo::GraphQL.sdl_types.sort { |a,b| a.name <=> b.name }.each { |t|
137
+ write_type_stub(@out, t, depth)
138
+ @out.puts
139
+ }
140
+ unless @mod.nil?
141
+ @mod.split('::').size.times {
142
+ depth -= 1
143
+ @out.puts("#{' ' * depth}end")
144
+ }
145
+ end
146
+ @out.close unless STDOUT == @out
147
+ else
148
+ Agoo::GraphQL.sdl_types.each { |t|
149
+ write_type_stub_file(t)
150
+ }
151
+ unless @mod.nil?
152
+ path = @dir
153
+ path += '/' + @mod.split('::').map { |m| m.downcase }.join('/')
154
+ assure_parent_dir(path)
155
+ m = path.split('/')[-1]
156
+ path += '.rb'
157
+
158
+ File.open(path, 'w') { |f|
159
+ f.puts "# Classes for schema file #{@files[0]}"
160
+ f.puts
161
+ Agoo::GraphQL.sdl_types.sort { |a,b| a.name <=> b.name }.each { |t|
162
+ f.puts "require_relative '#{m}/#{t.name.downcase}'"
163
+ }
164
+ }
165
+ end
166
+ end
@@ -223,25 +223,25 @@ url_ssl(agooErr err, const char *url) {
223
223
 
224
224
  agooBind
225
225
  agoo_bind_url(agooErr err, const char *url) {
226
- if (0 == strncmp("tcp://", url, 6)) {
226
+ if (0 == strncasecmp("tcp://", url, 6)) {
227
227
  if ('[' == url[6]) {
228
228
  return url_tcp6(err, url + 6, "tcp");
229
229
  }
230
230
  return url_tcp(err, url + 6, "tcp");
231
231
  }
232
- if (0 == strncmp("http://", url, 7)) {
232
+ if (0 == strncasecmp("http://", url, 7)) {
233
233
  if ('[' == url[7]) {
234
234
  return url_tcp6(err, url + 7, "http");
235
235
  }
236
236
  return url_tcp(err, url + 7, "http");
237
237
  }
238
- if (0 == strncmp("unix://", url, 7)) {
238
+ if (0 == strncasecmp("unix://", url, 7)) {
239
239
  return url_named(err, url + 7);
240
240
  }
241
- if (0 == strncmp("https://", url, 8)) {
241
+ if (0 == strncasecmp("https://", url, 8)) {
242
242
  return url_ssl(err, url + 8);
243
243
  }
244
- if (0 == strncmp("ssl://", url, 6)) {
244
+ if (0 == strncasecmp("ssl://", url, 6)) {
245
245
  return url_ssl(err, url + 6);
246
246
  }
247
247
  // All others assume http
@@ -286,7 +286,9 @@ usual_listen(agooErr err, agooBind b) {
286
286
  #ifdef OSX_OS
287
287
  setsockopt(b->fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
288
288
  #endif
289
+ #ifdef SO_REUSEPORT
289
290
  setsockopt(b->fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
291
+ #endif
290
292
  setsockopt(b->fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
291
293
  if (AF_INET6 == b->family) {
292
294
  struct sockaddr_in6 addr;
@@ -173,7 +173,7 @@ agoo_con_header_value(const char *header, int hlen, const char *key, int *vlen)
173
173
  int klen = (int)strlen(key);
174
174
 
175
175
  while (h < hend) {
176
- if (0 == strncmp(key, h, klen) && ':' == h[klen]) {
176
+ if (0 == strncasecmp(key, h, klen) && ':' == h[klen]) {
177
177
  h += klen + 1;
178
178
  for (; ' ' == *h; h++) {
179
179
  }
@@ -36,8 +36,9 @@ static const char query_str[] = "query";
36
36
  static const char subscription_str[] = "subscription";
37
37
  static const char variables_str[] = "variables";
38
38
 
39
-
40
39
  gqlValue (*gql_doc_eval_func)(agooErr err, gqlDoc doc) = NULL;
40
+ agooText (*gql_build_headers)(agooErr err, agooReq req, agooText headers) = NULL;
41
+ agooText gql_headers = NULL;
41
42
 
42
43
  static void
43
44
  err_resp(agooRes res, agooErr err, int status) {
@@ -93,25 +94,74 @@ value_resp(agooReq req, gqlValue result, int status, int indent) {
93
94
  break;
94
95
  default:
95
96
  agoo_log_cat(&agoo_error_cat, "Did not expect an HTTP status of %d.", status);
96
- return;
97
+ agoo_err_set(&err, AGOO_ERR_EVAL, "Did not expect an HTTP status of %d.", status);
98
+ goto FAILED;
97
99
  }
98
100
  agoo_res_message_push(res, text);
99
101
  return;
100
102
  }
101
103
  if (AGOO_ERR_OK != gql_object_set(&err, msg, "data", result)) {
102
- err_resp(res, &err, 500);
103
- gql_value_destroy(result);
104
- return;
104
+ goto FAILED;
105
105
  }
106
106
  text = gql_value_json(text, msg, indent, 0);
107
107
  gql_value_destroy(msg); // also destroys result
108
+ result = NULL;
108
109
 
109
110
  cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 %d %s\r\nContent-Type: application/json\r\nContent-Length: %ld\r\n\r\n",
110
111
  status, agoo_http_code_message(status), text->len);
111
- if (NULL == (text = agoo_text_prepend(text, buf, cnt))) {
112
- agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
112
+ if (NULL != gql_headers || NULL != gql_build_headers) {
113
+ agooText headers = agoo_text_allocate(4094);
114
+
115
+ headers = agoo_text_append(headers, buf, cnt - 2);
116
+ if (NULL == headers) {
117
+ agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
118
+ return;
119
+ }
120
+ if (NULL != gql_headers) {
121
+ headers = agoo_text_append(headers, gql_headers->text, (int)gql_headers->len);
122
+ } else {
123
+ headers = gql_build_headers(&err, req, headers);
124
+ }
125
+ headers = agoo_text_append(headers, "\r\n", 2);
126
+ if (NULL == headers) {
127
+ agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
128
+ return;
129
+ }
130
+ if (NULL == (text = agoo_text_prepend(text, headers->text, (int)headers->len))) {
131
+ agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
132
+ agoo_text_release(headers);
133
+ return;
134
+ }
135
+ agoo_text_release(headers);
136
+ } else {
137
+ if (NULL == (text = agoo_text_prepend(text, buf, cnt))) {
138
+ agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
139
+ return;
140
+ }
113
141
  }
114
142
  agoo_res_message_push(res, text);
143
+
144
+ return;
145
+
146
+ FAILED:
147
+ err_resp(res, &err, 500);
148
+ if (NULL != result) {
149
+ gql_value_destroy(result);
150
+ }
151
+ return;
152
+ }
153
+
154
+ agooText
155
+ gql_add_header(agooErr err, agooText headers, const char *key, const char *value) {
156
+ headers = agoo_text_append(headers, key, -1);
157
+ headers = agoo_text_append(headers, ": ", 2);
158
+ headers = agoo_text_append(headers, value, -1);
159
+ headers = agoo_text_append(headers, "\r\n", 2);
160
+
161
+ if (NULL == headers) {
162
+ AGOO_ERR_MEM(err, "headers");
163
+ }
164
+ return headers;
115
165
  }
116
166
 
117
167
  gqlValue
@@ -416,6 +466,7 @@ gql_eval_get_hook(agooReq req) {
416
466
  return;
417
467
  }
418
468
  set_doc_op(doc, op_name, oplen);
469
+ doc->req = req;
419
470
 
420
471
  if (NULL == gql_doc_eval_func) {
421
472
  result = gql_doc_eval(&err, doc);
@@ -488,8 +539,6 @@ eval_post(agooErr err, agooReq req) {
488
539
  gqlValue result = NULL;
489
540
  gqlValue j = NULL;
490
541
 
491
- // TBD handle query parameter and concatenate with JSON body variables if present
492
-
493
542
  op_name = agoo_req_query_value(req, operation_name_str, sizeof(operation_name_str) - 1, &oplen);
494
543
  var_json = agoo_req_query_value(req, variables_str, sizeof(variables_str) - 1, &vlen);
495
544
 
@@ -502,11 +551,11 @@ eval_post(agooErr err, agooReq req) {
502
551
  agoo_err_set(err, AGOO_ERR_TYPE, "required Content-Type not in the HTTP header");
503
552
  return NULL;
504
553
  }
505
- if (0 == strncmp(graphql_content_type, s, sizeof(graphql_content_type) - 1)) {
554
+ if (0 == strncasecmp(graphql_content_type, s, sizeof(graphql_content_type) - 1)) {
506
555
  if (NULL == (doc = sdl_parse_doc(err, req->body.start, req->body.len, vars, GQL_QUERY))) {
507
556
  return NULL;
508
557
  }
509
- } else if (0 == strncmp(json_content_type, s, sizeof(json_content_type) - 1)) {
558
+ } else if (0 == strncasecmp(json_content_type, s, sizeof(json_content_type) - 1)) {
510
559
  gqlLink m;
511
560
 
512
561
  if (NULL != (j = gql_json_parse(err, req->body.start, req->body.len))) {
@@ -532,19 +581,24 @@ eval_post(agooErr err, agooReq req) {
532
581
  } else if (0 == strcmp("variables", m->key)) {
533
582
  gqlLink link;
534
583
 
535
- if (GQL_SCALAR_OBJECT != m->value->type->scalar_kind) {
536
- agoo_err_set(err, AGOO_ERR_EVAL, "expected variables to be an object.");
537
- goto DONE;
538
- }
539
- for (link = m->value->members; NULL != link; link = link->next) {
540
- gqlVar v = gql_op_var_create(err, link->key, link->value->type, link->value);
584
+ switch (m->value->type->scalar_kind) {
585
+ case GQL_SCALAR_OBJECT:
586
+ for (link = m->value->members; NULL != link; link = link->next) {
587
+ gqlVar v = gql_op_var_create(err, link->key, link->value->type, link->value);
541
588
 
542
- link->value = NULL;
543
- if (NULL == v) {
544
- goto DONE;
589
+ link->value = NULL;
590
+ if (NULL == v) {
591
+ goto DONE;
592
+ }
593
+ v->next = vars;
594
+ vars = v;
545
595
  }
546
- v->next = vars;
547
- vars = v;
596
+ break;
597
+ case GQL_SCALAR_NULL:
598
+ break;
599
+ default:
600
+ agoo_err_set(err, AGOO_ERR_EVAL, "expected variables to be an object or null.");
601
+ goto DONE;
548
602
  }
549
603
  }
550
604
  }
@@ -559,6 +613,7 @@ eval_post(agooErr err, agooReq req) {
559
613
  return NULL;
560
614
  }
561
615
  set_doc_op(doc, op_name, oplen);
616
+ doc->req = req;
562
617
 
563
618
  if (NULL == gql_doc_eval_func) {
564
619
  result = gql_doc_eval(err, doc);
@@ -569,7 +624,9 @@ eval_post(agooErr err, agooReq req) {
569
624
  result = NULL;
570
625
  }
571
626
  DONE:
572
- gql_doc_destroy(doc);
627
+ if (NULL != doc) {
628
+ gql_doc_destroy(doc);
629
+ }
573
630
  gql_value_destroy(j);
574
631
 
575
632
  return result;
@@ -595,6 +652,13 @@ gql_eval_post_hook(agooReq req) {
595
652
  }
596
653
  }
597
654
 
655
+ void
656
+ gql_eval_options_hook(agooReq req) {
657
+ struct _agooErr err = AGOO_ERR_INIT;
658
+
659
+ value_resp(req, gql_object_create(&err), 200, 0);
660
+ }
661
+
598
662
  gqlValue
599
663
  gql_get_arg_value(gqlKeyVal args, const char *key) {
600
664
  gqlValue value = NULL;