agoo 2.10.0 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e446f82a7209a72954bdf44c5f345fd9c5e13384dc75d6ce8faa7149d52f023
4
- data.tar.gz: dea80f2d75d1f4affce64efece36dae4f7753e243d085faf8b7f0b2746aea2db
3
+ metadata.gz: 158117188fe96c69828c60595d0a38afe61bd10923f15236613ca7efbd323623
4
+ data.tar.gz: da8bea92f370d7e8d66c39c8ff693611e971e1805b11ccf8ecaaa8d9a01eabc3
5
5
  SHA512:
6
- metadata.gz: 6167c0841ed2f05935998cb861632949b9d437e1c590820477add9d0ef067abd0c1ea1db377279c8f82fd4d58efb6491daf198f979e0fa6dc3f989ba4cd217ef
7
- data.tar.gz: da0c4b54ee8f4db509e5e657bd2a919d0df4a314b7c6663d0e2afd0b4e89730fbaad7693f3888cefe984f8ca00d9e04237c5c51218e5d96e0278cc91eb483f5d
6
+ metadata.gz: 3387636bc61cd62a24ac7b71f1678c74c8e564c857e580acff1f93144868cd5fe614d49607c3a150f9486b779d4e3b925d8f52c391b02685054bf7a862420256
7
+ data.tar.gz: 781c95aad1a5161b6d0c069051a748e2b83a5c00d1f44ac7d14a37f897bdec02fc792a1e694db1acb8b4d124b53cdb9758237b1206aece31810b7c6513858b0e
@@ -4,6 +4,13 @@ All changes to the Agoo gem are documented here. Releases follow semantic versio
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.11.0] - [2019-09-15]
8
+
9
+ TLS using OpenSSL
10
+
11
+ ### Added
12
+ - TLS (SSL) support added.
13
+
7
14
  ## [2.10.0] - 2019-08-29
8
15
 
9
16
  GraphQL subscriptions
data/README.md CHANGED
@@ -88,6 +88,11 @@ Agoo is not available on Windows.
88
88
 
89
89
  ## News
90
90
 
91
+ - Version 2.11.0 supports GraphQL subscriptions. TLS (SSL,HTTPS)
92
+ support added. Examples for both. Related, the
93
+ [graphql-benchmark](https://github.com/the-benchmarker/graphql-benchmarks)
94
+ repo was given to [the-benchmarker](https://github.com/the-benchmarker).
95
+
91
96
  - Agoo has a new GraphQL module with a simple, easy to use
92
97
  API. Checkout the [hello](example/graphql/hello.rb) or
93
98
  [song](example/graphql/song.rb) examples.
@@ -12,6 +12,7 @@
12
12
  #include "bind.h"
13
13
  #include "debug.h"
14
14
  #include "log.h"
15
+ #include "server.h"
15
16
 
16
17
  agooBind
17
18
  agoo_bind_port(agooErr err, int port) {
@@ -19,11 +20,10 @@ agoo_bind_port(agooErr err, int port) {
19
20
 
20
21
  if (NULL != b) {
21
22
  char id[1024];
22
-
23
+
23
24
  b->port = port;
24
25
  b->family = AF_INET;
25
26
  snprintf(id, sizeof(id) - 1, "http://:%d", port);
26
- strcpy(b->scheme, "http");
27
27
  if (NULL == (b->id = AGOO_STRDUP(id))) {
28
28
  AGOO_ERR_MEM(err, "strdup()");
29
29
  AGOO_FREE(b);
@@ -43,7 +43,7 @@ url_tcp(agooErr err, const char *url, const char *scheme) {
43
43
  struct in_addr addr = { .s_addr = 0 };
44
44
  int port;
45
45
  agooBind b;
46
-
46
+
47
47
  if (NULL == colon) {
48
48
  port = 80;
49
49
  } else if (15 < colon - url) {
@@ -74,8 +74,6 @@ url_tcp(agooErr err, const char *url, const char *scheme) {
74
74
  AGOO_FREE(b);
75
75
  return NULL;
76
76
  }
77
- strncpy(b->scheme, scheme, sizeof(b->scheme));
78
- b->scheme[sizeof(b->scheme) - 1] = '\0';
79
77
  b->kind = AGOO_CON_HTTP;
80
78
  b->read = NULL;
81
79
  b->write = NULL;
@@ -84,7 +82,7 @@ url_tcp(agooErr err, const char *url, const char *scheme) {
84
82
  return b;
85
83
  }
86
84
  AGOO_ERR_MEM(err, "Bind");
87
-
85
+
88
86
  return b;
89
87
  }
90
88
 
@@ -95,7 +93,7 @@ url_tcp6(agooErr err, const char *url, const char *scheme) {
95
93
  int port = 80;
96
94
  char buf[256];
97
95
  agooBind b;
98
-
96
+
99
97
  if (':' == *(end + 1)) {
100
98
  port = atoi(end + 2);
101
99
  }
@@ -117,9 +115,7 @@ url_tcp6(agooErr err, const char *url, const char *scheme) {
117
115
  AGOO_ERR_MEM(err, "strdup()");
118
116
  AGOO_FREE(b);
119
117
  return NULL;
120
- }
121
- strncpy(b->scheme, scheme, sizeof(b->scheme));
122
- b->scheme[sizeof(b->scheme) - 1] = '\0';
118
+ }
123
119
  b->kind = AGOO_CON_HTTP;
124
120
  b->read = NULL;
125
121
  b->write = NULL;
@@ -128,7 +124,7 @@ url_tcp6(agooErr err, const char *url, const char *scheme) {
128
124
  return b;
129
125
  }
130
126
  AGOO_ERR_MEM(err, "Bind");
131
-
127
+
132
128
  return b;
133
129
  }
134
130
 
@@ -143,7 +139,7 @@ url_named(agooErr err, const char *url) {
143
139
  if (NULL != b) {
144
140
  const char *fmt = "unix://%s";
145
141
  char id[1024];
146
-
142
+
147
143
  if (NULL == (b->name = AGOO_STRDUP(url))) {
148
144
  AGOO_ERR_MEM(err, "strdup()");
149
145
  AGOO_FREE(b);
@@ -155,7 +151,6 @@ url_named(agooErr err, const char *url) {
155
151
  AGOO_FREE(b);
156
152
  return NULL;
157
153
  }
158
- strcpy(b->scheme, "unix");
159
154
  b->kind = AGOO_CON_HTTP;
160
155
  b->read = NULL;
161
156
  b->write = NULL;
@@ -170,8 +165,60 @@ url_named(agooErr err, const char *url) {
170
165
 
171
166
  static agooBind
172
167
  url_ssl(agooErr err, const char *url) {
173
- // TBD
168
+ char *colon = index(url, ':');
169
+ struct in_addr addr = { .s_addr = 0 };
170
+ int port;
171
+ agooBind b;
172
+
173
+ #ifdef HAVE_OPENSSL_SSL_H
174
+ if (NULL == agoo_server.ssl_ctx) {
175
+ agoo_err_set(err, AGOO_ERR_ARG, "https requires an SSL certificate and private key. (%s)", url);
176
+ return NULL;
177
+ }
178
+ #else
179
+ agoo_err_set(err, AGOO_ERR_ARG, "https requires OpenSSL. Rebuild with OpenSSL. (%s)", url);
174
180
  return NULL;
181
+ #endif
182
+ if (NULL == colon) {
183
+ port = 443;
184
+ } else if (15 < colon - url) {
185
+ agoo_err_set(err, AGOO_ERR_ARG, "https bind address is not valid, too long. (%s)", url);
186
+ return NULL;
187
+ } else if (':' == *url) {
188
+ port = atoi(colon + 1);
189
+ } else {
190
+ char buf[32];
191
+
192
+ strncpy(buf, url, colon - url);
193
+ buf[colon - url] = '\0';
194
+ if (0 == inet_aton(buf, &addr)) {
195
+ agoo_err_set(err, AGOO_ERR_ARG, "https bind address is not valid. (%s)", url);
196
+ return NULL;
197
+ }
198
+ port = atoi(colon + 1);
199
+ }
200
+ if (NULL != (b = (agooBind)AGOO_CALLOC(1, sizeof(struct _agooBind)))) {
201
+ char id[64];
202
+
203
+ b->port = port;
204
+ b->addr4 = addr;
205
+ b->family = AF_INET;
206
+ snprintf(id, sizeof(id), "https://%s:%d", inet_ntoa(addr), port);
207
+ if (NULL == (b->id = AGOO_STRDUP(id))) {
208
+ AGOO_ERR_MEM(err, "strdup()");
209
+ AGOO_FREE(b);
210
+ return NULL;
211
+ }
212
+ b->kind = AGOO_CON_HTTPS;
213
+ b->read = NULL;
214
+ b->write = NULL;
215
+ b->events = NULL;
216
+
217
+ return b;
218
+ }
219
+ AGOO_ERR_MEM(err, "Bind");
220
+
221
+ return b;
175
222
  }
176
223
 
177
224
  agooBind
@@ -197,7 +244,7 @@ agoo_bind_url(agooErr err, const char *url) {
197
244
  if (0 == strncmp("ssl://", url, 6)) {
198
245
  return url_ssl(err, url + 6);
199
246
  }
200
- // All others assume
247
+ // All others assume http
201
248
  {
202
249
  char *colon = index(url, ':');
203
250
  char scheme[8];
@@ -220,9 +267,6 @@ void
220
267
  agoo_bind_destroy(agooBind b) {
221
268
  AGOO_FREE(b->id);
222
269
  AGOO_FREE(b->name);
223
- AGOO_FREE(b->key);
224
- AGOO_FREE(b->cert);
225
- AGOO_FREE(b->ca);
226
270
  AGOO_FREE(b);
227
271
  }
228
272
 
@@ -239,9 +283,9 @@ usual_listen(agooErr err, agooBind b) {
239
283
 
240
284
  return agoo_err_set(err, errno, "Server failed to open server socket. %s.", strerror(errno));
241
285
  }
242
- #ifdef OSX_OS
286
+ #ifdef OSX_OS
243
287
  setsockopt(b->fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
244
- #endif
288
+ #endif
245
289
  setsockopt(b->fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
246
290
  setsockopt(b->fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
247
291
  if (AF_INET6 == b->family) {
@@ -298,20 +342,11 @@ named_listen(agooErr err, agooBind b) {
298
342
  return AGOO_ERR_OK;
299
343
  }
300
344
 
301
- static int
302
- ssl_listen(agooErr err, agooBind b) {
303
- // TBD
304
- return AGOO_ERR_OK;
305
- }
306
-
307
345
  int
308
346
  agoo_bind_listen(agooErr err, agooBind b) {
309
347
  if (NULL != b->name) {
310
348
  return named_listen(err, b);
311
349
  }
312
- if (NULL != b->key) {
313
- return ssl_listen(err, b);
314
- }
315
350
  return usual_listen(err, b);
316
351
  }
317
352
 
@@ -22,16 +22,12 @@ typedef struct _agooBind {
22
22
  struct in_addr addr4;
23
23
  struct in6_addr addr6;
24
24
  };
25
- agooConKind kind;
26
25
  bool (*read)(struct _agooCon *c);
27
26
  bool (*write)(struct _agooCon *c);
28
27
  short (*events)(struct _agooCon *c);
29
- char scheme[8];
30
28
  char *name; // if set then Unix file
31
- char *key; // if set then SSL
32
- char *cert;
33
- char *ca;
34
29
  char *id;
30
+ agooConKind kind;
35
31
  } *agooBind;
36
32
 
37
33
  extern agooBind agoo_bind_url(agooErr err, const char *url);
@@ -83,6 +83,12 @@ agoo_con_destroy(agooCon c) {
83
83
  agoo_ws_req_close(c);
84
84
  }
85
85
  if (0 < c->sock) {
86
+ #ifdef HAVE_OPENSSL_SSL_H
87
+ if (NULL != c->ssl) {
88
+ SSL_free(c->ssl);
89
+ c->ssl = NULL;
90
+ }
91
+ #endif
86
92
  close(c->sock);
87
93
  c->sock = 0;
88
94
  }
@@ -282,7 +288,7 @@ con_header_read(agooCon c, size_t *mlenp) {
282
288
  }
283
289
  if (agoo_req_cat.on) {
284
290
  *hend = '\0';
285
- agoo_log_cat(&agoo_req_cat, "%llu: %s", (unsigned long long)c->id, c->buf);
291
+ agoo_log_cat(&agoo_req_cat, "%s %llu: %s", agoo_con_kind_str(c->bind->kind), (unsigned long long)c->id, c->buf);
286
292
  *hend = '\r';
287
293
  }
288
294
  for (b = c->buf; ' ' != *b; b++) {
@@ -480,6 +486,18 @@ check_upgrade(agooCon c) {
480
486
  }
481
487
  }
482
488
 
489
+ #ifdef HAVE_OPENSSL_SSL_H
490
+ static void
491
+ con_ssl_error(agooCon c, const char *filename, int line) {
492
+ char buf[224];
493
+ unsigned long e = ERR_get_error();
494
+
495
+ c->dead = true;
496
+ ERR_error_string_n(e, buf, sizeof(buf));
497
+ agoo_log_cat(&agoo_error_cat, "%s at %s:%d", buf, filename, line);
498
+ }
499
+ #endif
500
+
483
501
  bool
484
502
  agoo_con_http_read(agooCon c) {
485
503
  ssize_t cnt;
@@ -487,10 +505,34 @@ agoo_con_http_read(agooCon c) {
487
505
  if (c->dead || 0 == c->sock || c->closing) {
488
506
  return true;
489
507
  }
490
- if (NULL != c->req) {
491
- cnt = recv(c->sock, c->req->msg + c->bcnt, c->req->mlen - c->bcnt, 0);
508
+ if (AGOO_CON_HTTPS == c->bind->kind) {
509
+ #ifdef HAVE_OPENSSL_SSL_H
510
+ if (NULL != c->req) {
511
+ cnt = SSL_read(c->ssl, c->req->msg + c->bcnt, c->req->mlen - c->bcnt);
512
+ } else {
513
+ cnt = SSL_read(c->ssl, c->buf + c->bcnt, sizeof(c->buf) - c->bcnt - 1);
514
+ }
515
+ if (0 > cnt) {
516
+ unsigned long e = ERR_get_error();
517
+
518
+ if (0 == e) {
519
+ cnt = 0;
520
+ return false;
521
+ } else {
522
+ con_ssl_error(c, __FILE__, __LINE__);
523
+ return true;
524
+ }
525
+ }
526
+ #else
527
+ agoo_log_cat(&agoo_error_cat, "SSL not included in the build.");
528
+ c->dead = true;
529
+ #endif
492
530
  } else {
493
- cnt = recv(c->sock, c->buf + c->bcnt, sizeof(c->buf) - c->bcnt - 1, 0);
531
+ if (NULL != c->req) {
532
+ cnt = recv(c->sock, c->req->msg + c->bcnt, c->req->mlen - c->bcnt, 0);
533
+ } else {
534
+ cnt = recv(c->sock, c->buf + c->bcnt, sizeof(c->buf) - c->bcnt - 1, 0);
535
+ }
494
536
  }
495
537
  c->timeout = dtime() + CON_TIMEOUT;
496
538
  if (0 >= cnt) {
@@ -544,7 +586,7 @@ agoo_con_http_read(agooCon c) {
544
586
  long mlen;
545
587
 
546
588
  if (agoo_debug_cat.on && NULL != c->req && NULL != c->req->body.start) {
547
- agoo_log_cat(&agoo_debug_cat, "request on %llu: %s", (unsigned long long)c->id, c->req->body.start);
589
+ agoo_log_cat(&agoo_debug_cat, "%s request on %llu: %s", agoo_con_kind_str(c->bind->kind), (unsigned long long)c->id, c->req->body.start);
548
590
  }
549
591
  if (NULL == (res = agoo_res_create(c))) {
550
592
  c->req = NULL;
@@ -584,6 +626,79 @@ agoo_con_http_read(agooCon c) {
584
626
  return false;
585
627
  }
586
628
 
629
+ // return false to remove/close connection
630
+ bool
631
+ agoo_con_http_write(agooCon c) {
632
+ agooRes res = agoo_con_res_pop(c);
633
+ agooText message = agoo_res_message_peek(res);
634
+ ssize_t cnt;
635
+
636
+ if (NULL == message) {
637
+ return true;
638
+ }
639
+ c->timeout = dtime() + CON_TIMEOUT;
640
+ if (0 == c->wcnt) {
641
+ if (agoo_resp_cat.on) {
642
+ char buf[4096];
643
+ char *hend = strstr(message->text, "\r\n\r\n");
644
+
645
+ if (NULL == hend) {
646
+ hend = message->text + message->len;
647
+ }
648
+ if ((long)sizeof(buf) <= hend - message->text) {
649
+ hend = message->text + sizeof(buf) - 1;
650
+ }
651
+ memcpy(buf, message->text, hend - message->text);
652
+ buf[hend - message->text] = '\0';
653
+ agoo_log_cat(&agoo_resp_cat, "%s %llu: %s", agoo_con_kind_str(c->bind->kind), (unsigned long long)c->id, buf);
654
+ }
655
+ if (agoo_debug_cat.on) {
656
+ agoo_log_cat(&agoo_debug_cat, "%s response on %llu: %s", agoo_con_kind_str(c->bind->kind), (unsigned long long)c->id, message->text);
657
+ }
658
+ }
659
+ if (AGOO_CON_HTTPS == c->bind->kind) {
660
+ #ifdef HAVE_OPENSSL_SSL_H
661
+ if (0 >= (cnt = SSL_write(c->ssl, message->text + c->wcnt, message->len - c->wcnt))) {
662
+ unsigned long e = ERR_get_error();
663
+
664
+ if (0 == e) {
665
+ return true;
666
+ }
667
+ con_ssl_error(c, __FILE__, __LINE__);
668
+ return false;
669
+ }
670
+ #else
671
+ agoo_log_cat(&agoo_error_cat, "SSL not included in the build.");
672
+ c->dead = true;
673
+ #endif
674
+ } else {
675
+ if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, MSG_DONTWAIT))) {
676
+ if (EAGAIN == errno) {
677
+ return true;
678
+ }
679
+ agoo_log_cat(&agoo_error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
680
+
681
+ return false;
682
+ }
683
+ }
684
+ c->wcnt += cnt;
685
+ if (c->wcnt == message->len) { // finished
686
+ agooText next = agoo_res_message_next(res);
687
+
688
+ c->wcnt = 0;
689
+ if (NULL == next && res->final) {
690
+ bool done = res->close;
691
+
692
+ agoo_res_destroy(res);
693
+
694
+ return !done;
695
+ }
696
+ }
697
+ agoo_con_res_prepend(c, res);
698
+
699
+ return true;
700
+ }
701
+
587
702
  static bool
588
703
  con_ws_read(agooCon c) {
589
704
  ssize_t cnt;
@@ -692,62 +807,6 @@ con_ws_read(agooCon c) {
692
807
  return false;
693
808
  }
694
809
 
695
- // return false to remove/close connection
696
- bool
697
- agoo_con_http_write(agooCon c) {
698
- agooRes res = agoo_con_res_pop(c);
699
- agooText message = agoo_res_message_peek(res);
700
- ssize_t cnt;
701
-
702
- if (NULL == message) {
703
- return true;
704
- }
705
- c->timeout = dtime() + CON_TIMEOUT;
706
- if (0 == c->wcnt) {
707
- if (agoo_resp_cat.on) {
708
- char buf[4096];
709
- char *hend = strstr(message->text, "\r\n\r\n");
710
-
711
- if (NULL == hend) {
712
- hend = message->text + message->len;
713
- }
714
- if ((long)sizeof(buf) <= hend - message->text) {
715
- hend = message->text + sizeof(buf) - 1;
716
- }
717
- memcpy(buf, message->text, hend - message->text);
718
- buf[hend - message->text] = '\0';
719
- agoo_log_cat(&agoo_resp_cat, "%llu: %s", (unsigned long long)c->id, buf);
720
- }
721
- if (agoo_debug_cat.on) {
722
- agoo_log_cat(&agoo_debug_cat, "response on %llu: %s", (unsigned long long)c->id, message->text);
723
- }
724
- }
725
- if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, MSG_DONTWAIT))) {
726
- if (EAGAIN == errno) {
727
- return true;
728
- }
729
- agoo_log_cat(&agoo_error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
730
-
731
- return false;
732
- }
733
- c->wcnt += cnt;
734
- if (c->wcnt == message->len) { // finished
735
- agooText next = agoo_res_message_next(res);
736
-
737
- c->wcnt = 0;
738
- if (NULL == next && res->final) {
739
- bool done = res->close;
740
-
741
- agoo_res_destroy(res);
742
-
743
- return !done;
744
- }
745
- }
746
- agoo_con_res_prepend(c, res);
747
-
748
- return true;
749
- }
750
-
751
810
  static const char ping_msg[] = "\x89\x00";
752
811
  static const char pong_msg[] = "\x8a\x00";
753
812
 
@@ -1171,6 +1230,24 @@ queue_ready_io(void *ctx) {
1171
1230
  return AGOO_READY_IN;
1172
1231
  }
1173
1232
 
1233
+ static void
1234
+ con_ssl_setup(agooCon c) {
1235
+ #ifdef HAVE_OPENSSL_SSL_H
1236
+ if (NULL == (c->ssl = SSL_new(agoo_server.ssl_ctx))) {
1237
+ con_ssl_error(c, __FILE__, __LINE__);
1238
+ }
1239
+ if (!SSL_set_fd(c->ssl, c->sock)) {
1240
+ con_ssl_error(c, __FILE__, __LINE__);
1241
+ }
1242
+ if (!SSL_accept(c->ssl)) {
1243
+ con_ssl_error(c, __FILE__, __LINE__);
1244
+ }
1245
+ #else
1246
+ agoo_log_cat(&agoo_error_cat, "SSL not included in the build.");
1247
+ c->dead = true;
1248
+ #endif
1249
+ }
1250
+
1174
1251
  static bool
1175
1252
  con_queue_ready_read(agooReady ready, void *ctx) {
1176
1253
  agooConLoop loop = (agooConLoop)ctx;
@@ -1184,6 +1261,9 @@ con_queue_ready_read(agooReady ready, void *ctx) {
1184
1261
  agoo_log_cat(&agoo_error_cat, "Failed to add connection to manager. %s", err.msg);
1185
1262
  agoo_err_clear(&err);
1186
1263
  }
1264
+ if (AGOO_CON_HTTPS == c->bind->kind) {
1265
+ con_ssl_setup(c);
1266
+ }
1187
1267
  }
1188
1268
  return true;
1189
1269
  }
@@ -1249,6 +1329,9 @@ agoo_con_loop(void *x) {
1249
1329
  agoo_log_cat(&agoo_error_cat, "Failed to add connection to manager. %s", err.msg);
1250
1330
  agoo_err_clear(&err);
1251
1331
  }
1332
+ if (AGOO_CON_HTTPS == c->bind->kind) {
1333
+ con_ssl_setup(c);
1334
+ }
1252
1335
  }
1253
1336
  while (NULL != (pub = (agooPub)agoo_queue_pop(&loop->pub_queue, 0.0))) {
1254
1337
  process_pub_con(pub, loop);
@@ -7,6 +7,9 @@
7
7
  #include <pthread.h>
8
8
  #include <stdbool.h>
9
9
  #include <stdint.h>
10
+ #ifdef HAVE_OPENSSL_SSL_H
11
+ #include <openssl/ssl.h>
12
+ #endif
10
13
 
11
14
  #include "err.h"
12
15
  #include "req.h"
@@ -59,6 +62,9 @@ typedef struct _agooCon {
59
62
 
60
63
  struct _agooUpgraded *up; // only set for push connections
61
64
  struct _gqlSub *gsub; // for graphql subscription
65
+ #ifdef HAVE_OPENSSL_SSL_H
66
+ SSL *ssl;
67
+ #endif
62
68
  agooConLoop loop;
63
69
  } *agooCon;
64
70
 
@@ -283,7 +283,6 @@ read_number(agooErr err, agooDoc doc) {
283
283
  for (; '0' <= *doc->cur && *doc->cur <= '9'; doc->cur++) {
284
284
  exp = exp * 10 + (*doc->cur - '0');
285
285
  if (EXP_MAX <= exp) {
286
- // TBD maybe just ignore extra and continue till the end
287
286
  agoo_doc_err(doc, err, "number has too many digits");
288
287
  return NULL;
289
288
  }
@@ -115,7 +115,6 @@ agoo_domain_resolve(const char *host, char *buf, size_t blen) {
115
115
  *b++ = *m++;
116
116
  *b = '\0';
117
117
  }
118
- // TBD
119
118
  } else {
120
119
  *b++ = *p;
121
120
  }
@@ -25,7 +25,7 @@ agoo_err_set(agooErr err, int code, const char *fmt, ...) {
25
25
  int
26
26
  agoo_err_no(agooErr err, const char *fmt, ...) {
27
27
  int cnt = 0;
28
-
28
+
29
29
  if (NULL != fmt) {
30
30
  va_list ap;
31
31
 
@@ -53,7 +53,7 @@ agoo_err_clear(agooErr err) {
53
53
  const char*
54
54
  agoo_err_str(agooErrCode code) {
55
55
  const char *str = NULL;
56
-
56
+
57
57
  if (code < AGOO_ERR_START) {
58
58
  str = strerror(code);
59
59
  }
@@ -72,6 +72,7 @@ agoo_err_str(agooErrCode code) {
72
72
  case AGOO_ERR_TOO_MANY: str = "too many"; break;
73
73
  case AGOO_ERR_TYPE: str = "type error"; break;
74
74
  case AGOO_ERR_EVAL: str = "eval error"; break;
75
+ case AGOO_ERR_TLS: str = "TLS error"; break;
75
76
  default: str = "unknown error"; break;
76
77
  }
77
78
  }
@@ -82,5 +83,3 @@ int
82
83
  agoo_err_memory(agooErr err, const char *type, const char *file, int line) {
83
84
  return agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a %s at %s:%d.", type, file, line);
84
85
  }
85
-
86
-
@@ -29,6 +29,7 @@ typedef enum {
29
29
  AGOO_ERR_TOO_MANY,
30
30
  AGOO_ERR_TYPE,
31
31
  AGOO_ERR_EVAL,
32
+ AGOO_ERR_TLS,
32
33
  AGOO_ERR_LAST
33
34
  } agooErrCode;
34
35
 
@@ -3,15 +3,22 @@ require 'rbconfig'
3
3
 
4
4
  extension_name = 'agoo'
5
5
  dir_config(extension_name)
6
+ dir_config('openssl')
6
7
 
7
- $CFLAGS += " -DPLATFORM_LINUX" if 'x86_64-linux' == RUBY_PLATFORM
8
+ if 'x86_64-linux' == RUBY_PLATFORM
9
+ $CFLAGS += " -DPLATFORM_LINUX -std=gnu11"
10
+ else
11
+ $CFLAGS += ' -std=c11'
12
+ end
8
13
 
9
14
  # Adding the __attribute__ flag only works with gcc compilers and even then it
10
- # does not work to check args with varargs s just remove the check.
15
+ # does not work to check args with varargs so just remove the check.
11
16
  CONFIG['warnflags'].slice!(/ -Wsuggest-attribute=format/)
12
17
 
13
18
  have_header('stdatomic.h')
14
19
  #have_header('sys/epoll.h')
20
+ have_header('openssl/ssl.h')
21
+ have_library('ssl')
15
22
 
16
23
  create_makefile(File.join(extension_name, extension_name))
17
24
 
@@ -39,7 +39,6 @@ static const char variables_str[] = "variables";
39
39
 
40
40
  gqlValue (*gql_doc_eval_func)(agooErr err, gqlDoc doc) = NULL;
41
41
 
42
- // TBD errors should have message, location, and path
43
42
  static void
44
43
  err_resp(agooRes res, agooErr err, int status) {
45
44
  char buf[1024];
@@ -540,7 +539,7 @@ eval_post(agooErr err, agooReq req) {
540
539
  for (link = m->value->members; NULL != link; link = link->next) {
541
540
  gqlVar v = gql_op_var_create(err, link->key, link->value->type, link->value);
542
541
 
543
- link->value = NULL; // TBD is this correct?
542
+ link->value = NULL;
544
543
  if (NULL == v) {
545
544
  goto DONE;
546
545
  }
@@ -0,0 +1,16 @@
1
+ // Copyright (c) 2019, Peter Ohler, All rights reserved.
2
+
3
+ #include "kinds.h"
4
+
5
+ const char*
6
+ agoo_con_kind_str(agooConKind kind) {
7
+ switch (kind) {
8
+ case AGOO_CON_ANY: return "ANY";
9
+ case AGOO_CON_HTTP: return "HTTP";
10
+ case AGOO_CON_HTTPS: return "HTTPS";
11
+ case AGOO_CON_WS: return "WS";
12
+ case AGOO_CON_SSE: return "SSE";
13
+ default: break;
14
+ }
15
+ return "UNKNOWN";
16
+ }
@@ -6,8 +6,11 @@
6
6
  typedef enum {
7
7
  AGOO_CON_ANY = '\0',
8
8
  AGOO_CON_HTTP = 'H',
9
+ AGOO_CON_HTTPS = 'T',
9
10
  AGOO_CON_WS = 'W',
10
11
  AGOO_CON_SSE = 'S',
11
12
  } agooConKind;
12
13
 
14
+ extern const char* agoo_con_kind_str(agooConKind kind);
15
+
13
16
  #endif // AGOO_KINDS_H
@@ -23,6 +23,7 @@ static VALUE empty_val = Qundef;
23
23
  static VALUE get_val = Qundef;
24
24
  static VALUE head_val = Qundef;
25
25
  static VALUE http_val = Qundef;
26
+ static VALUE https_val = Qundef;
26
27
  static VALUE options_val = Qundef;
27
28
  static VALUE patch_val = Qundef;
28
29
  static VALUE path_info_val = Qundef;
@@ -262,7 +263,9 @@ rack_version(VALUE self) {
262
263
 
263
264
  static VALUE
264
265
  req_rack_url_scheme(agooReq r) {
265
- // TBD http or https when ssl is supported
266
+ if (AGOO_CON_HTTPS == r->res->con->bind->kind) {
267
+ return https_val;
268
+ }
266
269
  return http_val;
267
270
  }
268
271
 
@@ -691,6 +694,7 @@ request_init(VALUE mod) {
691
694
  get_val = rb_str_new_cstr("GET"); rb_gc_register_address(&get_val);
692
695
  head_val = rb_str_new_cstr("HEAD"); rb_gc_register_address(&head_val);
693
696
  http_val = rb_str_new_cstr("http"); rb_gc_register_address(&http_val);
697
+ https_val = rb_str_new_cstr("https"); rb_gc_register_address(&https_val);
694
698
  options_val = rb_str_new_cstr("OPTIONS"); rb_gc_register_address(&options_val);
695
699
  patch_val = rb_str_new_cstr("PATCH"); rb_gc_register_address(&patch_val);
696
700
  path_info_val = rb_str_new_cstr("PATH_INFO"); rb_gc_register_address(&path_info_val);
@@ -160,6 +160,21 @@ configure(agooErr err, int port, const char *root, VALUE options) {
160
160
  }
161
161
  }
162
162
  }
163
+ if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("ssl_cert"))))) {
164
+ rb_check_type(v, T_STRING);
165
+ const char *cert =StringValuePtr(v);
166
+
167
+ if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("ssl_key"))))) {
168
+ rb_check_type(v, T_STRING);
169
+ const char *key =StringValuePtr(v);
170
+
171
+ if (AGOO_ERR_OK != agoo_server_ssl_init(err, cert, key)) {
172
+ rb_raise(rb_eArgError, "%s", err->msg);
173
+ }
174
+ } else {
175
+ rb_raise(rb_eArgError, "An ssl_key must be provided if an ssl_cert was provided.");
176
+ }
177
+ }
163
178
  if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("bind"))))) {
164
179
  int len;
165
180
  int i;
@@ -258,6 +273,12 @@ configure(agooErr err, int port, const char *root, VALUE options) {
258
273
  * - *:bind* [_String_|_Array_] a binding or array of binds. Examples are: "http ://127.0.0.1:6464", "unix:///tmp/agoo.socket", "http ://[::1]:6464, or to not restrict the address "http ://:6464".
259
274
  *
260
275
  * - *:graphql* [_String_] path to GraphQL endpoint if support for GraphQL is desired.
276
+ *
277
+ * - *:max_push_pending* [_Integer_] maximum number or outstanding push messages, less than 1000.
278
+ *
279
+ * - *:ssl_sert* [_String_] filepath to the SSL certificate file.
280
+ *
281
+ * - *:ssl_key* [_String_] filepath to the SSL private key file.
261
282
  */
262
283
  static VALUE
263
284
  rserver_init(int argc, VALUE *argv, VALUE self) {
@@ -34,8 +34,6 @@ double agoo_io_loop_ratio = 0.5;
34
34
 
35
35
  int
36
36
  agoo_server_setup(agooErr err) {
37
- long i;
38
-
39
37
  memset(&agoo_server, 0, sizeof(struct _agooServer));
40
38
  pthread_mutex_init(&agoo_server.up_lock, 0);
41
39
  agoo_server.up_list = NULL;
@@ -47,6 +45,8 @@ agoo_server_setup(agooErr err) {
47
45
  AGOO_ERR_OK != agoo_queue_multi_init(err, &agoo_server.eval_queue, 1024, true, true)) {
48
46
  return err->code;
49
47
  }
48
+ long i;
49
+
50
50
  agoo_server.loop_max = 4;
51
51
  if (0 < (i = sysconf(_SC_NPROCESSORS_ONLN))) {
52
52
  i = (int)(i * agoo_io_loop_ratio);
@@ -58,6 +58,41 @@ agoo_server_setup(agooErr err) {
58
58
  return AGOO_ERR_OK;
59
59
  }
60
60
 
61
+ #ifdef HAVE_OPENSSL_SSL_H
62
+ static int
63
+ ssl_error(agooErr err, const char *filename, int line) {
64
+ char buf[224];
65
+ unsigned long e = ERR_get_error();
66
+
67
+ ERR_error_string_n(e, buf, sizeof(buf));
68
+
69
+ return agoo_err_set(err, AGOO_ERR_TLS, "%s at %s:%d", buf, filename, line);
70
+ }
71
+ #endif
72
+
73
+ int
74
+ agoo_server_ssl_init(agooErr err, const char *cert_pem, const char *key_pem) {
75
+ #ifdef HAVE_OPENSSL_SSL_H
76
+ SSL_load_error_strings();
77
+ SSL_library_init();
78
+ if (NULL == (agoo_server.ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) {
79
+ return ssl_error(err, __FILE__, __LINE__);
80
+ }
81
+ SSL_CTX_set_ecdh_auto(agoo_server.ssl_ctx, 1);
82
+
83
+ if (!SSL_CTX_use_certificate_file(agoo_server.ssl_ctx, cert_pem, SSL_FILETYPE_PEM)) {
84
+ return ssl_error(err, __FILE__, __LINE__);
85
+ }
86
+ if (!SSL_CTX_use_PrivateKey_file(agoo_server.ssl_ctx, key_pem, SSL_FILETYPE_PEM)) {
87
+ return ssl_error(err, __FILE__, __LINE__);
88
+ }
89
+ if (!SSL_CTX_check_private_key(agoo_server.ssl_ctx)) {
90
+ return agoo_err_set(err, AGOO_ERR_TLS, "TLS private key check failed");
91
+ }
92
+ #endif
93
+ return AGOO_ERR_OK;
94
+ }
95
+
61
96
  static void
62
97
  add_con_loop() {
63
98
  struct _agooErr err = AGOO_ERR_INIT;
@@ -255,6 +290,12 @@ agoo_server_shutdown(const char *app_name, void (*stop)()) {
255
290
  agoo_pages_cleanup();
256
291
  agoo_http_cleanup();
257
292
  agoo_domain_cleanup();
293
+ #ifdef HAVE_OPENSSL_SSL_H
294
+ if (NULL != agoo_server.ssl_ctx) {
295
+ SSL_CTX_free(agoo_server.ssl_ctx);
296
+ EVP_cleanup();
297
+ }
298
+ #endif
258
299
  }
259
300
  }
260
301
 
@@ -6,6 +6,12 @@
6
6
  #include <pthread.h>
7
7
  #include <stdbool.h>
8
8
 
9
+ #ifdef HAVE_OPENSSL_SSL_H
10
+ #include <openssl/bio.h>
11
+ #include <openssl/ssl.h>
12
+ #include <openssl/err.h>
13
+ #endif
14
+
9
15
  #include "atomic.h"
10
16
  #include "bind.h"
11
17
  #include "err.h"
@@ -28,6 +34,7 @@ typedef struct _agooServer {
28
34
  bool pedantic;
29
35
  bool root_first;
30
36
  bool rack_early_hints;
37
+ bool tls;
31
38
  pthread_t listen_thread;
32
39
  struct _agooQueue con_queue;
33
40
  agooHook hooks;
@@ -48,6 +55,9 @@ typedef struct _agooServer {
48
55
  void *env_nil_value;
49
56
  void *ctx_nil_value;
50
57
 
58
+ #ifdef HAVE_OPENSSL_SSL_H
59
+ SSL_CTX *ssl_ctx;
60
+ #endif
51
61
  // A count of the running threads from the wrapper or the server managed
52
62
  // threads.
53
63
  atomic_int running;
@@ -56,6 +66,7 @@ typedef struct _agooServer {
56
66
  extern int agoo_server_setup(agooErr err);
57
67
  extern void agoo_server_shutdown(const char *app_name, void (*stop)());
58
68
  extern void agoo_server_bind(agooBind b);
69
+ extern int agoo_server_ssl_init(agooErr err, const char *cert_pem, const char *key_pem);
59
70
 
60
71
  extern int setup_listen(agooErr err);
61
72
  extern int agoo_server_start(agooErr err, const char *app_name, const char *version);
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.10.0'
4
+ VERSION = '2.11.0'
5
5
  end
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.10.0
4
+ version: 2.11.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: 2019-08-29 00:00:00.000000000 Z
11
+ date: 2019-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -88,6 +88,7 @@ files:
88
88
  - ext/agoo/hook.h
89
89
  - ext/agoo/http.c
90
90
  - ext/agoo/http.h
91
+ - ext/agoo/kinds.c
91
92
  - ext/agoo/kinds.h
92
93
  - ext/agoo/log.c
93
94
  - ext/agoo/log.h