agoo 2.12.0 → 2.14.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad6f1d492f68755c2012b61062a79fe3351a1b80546838d19eb4315789f4585c
4
- data.tar.gz: 6a1a949bdf6fb76d1228e9e7e61ded57d76ca414a21529eec46ae1275e624277
3
+ metadata.gz: f70842cac2f630e74251967ca0accfcb2dacef7b143bb3707ae74d88a0f1da36
4
+ data.tar.gz: 75996d57b98c1d164fa44009f1c6037deb28f22d7179b7bd3ede0296f3d9a605
5
5
  SHA512:
6
- metadata.gz: b43da3e33ef225b675164ad4793b4304bc42db3c0a0803b84584250e58713d4026b10e9a450b3de8bbe18814a3ce102438fd1448658919e5a345fafe8583bc64
7
- data.tar.gz: fbe407798233527d8ce736bac219224b00714ca8a902b5110a5c15fd2f88a2962d04fdb6945517377c1cebdde623c2d7a0162fd414c2f17dfdb23ac01dc86779
6
+ metadata.gz: 39f02fdb5ab101d1414a57a7d712b6b4910b583e2509d0911d2cf5c398badfdb556c6847eaa7e108d280b590792bf3cb007514e4f6cd7c8f57ddb233299e3a0f
7
+ data.tar.gz: 10b2f2cc9d56d34e27dd597c5c2d0fa61bcf3921198efcbffcbfece23841656af9dc9eb6909b592e63ff30dd79aed315a55bacf97a471ac5c1c3e7acff4782f2
@@ -2,6 +2,50 @@
2
2
 
3
3
  All changes to the Agoo gem are documented here. Releases follow semantic versioning.
4
4
 
5
+ ## [2.14.0] - 2020-11-07
6
+
7
+ ### Added
8
+
9
+ - REMOTE_ADDR element added to requests/env argument to `call()`.
10
+
11
+ - Added check for multiple Content-Length headers.
12
+
13
+ - Multiple occurrances of a header are now passed to the Rack `call()` method as an array.
14
+
15
+ ## [2.13.0] - 2020-07-05
16
+
17
+ ### Added
18
+
19
+ - Agoo::Server.use() added. It works similar to the Rack use() function.
20
+
21
+ ### Fixed
22
+
23
+ - Header checks are now case insensitive to comply with RFC 7230.
24
+
25
+ ## [2.12.3] - 2020-03-28
26
+
27
+ rb_rescue2 termination
28
+
29
+ ### Fixed
30
+
31
+ - rb_rescue2 must be terminated by a (VALUE)0 and not simply 0.
32
+
33
+ ## [2.12.2] - 2020-03-22
34
+
35
+ Stub generator
36
+
37
+ ### Added
38
+
39
+ - GraphQL stub generator to generate Ruby class file corresponding to GraphQL schema type. The generator is `bin/agoo_stubs`.
40
+
41
+ ## [2.12.1] - 2020-03-17
42
+
43
+ Check SO_REUSEPORT
44
+
45
+ ### Fixed
46
+
47
+ - Verifiy SO_REUSEPORT is define for older OS versions.
48
+
5
49
  ## [2.12.0] - 2020-01-19
6
50
 
7
51
  Request GraphQL access and GraphiQL support
data/README.md CHANGED
@@ -68,6 +68,31 @@ sleep
68
68
  gem install agoo
69
69
  ```
70
70
 
71
+ ## Using agoo as server for rails
72
+
73
+ As agoo supports rack compatible apps you can use it for rails applications:
74
+
75
+ Add agoo to the Gemfile:
76
+
77
+ ```
78
+ # Gemfile
79
+ gem 'agoo'
80
+ ```
81
+
82
+ Install bundle:
83
+
84
+ ```
85
+ $ bundle install
86
+ ```
87
+
88
+ Start rails with agoo as server:
89
+
90
+ ```
91
+ $ rails server -u agoo
92
+ ```
93
+
94
+ Enjoy the increased performance!
95
+
71
96
  ## What Is This?
72
97
 
73
98
  Agoo is Japanese for a type of flying fish. This gem flies. It is a high
@@ -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;
@@ -63,6 +63,22 @@ agoo_con_create(agooErr err, int sock, uint64_t id, agooBind b) {
63
63
  if (NULL == (c = (agooCon)AGOO_CALLOC(1, sizeof(struct _agooCon)))) {
64
64
  AGOO_ERR_MEM(err, "Connection");
65
65
  } else {
66
+ // It would be better to get this information in server.c after
67
+ // accept() but that does not work on macOS so instead a call to
68
+ // getpeername() is used instead.
69
+ struct sockaddr_storage addr;
70
+ socklen_t len = sizeof(addr);
71
+
72
+ getpeername(sock, (struct sockaddr*)&addr, &len);
73
+ if (addr.ss_family == AF_INET) {
74
+ struct sockaddr_in *s = (struct sockaddr_in*)&addr;
75
+
76
+ inet_ntop(AF_INET, &s->sin_addr, c->remote, sizeof(c->remote));
77
+ } else {
78
+ struct sockaddr_in6 *s = (struct sockaddr_in6*)&addr;
79
+
80
+ inet_ntop(AF_INET6, &s->sin6_addr, c->remote, sizeof(c->remote));
81
+ }
66
82
  c->sock = sock;
67
83
  c->id = id;
68
84
  c->timeout = dtime() + CON_TIMEOUT;
@@ -173,7 +189,7 @@ agoo_con_header_value(const char *header, int hlen, const char *key, int *vlen)
173
189
  int klen = (int)strlen(key);
174
190
 
175
191
  while (h < hend) {
176
- if (0 == strncmp(key, h, klen) && ':' == h[klen]) {
192
+ if (0 == strncasecmp(key, h, klen) && ':' == h[klen]) {
177
193
  h += klen + 1;
178
194
  for (; ' ' == *h; h++) {
179
195
  }
@@ -437,6 +453,7 @@ con_header_read(agooCon c, size_t *mlenp) {
437
453
  c->req->method = method;
438
454
  c->req->upgrade = AGOO_UP_NONE;
439
455
  c->req->up = NULL;
456
+ memcpy(c->req->remote, c->remote, sizeof(c->remote));
440
457
  c->req->path.start = c->req->msg + (path.start - c->buf);
441
458
  c->req->path.len = (int)(path.end - path.start);
442
459
  c->req->query.start = c->req->msg + (query - c->buf);
@@ -3,6 +3,7 @@
3
3
  #ifndef AGOO_CON_H
4
4
  #define AGOO_CON_H
5
5
 
6
+ #include <arpa/inet.h>
6
7
  #include <poll.h>
7
8
  #include <pthread.h>
8
9
  #include <stdbool.h>
@@ -45,6 +46,7 @@ typedef struct _agooCon {
45
46
  struct _agooBind *bind;
46
47
  struct pollfd *pp;
47
48
  uint64_t id;
49
+ char remote[INET6_ADDRSTRLEN];
48
50
  char buf[MAX_HEADER_SIZE];
49
51
  size_t bcnt;
50
52
 
@@ -551,11 +551,11 @@ eval_post(agooErr err, agooReq req) {
551
551
  agoo_err_set(err, AGOO_ERR_TYPE, "required Content-Type not in the HTTP header");
552
552
  return NULL;
553
553
  }
554
- 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)) {
555
555
  if (NULL == (doc = sdl_parse_doc(err, req->body.start, req->body.len, vars, GQL_QUERY))) {
556
556
  return NULL;
557
557
  }
558
- } 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)) {
559
559
  gqlLink m;
560
560
 
561
561
  if (NULL != (j = gql_json_parse(err, req->body.start, req->body.len))) {
@@ -26,7 +26,7 @@ typedef struct _cache {
26
26
  struct _cache key_cache;
27
27
 
28
28
  // The rack spec indicates the characters (),/:;<=>?@[]{} are invalid which
29
- // clearly is not consisten with RFC7230 so stick with the RFC.
29
+ // clearly is not consistent with RFC7230 so stick with the RFC.
30
30
  static char header_value_chars[256] = "\
31
31
  xxxxxxxxxxoxxxxxxxxxxxxxxxxxxxxx\
32
32
  oooooooooooooooooooooooooooooooo\
@@ -50,7 +50,6 @@ static const char *header_keys[] = {
50
50
  "Accept-Encoding",
51
51
  "Accept-Features",
52
52
  "Accept-Language",
53
- "Accept-Language",
54
53
  "Accept-Patch",
55
54
  "Accept-Post",
56
55
  "Accept-Ranges",
@@ -74,7 +73,6 @@ static const char *header_keys[] = {
74
73
  "Approved",
75
74
  "Archive",
76
75
  "Archived-At",
77
- "Archived-At",
78
76
  "Article-Names",
79
77
  "Article-Updates",
80
78
  "Authentication-Control",
@@ -99,36 +97,27 @@ static const char *header_keys[] = {
99
97
  "Cc",
100
98
  "Close",
101
99
  "Comments",
102
- "Comments",
103
100
  "Compliance",
104
101
  "Connection",
105
102
  "Content-Alternative",
106
103
  "Content-Base",
107
- "Content-Base",
108
104
  "Content-Description",
109
105
  "Content-Disposition",
110
- "Content-Disposition",
111
106
  "Content-Duration",
112
107
  "Content-Encoding",
113
108
  "Content-ID",
114
- "Content-ID",
115
109
  "Content-Identifier",
116
110
  "Content-Language",
117
- "Content-Language",
118
111
  "Content-Length",
119
112
  "Content-Location",
120
- "Content-Location",
121
- "Content-MD5",
122
113
  "Content-MD5",
123
114
  "Content-Range",
124
115
  "Content-Return",
125
116
  "Content-Script-Type",
126
117
  "Content-Style-Type",
127
118
  "Content-Transfer-Encoding",
128
- "Content-Transfer-Encoding",
129
119
  "Content-Translation-Type",
130
120
  "Content-Type",
131
- "Content-Type",
132
121
  "Content-Version",
133
122
  "Content-features",
134
123
  "Control",
@@ -141,8 +130,7 @@ static const char *header_keys[] = {
141
130
  "DAV",
142
131
  "DKIM-Signature",
143
132
  "DL-Expansion-History",
144
- "Date",
145
- "Date",
133
+ "DNT",
146
134
  "Date",
147
135
  "Date-Received",
148
136
  "Default-Style",
@@ -182,7 +170,6 @@ static const char *header_keys[] = {
182
170
  "Downgraded-Sender",
183
171
  "Downgraded-To",
184
172
  "EDIINT-Features",
185
- "EDIINT-Features",
186
173
  "ETag",
187
174
  "Eesst-Version",
188
175
  "Encoding",
@@ -190,16 +177,13 @@ static const char *header_keys[] = {
190
177
  "Errors-To",
191
178
  "Expect",
192
179
  "Expires",
193
- "Expires",
194
- "Expires",
195
180
  "Expiry-Date",
196
181
  "Ext",
197
182
  "Followup-To",
198
183
  "Form-Sub",
199
184
  "Forwarded",
200
185
  "From",
201
- "From",
202
- "From",
186
+ "Front-End-Https",
203
187
  "Generate-Delivery-Report",
204
188
  "GetProfile",
205
189
  "HTTP2-Settings",
@@ -219,10 +203,8 @@ static const char *header_keys[] = {
219
203
  "Injection-Date",
220
204
  "Injection-Info",
221
205
  "Jabber-ID",
222
- "Jabber-ID",
223
206
  "Keep-Alive",
224
207
  "Keywords",
225
- "Keywords",
226
208
  "Label",
227
209
  "Language",
228
210
  "Last-Modified",
@@ -240,7 +222,6 @@ static const char *header_keys[] = {
240
222
  "Location",
241
223
  "Lock-Token",
242
224
  "MIME-Version",
243
- "MIME-Version",
244
225
  "MMHS-Acp127-Message-Identifier",
245
226
  "MMHS-Authorizing-Users",
246
227
  "MMHS-Codress-Message-Indicator",
@@ -262,8 +243,6 @@ static const char *header_keys[] = {
262
243
  "Memento-Datetime",
263
244
  "Message-Context",
264
245
  "Message-ID",
265
- "Message-ID",
266
- "Message-ID",
267
246
  "Message-Type",
268
247
  "Meter",
269
248
  "Method-Check",
@@ -279,7 +258,6 @@ static const char *header_keys[] = {
279
258
  "Optional-WWW-Authenticate",
280
259
  "Ordering-Type",
281
260
  "Organization",
282
- "Organization",
283
261
  "Origin",
284
262
  "Original-Encoded-Information-Types",
285
263
  "Original-From",
@@ -292,7 +270,6 @@ static const char *header_keys[] = {
292
270
  "P3P",
293
271
  "PEP",
294
272
  "PICS-Label",
295
- "PICS-Label",
296
273
  "Path",
297
274
  "Pep-Info",
298
275
  "Position",
@@ -311,6 +288,7 @@ static const char *header_keys[] = {
311
288
  "Proxy-Authenticate",
312
289
  "Proxy-Authentication-Info",
313
290
  "Proxy-Authorization",
291
+ "Proxy-Connection",
314
292
  "Proxy-Features",
315
293
  "Proxy-Instruction",
316
294
  "Public",
@@ -321,13 +299,11 @@ static const char *header_keys[] = {
321
299
  "Received-SPF",
322
300
  "Redirect-Ref",
323
301
  "References",
324
- "References",
325
302
  "Referer",
326
303
  "Referer-Root",
327
304
  "Relay-Version",
328
305
  "Reply-By",
329
306
  "Reply-To",
330
- "Reply-To",
331
307
  "Require-Recipient-Valid-Since",
332
308
  "Resent-Bcc",
333
309
  "Resent-Cc",
@@ -345,6 +321,7 @@ static const char *header_keys[] = {
345
321
  "SIO-Label-History",
346
322
  "SLUG",
347
323
  "Safe",
324
+ "Save-Data",
348
325
  "Schedule-Reply",
349
326
  "Schedule-Tag",
350
327
  "Sec-WebSocket-Accept",
@@ -367,11 +344,9 @@ static const char *header_keys[] = {
367
344
  "Strict-Transport-Security",
368
345
  "SubOK",
369
346
  "Subject",
370
- "Subject",
371
347
  "Subst",
372
348
  "Summary",
373
349
  "Supersedes",
374
- "Supersedes",
375
350
  "Surrogate-Capability",
376
351
  "Surrogate-Control",
377
352
  "TCN",
@@ -390,9 +365,9 @@ static const char *header_keys[] = {
390
365
  "UA-Windowpixels",
391
366
  "URI",
392
367
  "Upgrade",
368
+ "Upgrade-Insecure-Requests",
393
369
  "Urgency",
394
370
  "User-Agent",
395
- "User-Agent",
396
371
  "VBR-Info",
397
372
  "Variant-Vary",
398
373
  "Vary",
@@ -401,22 +376,32 @@ static const char *header_keys[] = {
401
376
  "WWW-Authenticate",
402
377
  "Want-Digest",
403
378
  "Warning",
404
- "X-Archived-At",
379
+ "X-ATT-DeviceId",
405
380
  "X-Archived-At",
406
381
  "X-Content-Type-Options",
382
+ "X-Correlation-ID",
383
+ "X-Csrf-Token",
407
384
  "X-Device-Accept",
408
385
  "X-Device-Accept-Charset",
409
386
  "X-Device-Accept-Encoding",
410
387
  "X-Device-Accept-Language",
411
388
  "X-Device-User-Agent",
389
+ "X-Forwarded-For",
390
+ "X-Forwarded-Host",
391
+ "X-Forwarded-Proto",
412
392
  "X-Frame-Options",
393
+ "X-Http-Method-Override",
413
394
  "X-Mittente",
414
395
  "X-PGP-Sig",
396
+ "X-Request-ID",
397
+ "X-Requested-With",
415
398
  "X-Ricevuta",
416
399
  "X-Riferimento-Message-ID",
417
400
  "X-TipoRicevuta",
418
401
  "X-Trasporto",
402
+ "X-UIDH",
419
403
  "X-VerificaSicurezza",
404
+ "X-Wap-Profile",
420
405
  "X-XSS-Protection",
421
406
  "X400-Content-Identifier",
422
407
  "X400-Content-Return",
@@ -469,7 +454,7 @@ key_set(const char *key) {
469
454
  int64_t h = calc_hash(key, &len);
470
455
  Slot *bucket = get_bucketp(h);
471
456
  Slot s;
472
-
457
+
473
458
  if (NULL != (s = (Slot)AGOO_MALLOC(sizeof(struct _slot)))) {
474
459
  s->hash = h;
475
460
  s->klen = len;
@@ -482,7 +467,7 @@ key_set(const char *key) {
482
467
  void
483
468
  agoo_http_init() {
484
469
  const char **kp = header_keys;
485
-
470
+
486
471
  memset(&key_cache, 0, sizeof(struct _cache));
487
472
  for (; NULL != *kp; kp++) {
488
473
  key_set(*kp);
@@ -550,7 +535,7 @@ agoo_http_header_ok(agooErr err, const char *key, int klen, const char *value, i
550
535
  const char*
551
536
  agoo_http_code_message(int code) {
552
537
  const char *msg = "";
553
-
538
+
554
539
  switch (code) {
555
540
  case 100: msg = "Continue"; break;
556
541
  case 101: msg = "Switching Protocols"; break;
@@ -14,7 +14,7 @@ agooReq
14
14
  agoo_req_create(size_t mlen) {
15
15
  size_t size = mlen + sizeof(struct _agooReq) - 7;
16
16
  agooReq req = (agooReq)AGOO_MALLOC(size);
17
-
17
+
18
18
  if (NULL != req) {
19
19
  memset(req, 0, size);
20
20
  req->env = agoo_server.env_nil_value;
@@ -56,7 +56,7 @@ agoo_req_port(agooReq r) {
56
56
  int len;
57
57
  const char *host;
58
58
  const char *colon;
59
-
59
+
60
60
  if (NULL == (host = agoo_con_header_value(r->header.start, r->header.len, "Host", &len))) {
61
61
  return 0;
62
62
  }
@@ -94,7 +94,7 @@ agoo_req_query_value(agooReq r, const char *key, int klen, int *vlenp) {
94
94
  static int
95
95
  hexVal(int c) {
96
96
  int h = -1;
97
-
97
+
98
98
  if ('0' <= c && c <= '9') {
99
99
  h = c - '0';
100
100
  } else if ('a' <= c && c <= 'f') {
@@ -110,12 +110,12 @@ agoo_req_query_decode(char *s, int len) {
110
110
  char *sn = s;
111
111
  char *so = s;
112
112
  char *end = s + len;
113
-
113
+
114
114
  while (so < end) {
115
115
  if ('%' == *so) {
116
116
  int n;
117
117
  int c = 0;
118
-
118
+
119
119
  so++;
120
120
  if (0 > (c = hexVal(*so))) {
121
121
  *sn++ = '%';
@@ -133,7 +133,7 @@ agoo_req_query_decode(char *s, int len) {
133
133
  }
134
134
  }
135
135
  *sn = '\0';
136
-
136
+
137
137
  return (int)(sn - s);
138
138
  }
139
139
 
@@ -145,9 +145,9 @@ agoo_req_header_value(agooReq req, const char *key, int *vlen) {
145
145
  const char *hend = h + req->header.len;
146
146
  const char *value;
147
147
  int klen = (int)strlen(key);
148
-
148
+
149
149
  while (h < hend) {
150
- if (0 == strncmp(key, h, klen) && ':' == h[klen]) {
150
+ if (0 == strncasecmp(key, h, klen) && ':' == h[klen]) {
151
151
  h += klen + 1;
152
152
  for (; ' ' == *h; h++) {
153
153
  }
@@ -155,7 +155,7 @@ agoo_req_header_value(agooReq req, const char *key, int *vlen) {
155
155
  for (; '\r' != *h && '\0' != *h; h++) {
156
156
  }
157
157
  *vlen = (int)(h - value);
158
-
158
+
159
159
  return value;
160
160
  }
161
161
  for (; h < hend; h++) {
@@ -167,4 +167,3 @@ agoo_req_header_value(agooReq req, const char *key, int *vlen) {
167
167
  }
168
168
  return NULL;
169
169
  }
170
-
@@ -3,6 +3,7 @@
3
3
  #ifndef AGOO_REQ_H
4
4
  #define AGOO_REQ_H
5
5
 
6
+ #include <arpa/inet.h>
6
7
  #include <stdint.h>
7
8
 
8
9
  #include "hook.h"
@@ -32,6 +33,7 @@ typedef struct _agooReq {
32
33
  struct _agooStr query;
33
34
  struct _agooStr header;
34
35
  struct _agooStr body;
36
+ char remote[INET6_ADDRSTRLEN];
35
37
  void *env;
36
38
  agooHook hook;
37
39
  size_t mlen; // allocated msg length
@@ -43,6 +43,7 @@ static VALUE rack_upgrade_val = Qundef;
43
43
  static VALUE rack_url_scheme_val = Qundef;
44
44
  static VALUE rack_version_val = Qundef;
45
45
  static VALUE rack_version_val_val = Qundef;
46
+ static VALUE remote_addr_val = Qundef;
46
47
  static VALUE request_method_val = Qundef;
47
48
  static VALUE script_name_val = Qundef;
48
49
  static VALUE server_name_val = Qundef;
@@ -96,6 +97,27 @@ method(VALUE self) {
96
97
  return req_method((agooReq)DATA_PTR(self));
97
98
  }
98
99
 
100
+ static VALUE
101
+ req_remote_addr(agooReq r) {
102
+
103
+ if (NULL == r) {
104
+ rb_raise(rb_eArgError, "Request is no longer valid.");
105
+ }
106
+ return rb_str_new(r->remote, strlen(r->remote));
107
+ }
108
+
109
+ /* Document-method: remote_addr
110
+ *
111
+ * call-seq: remote_addr()
112
+ *
113
+ * Returns the remote address.
114
+ */
115
+ static VALUE
116
+ remote_addr(VALUE self) {
117
+ return req_remote_addr((agooReq)DATA_PTR(self));
118
+ }
119
+
120
+
99
121
  static VALUE
100
122
  req_script_name(agooReq r) {
101
123
  // The logic is a bit tricky here and for path_info. If the HTTP path is /
@@ -366,14 +388,29 @@ rack_run_once(VALUE self) {
366
388
 
367
389
  static void
368
390
  add_header_value(VALUE hh, const char *key, int klen, const char *val, int vlen) {
391
+ VALUE v;
392
+
369
393
  if (sizeof(content_type) - 1 == klen && 0 == strncasecmp(key, content_type, sizeof(content_type) - 1)) {
370
- rb_hash_aset(hh, content_type_val, rb_str_new(val, vlen));
394
+ if (Qnil == (v = rb_hash_lookup2(hh, content_type_val, Qnil))) {
395
+ rb_hash_aset(hh, content_type_val, rb_str_new(val, vlen));
396
+ } else {
397
+ volatile VALUE a = rb_ary_new();
398
+
399
+ rb_ary_push(a, v);
400
+ rb_ary_push(a, rb_str_new(val, vlen));
401
+ rb_hash_aset(hh, content_type_val, a);
402
+ }
371
403
  } else if (sizeof(content_length) - 1 == klen && 0 == strncasecmp(key, content_length, sizeof(content_length) - 1)) {
372
- rb_hash_aset(hh, content_length_val, rb_str_new(val, vlen));
404
+ if (Qnil == (v = rb_hash_lookup2(hh, content_length_val, Qnil))) {
405
+ rb_hash_aset(hh, content_length_val, rb_str_new(val, vlen));
406
+ } else {
407
+ rb_raise(rb_eArgError, "Multiple Content-Length headers.");
408
+ }
373
409
  } else {
374
410
  char hkey[1024];
375
411
  char *k = hkey;
376
412
  volatile VALUE sval = rb_str_new(val, vlen);
413
+ volatile VALUE kval;
377
414
 
378
415
  strcpy(hkey, "HTTP_");
379
416
  k = hkey + 5;
@@ -392,7 +429,16 @@ add_header_value(VALUE hh, const char *key, int klen, const char *val, int vlen)
392
429
  *k = toupper(*k);
393
430
  }
394
431
  }
395
- rb_hash_aset(hh, rb_str_new(hkey, klen + 5), sval);
432
+ kval = rb_str_new(hkey, klen + 5);
433
+ if (Qnil == (v = rb_hash_lookup2(hh, kval, Qnil))) {
434
+ rb_hash_aset(hh, kval, sval);
435
+ } else {
436
+ volatile VALUE a = rb_ary_new();
437
+
438
+ rb_ary_push(a, v);
439
+ rb_ary_push(a, sval);
440
+ rb_hash_aset(hh, kval, a);
441
+ }
396
442
  }
397
443
  }
398
444
 
@@ -545,8 +591,9 @@ request_env(agooReq req, VALUE self) {
545
591
  rb_hash_aset(env, script_name_val, req_script_name(req));
546
592
  rb_hash_aset(env, path_info_val, req_path_info(req));
547
593
  rb_hash_aset(env, query_string_val, req_query_string(req));
548
- rb_hash_aset(env, server_name_val, req_server_name(req));
594
+ rb_hash_aset(env, remote_addr_val, req_remote_addr(req));
549
595
  rb_hash_aset(env, server_port_val, req_server_port(req));
596
+ rb_hash_aset(env, server_name_val, req_server_name(req));
550
597
  fill_headers(req, env);
551
598
  rb_hash_aset(env, rack_version_val, rack_version_val_val);
552
599
  rb_hash_aset(env, rack_url_scheme_val, req_rack_url_scheme(req));
@@ -663,6 +710,7 @@ request_init(VALUE mod) {
663
710
  rb_define_method(req_class, "query_string", query_string, 0);
664
711
  rb_define_method(req_class, "server_name", server_name, 0);
665
712
  rb_define_method(req_class, "server_port", server_port, 0);
713
+ rb_define_method(req_class, "remote_addr", remote_addr, 0);
666
714
  rb_define_method(req_class, "rack_version", rack_version, 0);
667
715
  rb_define_method(req_class, "rack_url_scheme", rack_url_scheme, 0);
668
716
  rb_define_method(req_class, "rack_input", rack_input, 0);
@@ -713,6 +761,7 @@ request_init(VALUE mod) {
713
761
  rack_upgrade_val = rb_str_new_cstr("rack.upgrade?"); rb_gc_register_address(&rack_upgrade_val);
714
762
  rack_url_scheme_val = rb_str_new_cstr("rack.url_scheme"); rb_gc_register_address(&rack_url_scheme_val);
715
763
  rack_version_val = rb_str_new_cstr("rack.version"); rb_gc_register_address(&rack_version_val);
764
+ remote_addr_val = rb_str_new_cstr("REMOTE_ADDR"); rb_gc_register_address(&remote_addr_val);
716
765
  request_method_val = rb_str_new_cstr("REQUEST_METHOD"); rb_gc_register_address(&request_method_val);
717
766
  script_name_val = rb_str_new_cstr("SCRIPT_NAME"); rb_gc_register_address(&script_name_val);
718
767
  server_name_val = rb_str_new_cstr("SERVER_NAME"); rb_gc_register_address(&server_name_val);
@@ -37,6 +37,9 @@ typedef struct _bhArgs {
37
37
  static VALUE graphql_class = Qundef;
38
38
  static VALUE vroot = Qnil;
39
39
  static VALUE build_headers_func = Qnil;
40
+ static VALUE arg_clas = Qnil;
41
+ static VALUE field_clas = Qnil;
42
+ static VALUE type_clas = Qnil;
40
43
 
41
44
  static ID call_id;
42
45
 
@@ -101,7 +104,7 @@ call_eval(VALUE x) {
101
104
 
102
105
  static void*
103
106
  protect_eval(void *x) {
104
- rb_rescue2(call_eval, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
107
+ rb_rescue2(call_eval, (VALUE)x, rescue_error, (VALUE)x, rb_eException, (VALUE)0);
105
108
 
106
109
  return NULL;
107
110
  }
@@ -679,7 +682,7 @@ graphql_schema(VALUE self, VALUE root) {
679
682
  printf("*-*-* %s\n", err.msg);
680
683
  exit(6);
681
684
  }
682
- rb_rescue2(rescue_yield, Qnil, rescue_yield_error, (VALUE)&err, rb_eException, 0);
685
+ rb_rescue2(rescue_yield, Qnil, rescue_yield_error, (VALUE)&err, rb_eException, (VALUE)0);
683
686
  if (AGOO_ERR_OK != err.code) {
684
687
  printf("*-*-* %s\n", err.msg);
685
688
  exit(7);
@@ -774,16 +777,16 @@ graphql_load_file(VALUE self, VALUE path) {
774
777
  return Qnil;
775
778
  }
776
779
 
777
- /* Document-method: dump_sdl
780
+ /* Document-method: sdl_dump
778
781
  *
779
- * call-seq: dump_sdl()
782
+ * call-seq: sdl_dump()
780
783
  *
781
784
  * The preferred method of inspecting a GraphQL schema is to use introspection
782
785
  * queries but for debugging and for reviewing the schema a dump of the schema
783
- * as SDL can be helpful. The _#dump_sdl_ method returns the schema as an SDL
786
+ * as SDL can be helpful. The _#sdl_dump_ method returns the schema as an SDL
784
787
  * string.
785
788
  *
786
- * - *options* [_Hash_] server options
789
+ * - *options* [_Hash_] options
787
790
  *
788
791
  * - *:with_description* [_true_|_false_] if true the description strings are included. If false they are not included.
789
792
  *
@@ -817,6 +820,79 @@ graphql_sdl_dump(VALUE self, VALUE options) {
817
820
  return dump;
818
821
  }
819
822
 
823
+ static void
824
+ type_cb(gqlType type, void *ctx) {
825
+ VALUE rtypes = (VALUE)ctx;
826
+ VALUE t;
827
+
828
+ if (GQL_OBJECT != type->kind || type->core) {
829
+ return;
830
+ }
831
+ t = rb_obj_alloc(type_clas);
832
+ rb_ivar_set(t, rb_intern("@name"), rb_str_new_cstr(type->name));
833
+ if (NULL != type->desc) {
834
+ rb_ivar_set(t, rb_intern("@description"), rb_str_new_cstr(type->desc));
835
+ }
836
+ if (NULL != type->fields) {
837
+ VALUE fields = rb_ary_new();
838
+ VALUE field;
839
+ gqlField f;
840
+
841
+ rb_ivar_set(t, rb_intern("@fields"), fields);
842
+ for (f = type->fields; NULL != f; f = f->next) {
843
+ field = rb_obj_alloc(field_clas);
844
+ rb_ary_push(fields, field);
845
+ rb_ivar_set(field, rb_intern("@name"), rb_str_new_cstr(f->name));
846
+ if (NULL != f->desc) {
847
+ rb_ivar_set(field, rb_intern("@description"), rb_str_new_cstr(f->desc));
848
+ }
849
+ if (NULL != f->type) {
850
+ rb_ivar_set(field, rb_intern("@type_name"), rb_str_new_cstr(f->type->name));
851
+ }
852
+ if (NULL != f->default_value) {
853
+ rb_ivar_set(field, rb_intern("@default_value"), gval_to_ruby(f->default_value));
854
+ }
855
+ if (NULL != f->args) {
856
+ VALUE args = rb_ary_new();
857
+ VALUE arg;
858
+ gqlArg a;
859
+
860
+ rb_ivar_set(field, rb_intern("@args"), args);
861
+ for (a = f->args; NULL != a; a = a->next) {
862
+ arg = rb_obj_alloc(arg_clas);
863
+ rb_ary_push(args, arg);
864
+ rb_ivar_set(arg, rb_intern("@name"), rb_str_new_cstr(a->name));
865
+ if (NULL != a->desc) {
866
+ rb_ivar_set(arg, rb_intern("@description"), rb_str_new_cstr(a->desc));
867
+ }
868
+ if (NULL != a->type) {
869
+ rb_ivar_set(arg, rb_intern("@type_name"), rb_str_new_cstr(a->type->name));
870
+ }
871
+ if (NULL != a->default_value) {
872
+ rb_ivar_set(arg, rb_intern("@default_value"), gval_to_ruby(a->default_value));
873
+ }
874
+ }
875
+ }
876
+ }
877
+ }
878
+ rb_ary_push(rtypes, t);
879
+ }
880
+
881
+ /* Document-method: sdl_types
882
+ *
883
+ * call-seq: sdl_types()
884
+ *
885
+ * Returns an array of all SDL types as Ruby objects.
886
+ */
887
+ static VALUE
888
+ graphql_sdl_types(VALUE self) {
889
+ VALUE rtypes = rb_ary_new();
890
+
891
+ gql_type_iterate(type_cb, (void*)rtypes);
892
+
893
+ return rtypes;
894
+ }
895
+
820
896
  /* Document-method: publish
821
897
  *
822
898
  * call-seq: publish(subject, event)
@@ -876,7 +952,7 @@ inner_build_headers(VALUE x) {
876
952
 
877
953
  static void*
878
954
  protected_build_headers(void *x) {
879
- return (void*)rb_rescue2(inner_build_headers, (VALUE)x, rescue_build_header, (VALUE)x, rb_eException, 0);
955
+ return (void*)rb_rescue2(inner_build_headers, (VALUE)x, rescue_build_header, (VALUE)x, rb_eException, (VALUE)0);
880
956
  }
881
957
 
882
958
  static agooText
@@ -963,10 +1039,16 @@ graphql_init(VALUE mod) {
963
1039
 
964
1040
  rb_define_module_function(graphql_class, "sdl_dump", graphql_sdl_dump, 1);
965
1041
 
1042
+ rb_define_module_function(graphql_class, "sdl_types", graphql_sdl_types, 0);
1043
+
966
1044
  rb_define_module_function(graphql_class, "publish", graphql_publish, 2);
967
1045
 
968
1046
  rb_define_module_function(graphql_class, "build_headers=", graphql_build_headers, 1);
969
1047
  rb_define_module_function(graphql_class, "headers", graphql_headers, 1);
970
1048
 
1049
+ arg_clas = rb_const_get_at(graphql_class, rb_intern("Arg"));
1050
+ field_clas = rb_const_get_at(graphql_class, rb_intern("Field"));
1051
+ type_clas = rb_const_get_at(graphql_class, rb_intern("Type"));
1052
+
971
1053
  call_id = rb_intern("call");
972
1054
  }
@@ -103,6 +103,7 @@ configure(agooErr err, int port, const char *root, VALUE options) {
103
103
  }
104
104
  agoo_server.thread_cnt = 0;
105
105
  the_rserver.worker_cnt = 1;
106
+ the_rserver.uses = NULL;
106
107
  atomic_init(&agoo_server.running, 0);
107
108
  agoo_server.listen_thread = 0;
108
109
  agoo_server.con_loops = NULL;
@@ -384,7 +385,7 @@ handle_base_inner(VALUE x) {
384
385
 
385
386
  static void*
386
387
  handle_base(void *x) {
387
- rb_rescue2(handle_base_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
388
+ rb_rescue2(handle_base_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, (VALUE)0);
388
389
 
389
390
  return NULL;
390
391
  }
@@ -613,7 +614,7 @@ handle_rack_inner(VALUE x) {
613
614
  static void*
614
615
  handle_rack(void *x) {
615
616
  //rb_gc_disable();
616
- rb_rescue2(handle_rack_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
617
+ rb_rescue2(handle_rack_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, (VALUE)0);
617
618
  //rb_gc_enable();
618
619
  //rb_gc();
619
620
 
@@ -638,7 +639,7 @@ handle_wab_inner(VALUE x) {
638
639
 
639
640
  static void*
640
641
  handle_wab(void *x) {
641
- rb_rescue2(handle_wab_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
642
+ rb_rescue2(handle_wab_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, (VALUE)0);
642
643
 
643
644
  return NULL;
644
645
  }
@@ -696,7 +697,7 @@ handle_push_inner(VALUE x) {
696
697
 
697
698
  static void*
698
699
  handle_push(void *x) {
699
- rb_rescue2(handle_push_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
700
+ rb_rescue2(handle_push_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, (VALUE)0);
700
701
  return NULL;
701
702
  }
702
703
 
@@ -1014,6 +1015,14 @@ handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
1014
1015
  }
1015
1016
  }
1016
1017
  }
1018
+ if (NULL != the_rserver.uses) {
1019
+ RUse u;
1020
+
1021
+ for (u = the_rserver.uses; NULL != u; u = u->next) {
1022
+ u->argv[0] = handler;
1023
+ handler = rb_funcall2(u->clas, rb_intern("new"), u->argc, u->argv);
1024
+ }
1025
+ }
1017
1026
  if (NULL == (hook = rhook_create(meth, pat, handler, &agoo_server.eval_queue))) {
1018
1027
  rb_raise(rb_eStandardError, "out of memory.");
1019
1028
  } else {
@@ -1195,6 +1204,40 @@ rack_early_hints(VALUE self, VALUE on) {
1195
1204
  return on;
1196
1205
  }
1197
1206
 
1207
+ /* Document-method: use
1208
+ *
1209
+ * call-seq: use(middleware, *args)
1210
+ *
1211
+ * The use function must be called before the _handle_ functions. Any
1212
+ * invocations of _use_ apply only to handlers called after the call to use.
1213
+ */
1214
+ static VALUE
1215
+ use(int argc, VALUE *argv, VALUE self) {
1216
+ VALUE mc;
1217
+ RUse u;
1218
+
1219
+ if (argc < 1) { // at least the middleware class must be provided.
1220
+ rb_raise(rb_eArgError, "no middleware class provided");
1221
+ }
1222
+ mc = argv[0];
1223
+ if (T_CLASS != rb_type(mc)) {
1224
+ rb_raise(rb_eArgError, "the first argument to use must be a class");
1225
+ }
1226
+ if (NULL == (u = (RUse)AGOO_MALLOC(sizeof(struct _rUse)))) {
1227
+ rb_raise(rb_eNoMemError, "Failed to allocate memory for a middleware use.");
1228
+ }
1229
+ u->clas = mc;
1230
+ u->argc = argc;
1231
+ if (NULL == (u->argv = (VALUE*)AGOO_MALLOC(sizeof(VALUE) * u->argc))) {
1232
+ rb_raise(rb_eNoMemError, "Failed to allocate memory for a middleware use.");
1233
+ }
1234
+ memcpy(u->argv, argv, argc * sizeof(VALUE));
1235
+ u->next = the_rserver.uses;
1236
+ the_rserver.uses = u;
1237
+
1238
+ return Qnil;
1239
+ }
1240
+
1198
1241
  /* Document-class: Agoo::Server
1199
1242
  *
1200
1243
  * An HTTP server that support the rack API as well as some other optimized
@@ -1214,6 +1257,7 @@ server_init(VALUE mod) {
1214
1257
  rb_define_module_function(server_mod, "path_group", path_group, 2);
1215
1258
  rb_define_module_function(server_mod, "header_rule", header_rule, 4);
1216
1259
  rb_define_module_function(server_mod, "domain", domain, 2);
1260
+ rb_define_module_function(server_mod, "use", use, -1);
1217
1261
 
1218
1262
  rb_define_module_function(server_mod, "rack_early_hints", rack_early_hints, 1);
1219
1263
 
@@ -7,10 +7,18 @@
7
7
 
8
8
  #define MAX_WORKERS 32
9
9
 
10
+ typedef struct _rUse {
11
+ struct _rUse *next;
12
+ VALUE clas;
13
+ VALUE *argv;
14
+ int argc;
15
+ } *RUse;
16
+
10
17
  typedef struct _rServer {
11
18
  int worker_cnt;
12
19
  int worker_pids[MAX_WORKERS];
13
20
  VALUE *eval_threads; // Qnil terminated
21
+ RUse uses;
14
22
  } *RServer;
15
23
 
16
24
  extern struct _rServer the_rserver;
@@ -160,8 +160,8 @@ listen_loop(void *x) {
160
160
  //fcntl(client_sock, F_SETFL, FNDELAY);
161
161
  setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
162
162
  setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
163
- agoo_log_cat(&agoo_con_cat, "Server with pid %d accepted connection %llu on %s [%d]",
164
- getpid(), (unsigned long long)cnt, b->id, con->sock);
163
+ agoo_log_cat(&agoo_con_cat, "Server with pid %d accepted connection %llu on %s [%d] from %s",
164
+ getpid(), (unsigned long long)cnt, b->id, con->sock, con->remote);
165
165
 
166
166
  con_cnt = atomic_fetch_add(&agoo_server.con_cnt, 1);
167
167
  if (agoo_server.loop_max > agoo_server.loop_cnt && agoo_server.loop_cnt * LOOP_UP < con_cnt) {
@@ -189,6 +189,7 @@ agoo_ws_create_req(agooCon c, long mlen) {
189
189
  c->req->method = (AGOO_WS_OP_BIN == op) ? AGOO_ON_BIN : AGOO_ON_MSG;
190
190
  c->req->upgrade = AGOO_UP_NONE;
191
191
  c->req->up = c->up;
192
+ memcpy(c->req->remote, c->remote, sizeof(c->remote));
192
193
  c->req->res = NULL;
193
194
  if (c->up->on_msg) {
194
195
  c->req->hook = agoo_hook_create(AGOO_NONE, NULL, c->up->ctx, PUSH_HOOK, &agoo_server.eval_queue);
@@ -4,6 +4,7 @@ end
4
4
 
5
5
  require 'stringio' # needed for Ruby 2.7
6
6
  require 'agoo/version'
7
+ require 'agoo/graphql'
7
8
  require 'rack/handler/agoo'
8
9
  require 'agoo/agoo' # C extension
9
10
 
@@ -0,0 +1,9 @@
1
+
2
+ module Agoo
3
+ class GraphQL
4
+ end
5
+ end
6
+
7
+ require 'agoo/graphql/arg'
8
+ require 'agoo/graphql/field'
9
+ require 'agoo/graphql/type'
@@ -0,0 +1,13 @@
1
+
2
+ module Agoo
3
+ class GraphQL
4
+
5
+ class Arg
6
+ attr_accessor :name
7
+ attr_accessor :description
8
+ attr_accessor :type_name
9
+ attr_accessor :default_value
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+
2
+ module Agoo
3
+ class GraphQL
4
+
5
+ class Field
6
+ attr_accessor :name
7
+ attr_accessor :description
8
+ attr_accessor :type_name
9
+ attr_accessor :args
10
+ attr_accessor :default_value
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+
2
+ module Agoo
3
+ class GraphQL
4
+
5
+ class Type
6
+ attr_accessor :name
7
+ attr_accessor :description
8
+ attr_accessor :fields
9
+ end
10
+
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.12.0'
4
+ VERSION = '2.14.0'
5
5
  end
@@ -82,7 +82,7 @@ class BaseHandlerTest < Minitest::Test
82
82
  GC.start
83
83
  Agoo::shutdown
84
84
  }
85
-
85
+
86
86
  def test_eval
87
87
  uri = URI('http://localhost:6470/tellme?a=1')
88
88
  req = Net::HTTP::Get.new(uri)
@@ -132,13 +132,13 @@ class BaseHandlerTest < Minitest::Test
132
132
  req['Accept-Encoding'] = '*'
133
133
  req['Accept'] = 'application/json'
134
134
  req['User-Agent'] = 'Ruby'
135
-
135
+
136
136
  res = Net::HTTP.start(uri.hostname, uri.port) { |h|
137
137
  h.request(req)
138
138
  }
139
139
  assert_equal(Net::HTTPNoContent, res.class)
140
140
  end
141
-
141
+
142
142
  def test_put
143
143
  uri = URI('http://localhost:6470/makeme')
144
144
  req = Net::HTTP::Put.new(uri)
@@ -147,7 +147,7 @@ class BaseHandlerTest < Minitest::Test
147
147
  req['Accept'] = 'application/json'
148
148
  req['User-Agent'] = 'Ruby'
149
149
  req.body = 'hello'
150
-
150
+
151
151
  res = Net::HTTP.start(uri.hostname, uri.port) { |h|
152
152
  h.request(req)
153
153
  }
@@ -105,6 +105,7 @@ size=big
105
105
  "PATH_INFO" => "/tellme",
106
106
  "QUERY_STRING" => "a=1",
107
107
  "REQUEST_METHOD" => "GET",
108
+ "REMOTE_ADDR" => "127.0.0.1",
108
109
  "SCRIPT_NAME" => "",
109
110
  "SERVER_NAME" => "localhost",
110
111
  "SERVER_PORT" => "6467",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: agoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.12.0
4
+ version: 2.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-19 00:00:00.000000000 Z
11
+ date: 2020-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -34,6 +34,7 @@ description: A fast HTTP server supporting rack.
34
34
  email: peter@ohler.com
35
35
  executables:
36
36
  - agoo
37
+ - agoo_stubs
37
38
  extensions:
38
39
  - ext/agoo/extconf.rb
39
40
  extra_rdoc_files:
@@ -45,6 +46,7 @@ files:
45
46
  - LICENSE
46
47
  - README.md
47
48
  - bin/agoo
49
+ - bin/agoo_stubs
48
50
  - ext/agoo/agoo.c
49
51
  - ext/agoo/atomic.h
50
52
  - ext/agoo/base64.c
@@ -142,6 +144,10 @@ files:
142
144
  - ext/agoo/websocket.c
143
145
  - ext/agoo/websocket.h
144
146
  - lib/agoo.rb
147
+ - lib/agoo/graphql.rb
148
+ - lib/agoo/graphql/arg.rb
149
+ - lib/agoo/graphql/field.rb
150
+ - lib/agoo/graphql/type.rb
145
151
  - lib/agoo/version.rb
146
152
  - lib/rack/handler/agoo.rb
147
153
  - test/base_handler_test.rb