agoo 2.12.0 → 2.14.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: 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