agoo 2.15.14 → 2.15.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12e12e3428c92bdb7c5426a20a481935f89a7759a59ae9e7b66da2d49ecc4129
4
- data.tar.gz: 342b51f1de8b8d6a589ff36717d84ed4e0e8a9898d1ff0b2439749a9b26e327e
3
+ metadata.gz: 4082bd93ed8668165d61bae2e0cc68d6bb4ff4c015172e281f1f4c81193e635f
4
+ data.tar.gz: a93f805c0d630183abd684e1c995b649f42573d41dcb01e48e68949ec520a036
5
5
  SHA512:
6
- metadata.gz: d0d8a64a93f955a5dd89d5b69e82b3c4aef9454aedda197227e79f6d3892c1650a7a766b8025a648e865f6350331642a754dc29ce4c5643c6d8a1ad42f2a0e23
7
- data.tar.gz: dfad2c83a41d4a44e1af4181caa6556f4e0fa67c46f23066365f7a97460fb8561191770750f35f41226e4f3b66fb41596b8c4eb50980a485935d8e7fbd5dc98c
6
+ metadata.gz: c9c668862fd616a3579ab3164ba98a04fbdf58ac4ae86ddd7d28facfbb6e665ced1298becace7ed5047fb6b9a1a0a2a6a79b87661139f1b3fc9561e499277c76
7
+ data.tar.gz: 96f3f2c3c732e0bc26438185399b1aeb0494f63564678e2c2bb6ba8340c199dd8950aec1dd347bcf75513b87bbb50240efba9a858ee9684bc8a3acec396caf10
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All changes to the Agoo gem are documented here. Releases follow semantic versioning.
4
4
 
5
+ ## [2.15.15] - 2026-05-09
6
+
7
+ ### Fixed
8
+
9
+ - Moved URL decoding to before page lookup invalid denial.
10
+
11
+ - Eliminated deprecated code warnings.
12
+
13
+ - Fix issue Connection string too long error.
14
+
5
15
  ## [2.15.14] - 2025-09-24
6
16
 
7
17
  ### Fixed
data/ext/agoo/con.c CHANGED
@@ -26,8 +26,6 @@
26
26
  #include "upgraded.h"
27
27
  #include "websocket.h"
28
28
 
29
- #define INITIAL_POLL_SIZE 1024
30
-
31
29
  double con_timeout = 30.0;
32
30
 
33
31
  typedef enum {
data/ext/agoo/doc.c CHANGED
@@ -13,7 +13,7 @@
13
13
  #define EXP_MAX 100000
14
14
  #define DEC_MAX 16
15
15
 
16
- static char char_map[256] = "\
16
+ static char char_map[257] = "\
17
17
  .........ww..w..................\
18
18
  wpq.....pp..w.p.ttttttttttp..p..\
19
19
  pttttttttttttttttttttttttttp.p.t\
@@ -23,7 +23,7 @@ pttttttttttttttttttttttttttp.p.t\
23
23
  ................................\
24
24
  ................................";
25
25
 
26
- static char json_map[256] = "\
26
+ static char json_map[257] = "\
27
27
  .........ww..w..................\
28
28
  wpq.....pp..c.p.ttttttttttp..p..\
29
29
  pttttttttttttttttttttttttttp.p.t\
@@ -33,7 +33,7 @@ pttttttttttttttttttttttttttp.p.t\
33
33
  ................................\
34
34
  ................................";
35
35
 
36
- static char value_map[256] = "\
36
+ static char value_map[257] = "\
37
37
  .........ww..w..................\
38
38
  wpq.....pp..ctt.ttttttttttt..p..\
39
39
  pttttttttttttttttttttttttttp.p.t\
@@ -24,6 +24,22 @@ eh_free(void *ptr) {
24
24
  }
25
25
  }
26
26
 
27
+ static size_t
28
+ eh_size(const void *ptr) {
29
+ return sizeof(struct _earlyHints);
30
+ }
31
+
32
+ static const rb_data_type_t early_hints_type = {
33
+ .wrap_struct_name = "early_hints",
34
+ .function = {
35
+ .dmark = NULL,
36
+ .dfree = eh_free,
37
+ .dsize = eh_size,
38
+ },
39
+ .data = NULL,
40
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
41
+ };
42
+
27
43
  VALUE
28
44
  agoo_early_hints_new(agooReq req) {
29
45
  EarlyHints eh = (EarlyHints)AGOO_CALLOC(1, sizeof(struct _earlyHints));
@@ -33,7 +49,7 @@ agoo_early_hints_new(agooReq req) {
33
49
  }
34
50
  eh->req = req;
35
51
 
36
- return Data_Wrap_Struct(eh_class, NULL, eh_free, eh);
52
+ return TypedData_Wrap_Struct(eh_class, &early_hints_type, eh);
37
53
  }
38
54
 
39
55
  /* Document-method: call
@@ -22,6 +22,22 @@ es_free(void *ptr) {
22
22
  }
23
23
  }
24
24
 
25
+ static size_t
26
+ es_size(const void *ptr) {
27
+ return sizeof(struct _errorStream);
28
+ }
29
+
30
+ static const rb_data_type_t error_stream_type = {
31
+ .wrap_struct_name = "error_stream",
32
+ .function = {
33
+ .dmark = NULL,
34
+ .dfree = es_free,
35
+ .dsize = es_size,
36
+ },
37
+ .data = NULL,
38
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
39
+ };
40
+
25
41
  VALUE
26
42
  error_stream_new(void) {
27
43
  ErrorStream es = (ErrorStream)AGOO_MALLOC(sizeof(struct _errorStream));
@@ -32,7 +48,7 @@ error_stream_new(void) {
32
48
  es->server = NULL;
33
49
  es->text = NULL;
34
50
 
35
- return Data_Wrap_Struct(es_class, NULL, es_free, es);
51
+ return TypedData_Wrap_Struct(es_class, &error_stream_type, es);
36
52
  }
37
53
 
38
54
  /* Document-method: puts
data/ext/agoo/gqlvalue.c CHANGED
@@ -10,7 +10,7 @@
10
10
  #include "graphql.h"
11
11
  #include "sectime.h"
12
12
 
13
- static const char spaces[256] = "\n ";
13
+ static const char spaces[257] = "\n ";
14
14
 
15
15
  // Null type
16
16
  static agooText
data/ext/agoo/graphql.c CHANGED
@@ -24,7 +24,7 @@ typedef struct _slot {
24
24
 
25
25
  static Slot buckets[BUCKET_SIZE];
26
26
 
27
- static uint8_t name_chars[256] = "\
27
+ static uint8_t name_chars[257] = "\
28
28
  \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
29
29
  \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
30
30
  \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
@@ -43,7 +43,7 @@ static uint8_t name_chars[256] = "\
43
43
  \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
44
44
  ";
45
45
 
46
- static const char spaces[16] = " ";
46
+ static const char spaces[17] = " ";
47
47
 
48
48
  gqlDir gql_directives = NULL;
49
49
 
data/ext/agoo/http.c CHANGED
@@ -27,7 +27,7 @@ struct _cache key_cache;
27
27
 
28
28
  // The rack spec indicates the characters (),/:;<=>?@[]{} are invalid which
29
29
  // clearly is not consistent with RFC7230 so stick with the RFC.
30
- static char header_value_chars[256] = "\
30
+ static char header_value_chars[257] = "\
31
31
  xxxxxxxxxxoxxxxxxxxxxxxxxxxxxxxx\
32
32
  oooooooooooooooooooooooooooooooo\
33
33
  oooooooooooooooooooooooooooooooo\
data/ext/agoo/page.c CHANGED
@@ -788,114 +788,66 @@ page_check(agooErr err, agooPage page) {
788
788
  agooPage
789
789
  agoo_page_get(agooErr err, const char *path, int plen, const char *root) {
790
790
  agooPage page = NULL;
791
+ char full_path[2048];
792
+ char *s;
791
793
 
792
- if (NULL != strstr(path, "../")) {
794
+ if (NULL != root) {
795
+ s = stpcpy(full_path, root);
796
+ } else if (NULL != cache.root) {
797
+ s = stpcpy(full_path, cache.root);
798
+ } else {
799
+ s = full_path;
800
+ }
801
+ // This catches the accidental repeated / but not the intentional URL
802
+ // encoding which is detected and denied later.
803
+ if ('/' != *(s - 1) && '/' != *path) {
804
+ *s++ = '/';
805
+ }
806
+ if ((int)sizeof(full_path) <= plen + (s - full_path)) {
807
+ AGOO_ERR_MEM(err, "Page path");
808
+ return NULL;
809
+ }
810
+ if (NULL != memchr(path, '%', plen)) {
811
+ const char *pend = path + plen;
812
+ const char *pp = path;
813
+
814
+ for (; pp < pend; pp++) {
815
+ if ('%' != *pp) {
816
+ *s++ = *pp;
817
+ continue;
818
+ }
819
+ *s++ = parse_percent_seq(pp+1);
820
+ pp += 2;
821
+ }
822
+ } else {
823
+ strncpy(s, path, plen);
824
+ s += plen;
825
+ }
826
+ *s = '\0';
827
+ // full_path is now URL decoded.
828
+ if (NULL != strstr(full_path, "../") || NULL != strstr(full_path, "//")) {
793
829
  return NULL;
794
830
  }
795
- if (NULL != root) {
796
- char full_path[2048];
797
- char *s = stpcpy(full_path, root);
798
-
799
- if (NULL != strstr(path, "../")) {
800
- return NULL;
801
- }
802
- if ((int)sizeof(full_path) <= plen + (s - full_path)) {
803
- AGOO_ERR_MEM(err, "Page path");
804
- return NULL;
805
- }
806
- if ('/' != *(s - 1) && '/' != *path) {
807
- *s++ = '/';
808
- }
809
- // TBD if path has % then ...
810
- if (NULL != memchr(path, '%', plen)) {
811
- const char *pend = path + plen;
812
- const char *pp = path;
813
-
814
- for (; pp < pend; pp++) {
815
- if ('%' != *pp) {
816
- *s++ = *pp;
817
- continue;
818
- }
819
- *s++ = parse_percent_seq(pp+1);
820
- pp += 2;
821
- }
822
- } else {
823
- strncpy(s, path, plen);
824
- s += plen;
825
- }
826
- *s = '\0';
827
- plen = (int)(s - full_path);
828
- if (NULL == (page = cache_root_get(full_path, plen))) {
829
- if (NULL != cache.root) {
830
- agooPage old;
831
-
832
- if (NULL == (page = agoo_page_create(full_path))) {
833
- AGOO_ERR_MEM(err, "Page");
834
- return NULL;
835
- }
836
- if (!update_contents(page) || NULL == page->resp) {
837
- agoo_page_destroy(page);
838
- agoo_err_set(err, AGOO_ERR_NOT_FOUND, "not found.");
839
- return NULL;
840
- }
841
- if (NULL != (old = cache_root_set(full_path, plen, page))) {
842
- agoo_page_destroy(old);
843
- }
844
- }
845
- } else {
846
- page = page_check(err, page);
847
- }
831
+ plen = (int)(s - full_path);
832
+ if (NULL == (page = cache_root_get(full_path, plen))) {
833
+ if (NULL != cache.root) {
834
+ agooPage old;
835
+
836
+ if (NULL == (page = agoo_page_create(full_path))) {
837
+ AGOO_ERR_MEM(err, "Page");
838
+ return NULL;
839
+ }
840
+ if (!update_contents(page) || NULL == page->resp) {
841
+ agoo_page_destroy(page);
842
+ agoo_err_set(err, AGOO_ERR_NOT_FOUND, "not found.");
843
+ return NULL;
844
+ }
845
+ if (NULL != (old = cache_root_set(full_path, plen, page))) {
846
+ agoo_page_destroy(old);
847
+ }
848
+ }
848
849
  } else {
849
- if (NULL == (page = cache_get(path, plen))) {
850
- bool has_percent = NULL != memchr(path, '%', plen);
851
-
852
- if (NULL != cache.root || has_percent) {
853
- agooPage old;
854
- char full_path[2048];
855
- char *s = stpcpy(full_path, cache.root);
856
-
857
- if ('/' != *(s - 1) && '/' != *path) {
858
- *s++ = '/';
859
- }
860
- if ((int)sizeof(full_path) <= plen + (s - full_path)) {
861
- AGOO_ERR_MEM(err, "Page path");
862
- return NULL;
863
- }
864
- if (has_percent) {
865
- const char *pend = path + plen;
866
- const char *pp = path;
867
-
868
- for (; pp < pend; pp++) {
869
- if ('%' != *pp) {
870
- *s++ = *pp;
871
- continue;
872
- }
873
- *s++ = parse_percent_seq(pp+1);
874
- pp += 2;
875
- }
876
- *s = '\0';
877
- } else {
878
- strncpy(s, path, plen);
879
- s[plen] = '\0';
880
- }
881
- if (NULL == (page = agoo_page_create(full_path))) { // TBD full_path or original path?
882
- AGOO_ERR_MEM(err, "Page");
883
- return NULL;
884
- }
885
- plen = (int)strlen(full_path);
886
- if (!update_contents(page) || NULL == page->resp) {
887
- agoo_page_destroy(page);
888
- agoo_err_set(err, AGOO_ERR_NOT_FOUND, "not found.");
889
- return NULL;
890
- }
891
- // Cache key is the original path/plen.
892
- if (NULL != (old = cache_set(path, plen, page))) {
893
- agoo_page_destroy(old);
894
- }
895
- }
896
- } else {
897
- page = page_check(err, page);
898
- }
850
+ page = page_check(err, page);
899
851
  }
900
852
  return page;
901
853
  }
data/ext/agoo/queue.c CHANGED
@@ -57,15 +57,20 @@ agoo_queue_multi_init(agooErr err, agooQueue q, size_t qsize, bool multi_push, b
57
57
 
58
58
  void
59
59
  agoo_queue_cleanup(agooQueue q) {
60
+ int sock = q->wsock;
61
+
62
+ q->wsock = 0;
63
+ if (0 < sock) {
64
+ close(sock);
65
+ }
66
+ sock = q->rsock;
67
+ q->rsock = 0;
68
+ if (0 < sock) {
69
+ close(sock);
70
+ }
60
71
  AGOO_FREE(q->q);
61
72
  q->q = NULL;
62
73
  q->end = NULL;
63
- if (0 < q->wsock) {
64
- close(q->wsock);
65
- }
66
- if (0 < q->rsock) {
67
- close(q->rsock);
68
- }
69
74
  }
70
75
 
71
76
  void
data/ext/agoo/request.c CHANGED
@@ -483,7 +483,6 @@ fill_headers(agooReq r, VALUE hash) {
483
483
  if (NULL == r) {
484
484
  rb_raise(rb_eArgError, "Request is no longer valid.");
485
485
  }
486
-
487
486
  for (; h < end; h++) {
488
487
  switch (*h) {
489
488
  case ':':
@@ -515,10 +514,12 @@ fill_headers(agooReq r, VALUE hash) {
515
514
  } else if (sizeof(connection_key) - 1 == klen && 0 == strncasecmp(key, connection_key, sizeof(connection_key) - 1)) {
516
515
  char buf[1024];
517
516
 
518
- strncpy(buf, val, vend - val);
519
- buf[sizeof(buf)-1] = '\0';
520
- if (NULL != strstr(buf, upgrade_key)) {
521
- upgrade = true;
517
+ if (vend - val < (long)sizeof(buf) - 1) {
518
+ strncpy(buf, val, vend - val);
519
+ buf[sizeof(buf)-1] = '\0';
520
+ if (NULL != strstr(buf, upgrade_key)) {
521
+ upgrade = true;
522
+ }
522
523
  }
523
524
  } else if (sizeof(accept_key) - 1 == klen && 0 == strncasecmp(key, accept_key, sizeof(accept_key) - 1)) {
524
525
  if (sizeof(event_stream_val) - 1 == vend - val &&
@@ -739,10 +740,28 @@ request_mark(void *ptr) {
739
740
  }
740
741
  }
741
742
 
743
+ static size_t
744
+ request_size(const void *ptr) {
745
+ agooReq r = (agooReq)ptr;
746
+
747
+ return sizeof(struct _agooReq) + r->mlen - 8;
748
+ }
749
+
750
+ static const rb_data_type_t request_type = {
751
+ .wrap_struct_name = "request",
752
+ .function = {
753
+ .dmark = request_mark,
754
+ .dfree = NULL,
755
+ .dsize = request_size,
756
+ },
757
+ .data = NULL,
758
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
759
+ };
760
+
742
761
  VALUE
743
762
  request_wrap(agooReq req) {
744
763
  // freed from the C side of things
745
- return Data_Wrap_Struct(req_class, request_mark, NULL, req);
764
+ return TypedData_Wrap_Struct(req_class, &request_type, req);
746
765
  }
747
766
 
748
767
  /* Document-class: Agoo::Request
data/ext/agoo/rresponse.c CHANGED
@@ -24,6 +24,22 @@ response_free(void *ptr) {
24
24
  AGOO_FREE(ptr);
25
25
  }
26
26
 
27
+ static size_t
28
+ response_size(const void *ptr) {
29
+ return sizeof(struct _agooResponse);
30
+ }
31
+
32
+ static const rb_data_type_t response_type = {
33
+ .wrap_struct_name = "response",
34
+ .function = {
35
+ .dmark = NULL,
36
+ .dfree = response_free,
37
+ .dsize = response_size,
38
+ },
39
+ .data = NULL,
40
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
41
+ };
42
+
27
43
  VALUE
28
44
  response_new(void) {
29
45
  agooResponse res = (agooResponse)AGOO_MALLOC(sizeof(struct _agooResponse));
@@ -34,7 +50,7 @@ response_new(void) {
34
50
  memset(res, 0, sizeof(struct _agooResponse));
35
51
  res->code = 200;
36
52
 
37
- return Data_Wrap_Struct(res_class, NULL, response_free, res);
53
+ return TypedData_Wrap_Struct(res_class, &response_type, res);
38
54
  }
39
55
 
40
56
  /* Document-method: to_s
data/ext/agoo/rserver.c CHANGED
@@ -1292,6 +1292,23 @@ use(int argc, VALUE *argv, VALUE self) {
1292
1292
  return Qnil;
1293
1293
  }
1294
1294
 
1295
+ static size_t
1296
+ server_size(const void *ptr) {
1297
+ return sizeof(struct _agooServer);
1298
+ }
1299
+
1300
+ static const rb_data_type_t server_type = {
1301
+ .wrap_struct_name = "server",
1302
+ .function = {
1303
+ .dmark = server_mark,
1304
+ .dfree = NULL,
1305
+ .dsize = server_size,
1306
+ },
1307
+ .data = NULL,
1308
+ .flags = 0,
1309
+ };
1310
+
1311
+
1295
1312
  /* Document-class: Agoo::Server
1296
1313
  *
1297
1314
  * An HTTP server that support the rack API as well as some other optimized
@@ -1335,7 +1352,8 @@ server_init(VALUE mod) {
1335
1352
 
1336
1353
  push_env_key = rb_str_new_cstr("rack.upgrade"); rb_gc_register_address(&push_env_key);
1337
1354
 
1338
- rserver = Data_Wrap_Struct(rb_cObject, server_mark, NULL, strdup("dummy"));
1355
+ rserver = TypedData_Wrap_Struct(rb_cObject, &server_type, strdup("dummy"));
1356
+
1339
1357
  rb_gc_register_address(&rserver);
1340
1358
 
1341
1359
  agoo_http_init();
data/ext/agoo/rupgraded.c CHANGED
@@ -231,6 +231,22 @@ on_destroy(agooUpgraded up) {
231
231
  }
232
232
  }
233
233
 
234
+ static size_t
235
+ upgraded_size(const void *ptr) {
236
+ return sizeof(struct _agooUpgraded);
237
+ }
238
+
239
+ static const rb_data_type_t upgraded_type = {
240
+ .wrap_struct_name = "upgraded",
241
+ .function = {
242
+ .dmark = NULL,
243
+ .dfree = NULL,
244
+ .dsize = upgraded_size,
245
+ },
246
+ .data = NULL,
247
+ .flags = 0,
248
+ };
249
+
234
250
  agooUpgraded
235
251
  rupgraded_create(agooCon c, VALUE obj, VALUE env) {
236
252
  agooUpgraded up;
@@ -246,7 +262,7 @@ rupgraded_create(agooCon c, VALUE obj, VALUE env) {
246
262
  up->on_error = rb_respond_to(obj, rb_intern("on_error"));
247
263
  up->on_destroy = on_destroy;
248
264
 
249
- up->wrap = (void*)Data_Wrap_Struct(upgraded_class, NULL, NULL, up);
265
+ up->wrap = (void*)TypedData_Wrap_Struct(upgraded_class, &upgraded_type, up);
250
266
 
251
267
  agoo_server_add_upgraded(up);
252
268
 
data/ext/agoo/server.c CHANGED
@@ -163,12 +163,12 @@ listen_loop(void *x) {
163
163
  agoo_log_cat(&agoo_con_cat, "Server with pid %d accepted connection %llu on %s [%d] from %s",
164
164
  getpid(), (unsigned long long)cnt, b->id, con->sock, con->remote);
165
165
 
166
- /* TBD
167
- con_cnt = atomic_fetch_add(&agoo_server.con_cnt, 1);
168
- if (agoo_server.loop_max > agoo_server.loop_cnt && agoo_server.loop_cnt * LOOP_UP < con_cnt) {
169
- add_con_loop();
170
- }
171
- */
166
+ /* TBD
167
+ con_cnt = atomic_fetch_add(&agoo_server.con_cnt, 1);
168
+ if (agoo_server.loop_max > agoo_server.loop_cnt && agoo_server.loop_cnt * LOOP_UP < con_cnt) {
169
+ add_con_loop();
170
+ }
171
+ */
172
172
  agoo_queue_push(&agoo_server.con_queue, (void*)con);
173
173
  }
174
174
  }
data/ext/agoo/text.c CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  static const char hex_chars[17] = "0123456789abcdef";
12
12
 
13
- static char json_chars[256] = "\
13
+ static char json_chars[257] = "\
14
14
  66666666222622666666666666666666\
15
15
  11211111111111111111111111111111\
16
16
  11111111111111111111111111112111\
data/lib/agoo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.15.14'
4
+ VERSION = '2.15.15'
5
5
  end
@@ -32,8 +32,6 @@ class BaseHandlerTest < Minitest::Test
32
32
  elsif 'DELETE' == req.request_method
33
33
  res.code = 200
34
34
  res.body = req.body
35
-
36
-
37
35
  end
38
36
  end
39
37
  end
@@ -131,6 +129,49 @@ class BaseHandlerTest < Minitest::Test
131
129
  }
132
130
  end
133
131
 
132
+ def test_long_connection
133
+ uri = URI('http://localhost:6470/tellme?a=1')
134
+ req = Net::HTTP::Get.new(uri)
135
+ # Set the headers the way we want them.
136
+ req['Accept-Encoding'] = '*'
137
+ req['Accept'] = 'application/json'
138
+ req['User-Agent'] = 'Ruby'
139
+ req['Connection'] = 'X'* 1024 # should not cause a failure
140
+
141
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
142
+ h.request(req)
143
+ }
144
+ content = res.body
145
+ obj = Oj.load(content, mode: :strict)
146
+
147
+ expect = {
148
+ "HTTP_ACCEPT" => "application/json",
149
+ "HTTP_ACCEPT_ENCODING" => "*",
150
+ "HTTP_USER_AGENT" => "Ruby",
151
+ "PATH_INFO" => "/tellme",
152
+ "QUERY_STRING" => "a=1",
153
+ "REQUEST_METHOD" => "GET",
154
+ "SCRIPT_NAME" => "",
155
+ "SERVER_NAME" => "localhost",
156
+ "SERVER_PORT" => "6470",
157
+ "rack.errors" => nil,
158
+ "rack.input" => nil,
159
+ "rack.multiprocess" => false,
160
+ "rack.multithread" => false,
161
+ "rack.run_once" => false,
162
+ "rack.url_scheme" => "http",
163
+ "rack.version" => [1, 3],
164
+ "rack.logger" => nil,
165
+ }
166
+ expect.each_pair { |k,v|
167
+ if v.nil?
168
+ assert_nil(obj[k], k)
169
+ else
170
+ assert_equal(v, obj[k], k)
171
+ end
172
+ }
173
+ end
174
+
134
175
  def test_post
135
176
  uri = URI('http://localhost:6470/makeme')
136
177
  req = Net::HTTP::Post.new(uri)
data/test/static_test.rb CHANGED
@@ -124,4 +124,16 @@ class StaticTest < Minitest::Test
124
124
  assert_equal("404", res.code)
125
125
  end
126
126
 
127
+ def test_fetch_encoded
128
+ uri = URI('http://localhost:6469/%2e%2e%2ftests.sh')
129
+ res = Net::HTTP.get_response(uri)
130
+ assert_equal("404", res.code)
131
+ end
132
+
133
+ def test_fetch_double_slash
134
+ uri = URI('http://localhost:6469/%2f/nest/something.txt')
135
+ res = Net::HTTP.get_response(uri)
136
+ assert_equal("404", res.code)
137
+ end
138
+
127
139
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: agoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.15.14
4
+ version: 2.15.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-24 00:00:00.000000000 Z
10
+ date: 2026-05-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: oj
@@ -207,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
207
  version: '0'
208
208
  requirements:
209
209
  - Linux or macOS
210
- rubygems_version: 3.6.9
210
+ rubygems_version: 4.0.3
211
211
  specification_version: 4
212
212
  summary: An HTTP server
213
213
  test_files: