agoo 2.1.3 → 2.2.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: e352cb4e13de1ea7e2bde18b69084458a22061b34d2e2876a958e6580719e9f5
4
- data.tar.gz: fe95bb555e84f531835cf6964266926ee57e585b2aeb1839df3dcf448f08e1b2
3
+ metadata.gz: 2aebba91a863e1bdbaa53c1d31560728968f7d2e27c27af21691bf8d502f4afb
4
+ data.tar.gz: e0b3efd1eaa9dbe2629738c3b8ecdc0e61e208ae94f54598a182791c23d4873c
5
5
  SHA512:
6
- metadata.gz: 59e7282f5603b4849ab7e6d5b007099e3533421cb00a305ee89c7709dee1ac75dea40d559adf906800cba06aa23acfb4ce3847d6200fb9f67ddd2f800de6aaf1
7
- data.tar.gz: 5133a7c384c40a87e2a4578a25753bd0fbd8f7011e1214a1f52ff71f9573423bc39a1aa53cbd5d63fd0ce2fe0b7b9147f5c22d0bf6738516d881e7617fb0e474
6
+ metadata.gz: 82944a0b58ea473b2b4228c3d59901b29fdae19ef3ad1711a84980d7d2028418abaeb9f05b93d4d7f8649e0ceeb3bebaaaa5d448ac48dcbc647cdeeaf494a169
7
+ data.tar.gz: 84894610f09ea3a7c089cc11e4edc3479bf641dda306f7e9dbf89fc88e4c0c8018d0d84a8e7df98c6296215f9c4a26cacc6b3c593e63b869308177f820411cee
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### 2.2.0 - 2018-05-30
4
+
5
+ - Clustering now supported with forked workers making Agoo even faster.
6
+
7
+ - Changed addition header keys to be all uppercase and also replace `-` with `_` to match Rack unit tests instead of the spec.
8
+
3
9
  ### 2.1.3 - 2018-05-16
4
10
 
5
11
  - Optimized `rackup` to look for files in the root directory before calling rackup as the default. The root is now set to `./public` instead of `.`. The `:rmux` (rack multiplex) turns that off.
data/README.md CHANGED
@@ -52,13 +52,17 @@ use of rack compatible gems such as Hanami and Rails. Agoo also supports WebSock
52
52
 
53
53
  ## News
54
54
 
55
+ - Clustered Agoo is ready. For slower application and a machine with multiple
56
+ cores a significant improvement is performance is realized. The application
57
+ must be stateless in that no data is shared between workers.
58
+
55
59
  - WebSocket and SSE are supported and a PR has been submitted to updated the
56
60
  Rack spec. Go over to the [proposed Rack
57
61
  extension](https://github.com/rack/rack/pull/1272) and give it a look and a
58
62
  thumbs-up or heart if you like it.
59
63
 
60
- - Agoo now serves Rails static assets more than 2000 times faster than the
61
- default Puma. Thats right, [2000 times faster](misc/rails.md).
64
+ - Agoo now serves Rails static assets more than 8000 times faster than the
65
+ default Puma. Thats right, [8000 times faster](misc/rails.md).
62
66
 
63
67
  ## Releases
64
68
 
@@ -168,7 +168,6 @@ con_header_read(Con c) {
168
168
  Hook hook = NULL;
169
169
  Page p;
170
170
  struct _Err err = ERR_INIT;
171
- Res res;
172
171
 
173
172
  if (NULL == hend) {
174
173
  if (sizeof(c->buf) - 1 <= c->bcnt) {
@@ -25,6 +25,10 @@ static const char log_name[] = "agoo.log";
25
25
  static const char log_prefix[] = "agoo.log.";
26
26
  static const char log_format[] = "%s/agoo.log.%d";
27
27
 
28
+ static const char log_pid_name[] = "agoo.log_%d";
29
+ static const char log_pid_prefix[] = "agoo.log_%d.";
30
+ static const char log_pid_format[] = "%s/agoo.log_%d.%d";
31
+
28
32
  static struct _Color colors[] = {
29
33
  { .name = "black", .ansi = "\033[30;1m" },
30
34
  { .name = "red", .ansi = "\033[31;1m" },
@@ -174,19 +178,30 @@ remove_old_logs() {
174
178
  char *end;
175
179
  char path[1024];
176
180
  DIR *dir = opendir(the_log.dir);
177
-
181
+ char prefix[32];
182
+ int psize;
183
+ char name[32];
184
+
185
+ if (the_log.with_pid) {
186
+ psize = sprintf(prefix, log_pid_prefix, getpid());
187
+ sprintf(name, log_pid_name, getpid());
188
+ } else {
189
+ memcpy(prefix, log_prefix, sizeof(log_prefix));
190
+ psize = (int)sizeof(log_prefix) - 1;
191
+ memcpy(name, log_name, sizeof(log_name));
192
+ }
178
193
  while (NULL != (de = readdir(dir))) {
179
194
  if ('.' == *de->d_name || '\0' == *de->d_name) {
180
195
  continue;
181
196
  }
182
- if (0 != strncmp(log_prefix, de->d_name, sizeof(log_prefix) - 1)) {
197
+ if (0 != strncmp(prefix, de->d_name, psize)) {
183
198
  continue;
184
199
  }
185
200
  // Don't remove the primary log file.
186
- if (0 == strcmp(log_name, de->d_name)) {
201
+ if (0 == strcmp(name, de->d_name)) {
187
202
  continue;
188
203
  }
189
- seq = strtol(de->d_name + sizeof(log_prefix) - 1, &end, 10);
204
+ seq = strtol(de->d_name + psize, &end, 10);
190
205
  if (the_log.max_files < seq) {
191
206
  snprintf(path, sizeof(path), "%s/%s", the_log.dir, de->d_name);
192
207
  remove(path);
@@ -204,13 +219,26 @@ log_rotate() {
204
219
  fclose(the_log.file);
205
220
  the_log.file = NULL;
206
221
  }
207
- for (int seq = the_log.max_files; 0 < seq; seq--) {
208
- snprintf(to, sizeof(to), log_format, the_log.dir, seq + 1);
209
- snprintf(from, sizeof(from), log_format, the_log.dir, seq);
210
- rename(from, to);
222
+ if (the_log.with_pid) {
223
+ char name[32];
224
+
225
+ sprintf(name, log_pid_name, getpid());
226
+ for (int seq = the_log.max_files; 0 < seq; seq--) {
227
+ snprintf(to, sizeof(to), log_pid_format, the_log.dir, getpid(), seq + 1);
228
+ snprintf(from, sizeof(from), log_pid_format, the_log.dir, getpid(), seq);
229
+ rename(from, to);
230
+ }
231
+ snprintf(to, sizeof(to), log_pid_format, the_log.dir, getpid(), 1);
232
+ snprintf(from, sizeof(from), "%s/%s", the_log.dir, name);
233
+ } else {
234
+ for (int seq = the_log.max_files; 0 < seq; seq--) {
235
+ snprintf(to, sizeof(to), log_format, the_log.dir, seq + 1);
236
+ snprintf(from, sizeof(from), log_format, the_log.dir, seq);
237
+ rename(from, to);
238
+ }
239
+ snprintf(to, sizeof(to), log_format, the_log.dir, 1);
240
+ snprintf(from, sizeof(from), "%s/%s", the_log.dir, log_name);
211
241
  }
212
- snprintf(to, sizeof(to), log_format, the_log.dir, 1);
213
- snprintf(from, sizeof(from), "%s/%s", the_log.dir, log_name);
214
242
  rename(from, to);
215
243
 
216
244
  the_log.file = fopen(from, "w");
@@ -272,8 +300,11 @@ static void
272
300
  open_log_file() {
273
301
  char path[1024];
274
302
 
275
- snprintf(path, sizeof(path), "%s/%s", the_log.dir, log_name);
276
-
303
+ if (the_log.with_pid) {
304
+ snprintf(path, sizeof(path), "%s/%s_%d", the_log.dir, log_name, getpid());
305
+ } else {
306
+ snprintf(path, sizeof(path), "%s/%s", the_log.dir, log_name);
307
+ }
277
308
  the_log.file = fopen(path, "a");
278
309
  if (NULL == the_log.file) {
279
310
  rb_raise(rb_eIOError, "Failed to create '%s'.", path);
@@ -867,6 +898,7 @@ log_init(VALUE mod) {
867
898
  the_log.console = true;
868
899
  the_log.classic = true;
869
900
  the_log.colorize = true;
901
+ the_log.with_pid = false;
870
902
  the_log.zone = (int)(timegm(tm) - t);
871
903
  the_log.day_start = 0;
872
904
  the_log.day_end = 0;
@@ -897,5 +929,21 @@ log_init(VALUE mod) {
897
929
  log_cat_reg(&eval_cat, "eval", INFO, BLUE, false);
898
930
  log_cat_reg(&push_cat, "push", INFO, DARK_CYAN, false);
899
931
 
932
+ log_start(false);
933
+ }
934
+
935
+ void
936
+ log_start(bool with_pid) {
937
+ if (NULL != the_log.file) {
938
+ fclose(the_log.file);
939
+ the_log.file = NULL;
940
+ }
941
+ the_log.with_pid = with_pid;
942
+ if (with_pid && '\0' != *the_log.dir) {
943
+ if (0 != mkdir(the_log.dir, 0770) && EEXIST != errno) {
944
+ rb_raise(rb_eIOError, "Failed to create '%s'.", the_log.dir);
945
+ }
946
+ open_log_file();
947
+ }
900
948
  pthread_create(&the_log.thread, NULL, loop, log);
901
949
  }
@@ -75,6 +75,7 @@ struct _Log {
75
75
  bool console; // if true print log message to stdout
76
76
  bool classic; // classic in stdout
77
77
  bool colorize; // color in stdout
78
+ bool with_pid;
78
79
  int zone; // timezone offset from GMT in seconds
79
80
  int64_t day_start;
80
81
  int64_t day_end;
@@ -116,4 +117,6 @@ extern LogCat log_cat_find(const char *label);
116
117
  extern void log_cat(LogCat cat, const char *fmt, ...);
117
118
  extern void log_catv(LogCat cat, const char *fmt, va_list ap);
118
119
 
120
+ extern void log_start(bool with_pid);
121
+
119
122
  #endif /* __AGOO_LOG_H__ */
@@ -467,7 +467,6 @@ group_get(Err err, Cache cache, const char *path, int plen) {
467
467
  char full_path[2048];
468
468
  char *s;
469
469
  Dir d;
470
- double now;
471
470
 
472
471
  if (NULL != strstr(path, "../")) {
473
472
  return NULL;
@@ -398,7 +398,11 @@ add_header_value(VALUE hh, const char *key, int klen, const char *val, int vlen)
398
398
  rb_hash_aset(hh, rb_str_new(hkey, klen + 5), sval);
399
399
  // Contrary to the Rack spec, Rails expects all upper case keys so add those as well.
400
400
  for (k = hkey + 5; '\0' != *k; k++) {
401
- *k = toupper(*k);
401
+ if ('-' == *k) {
402
+ *k = '_';
403
+ } else {
404
+ *k = toupper(*k);
405
+ }
402
406
  }
403
407
  rb_hash_aset(hh, rb_str_new(hkey, klen + 5), sval);
404
408
  }
@@ -6,6 +6,7 @@
6
6
  #include <netdb.h>
7
7
  #include <netinet/tcp.h>
8
8
  #include <poll.h>
9
+ #include <signal.h>
9
10
  #include <stdarg.h>
10
11
  #include <stdio.h>
11
12
  #include <stdlib.h>
@@ -13,6 +14,8 @@
13
14
  #include <sys/select.h>
14
15
  #include <sys/socket.h>
15
16
  #include <sys/time.h>
17
+ #include <sys/types.h>
18
+ #include <sys/wait.h>
16
19
  #include <unistd.h>
17
20
  #include <stdatomic.h>
18
21
 
@@ -81,17 +84,19 @@ server_mark(void *ptr) {
81
84
  void
82
85
  server_shutdown() {
83
86
  if (the_server.inited) {
84
- log_cat(&info_cat, "Agoo shutting down.");
87
+ log_cat(&info_cat, "Agoo with pid %d shutting down.", getpid());
85
88
  the_server.inited = false;
86
89
  if (the_server.active) {
90
+ double giveup = dtime() + 1.0;
91
+
87
92
  the_server.active = false;
88
- if (0 != the_server.listen_thread) {
89
- pthread_join(the_server.listen_thread, NULL);
90
- the_server.listen_thread = 0;
91
- }
92
- if (0 != the_server.con_thread) {
93
- pthread_join(the_server.con_thread, NULL);
94
- the_server.con_thread = 0;
93
+ pthread_detach(the_server.listen_thread);
94
+ pthread_detach(the_server.con_thread);
95
+ while (0 < atomic_load(&the_server.running)) {
96
+ dsleep(0.1);
97
+ if (giveup < dtime()) {
98
+ break;
99
+ }
95
100
  }
96
101
  sub_cleanup(&the_server.sub_cache);
97
102
  // The preferred method to of waiting for the ruby threads would
@@ -123,6 +128,42 @@ server_shutdown() {
123
128
  queue_cleanup(&the_server.eval_queue);
124
129
  pages_cleanup(&the_server.pages);
125
130
  http_cleanup();
131
+
132
+ if (1 < the_server.worker_cnt && getpid() == *the_server.worker_pids) {
133
+ int i;
134
+ int status;
135
+ int exit_cnt = 1;
136
+ int j;
137
+
138
+ for (i = 1; i < the_server.worker_cnt; i++) {
139
+ kill(the_server.worker_pids[i], SIGKILL);
140
+ }
141
+ for (j = 0; j < 20; j++) {
142
+ for (i = 1; i < the_server.worker_cnt; i++) {
143
+ if (0 == the_server.worker_pids[i]) {
144
+ continue;
145
+ }
146
+ if (0 < waitpid(the_server.worker_pids[i], &status, WNOHANG)) {
147
+ if (WIFEXITED(status)) {
148
+ //printf("exited, status=%d for %d\n", the_server.worker_pids[i], WEXITSTATUS(status));
149
+ the_server.worker_pids[i] = 0;
150
+ exit_cnt++;
151
+ } else if (WIFSIGNALED(status)) {
152
+ //printf("*** killed by signal %d for %d\n", the_server.worker_pids[i], WTERMSIG(status));
153
+ the_server.worker_pids[i] = 0;
154
+ exit_cnt++;
155
+ }
156
+ }
157
+ }
158
+ if (the_server.worker_cnt <= exit_cnt) {
159
+ break;
160
+ }
161
+ dsleep(0.2);
162
+ }
163
+ if (exit_cnt < the_server.worker_cnt) {
164
+ printf("*-*-* Some workers did not exit.\n");
165
+ }
166
+ }
126
167
  }
127
168
  }
128
169
 
@@ -131,7 +172,9 @@ configure(Err err, int port, const char *root, VALUE options) {
131
172
  the_server.port = port;
132
173
  the_server.pages.root = strdup(root);
133
174
  the_server.thread_cnt = 0;
175
+ the_server.worker_cnt = 1;
134
176
  the_server.running = 0;
177
+ the_server.fd = 0;
135
178
  the_server.listen_thread = 0;
136
179
  the_server.con_thread = 0;
137
180
  the_server.max_push_pending = 32;
@@ -148,6 +191,15 @@ configure(Err err, int port, const char *root, VALUE options) {
148
191
  rb_raise(rb_eArgError, "thread_count must be between 1 and 1000.");
149
192
  }
150
193
  }
194
+ if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("worker_count"))))) {
195
+ int wc = FIX2INT(v);
196
+
197
+ if (1 <= wc || wc < MAX_WORKERS) {
198
+ the_server.worker_cnt = wc;
199
+ } else {
200
+ rb_raise(rb_eArgError, "thread_count must be between 1 and %d.", MAX_WORKERS);
201
+ }
202
+ }
151
203
  if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("max_push_pending"))))) {
152
204
  int tc = FIX2INT(v);
153
205
 
@@ -214,6 +266,8 @@ configure(Err err, int port, const char *root, VALUE options) {
214
266
  * - *:pedantic* [_true_|_false_] if true response header and status codes are checked and an exception raised if they violate the rack spec at https://github.com/rack/rack/blob/master/SPEC, https://tools.ietf.org/html/rfc3875#section-4.1.18, or https://tools.ietf.org/html/rfc7230.
215
267
  *
216
268
  * - *:thread_count* [_Integer_] number of ruby worker threads. Defaults to one. If zero then the _start_ function will not return but instead will proess using the thread that called _start_. Usually the default is best unless the workers are making IO calls.
269
+ *
270
+ * - *:worker_count* [_Integer_] number of workers to fork. Defaults to one which is not to fork.
217
271
  */
218
272
  static VALUE
219
273
  rserver_init(int argc, VALUE *argv, VALUE self) {
@@ -250,40 +304,46 @@ rserver_init(int argc, VALUE *argv, VALUE self) {
250
304
  return Qnil;
251
305
  }
252
306
 
253
- static void*
254
- listen_loop(void *x) {
307
+ static void
308
+ setup_listen() {
255
309
  struct sockaddr_in addr;
256
310
  int optval = 1;
257
- struct pollfd pa[1];
258
- struct pollfd *fp = pa;
259
- struct _Err err = ERR_INIT;
260
- struct sockaddr_in client_addr;
261
- int client_sock;
262
- socklen_t alen = 0;
263
- Con con;
264
- int i;
265
- uint64_t cnt = 0;
266
311
 
267
- if (0 >= (pa->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
312
+ if (0 >= (the_server.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
268
313
  log_cat(&error_cat, "Server failed to open server socket. %s.", strerror(errno));
269
314
  atomic_fetch_sub(&the_server.running, 1);
270
- return NULL;
315
+ rb_raise(rb_eIOError, "Server failed to open server socket. %s.", strerror(errno));
271
316
  }
272
317
  #ifdef OSX_OS
273
- setsockopt(pa->fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
318
+ setsockopt(the_server.fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
274
319
  #endif
275
320
  memset(&addr, 0, sizeof(addr));
276
321
  addr.sin_family = AF_INET;
277
322
  addr.sin_addr.s_addr = INADDR_ANY;
278
323
  addr.sin_port = htons(the_server.port);
279
- setsockopt(pa->fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
280
- setsockopt(pa->fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
281
- if (0 > bind(fp->fd, (struct sockaddr*)&addr, sizeof(addr))) {
324
+ setsockopt(the_server.fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
325
+ setsockopt(the_server.fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
326
+ if (0 > bind(the_server.fd, (struct sockaddr*)&addr, sizeof(addr))) {
282
327
  log_cat(&error_cat, "Server failed to bind server socket. %s.", strerror(errno));
283
328
  atomic_fetch_sub(&the_server.running, 1);
284
- return NULL;
329
+ rb_raise(rb_eIOError, "Server failed to bind server socket. %s.", strerror(errno));
285
330
  }
286
- listen(pa->fd, 1000);
331
+ listen(the_server.fd, 1000);
332
+ }
333
+
334
+ static void*
335
+ listen_loop(void *x) {
336
+ int optval = 1;
337
+ struct pollfd pa[1];
338
+ struct _Err err = ERR_INIT;
339
+ struct sockaddr_in client_addr;
340
+ int client_sock;
341
+ socklen_t alen = 0;
342
+ Con con;
343
+ int i;
344
+ uint64_t cnt = 0;
345
+
346
+ pa->fd = the_server.fd;
287
347
  pa->events = POLLIN;
288
348
  pa->revents = 0;
289
349
 
@@ -303,9 +363,9 @@ listen_loop(void *x) {
303
363
  }
304
364
  if (0 != (pa->revents & POLLIN)) {
305
365
  if (0 > (client_sock = accept(pa->fd, (struct sockaddr*)&client_addr, &alen))) {
306
- log_cat(&error_cat, "Server accept connection failed. %s.", strerror(errno));
366
+ log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), strerror(errno));
307
367
  } else if (NULL == (con = con_create(&err, client_sock, ++cnt))) {
308
- log_cat(&error_cat, "Server accept connection failed. %s.", err.msg);
368
+ log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), err.msg);
309
369
  close(client_sock);
310
370
  cnt--;
311
371
  err_clear(&err);
@@ -316,15 +376,16 @@ listen_loop(void *x) {
316
376
  fcntl(client_sock, F_SETFL, O_NONBLOCK);
317
377
  setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
318
378
  setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
319
- log_cat(&con_cat, "Server accepted connection %llu on port %d [%d]", (unsigned long long)cnt, the_server.port, con->sock);
379
+ log_cat(&con_cat, "Server with pid %d accepted connection %llu on port %d [%d]",
380
+ getpid(), (unsigned long long)cnt, the_server.port, con->sock);
320
381
  queue_push(&the_server.con_queue, (void*)con);
321
382
  }
322
383
  }
323
384
  if (0 != (pa->revents & (POLLERR | POLLHUP | POLLNVAL))) {
324
385
  if (0 != (pa->revents & (POLLHUP | POLLNVAL))) {
325
- log_cat(&error_cat, "Agoo server socket on port %d closed.", the_server.port);
386
+ log_cat(&error_cat, "Agoo server with pid %d socket on port %d closed.", getpid(), the_server.port);
326
387
  } else {
327
- log_cat(&error_cat, "Agoo server socket on port %d error.", the_server.port);
388
+ log_cat(&error_cat, "Agoo server with pid %d socket on port %d error.", getpid(), the_server.port);
328
389
  }
329
390
  the_server.active = false;
330
391
  }
@@ -727,9 +788,36 @@ server_start(VALUE self) {
727
788
  VALUE *vp;
728
789
  int i;
729
790
  double giveup;
791
+ int pid;
730
792
 
793
+ *the_server.worker_pids = getpid();
794
+ setup_listen();
731
795
  the_server.active = true;
732
796
 
797
+ for (i = 1; i < the_server.worker_cnt; i++) {
798
+ #if 0
799
+ pid = fork();
800
+ #else
801
+ {
802
+ VALUE rpid = rb_funcall(rb_cObject, rb_intern("fork"), 0);
803
+
804
+ if (Qnil == rpid) {
805
+ pid = 0;
806
+ } else {
807
+ pid = NUM2INT(rpid);
808
+ }
809
+ }
810
+ #endif
811
+ if (0 > pid) { // error, use single process
812
+ log_cat(&error_cat, "Failed to fork. %s.", strerror(errno));
813
+ break;
814
+ } else if (0 == pid) {
815
+ log_start(true);
816
+ break;
817
+ } else {
818
+ the_server.worker_pids[i] = pid;
819
+ }
820
+ }
733
821
  pthread_create(&the_server.listen_thread, NULL, listen_loop, NULL);
734
822
  pthread_create(&the_server.con_thread, NULL, con_loop, NULL);
735
823
 
@@ -744,7 +832,7 @@ server_start(VALUE self) {
744
832
  VALUE agoo = rb_const_get_at(rb_cObject, rb_intern("Agoo"));
745
833
  VALUE v = rb_const_get_at(agoo, rb_intern("VERSION"));
746
834
 
747
- log_cat(&info_cat, "Agoo %s listening on port %d.", StringValuePtr(v), the_server.port);
835
+ log_cat(&info_cat, "Agoo %s with pid %d is listening on port %d.", StringValuePtr(v), getpid(), the_server.port);
748
836
  }
749
837
  if (0 >= the_server.thread_cnt) {
750
838
  Req req;
@@ -16,12 +16,16 @@
16
16
  #include "sub.h"
17
17
  #include "upgraded.h"
18
18
 
19
+ #define MAX_WORKERS 32
20
+
19
21
  typedef struct _Server {
20
22
  volatile bool inited;
21
23
  volatile bool active;
22
24
  int thread_cnt;
25
+ int worker_cnt;
23
26
  int max_push_pending;
24
27
  int port;
28
+ int fd;
25
29
  bool pedantic;
26
30
  bool root_first;
27
31
  atomic_int running;
@@ -40,6 +44,7 @@ typedef struct _Server {
40
44
  Hook hook404;
41
45
  struct _Queue eval_queue;
42
46
 
47
+ int worker_pids[MAX_WORKERS];
43
48
  VALUE *eval_threads; // Qnil terminated
44
49
  } *Server;
45
50
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.1.3'
4
+ VERSION = '2.2.0'
5
5
  end
@@ -10,12 +10,19 @@ module Rack
10
10
  # The Rack::Handler::Agoo module is a handler for common rack config.rb files.
11
11
  module Agoo
12
12
 
13
- # Run the server. Options are the same as for Agoo::Server plus a :port
14
- # and :root option.
13
+ # Run the server. Options are the same as for Agoo::Server plus a :port,
14
+ # :root, :rmux, and :wc option.
15
+ #
16
+ # - *:port [_Integer_] port to listen on
17
+ # - *:root [_String_] root or public directory for static assets
18
+ # - *:rmux [_true_|_false_] if true look in the root directory first before calling Ruby hooks
19
+ # - *:wc* [_Integer_] number of workers to fork. Defaults to one which is not to fork.
20
+ # - */path=MyHandler* path and class name to handle requests on that path
15
21
  def self.run(handler, options={})
16
22
  port = 9292
17
23
  root = './public'
18
24
  root_set = false
25
+ worker_count = 1;
19
26
  default_handler = nil
20
27
  not_found_handler = nil
21
28
  path_map = {}
@@ -30,6 +37,9 @@ module Rack
30
37
  root = v
31
38
  root_set = true
32
39
  options.delete(k)
40
+ elsif :wc == k
41
+ worker_count = v.to_i
42
+ options.delete(k)
33
43
  elsif :rmux == k
34
44
  options[:root_first] = false
35
45
  elsif k.nil?
@@ -44,6 +54,7 @@ module Rack
44
54
  end
45
55
  }
46
56
  options[:thread_count] = 0
57
+ options[:worker_count] = worker_count
47
58
  ::Agoo::Server.init(port, root, options)
48
59
  path_map.each { |path,handler|
49
60
  ::Agoo::Server.handle(nil, path, handler)
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.1.3
4
+ version: 2.2.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: 2018-05-16 00:00:00.000000000 Z
11
+ date: 2018-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -135,12 +135,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - Linux or macOS
137
137
  rubyforge_project: agoo
138
- rubygems_version: 2.7.3
138
+ rubygems_version: 2.7.6
139
139
  signing_key:
140
140
  specification_version: 4
141
141
  summary: An HTTP server
142
142
  test_files:
143
- - test/base_handler_test.rb
144
- - test/log_test.rb
145
143
  - test/rack_handler_test.rb
144
+ - test/base_handler_test.rb
146
145
  - test/static_test.rb
146
+ - test/log_test.rb