agoo 2.13.0 → 2.14.2

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: 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