agoo 2.13.0 → 2.14.2

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: df42da153e97e018d4984fa75be78ac86a1c8b97aed6b48d62ec7fe5047d7099
4
- data.tar.gz: e83a55eed4781bbb0ab08df41203509df6ec45d06817b87c9d61d25bca8a294a
3
+ metadata.gz: e538b3f1c7ed9ea23d8529c29bf5104d0882b0dcda49e6114bb011491b1e3ed7
4
+ data.tar.gz: ee1a1284779ec7108675794f4eff89f826c76f3a5e10a7f07a42bab8bbae31cb
5
5
  SHA512:
6
- metadata.gz: ac6e8d86a9b545d93c9ee033950422292b4ab2f740ba692f2c24940e928c42466b895dfae6ce5263040f2b53613a6c1241f5dfe5c0be21961f1ee885970ec220
7
- data.tar.gz: 9dd8414c88948198bd1a22d5aac236ef8d5bf4251020b7fcfc4b94837d8a48433aa6774ba9e13df1dc36dc1e29f1697dfcc2c21e8ef1a9dc6897d223b72eb946
6
+ metadata.gz: e28b50c1bea0934691679719532108fdd6d912f3beed8b9eabe6071ccd71af393b14f3c81d094cb82b5777a22884cc716e12d3c66629e39c5f7f1290919fc030
7
+ data.tar.gz: 1d478668d82b79acc3d2cabb56fe9aca55fb3513b4c5a5810c0d200a69b081573ff532a346c13d513fae0980fa6331f3701ee041d43ea7cfc32cd2d2a586d0c2
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All changes to the Agoo gem are documented here. Releases follow semantic versioning.
4
4
 
5
+ ## [2.14.2] - 2022-02-22
6
+
7
+ ### Fixed
8
+ - Invalid SDL now raises and exception instead of crashing.
9
+
10
+ ## [2.14.1] - 2021-06-09
11
+
12
+ ### Fixed
13
+ - Evaluating an empty GraphQL request with comments only no longer crashes.
14
+ - JSON parser bug fixed.
15
+
16
+ ## [2.14.0] - 2020-11-07
17
+
18
+ ### Added
19
+
20
+ - REMOTE_ADDR element added to requests/env argument to `call()`.
21
+
22
+ - Added check for multiple Content-Length headers.
23
+
24
+ - Multiple occurrances of a header are now passed to the Rack `call()` method as an array.
25
+
5
26
  ## [2.13.0] - 2020-07-05
6
27
 
7
28
  ### Added
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # agoo
1
+ # [![{}j](misc/agoo_128.svg)](http://www.ohler.com/agoo) Agoo
2
2
 
3
- [![Build Status](https://img.shields.io/travis/ohler55/agoo/master.svg)](http://travis-ci.org/ohler55/agoo?branch=master)
3
+ [![Build Status](https://img.shields.io/github/workflow/status/ohler55/agoo/CI?logo=github)](https://github.com/ohler55/agoo/actions/workflows/CI.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/agoo.svg)](https://badge.fury.io/rb/agoo)
5
5
  ![Gem](https://img.shields.io/gem/dt/agoo.svg) [![TideLift](https://tidelift.com/badges/github/ohler55/agoo)](https://tidelift.com/subscription/pkg/rubygems-agoo?utm_source=rubygems-agoo&utm_medium=referral&utm_campaign=readme)
6
6
 
@@ -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
@@ -138,5 +163,3 @@ the develop branch. Pull requests should be made against the develop branch.
138
163
  - *WABuR* *repo*: https://github.com/ohler55/wabur has an option to use Agoo
139
164
 
140
165
  - *Perfer* *repo*: https://github.com/ohler55/perfer
141
-
142
- Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announcements and news about the Agoo gem.
data/ext/agoo/con.c CHANGED
@@ -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;
@@ -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);
data/ext/agoo/con.h CHANGED
@@ -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
 
data/ext/agoo/extconf.rb CHANGED
@@ -20,7 +20,7 @@ CONFIG['warnflags'].slice!(/ -Wdeclaration-after-statement/)
20
20
  CONFIG['warnflags'].slice!(/ -Wmissing-noreturn/)
21
21
 
22
22
  have_header('stdatomic.h')
23
- #have_header('sys/epoll.h')
23
+ have_header('sys/epoll.h')
24
24
  have_header('openssl/ssl.h')
25
25
  have_library('ssl')
26
26
 
data/ext/agoo/gqleval.c CHANGED
@@ -620,7 +620,7 @@ eval_post(agooErr err, agooReq req) {
620
620
  } else {
621
621
  result = gql_doc_eval_func(err, doc);
622
622
  }
623
- if (GQL_SUBSCRIPTION == doc->op->kind) {
623
+ if (NULL != doc->op && GQL_SUBSCRIPTION == doc->op->kind) {
624
624
  result = NULL;
625
625
  }
626
626
  DONE:
data/ext/agoo/gqljson.c CHANGED
@@ -307,6 +307,7 @@ parse_object(agooErr err, agooDoc doc) {
307
307
  agoo_doc_skip_jwhite(doc);
308
308
  if ('}' != *doc->cur) {
309
309
  for (; doc->cur < doc->end; doc->cur++) {
310
+ agoo_doc_skip_jwhite(doc);
310
311
  if ('"' != *doc->cur) {
311
312
  return return_parse_err(err, doc, "expected an object key as a string", value);
312
313
  }
data/ext/agoo/graphql.c CHANGED
@@ -892,8 +892,8 @@ gql_assure_nonnull(agooErr err, gqlType base) {
892
892
 
893
893
  void
894
894
  gql_type_destroy(gqlType type) {
895
- type_destroy(type);
896
895
  type_remove(type);
896
+ type_destroy(type);
897
897
  }
898
898
 
899
899
  // If negative then there are non-simple-string characters.
data/ext/agoo/http.c CHANGED
@@ -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;
data/ext/agoo/ready.c CHANGED
@@ -5,7 +5,7 @@
5
5
  #include <string.h>
6
6
  #include <unistd.h>
7
7
 
8
- #if HAVE_SYS_EPOLL_H
8
+ #ifdef HAVE_SYS_EPOLL_H
9
9
  #include <sys/epoll.h>
10
10
  #else
11
11
  #include <ctype.h>
@@ -22,7 +22,7 @@
22
22
  // milliseconds
23
23
  #define MAX_WAIT 10
24
24
 
25
- #if HAVE_SYS_EPOLL_H
25
+ #ifdef HAVE_SYS_EPOLL_H
26
26
  #define EPOLL_SIZE 100
27
27
  #else
28
28
  #define INITIAL_POLL_SIZE 1024
@@ -34,7 +34,7 @@ typedef struct _link {
34
34
  int fd;
35
35
  void *ctx;
36
36
  agooHandler handler;
37
- #if HAVE_SYS_EPOLL_H
37
+ #ifdef HAVE_SYS_EPOLL_H
38
38
  uint32_t events; // last events set
39
39
  #else
40
40
  struct pollfd *pp;
@@ -45,7 +45,7 @@ struct _agooReady {
45
45
  Link links;
46
46
  int lcnt;
47
47
  double next_check;
48
- #if HAVE_SYS_EPOLL_H
48
+ #ifdef HAVE_SYS_EPOLL_H
49
49
  int epoll_fd;
50
50
  #else
51
51
  struct pollfd *pa;
@@ -82,7 +82,7 @@ agoo_ready_create(agooErr err) {
82
82
  ready->links = NULL;
83
83
  ready->lcnt = 0;
84
84
  ready->next_check = dtime() + CHECK_FREQ;
85
- #if HAVE_SYS_EPOLL_H
85
+ #ifdef HAVE_SYS_EPOLL_H
86
86
  if (0 > (ready->epoll_fd = epoll_create(1))) {
87
87
  agoo_err_no(err, "epoll create failed");
88
88
  return NULL;
@@ -111,7 +111,7 @@ agoo_ready_destroy(agooReady ready) {
111
111
  }
112
112
  AGOO_FREE(link);
113
113
  }
114
- #if HAVE_SYS_EPOLL_H
114
+ #ifdef HAVE_SYS_EPOLL_H
115
115
  close(ready->epoll_fd);
116
116
  #else
117
117
  AGOO_FREE(ready->pa);
@@ -137,7 +137,7 @@ agoo_ready_add(agooErr err,
137
137
  ready->links = link;
138
138
  ready->lcnt++;
139
139
 
140
- #if HAVE_SYS_EPOLL_H
140
+ #ifdef HAVE_SYS_EPOLL_H
141
141
  link->events = EPOLLIN;
142
142
  {
143
143
  struct epoll_event event = {
@@ -181,7 +181,7 @@ ready_remove(agooReady ready, Link link) {
181
181
  if (NULL != link->next) {
182
182
  link->next->prev = link->prev;
183
183
  }
184
- #if HAVE_SYS_EPOLL_H
184
+ #ifdef HAVE_SYS_EPOLL_H
185
185
  {
186
186
  struct epoll_event event = {
187
187
  .events = 0,
@@ -214,7 +214,7 @@ agoo_ready_go(agooErr err, agooReady ready) {
214
214
  Link link;
215
215
  Link next;
216
216
 
217
- #if HAVE_SYS_EPOLL_H
217
+ #ifdef HAVE_SYS_EPOLL_H
218
218
  struct epoll_event events[EPOLL_SIZE];
219
219
  struct epoll_event *ep;
220
220
  int cnt;
data/ext/agoo/req.h CHANGED
@@ -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
data/ext/agoo/request.c CHANGED
@@ -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);
data/ext/agoo/rserver.c CHANGED
@@ -523,14 +523,14 @@ handle_rack_inner(VALUE x) {
523
523
  if (Qnil != body) {
524
524
  body = rb_ivar_get(body, rb_intern("@body"));
525
525
  }
526
- if (rb_respond_to(body, rb_intern("each"))) {
527
- rb_iterate(rb_each, body, body_len_cb, (VALUE)&bsize);
526
+ if (rb_respond_to(body, each_id)) {
527
+ rb_block_call(body, each_id, 0, 0, body_len_cb, (VALUE)&bsize);
528
528
  }
529
529
  } else {
530
- rb_iterate(rb_each, bv, body_len_cb, (VALUE)&bsize);
530
+ rb_block_call(bv, each_id, 0, 0, body_len_cb, (VALUE)&bsize);
531
531
  }
532
532
  } else {
533
- rb_iterate(rb_each, bv, body_len_cb, (VALUE)&bsize);
533
+ rb_block_call(bv, each_id, 0, 0, body_len_cb, (VALUE)&bsize);
534
534
  }
535
535
  }
536
536
  switch (code) {
@@ -587,7 +587,7 @@ handle_rack_inner(VALUE x) {
587
587
  if (T_HASH == rb_type(hv)) {
588
588
  rb_hash_foreach(hv, header_cb, (VALUE)&t);
589
589
  } else {
590
- rb_iterate(rb_each, hv, header_each_cb, (VALUE)&t);
590
+ rb_block_call(hv, each_id, 0, 0, header_each_cb, (VALUE)&t);
591
591
  }
592
592
  }
593
593
  t = agoo_text_append(t, "\r\n", 2);
@@ -602,7 +602,7 @@ handle_rack_inner(VALUE x) {
602
602
  t = agoo_text_append(t, StringValuePtr(v), (int)RSTRING_LEN(v));
603
603
  }
604
604
  } else {
605
- rb_iterate(rb_each, bv, body_append_cb, (VALUE)&t);
605
+ rb_block_call(bv, each_id, 0, 0, body_append_cb, (VALUE)&t);
606
606
  }
607
607
  }
608
608
  agoo_res_message_push(req->res, t);
data/ext/agoo/server.c CHANGED
@@ -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) {
data/ext/agoo/websocket.c CHANGED
@@ -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);
data/lib/agoo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.13.0'
4
+ VERSION = '2.14.2'
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
  }
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__)
4
+ $root_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
5
+ %w(lib ext).each do |dir|
6
+ $: << File.join($root_dir, dir)
7
+ end
8
+
9
+ require 'minitest'
10
+ require 'minitest/autorun'
11
+
12
+ require 'agoo'
13
+
14
+ class Schema
15
+ attr_reader :query
16
+
17
+ def initialize()
18
+ end
19
+ end
20
+
21
+ class GraphQLErrTest < Minitest::Test
22
+
23
+ def test_bad_sdl
24
+ bad_sdl = %^
25
+ type Query @ruby(class: "Query" {
26
+ }
27
+ ^
28
+ Agoo::GraphQL.schema(Schema.new) {
29
+ assert_raises(StandardError) {
30
+ Agoo::GraphQL.load(bad_sdl)
31
+ }
32
+ }
33
+ end
34
+
35
+ end
data/test/graphql_test.rb CHANGED
@@ -79,6 +79,7 @@ end
79
79
  $songs_sdl = %^
80
80
  type Query @ruby(class: "Query") {
81
81
  artist(name: String!): Artist
82
+ artists: [Artist!]
82
83
  }
83
84
 
84
85
  type Mutation {
@@ -120,26 +121,30 @@ type Song {
120
121
  ^
121
122
 
122
123
  class Query
123
- attr_reader :artists
124
+ attr_reader :artistHash
124
125
 
125
126
  def initialize(artists)
126
- @artists = artists
127
+ @artistHash = artists
127
128
  end
128
129
 
129
130
  def artist(args)
130
- @artists[args['name']]
131
+ @artistHash[args['name']]
132
+ end
133
+
134
+ def artists(args)
135
+ @artistHash.values
131
136
  end
132
137
  end
133
138
 
134
139
  class Mutation
135
- attr_reader :artists
140
+ attr_reader :artistHash
136
141
 
137
142
  def initialize(artists)
138
- @artists = artists
143
+ @artistHash = artists
139
144
  end
140
145
 
141
146
  def like(args)
142
- artist = @artists[args['artist']]
147
+ artist = @artistHash[args['artist']]
143
148
  artist.like
144
149
  artist
145
150
  end
@@ -187,6 +192,7 @@ type Mutation @ruby(class: "Mutation") {
187
192
 
188
193
  type Query @ruby(class: "Query") {
189
194
  artist(name: String!): Artist
195
+ artists: [Artist!]
190
196
  }
191
197
 
192
198
  type Song @ruby(class: "Song") {
@@ -452,6 +458,30 @@ fragment basic on Artist {
452
458
  post_test(uri, body, 'application/graphql', expect)
453
459
  end
454
460
 
461
+ def test_post_json_fragment
462
+ uri = URI('http://localhost:6472/graphql?indent=2')
463
+ body = %^{
464
+ "query": "fragment basic on Artist {name origin} query list($filter: String) {artists {...basic}}",
465
+ "operationName": "list",
466
+ "variables": {"filters": {}}
467
+ }^
468
+ expect = %^{
469
+ "data":{
470
+ "artists":[
471
+ {
472
+ "name":"Fazerdaze",
473
+ "origin":[
474
+ "Morningside",
475
+ "Auckland",
476
+ "New Zealand"
477
+ ]
478
+ }
479
+ ]
480
+ }
481
+ }^
482
+ post_test(uri, body, 'application/json', expect)
483
+ end
484
+
455
485
  def test_post_inline
456
486
  uri = URI('http://localhost:6472/graphql?indent=2')
457
487
  body = %^
@@ -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.13.0
4
+ version: 2.14.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-05 00:00:00.000000000 Z
11
+ date: 2022-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -154,6 +154,7 @@ files:
154
154
  - test/bind_test.rb
155
155
  - test/domain_test.rb
156
156
  - test/early_hints_test.rb
157
+ - test/graphql_error_test.rb
157
158
  - test/graphql_test.rb
158
159
  - test/hijack_test.rb
159
160
  - test/log_test.rb
@@ -163,7 +164,7 @@ homepage: https://github.com/ohler55/agoo
163
164
  licenses:
164
165
  - MIT
165
166
  metadata: {}
166
- post_install_message:
167
+ post_install_message:
167
168
  rdoc_options:
168
169
  - "-t"
169
170
  - Agoo
@@ -189,17 +190,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
190
  version: '0'
190
191
  requirements:
191
192
  - Linux or macOS
192
- rubygems_version: 3.1.2
193
- signing_key:
193
+ rubygems_version: 3.3.3
194
+ signing_key:
194
195
  specification_version: 4
195
196
  summary: An HTTP server
196
197
  test_files:
197
- - test/graphql_test.rb
198
- - test/hijack_test.rb
199
- - test/rack_handler_test.rb
200
198
  - test/base_handler_test.rb
201
- - test/log_test.rb
199
+ - test/bind_test.rb
202
200
  - test/domain_test.rb
203
201
  - test/early_hints_test.rb
204
- - test/bind_test.rb
202
+ - test/graphql_error_test.rb
203
+ - test/graphql_test.rb
204
+ - test/hijack_test.rb
205
+ - test/log_test.rb
206
+ - test/rack_handler_test.rb
205
207
  - test/static_test.rb