agoo 2.1.1 → 2.1.3
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +8 -8
- data/ext/agoo/con.c +49 -29
- data/ext/agoo/page.c +61 -52
- data/ext/agoo/server.c +5 -1
- data/ext/agoo/server.h +1 -0
- data/ext/agoo/upgraded.c +19 -0
- data/lib/agoo/version.rb +1 -1
- data/lib/rack/handler/agoo.rb +8 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e352cb4e13de1ea7e2bde18b69084458a22061b34d2e2876a958e6580719e9f5
|
4
|
+
data.tar.gz: fe95bb555e84f531835cf6964266926ee57e585b2aeb1839df3dcf448f08e1b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59e7282f5603b4849ab7e6d5b007099e3533421cb00a305ee89c7709dee1ac75dea40d559adf906800cba06aa23acfb4ce3847d6200fb9f67ddd2f800de6aaf1
|
7
|
+
data.tar.gz: 5133a7c384c40a87e2a4578a25753bd0fbd8f7011e1214a1f52ff71f9573423bc39a1aa53cbd5d63fd0ce2fe0b7b9147f5c22d0bf6738516d881e7617fb0e474
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
### 2.1.3 - 2018-05-16
|
4
|
+
|
5
|
+
- 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.
|
6
|
+
|
7
|
+
### 2.1.2 - 2018-05-15
|
8
|
+
|
9
|
+
- Added `#open?` method to the Upgraded (connection client) class.
|
10
|
+
|
11
|
+
- Slight improvement performcance serving static assets for Rails.
|
12
|
+
|
3
13
|
### 2.1.1 - 2018-05-11
|
4
14
|
|
5
15
|
- Subject can now be Symbols or any other object that responds top `#to_s`.
|
data/README.md
CHANGED
@@ -41,11 +41,11 @@ performance HTTP server that serves static resource at hundreds of thousands
|
|
41
41
|
of fetchs per second. A a simple hello world Ruby handler at over 100,000
|
42
42
|
requests per second on a desktop computer. That places Agoo at about 85 times
|
43
43
|
faster than Sinatra and 1000 times faster than Rails. In both cases the
|
44
|
-
latency was two orders of magnitude lower or more. Checkout the
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
latency was two orders of magnitude lower or more. Checkout the
|
45
|
+
[benchmarks](http://opo.technology/benchmarks.html#web_benchmarks). Note that
|
46
|
+
the benchmarks had to use a C program called
|
47
|
+
[Perfer](https://github.com/ohler55/perfer) to hit the Agoo limits. Ruby
|
48
|
+
benchmarks driver could not push Agoo hard enough.
|
49
49
|
|
50
50
|
Agoo supports the [Ruby rack API](https://rack.github.io) which allows for the
|
51
51
|
use of rack compatible gems such as Hanami and Rails. Agoo also supports WebSockets and SSE.
|
@@ -57,8 +57,8 @@ use of rack compatible gems such as Hanami and Rails. Agoo also supports WebSock
|
|
57
57
|
extension](https://github.com/rack/rack/pull/1272) and give it a look and a
|
58
58
|
thumbs-up or heart if you like it.
|
59
59
|
|
60
|
-
- Agoo now serves Rails static assets 2000 times faster than the
|
61
|
-
Puma. Thats right, 2000 times faster.
|
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).
|
62
62
|
|
63
63
|
## Releases
|
64
64
|
|
@@ -69,7 +69,7 @@ the develop branch. Pull requests should be made against the develop branch.
|
|
69
69
|
|
70
70
|
## Links
|
71
71
|
|
72
|
-
- *Documentation*: http://rubydoc.info/gems/agoo
|
72
|
+
- *Documentation*: http://rubydoc.info/gems/agoo or http://www.ohler.com/agoo/doc/index.html
|
73
73
|
|
74
74
|
- *GitHub* *repo*: https://github.com/ohler55/agoo
|
75
75
|
|
data/ext/agoo/con.c
CHANGED
@@ -124,6 +124,31 @@ should_close(const char *header, int hlen) {
|
|
124
124
|
return false;
|
125
125
|
}
|
126
126
|
|
127
|
+
static bool
|
128
|
+
page_response(Con c, Page p, char *hend) {
|
129
|
+
Res res;
|
130
|
+
char *b;
|
131
|
+
|
132
|
+
if (NULL == (res = res_create(c))) {
|
133
|
+
return true;
|
134
|
+
}
|
135
|
+
if (NULL == c->res_tail) {
|
136
|
+
c->res_head = res;
|
137
|
+
} else {
|
138
|
+
c->res_tail->next = res;
|
139
|
+
}
|
140
|
+
c->res_tail = res;
|
141
|
+
|
142
|
+
b = strstr(c->buf, "\r\n");
|
143
|
+
res->close = should_close(b, (int)(hend - b));
|
144
|
+
if (res->close) {
|
145
|
+
c->closing = true;
|
146
|
+
}
|
147
|
+
res_set_message(res, p->resp);
|
148
|
+
|
149
|
+
return false;
|
150
|
+
}
|
151
|
+
|
127
152
|
// Returns:
|
128
153
|
// 0 - when header has not been read
|
129
154
|
// message length - when length can be determined
|
@@ -244,26 +269,35 @@ con_header_read(Con c) {
|
|
244
269
|
mlen = hend - c->buf + 4 + clen;
|
245
270
|
if (GET == method &&
|
246
271
|
NULL != (p = group_get(&err, &the_server.pages, path, (int)(pend - path)))) {
|
247
|
-
if (
|
272
|
+
if (page_response(c, p, hend)) {
|
248
273
|
return bad_request(c, 500, __LINE__);
|
249
274
|
}
|
250
|
-
|
251
|
-
c->res_head = res;
|
252
|
-
} else {
|
253
|
-
c->res_tail->next = res;
|
254
|
-
}
|
255
|
-
c->res_tail = res;
|
275
|
+
return -mlen;
|
256
276
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
277
|
+
// TBD int hook_or_page(method, path, pend, &hook)
|
278
|
+
// return http status
|
279
|
+
// 0 is not handled
|
280
|
+
// 200 means taken care of
|
281
|
+
// default (over 200) call bad_request
|
282
|
+
}
|
283
|
+
if (GET == method && the_server.root_first &&
|
284
|
+
NULL != (p = page_get(&err, &the_server.pages, path, (int)(pend - path)))) {
|
285
|
+
if (page_response(c, p, hend)) {
|
286
|
+
return bad_request(c, 500, __LINE__);
|
261
287
|
}
|
262
|
-
res_set_message(res, p->resp);
|
263
|
-
|
264
288
|
return -mlen;
|
265
|
-
}
|
289
|
+
}
|
290
|
+
if (NULL == (hook = hook_find(the_server.hooks, method, path, pend))) {
|
266
291
|
if (GET == method) {
|
292
|
+
if (the_server.root_first) { // already checked
|
293
|
+
if (NULL != the_server.hook404) {
|
294
|
+
// There would be too many parameters to pass to a
|
295
|
+
// separate function so just goto the hook processing.
|
296
|
+
hook = the_server.hook404;
|
297
|
+
goto HOOKED;
|
298
|
+
}
|
299
|
+
return bad_request(c, 404, __LINE__);
|
300
|
+
}
|
267
301
|
if (NULL == (p = page_get(&err, &the_server.pages, path, (int)(pend - path)))) {
|
268
302
|
if (NULL != the_server.hook404) {
|
269
303
|
// There would be too many parameters to pass to a
|
@@ -273,23 +307,9 @@ con_header_read(Con c) {
|
|
273
307
|
}
|
274
308
|
return bad_request(c, 404, __LINE__);
|
275
309
|
}
|
276
|
-
|
310
|
+
if (page_response(c, p, hend)) {
|
277
311
|
return bad_request(c, 500, __LINE__);
|
278
312
|
}
|
279
|
-
if (NULL == c->res_tail) {
|
280
|
-
c->res_head = res;
|
281
|
-
} else {
|
282
|
-
c->res_tail->next = res;
|
283
|
-
}
|
284
|
-
c->res_tail = res;
|
285
|
-
|
286
|
-
b = strstr(c->buf, "\r\n");
|
287
|
-
res->close = should_close(b, (int)(hend - b));
|
288
|
-
if (res->close) {
|
289
|
-
c->closing = true;
|
290
|
-
}
|
291
|
-
res_set_message(res, p->resp);
|
292
|
-
|
293
313
|
return -mlen;
|
294
314
|
}
|
295
315
|
}
|
data/ext/agoo/page.c
CHANGED
@@ -401,11 +401,33 @@ update_contents(Cache cache, Page p) {
|
|
401
401
|
return true;
|
402
402
|
}
|
403
403
|
|
404
|
+
static Page
|
405
|
+
page_check(Err err, Cache cache, Page page) {
|
406
|
+
double now = dtime();
|
407
|
+
|
408
|
+
if (page->last_check + PAGE_RECHECK_TIME < now) {
|
409
|
+
struct stat fattr;
|
410
|
+
|
411
|
+
if (0 == stat(page->path, &fattr) && page->mtime != fattr.st_mtime) {
|
412
|
+
update_contents(cache, page);
|
413
|
+
if (NULL == page->resp) {
|
414
|
+
page_destroy(page);
|
415
|
+
err_set(err, ERR_NOT_FOUND, "not found.");
|
416
|
+
return NULL;
|
417
|
+
}
|
418
|
+
}
|
419
|
+
page->last_check = now;
|
420
|
+
}
|
421
|
+
return page;
|
422
|
+
}
|
423
|
+
|
404
424
|
Page
|
405
425
|
page_get(Err err, Cache cache, const char *path, int plen) {
|
406
426
|
Page page;
|
407
427
|
|
408
|
-
|
428
|
+
if (NULL != strstr(path, "../")) {
|
429
|
+
return NULL;
|
430
|
+
}
|
409
431
|
if (NULL == (page = cache_get(cache, path, plen))) {
|
410
432
|
Page old;
|
411
433
|
char full_path[2048];
|
@@ -433,21 +455,7 @@ page_get(Err err, Cache cache, const char *path, int plen) {
|
|
433
455
|
page_destroy(old);
|
434
456
|
}
|
435
457
|
} else {
|
436
|
-
|
437
|
-
|
438
|
-
if (page->last_check + PAGE_RECHECK_TIME < now) {
|
439
|
-
struct stat fattr;
|
440
|
-
|
441
|
-
if (0 == stat(page->path, &fattr) && page->mtime != fattr.st_mtime) {
|
442
|
-
update_contents(cache, page);
|
443
|
-
if (NULL == page->resp) {
|
444
|
-
page_destroy(page);
|
445
|
-
err_set(err, ERR_NOT_FOUND, "not found.");
|
446
|
-
return NULL;
|
447
|
-
}
|
448
|
-
}
|
449
|
-
page->last_check = now;
|
450
|
-
}
|
458
|
+
page = page_check(err, cache, page);
|
451
459
|
}
|
452
460
|
return page;
|
453
461
|
}
|
@@ -457,11 +465,13 @@ group_get(Err err, Cache cache, const char *path, int plen) {
|
|
457
465
|
Page page = NULL;
|
458
466
|
Group g = NULL;
|
459
467
|
char full_path[2048];
|
460
|
-
Page old;
|
461
468
|
char *s;
|
462
469
|
Dir d;
|
470
|
+
double now;
|
463
471
|
|
464
|
-
|
472
|
+
if (NULL != strstr(path, "../")) {
|
473
|
+
return NULL;
|
474
|
+
}
|
465
475
|
for (g = cache->groups; NULL != g; g = g->next) {
|
466
476
|
if (g->plen < plen && 0 == strncmp(path, g->path, g->plen) && '/' == path[g->plen]) {
|
467
477
|
break;
|
@@ -471,55 +481,54 @@ group_get(Err err, Cache cache, const char *path, int plen) {
|
|
471
481
|
return NULL;
|
472
482
|
}
|
473
483
|
for (d = g->dirs; NULL != d; d = d->next) {
|
474
|
-
if (sizeof(full_path) <= d->plen + plen) {
|
484
|
+
if ((int)sizeof(full_path) <= d->plen + plen) {
|
475
485
|
continue;
|
476
486
|
}
|
477
487
|
s = stpcpy(full_path, d->path);
|
478
488
|
strncpy(s, path + g->plen, plen - g->plen);
|
479
489
|
s += plen - g->plen;
|
480
490
|
*s = '\0';
|
481
|
-
if (
|
491
|
+
if (NULL != (page = cache_get(cache, full_path, s - full_path))) {
|
482
492
|
break;
|
483
493
|
}
|
484
494
|
}
|
485
|
-
if (NULL ==
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
495
|
+
if (NULL == page) {
|
496
|
+
for (d = g->dirs; NULL != d; d = d->next) {
|
497
|
+
if ((int)sizeof(full_path) <= d->plen + plen) {
|
498
|
+
continue;
|
499
|
+
}
|
500
|
+
s = stpcpy(full_path, d->path);
|
501
|
+
strncpy(s, path + g->plen, plen - g->plen);
|
502
|
+
s += plen - g->plen;
|
503
|
+
*s = '\0';
|
504
|
+
if (0 == access(full_path, R_OK)) {
|
505
|
+
break;
|
506
|
+
}
|
496
507
|
}
|
497
|
-
if (
|
498
|
-
page_destroy(page);
|
499
|
-
err_set(err, ERR_NOT_FOUND, "not found.");
|
508
|
+
if (NULL == d) {
|
500
509
|
return NULL;
|
501
510
|
}
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
if (
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
511
|
+
plen = s - full_path;
|
512
|
+
path = full_path;
|
513
|
+
if (NULL == (page = cache_get(cache, path, plen))) {
|
514
|
+
Page old;
|
515
|
+
|
516
|
+
if (NULL == (page = page_create(path))) {
|
517
|
+
err_set(err, ERR_MEMORY, "Failed to allocate memory for Page.");
|
518
|
+
return NULL;
|
519
|
+
}
|
520
|
+
if (!update_contents(cache, page) || NULL == page->resp) {
|
521
|
+
page_destroy(page);
|
522
|
+
err_set(err, ERR_NOT_FOUND, "not found.");
|
523
|
+
return NULL;
|
524
|
+
}
|
525
|
+
if (NULL != (old = cache_set(cache, path, plen, page))) {
|
526
|
+
page_destroy(old);
|
518
527
|
}
|
519
|
-
page->last_check = now;
|
520
528
|
}
|
529
|
+
return page;
|
521
530
|
}
|
522
|
-
return page;
|
531
|
+
return page_check(err, cache, page);
|
523
532
|
}
|
524
533
|
|
525
534
|
Group
|
data/ext/agoo/server.c
CHANGED
@@ -135,6 +135,7 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
135
135
|
the_server.listen_thread = 0;
|
136
136
|
the_server.con_thread = 0;
|
137
137
|
the_server.max_push_pending = 32;
|
138
|
+
the_server.root_first = false;
|
138
139
|
if (Qnil != options) {
|
139
140
|
VALUE v;
|
140
141
|
|
@@ -159,6 +160,9 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
159
160
|
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("pedantic"))))) {
|
160
161
|
the_server.pedantic = (Qtrue == v);
|
161
162
|
}
|
163
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("root_first"))))) {
|
164
|
+
the_server.root_first = (Qtrue == v);
|
165
|
+
}
|
162
166
|
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("Port"))))) {
|
163
167
|
if (rb_cInteger == rb_obj_class(v)) {
|
164
168
|
the_server.port = NUM2INT(v);
|
@@ -207,7 +211,7 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
207
211
|
*
|
208
212
|
* - *options* [_Hash_] server options
|
209
213
|
*
|
210
|
-
* - *:pedantic* [_true_|_false_] if true response header and status codes are
|
214
|
+
* - *: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.
|
211
215
|
*
|
212
216
|
* - *: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.
|
213
217
|
*/
|
data/ext/agoo/server.h
CHANGED
data/ext/agoo/upgraded.c
CHANGED
@@ -283,6 +283,24 @@ pending(VALUE self) {
|
|
283
283
|
return INT2NUM(pending);
|
284
284
|
}
|
285
285
|
|
286
|
+
/* Document-method: open?
|
287
|
+
*
|
288
|
+
* call-seq: open?()
|
289
|
+
*
|
290
|
+
* Returns true if the connection is open and false otherwise.
|
291
|
+
*/
|
292
|
+
static VALUE
|
293
|
+
up_open(VALUE self) {
|
294
|
+
Upgraded up = get_upgraded(self);
|
295
|
+
int pending = -1;
|
296
|
+
|
297
|
+
if (NULL != up) {
|
298
|
+
pending = atomic_load(&up->pending);
|
299
|
+
atomic_fetch_sub(&up->ref_cnt, 1);
|
300
|
+
}
|
301
|
+
return 0 <= pending ? Qtrue : Qfalse;
|
302
|
+
}
|
303
|
+
|
286
304
|
/* Document-method: protocol
|
287
305
|
*
|
288
306
|
* call-seq: protocol()
|
@@ -371,6 +389,7 @@ upgraded_init(VALUE mod) {
|
|
371
389
|
rb_define_method(upgraded_class, "pending", pending, 0);
|
372
390
|
rb_define_method(upgraded_class, "protocol", protocol, 0);
|
373
391
|
rb_define_method(upgraded_class, "publish", ragoo_publish, 2);
|
392
|
+
rb_define_method(upgraded_class, "open?", up_open, 0);
|
374
393
|
|
375
394
|
on_open_id = rb_intern("on_open");
|
376
395
|
to_s_id = rb_intern("to_s");
|
data/lib/agoo/version.rb
CHANGED
data/lib/rack/handler/agoo.rb
CHANGED
@@ -10,14 +10,16 @@ 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 :
|
13
|
+
# Run the server. Options are the same as for Agoo::Server plus a :port
|
14
14
|
# and :root option.
|
15
15
|
def self.run(handler, options={})
|
16
16
|
port = 9292
|
17
|
-
root = '
|
17
|
+
root = './public'
|
18
|
+
root_set = false
|
18
19
|
default_handler = nil
|
19
20
|
not_found_handler = nil
|
20
21
|
path_map = {}
|
22
|
+
options[:root_first] = true # the default for rack
|
21
23
|
|
22
24
|
default_handler = handler unless handler.nil?
|
23
25
|
options.each { |k,v|
|
@@ -26,7 +28,10 @@ module Rack
|
|
26
28
|
options.delete(k)
|
27
29
|
elsif :root == k
|
28
30
|
root = v
|
31
|
+
root_set = true
|
29
32
|
options.delete(k)
|
33
|
+
elsif :rmux == k
|
34
|
+
options[:root_first] = false
|
30
35
|
elsif k.nil?
|
31
36
|
not_found_handler = v
|
32
37
|
options.delete(k)
|
@@ -46,6 +51,7 @@ module Rack
|
|
46
51
|
begin
|
47
52
|
# If Rails is loaded this should work else just ignore.
|
48
53
|
::Agoo::Server.path_group('/assets', Rails.configuration.assets.paths)
|
54
|
+
root = Rails.public_path unless root_set
|
49
55
|
rescue Exception
|
50
56
|
end
|
51
57
|
unless default_handler.nil?
|
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.
|
4
|
+
version: 2.1.3
|
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-
|
11
|
+
date: 2018-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|