roda 3.28.0 → 3.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/README.rdoc +15 -1
  4. data/doc/conventions.rdoc +17 -8
  5. data/doc/release_notes/3.29.0.txt +15 -0
  6. data/doc/release_notes/3.30.0.txt +14 -0
  7. data/doc/release_notes/3.31.0.txt +11 -0
  8. data/doc/release_notes/3.32.0.txt +42 -0
  9. data/doc/release_notes/3.33.0.txt +8 -0
  10. data/lib/roda.rb +3 -0
  11. data/lib/roda/plugins/_symbol_regexp_matchers.rb +2 -0
  12. data/lib/roda/plugins/assets.rb +26 -5
  13. data/lib/roda/plugins/caching.rb +2 -0
  14. data/lib/roda/plugins/common_logger.rb +1 -1
  15. data/lib/roda/plugins/content_security_policy.rb +2 -0
  16. data/lib/roda/plugins/default_headers.rb +2 -0
  17. data/lib/roda/plugins/exception_page.rb +9 -5
  18. data/lib/roda/plugins/hash_matcher.rb +1 -1
  19. data/lib/roda/plugins/header_matchers.rb +5 -1
  20. data/lib/roda/plugins/indifferent_params.rb +4 -0
  21. data/lib/roda/plugins/partials.rb +33 -6
  22. data/lib/roda/plugins/path.rb +42 -15
  23. data/lib/roda/plugins/placeholder_string_matchers.rb +2 -0
  24. data/lib/roda/plugins/public.rb +25 -17
  25. data/lib/roda/plugins/relative_path.rb +73 -0
  26. data/lib/roda/plugins/render.rb +17 -5
  27. data/lib/roda/plugins/render_each.rb +11 -3
  28. data/lib/roda/plugins/run_append_slash.rb +1 -1
  29. data/lib/roda/plugins/symbol_matchers.rb +2 -2
  30. data/lib/roda/version.rb +1 -1
  31. metadata +13 -214
  32. data/Rakefile +0 -108
  33. data/doc/release_notes/1.0.0.txt +0 -329
  34. data/doc/release_notes/1.1.0.txt +0 -226
  35. data/doc/release_notes/1.2.0.txt +0 -406
  36. data/doc/release_notes/1.3.0.txt +0 -109
  37. data/doc/release_notes/2.0.0.txt +0 -75
  38. data/doc/release_notes/2.1.0.txt +0 -124
  39. data/doc/release_notes/2.10.0.txt +0 -27
  40. data/doc/release_notes/2.11.0.txt +0 -70
  41. data/doc/release_notes/2.12.0.txt +0 -40
  42. data/doc/release_notes/2.13.0.txt +0 -10
  43. data/doc/release_notes/2.14.0.txt +0 -44
  44. data/doc/release_notes/2.15.0.txt +0 -53
  45. data/doc/release_notes/2.16.0.txt +0 -48
  46. data/doc/release_notes/2.17.0.txt +0 -62
  47. data/doc/release_notes/2.18.0.txt +0 -69
  48. data/doc/release_notes/2.19.0.txt +0 -30
  49. data/doc/release_notes/2.2.0.txt +0 -97
  50. data/doc/release_notes/2.20.0.txt +0 -5
  51. data/doc/release_notes/2.21.0.txt +0 -17
  52. data/doc/release_notes/2.22.0.txt +0 -41
  53. data/doc/release_notes/2.23.0.txt +0 -29
  54. data/doc/release_notes/2.24.0.txt +0 -65
  55. data/doc/release_notes/2.25.0.txt +0 -14
  56. data/doc/release_notes/2.26.0.txt +0 -13
  57. data/doc/release_notes/2.27.0.txt +0 -56
  58. data/doc/release_notes/2.28.0.txt +0 -17
  59. data/doc/release_notes/2.29.0.txt +0 -156
  60. data/doc/release_notes/2.3.0.txt +0 -109
  61. data/doc/release_notes/2.4.0.txt +0 -55
  62. data/doc/release_notes/2.5.0.txt +0 -23
  63. data/doc/release_notes/2.5.1.txt +0 -4
  64. data/doc/release_notes/2.6.0.txt +0 -21
  65. data/doc/release_notes/2.7.0.txt +0 -75
  66. data/doc/release_notes/2.8.0.txt +0 -44
  67. data/doc/release_notes/2.9.0.txt +0 -6
  68. data/spec/all.rb +0 -1
  69. data/spec/assets/css/app.scss +0 -1
  70. data/spec/assets/css/no_access.css +0 -1
  71. data/spec/assets/css/raw.css +0 -1
  72. data/spec/assets/js/head/app.js +0 -1
  73. data/spec/composition_spec.rb +0 -31
  74. data/spec/define_roda_method_spec.rb +0 -274
  75. data/spec/env_spec.rb +0 -11
  76. data/spec/freeze_spec.rb +0 -37
  77. data/spec/integration_spec.rb +0 -209
  78. data/spec/matchers_spec.rb +0 -832
  79. data/spec/opts_spec.rb +0 -42
  80. data/spec/plugin/_after_hook_spec.rb +0 -19
  81. data/spec/plugin/all_verbs_spec.rb +0 -29
  82. data/spec/plugin/assets_preloading_spec.rb +0 -98
  83. data/spec/plugin/assets_spec.rb +0 -745
  84. data/spec/plugin/backtracking_array_spec.rb +0 -42
  85. data/spec/plugin/branch_locals_spec.rb +0 -106
  86. data/spec/plugin/caching_spec.rb +0 -337
  87. data/spec/plugin/chunked_spec.rb +0 -201
  88. data/spec/plugin/class_level_routing_spec.rb +0 -164
  89. data/spec/plugin/class_matchers_spec.rb +0 -40
  90. data/spec/plugin/common_logger_spec.rb +0 -85
  91. data/spec/plugin/content_for_spec.rb +0 -162
  92. data/spec/plugin/content_security_policy_spec.rb +0 -175
  93. data/spec/plugin/cookies_spec.rb +0 -51
  94. data/spec/plugin/csrf_spec.rb +0 -111
  95. data/spec/plugin/default_headers_spec.rb +0 -82
  96. data/spec/plugin/default_status_spec.rb +0 -95
  97. data/spec/plugin/delay_build_spec.rb +0 -23
  98. data/spec/plugin/delegate_spec.rb +0 -23
  99. data/spec/plugin/delete_empty_headers_spec.rb +0 -27
  100. data/spec/plugin/direct_call_spec.rb +0 -28
  101. data/spec/plugin/disallow_file_uploads_spec.rb +0 -25
  102. data/spec/plugin/drop_body_spec.rb +0 -24
  103. data/spec/plugin/early_hints_spec.rb +0 -19
  104. data/spec/plugin/empty_root_spec.rb +0 -14
  105. data/spec/plugin/environments_spec.rb +0 -42
  106. data/spec/plugin/error_email_spec.rb +0 -97
  107. data/spec/plugin/error_handler_spec.rb +0 -216
  108. data/spec/plugin/error_mail_spec.rb +0 -93
  109. data/spec/plugin/exception_page_spec.rb +0 -168
  110. data/spec/plugin/flash_spec.rb +0 -121
  111. data/spec/plugin/h_spec.rb +0 -11
  112. data/spec/plugin/halt_spec.rb +0 -119
  113. data/spec/plugin/hash_matcher_spec.rb +0 -27
  114. data/spec/plugin/hash_routes_spec.rb +0 -535
  115. data/spec/plugin/head_spec.rb +0 -52
  116. data/spec/plugin/header_matchers_spec.rb +0 -98
  117. data/spec/plugin/heartbeat_spec.rb +0 -74
  118. data/spec/plugin/hooks_spec.rb +0 -152
  119. data/spec/plugin/indifferent_params_spec.rb +0 -14
  120. data/spec/plugin/json_parser_spec.rb +0 -141
  121. data/spec/plugin/json_spec.rb +0 -83
  122. data/spec/plugin/mail_processor_spec.rb +0 -451
  123. data/spec/plugin/mailer_spec.rb +0 -282
  124. data/spec/plugin/match_affix_spec.rb +0 -43
  125. data/spec/plugin/match_hook_spec.rb +0 -79
  126. data/spec/plugin/middleware_spec.rb +0 -237
  127. data/spec/plugin/middleware_stack_spec.rb +0 -81
  128. data/spec/plugin/module_include_spec.rb +0 -48
  129. data/spec/plugin/multi_route_spec.rb +0 -268
  130. data/spec/plugin/multi_run_spec.rb +0 -87
  131. data/spec/plugin/multi_view_spec.rb +0 -50
  132. data/spec/plugin/multibyte_string_matcher_spec.rb +0 -44
  133. data/spec/plugin/named_templates_spec.rb +0 -96
  134. data/spec/plugin/not_allowed_spec.rb +0 -69
  135. data/spec/plugin/not_found_spec.rb +0 -128
  136. data/spec/plugin/optimized_string_matchers_spec.rb +0 -43
  137. data/spec/plugin/padrino_render_spec.rb +0 -34
  138. data/spec/plugin/param_matchers_spec.rb +0 -69
  139. data/spec/plugin/params_capturing_spec.rb +0 -33
  140. data/spec/plugin/partials_spec.rb +0 -43
  141. data/spec/plugin/pass_spec.rb +0 -29
  142. data/spec/plugin/path_matchers_spec.rb +0 -42
  143. data/spec/plugin/path_rewriter_spec.rb +0 -45
  144. data/spec/plugin/path_spec.rb +0 -222
  145. data/spec/plugin/placeholder_string_matchers_spec.rb +0 -126
  146. data/spec/plugin/precompile_templates_spec.rb +0 -61
  147. data/spec/plugin/public_spec.rb +0 -85
  148. data/spec/plugin/render_each_spec.rb +0 -82
  149. data/spec/plugin/render_locals_spec.rb +0 -114
  150. data/spec/plugin/render_spec.rb +0 -912
  151. data/spec/plugin/request_aref_spec.rb +0 -51
  152. data/spec/plugin/request_headers_spec.rb +0 -39
  153. data/spec/plugin/response_request_spec.rb +0 -43
  154. data/spec/plugin/route_block_args_spec.rb +0 -86
  155. data/spec/plugin/route_csrf_spec.rb +0 -305
  156. data/spec/plugin/run_append_slash_spec.rb +0 -77
  157. data/spec/plugin/run_handler_spec.rb +0 -53
  158. data/spec/plugin/sessions_spec.rb +0 -452
  159. data/spec/plugin/shared_vars_spec.rb +0 -45
  160. data/spec/plugin/sinatra_helpers_spec.rb +0 -537
  161. data/spec/plugin/slash_path_empty_spec.rb +0 -22
  162. data/spec/plugin/static_routing_spec.rb +0 -192
  163. data/spec/plugin/static_spec.rb +0 -30
  164. data/spec/plugin/status_303_spec.rb +0 -28
  165. data/spec/plugin/status_handler_spec.rb +0 -158
  166. data/spec/plugin/streaming_spec.rb +0 -246
  167. data/spec/plugin/strip_path_prefix_spec.rb +0 -24
  168. data/spec/plugin/symbol_matchers_spec.rb +0 -51
  169. data/spec/plugin/symbol_status_spec.rb +0 -25
  170. data/spec/plugin/symbol_views_spec.rb +0 -32
  171. data/spec/plugin/timestamp_public_spec.rb +0 -85
  172. data/spec/plugin/type_routing_spec.rb +0 -348
  173. data/spec/plugin/typecast_params_spec.rb +0 -1370
  174. data/spec/plugin/unescape_path_spec.rb +0 -22
  175. data/spec/plugin/view_options_spec.rb +0 -170
  176. data/spec/plugin_spec.rb +0 -71
  177. data/spec/redirect_spec.rb +0 -41
  178. data/spec/request_spec.rb +0 -97
  179. data/spec/response_spec.rb +0 -199
  180. data/spec/route_spec.rb +0 -39
  181. data/spec/session_middleware_spec.rb +0 -129
  182. data/spec/session_spec.rb +0 -37
  183. data/spec/spec_helper.rb +0 -137
  184. data/spec/version_spec.rb +0 -14
  185. data/spec/views/_test.erb +0 -1
  186. data/spec/views/a.erb +0 -1
  187. data/spec/views/a.rdoc +0 -2
  188. data/spec/views/about.erb +0 -1
  189. data/spec/views/about.str +0 -1
  190. data/spec/views/about/_test.css.gz +0 -0
  191. data/spec/views/about/_test.erb +0 -1
  192. data/spec/views/about/_test.erb.gz +0 -0
  193. data/spec/views/about/comp_test.erb +0 -1
  194. data/spec/views/b.erb +0 -1
  195. data/spec/views/c.erb +0 -1
  196. data/spec/views/comp_layout.erb +0 -1
  197. data/spec/views/comp_test.erb +0 -1
  198. data/spec/views/content-yield.erb +0 -1
  199. data/spec/views/each.str +0 -1
  200. data/spec/views/home.erb +0 -2
  201. data/spec/views/home.str +0 -2
  202. data/spec/views/iv.erb +0 -1
  203. data/spec/views/layout-alternative.erb +0 -2
  204. data/spec/views/layout-yield.erb +0 -3
  205. data/spec/views/layout.erb +0 -2
  206. data/spec/views/layout.str +0 -2
  207. data/spec/views/multiple-layout.erb +0 -1
  208. data/spec/views/multiple.erb +0 -1
@@ -1,77 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- describe "run_append_slash plugin" do
4
- before do
5
- sub2 = app do |r|
6
- r.root do
7
- 'sub-bar-root'
8
- end
9
-
10
- r.get 'baz' do
11
- 'sub-bar-baz'
12
- end
13
- end
14
-
15
- sub1 = app(:run_append_slash) do |r|
16
- r.root do
17
- 'sub-root'
18
- end
19
-
20
- r.get 'foo' do
21
- 'sub-foo'
22
- end
23
-
24
- r.on 'bar' do
25
- r.run sub2
26
- end
27
- end
28
-
29
- app(:bare) do
30
- route do |r|
31
- r.root do
32
- 'root'
33
- end
34
-
35
- r.on 'sub' do
36
- r.run sub1
37
- end
38
- end
39
- end
40
- end
41
-
42
- it "internally appends a missing trailing slash to #run sub apps" do
43
- # Without append slash
44
- body.must_equal 'root'
45
- status('/sub').must_equal 404
46
- body('/sub/').must_equal 'sub-root'
47
- body('/sub/foo').must_equal 'sub-foo'
48
- status('/sub/foo/').must_equal 404
49
- body('/sub/bar/').must_equal 'sub-bar-root'
50
- body('/sub/bar/baz').must_equal 'sub-bar-baz'
51
- status('/sub/bar/baz/').must_equal 404
52
-
53
- # With append slash
54
- app.plugin :run_append_slash
55
- body('/sub').must_equal 'sub-root'
56
- body('/sub/').must_equal 'sub-root'
57
- body('/sub/foo').must_equal 'sub-foo'
58
- status('/sub/foo/').must_equal 404
59
- body('/sub/bar').must_equal 'sub-bar-root'
60
- body('/sub/bar/').must_equal 'sub-bar-root'
61
- body('/sub/bar/baz').must_equal 'sub-bar-baz'
62
- status('/sub/bar/baz/').must_equal 404
63
- end
64
-
65
- it "redirects #run sub apps when trailing slash is missing" do
66
- app.plugin :run_append_slash, :use_redirects => true
67
- status('/sub').must_equal 302
68
- header('Location', '/sub').must_equal '/sub/'
69
- body('/sub/').must_equal 'sub-root'
70
- body('/sub/foo').must_equal 'sub-foo'
71
- status('/sub/foo/').must_equal 404
72
- body('/sub/bar').must_equal 'sub-bar-root'
73
- body('/sub/bar/').must_equal 'sub-bar-root'
74
- body('/sub/bar/baz').must_equal 'sub-bar-baz'
75
- status('/sub/bar/baz/').must_equal 404
76
- end
77
- end
@@ -1,53 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- describe "run_handler plugin" do
4
- it "makes r.run :not_found=>:pass keep going on 404" do
5
- pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
6
- app(:run_handler) do |r|
7
- r.run pr, :not_found=>:pass
8
- 'a'
9
- end
10
-
11
- status.must_equal 201
12
- body.must_equal 'b'
13
- status('/a').must_equal 200
14
- body('/a').must_equal 'a'
15
- end
16
-
17
- it "makes r.run with a block yield rack app to block, and have it be thrown afterward" do
18
- pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
19
- app(:run_handler) do |r|
20
- r.run(pr){|a| a[0] *= 2}
21
- 'a'
22
- end
23
-
24
- status.must_equal 402
25
- status('/a').must_equal 808
26
- end
27
-
28
- it "works when both :not_found=>:pass and block are given" do
29
- pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 202 : 201), {}, ['b']]}
30
- app(:run_handler) do |r|
31
- r.run(pr, :not_found=>:pass){|a| a[0] *= 2}
32
- 'a'
33
- end
34
-
35
- status.must_equal 402
36
- body.must_equal 'b'
37
- status('/a').must_equal 200
38
- body('/a').must_equal 'a'
39
- end
40
-
41
- it "makes r.run work normally if not given an option or block" do
42
- pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
43
- app(:run_handler) do |r|
44
- r.run pr
45
- 'a'
46
- end
47
-
48
- status.must_equal 201
49
- body.must_equal 'b'
50
- status('/a').must_equal 404
51
- body('/a').must_equal 'b'
52
- end
53
- end
@@ -1,452 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- if RUBY_VERSION >= '2'
4
- [true, false].each do |per_cookie_cipher_secret|
5
- describe "sessions plugin with per_cookie_cipher_secret: #{per_cookie_cipher_secret}" do
6
- include CookieJar
7
-
8
- def req(path, opts={})
9
- @errors ||= (errors = []; def errors.puts(s) self << s; end; errors)
10
- super(path, opts.merge('rack.errors'=>@errors))
11
- end
12
-
13
- def errors
14
- e = @errors.dup
15
- @errors.clear
16
- e
17
- end
18
-
19
- before do
20
- app(:bare) do
21
- plugin :sessions, :secret=>'1'*64, :per_cookie_cipher_secret=>per_cookie_cipher_secret
22
- route do |r|
23
- if r.GET['sut']
24
- session
25
- env['roda.session.updated_at'] -= r.GET['sut'].to_i if r.GET['sut']
26
- end
27
- r.get('s', String, String){|k, v| session[k] = v}
28
- r.get('g', String){|k| session[k].to_s}
29
- r.get('sct'){|i| session; env['roda.session.created_at'].to_s}
30
- r.get('ssct', Integer){|i| session; (env['roda.session.created_at'] -= i).to_s}
31
- r.get('ssct2', Integer, String, String){|i, k, v| session[k] = v; (env['roda.session.created_at'] -= i).to_s}
32
- r.get('sc'){session.clear; 'c'}
33
- r.get('cs', String, String){|k, v| clear_session; session[k] = v}
34
- r.get('cat'){t = r.session_created_at; t.strftime("%F") if t}
35
- r.get('uat'){t = r.session_updated_at; t.strftime("%F") if t}
36
- ''
37
- end
38
- end
39
- end
40
-
41
- it "requires appropriate :secret option" do
42
- proc{app(:bare){plugin :sessions}}.must_raise Roda::RodaError
43
- proc{app(:bare){plugin :sessions, :secret=>Object.new}}.must_raise Roda::RodaError
44
- proc{app(:bare){plugin :sessions, :secret=>'1'*63}}.must_raise Roda::RodaError
45
- end
46
-
47
- it "has session store data between requests" do
48
- req('/').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"0"}, [""]]
49
- body('/s/foo/bar').must_equal 'bar'
50
- body('/g/foo').must_equal 'bar'
51
-
52
- body('/s/foo/baz').must_equal 'baz'
53
- body('/g/foo').must_equal 'baz'
54
-
55
- body("/s/foo/\u1234").must_equal "\u1234"
56
- body("/g/foo").must_equal "\u1234"
57
-
58
- errors.must_equal []
59
- end
60
-
61
- it "supports loading sessions created when per_cookie_cipher_secret: #{!per_cookie_cipher_secret} " do
62
- req('/').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"0"}, [""]]
63
- body('/s/foo/bar').must_equal 'bar'
64
- body('/g/foo').must_equal 'bar'
65
-
66
- app.plugin :sessions, :per_cookie_cipher_secret=>!per_cookie_cipher_secret
67
-
68
- body('/s/foo/baz').must_equal 'baz'
69
- body('/g/foo').must_equal 'baz'
70
-
71
- errors.must_equal []
72
- end
73
-
74
- it "allows accessing session creation and last update times" do
75
- status('/cat').must_equal 404
76
- status('/uat').must_equal 404
77
- status('/s/foo/bar').must_equal 200
78
- body('/cat').must_equal Date.today.strftime("%F")
79
- body('/uat').must_equal Date.today.strftime("%F")
80
- status('/ssct2/172800/bar/baz').must_equal 200
81
- body('/cat').must_equal((Date.today - 2).strftime("%F"))
82
- end
83
-
84
- it "does not add Set-Cookie header if session does not change, unless outside :skip_within seconds" do
85
- req('/').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"0"}, [""]]
86
- _, h, b = req('/s/foo/bar')
87
- h['Set-Cookie'].must_match(/\Aroda.session/)
88
- b.must_equal ["bar"]
89
- req('/g/foo').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["bar"]]
90
- req('/s/foo/bar').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["bar"]]
91
-
92
- _, h, b = req('/s/foo/baz')
93
- h['Set-Cookie'].must_match(/\Aroda.session/)
94
- b.must_equal ["baz"]
95
- req('/g/foo').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["baz"]]
96
-
97
- req('/g/foo', 'QUERY_STRING'=>'sut=3500').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["baz"]]
98
- _, h, b = req('/g/foo', 'QUERY_STRING'=>'sut=3700')
99
- h['Set-Cookie'].must_match(/\Aroda.session/)
100
- b.must_equal ["baz"]
101
-
102
- @app.plugin(:sessions, :skip_within=>3800)
103
- req('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["baz"]]
104
- _, h, b = req('/g/foo', 'QUERY_STRING'=>'sut=3900')
105
- h['Set-Cookie'].must_match(/\Aroda.session/)
106
- b.must_equal ["baz"]
107
-
108
- errors.must_equal []
109
- end
110
-
111
- it "removes session cookie when session is submitted but empty after request" do
112
- body('/s/foo/bar').must_equal 'bar'
113
- body('/sct').to_i
114
- body('/g/foo').must_equal 'bar'
115
-
116
- _, h, b = req('/sc')
117
-
118
- # Parameters can come in any order, and only the final parameter may omit the ;
119
- ['roda.session=', 'max-age=0', 'path=/'].each do |param|
120
- h['Set-Cookie'].must_match(/#{Regexp.escape(param)}(;|\z)/)
121
- end
122
- h['Set-Cookie'].must_match(/expires=Thu, 01 Jan 1970 00:00:00 (-0000|GMT)(;|\z)/)
123
-
124
- b.must_equal ['c']
125
-
126
- errors.must_equal []
127
- end
128
-
129
- it "removes session cookie even when max-age and expires are in cookie options" do
130
- app.plugin :sessions, :cookie_options=>{:max_age=>'1000', :expires=>Time.now+1000}
131
- body('/s/foo/bar').must_equal 'bar'
132
- body('/sct').to_i
133
- body('/g/foo').must_equal 'bar'
134
-
135
- _, h, b = req('/sc')
136
-
137
- # Parameters can come in any order, and only the final parameter may omit the ;
138
- ['roda.session=', 'max-age=0', 'path=/'].each do |param|
139
- h['Set-Cookie'].must_match(/#{Regexp.escape(param)}(;|\z)/)
140
- end
141
- h['Set-Cookie'].must_match(/expires=Thu, 01 Jan 1970 00:00:00 (-0000|GMT)(;|\z)/)
142
-
143
- b.must_equal ['c']
144
-
145
- errors.must_equal []
146
- end
147
-
148
- it "sets new session create time when clear_session is called even when session is not empty when serializing" do
149
- body('/s/foo/bar').must_equal 'bar'
150
- sct = body('/sct').to_i
151
- body('/g/foo').must_equal 'bar'
152
- body('/sct').to_i.must_equal sct
153
- body('/ssct/10').to_i.must_equal(sct - 10)
154
-
155
- body('/cs/foo/baz').must_equal 'baz'
156
- body('/sct').to_i.must_be :>=, sct
157
-
158
- errors.must_equal []
159
- end
160
-
161
- it "should include HttpOnly and secure cookie options appropriately" do
162
- h = header('Set-Cookie', '/s/foo/bar')
163
- h.must_include('; HttpOnly')
164
- h.wont_include('; secure')
165
-
166
- h = header('Set-Cookie', '/s/foo/baz', 'HTTPS'=>'on')
167
- h.must_include('; HttpOnly')
168
- h.must_include('; secure')
169
-
170
- @app.plugin(:sessions, :cookie_options=>{})
171
- h = header('Set-Cookie', '/s/foo/bar')
172
- h.must_include('; HttpOnly')
173
- h.wont_include('; secure')
174
- end
175
-
176
- it "should merge :cookie_options options into the default cookie options" do
177
- @app.plugin(:sessions, :cookie_options=>{:secure=>true})
178
- h = header('Set-Cookie', '/s/foo/bar')
179
- h.must_include('; HttpOnly')
180
- h.must_include('; path=/')
181
- h.must_include('; secure')
182
- end
183
-
184
- it "handles secret rotation using :old_secret option" do
185
- body('/s/foo/bar').must_equal 'bar'
186
- body('/g/foo').must_equal 'bar'
187
-
188
- old_cookie = @cookie
189
- @app.plugin(:sessions, :secret=>'2'*64, :old_secret=>'1'*64)
190
- body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
191
-
192
- @app.plugin(:sessions, :secret=>'2'*64, :old_secret=>nil)
193
- body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
194
-
195
- @cookie = old_cookie
196
- body('/g/foo').must_equal ''
197
- errors.must_equal ["Not decoding session: HMAC invalid"]
198
-
199
- proc{app(:bare){plugin :sessions, :old_secret=>'1'*63}}.must_raise Roda::RodaError
200
- proc{app(:bare){plugin :sessions, :old_secret=>Object.new}}.must_raise Roda::RodaError
201
- end
202
-
203
- it "handles secret rotation using :old_secret option when also changing :per_cookie_cipher_secret option" do
204
- body('/s/foo/bar').must_equal 'bar'
205
- body('/g/foo').must_equal 'bar'
206
-
207
- old_cookie = @cookie
208
- @app.plugin(:sessions, :secret=>'2'*64, :old_secret=>'1'*64, :per_cookie_cipher_secret=>!per_cookie_cipher_secret)
209
-
210
- body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
211
-
212
- @app.plugin(:sessions, :secret=>'2'*64, :old_secret=>nil)
213
- body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
214
-
215
- @cookie = old_cookie
216
- body('/g/foo').must_equal ''
217
- errors.must_equal ["Not decoding session: HMAC invalid"]
218
-
219
- proc{app(:bare){plugin :sessions, :old_secret=>'1'*63}}.must_raise Roda::RodaError
220
- proc{app(:bare){plugin :sessions, :old_secret=>Object.new}}.must_raise Roda::RodaError
221
- end
222
-
223
- it "pads data by default to make it more difficult to guess session contents based on size" do
224
- long = "bar"*35
225
-
226
- _, h1, b = req('/s/foo/bar')
227
- b.must_equal ['bar']
228
- _, h2, b = req('/s/foo/bar', 'QUERY_STRING'=>'sut=3700')
229
- b.must_equal ['bar']
230
- _, h3, b = req('/s/foo/bar2')
231
- b.must_equal ['bar2']
232
- _, h4, b = req("/s/foo/#{long}")
233
- b.must_equal [long]
234
- h1['Set-Cookie'].length.must_equal h2['Set-Cookie'].length
235
- h1['Set-Cookie'].wont_equal h2['Set-Cookie']
236
- h1['Set-Cookie'].length.must_equal h3['Set-Cookie'].length
237
- h1['Set-Cookie'].wont_equal h3['Set-Cookie']
238
- h1['Set-Cookie'].length.wont_equal h4['Set-Cookie'].length
239
-
240
- @app.plugin(:sessions, :pad_size=>256)
241
-
242
- _, h1, b = req('/s/foo/bar')
243
- b.must_equal ['bar']
244
- _, h2, b = req('/s/foo/bar', 'QUERY_STRING'=>'sut=3700')
245
- b.must_equal ['bar']
246
- _, h3, b = req('/s/foo/bar2')
247
- b.must_equal ['bar2']
248
- _, h4, b = req("/s/foo/#{long}")
249
- b.must_equal [long]
250
- h1['Set-Cookie'].length.must_equal h2['Set-Cookie'].length
251
- h1['Set-Cookie'].wont_equal h2['Set-Cookie']
252
- h1['Set-Cookie'].length.must_equal h3['Set-Cookie'].length
253
- h1['Set-Cookie'].wont_equal h3['Set-Cookie']
254
- h1['Set-Cookie'].length.must_equal h4['Set-Cookie'].length
255
- h1['Set-Cookie'].wont_equal h3['Set-Cookie']
256
-
257
- @app.plugin(:sessions, :pad_size=>nil)
258
-
259
- _, h1, b = req('/s/foo/bar')
260
- b.must_equal ['bar']
261
- _, h2, b = req('/s/foo/bar', 'QUERY_STRING'=>'sut=3700')
262
- b.must_equal ['bar']
263
- _, h3, b = req('/s/foo/bar2')
264
- b.must_equal ['bar2']
265
- h1['Set-Cookie'].length.must_equal h2['Set-Cookie'].length
266
- h1['Set-Cookie'].wont_equal h2['Set-Cookie']
267
- if !defined?(JRUBY_VERSION) || JRUBY_VERSION >= '9.2'
268
- h1['Set-Cookie'].length.wont_equal h3['Set-Cookie'].length
269
- end
270
-
271
- proc{@app.plugin(:sessions, :pad_size=>0)}.must_raise Roda::RodaError
272
- proc{@app.plugin(:sessions, :pad_size=>1)}.must_raise Roda::RodaError
273
- proc{@app.plugin(:sessions, :pad_size=>Object.new)}.must_raise Roda::RodaError
274
-
275
- errors.must_equal []
276
- end
277
-
278
- it "compresses data over a certain size by default" do
279
- long = 'b'*8192
280
- proc{body("/s/foo/#{long}")}.must_raise Roda::RodaPlugins::Sessions::CookieTooLarge
281
-
282
- @app.plugin(:sessions, :gzip_over=>8000)
283
- body("/s/foo/#{long}").must_equal long
284
- body("/g/foo", 'QUERY_STRING'=>'sut=3700').must_equal long
285
-
286
- @app.plugin(:sessions, :gzip_over=>15000)
287
- proc{body("/g/foo", 'QUERY_STRING'=>'sut=3700')}.must_raise Roda::RodaPlugins::Sessions::CookieTooLarge
288
-
289
- errors.must_equal []
290
- end
291
-
292
- it "raises CookieTooLarge if cookie is too large" do
293
- proc{req('/s/foo/'+Base64.urlsafe_encode64(SecureRandom.random_bytes(8192)))}.must_raise Roda::RodaPlugins::Sessions::CookieTooLarge
294
- end
295
-
296
- it "ignores session cookies if session exceeds max time since create" do
297
- body("/s/foo/bar").must_equal 'bar'
298
- body("/g/foo").must_equal 'bar'
299
-
300
- @app.plugin(:sessions, :max_seconds=>-1)
301
- body("/g/foo").must_equal ''
302
- errors.must_equal ["Not returning session: maximum session time expired"]
303
-
304
- @app.plugin(:sessions, :max_seconds=>10)
305
- body("/s/foo/bar").must_equal 'bar'
306
- body("/g/foo").must_equal 'bar'
307
-
308
- errors.must_equal []
309
- end
310
-
311
- it "ignores session cookies if session exceeds max idle time since update" do
312
- body("/s/foo/bar").must_equal 'bar'
313
- body("/g/foo").must_equal 'bar'
314
-
315
- @app.plugin(:sessions, :max_idle_seconds=>-1)
316
- body("/g/foo").must_equal ''
317
- errors.must_equal ["Not returning session: maximum session idle time expired"]
318
-
319
- @app.plugin(:sessions, :max_idle_seconds=>10)
320
- body("/s/foo/bar").must_equal 'bar'
321
- body("/g/foo").must_equal 'bar'
322
-
323
- errors.must_equal []
324
- end
325
-
326
- it "supports :serializer and :parser options to override serializer/deserializer" do
327
- body('/s/foo/bar').must_equal 'bar'
328
-
329
- @app.plugin(:sessions, :parser=>proc{|s| JSON.parse("{#{s[1...-1].reverse}}")})
330
- body('/g/rab').must_equal 'oof'
331
-
332
- @app.plugin(:sessions, :serializer=>proc{|s| s.to_json.upcase})
333
-
334
- body('/s/foo/baz').must_equal 'baz'
335
- body('/g/ZAB').must_equal 'OOF'
336
-
337
- errors.must_equal []
338
- end
339
-
340
- it "logs session decoding errors to rack.errors" do
341
- body('/s/foo/bar').must_equal 'bar'
342
- c = @cookie.dup
343
- k = c.split('=', 2)[0] + '='
344
-
345
- @cookie[20] = '!'
346
- body('/g/foo').must_equal ''
347
- errors.must_equal ["Unable to decode session: invalid base64"]
348
-
349
- @cookie = k+Base64.urlsafe_encode64('')
350
- body('/g/foo').must_equal ''
351
- errors.must_equal ["Unable to decode session: no data"]
352
-
353
- @cookie = k+Base64.urlsafe_encode64("\0" * 60)
354
- body('/g/foo').must_equal ''
355
- errors.must_equal ["Unable to decode session: data too short"]
356
-
357
- @cookie = k+Base64.urlsafe_encode64("\1" * 92)
358
- body('/g/foo').must_equal ''
359
- errors.must_equal ["Unable to decode session: data too short"]
360
-
361
- @cookie = k+Base64.urlsafe_encode64('1'*75)
362
- body('/g/foo').must_equal ''
363
- errors.must_equal ["Unable to decode session: version marker unsupported"]
364
-
365
- @cookie = k+Base64.urlsafe_encode64("\0"*75)
366
- body('/g/foo').must_equal ''
367
- errors.must_equal ["Not decoding session: HMAC invalid"]
368
- end
369
- end
370
-
371
- describe "sessions plugin" do
372
- include CookieJar
373
-
374
- def req(path, opts={})
375
- @errors ||= (errors = []; def errors.puts(s) self << s; end; errors)
376
- super(path, opts.merge('rack.errors'=>@errors))
377
- end
378
-
379
- def errors
380
- e = @errors.dup
381
- @errors.clear
382
- e
383
- end
384
-
385
- it "supports transparent upgrade from Rack::Session::Cookie with default HMAC and coder" do
386
- app(:bare) do
387
- use Rack::Session::Cookie, :secret=>'1'
388
- plugin :middleware_stack
389
- route do |r|
390
- r.get('s', String, String){|k, v| session[k] = {:a=>v}; v}
391
- r.get('g', String){|k| session[k].inspect}
392
- ''
393
- end
394
- end
395
-
396
- _, h, b = req('/s/foo/bar')
397
- (h['Set-Cookie'] =~ /\A(rack\.session=.*); path=\/; HttpOnly\z/).must_equal 0
398
- c = $1
399
- b.must_equal ['bar']
400
- _, h, b = req('/g/foo')
401
- h['Set-Cookie'].must_be_nil
402
- b.must_equal ['{:a=>"bar"}']
403
-
404
- @app.plugin :sessions, :secret=>'1'*64,
405
- :upgrade_from_rack_session_cookie_secret=>'1'
406
- @app.middleware_stack.remove{|m, *| m == Rack::Session::Cookie}
407
-
408
- @cookie = c.dup
409
- @cookie.slice!(15)
410
- body('/g/foo').must_equal 'nil'
411
- errors.must_equal ["Not decoding Rack::Session::Cookie session: HMAC invalid"]
412
-
413
- @cookie = c.split('--', 2)[0]
414
- body('/g/foo').must_equal 'nil'
415
- errors.must_equal ["Not decoding Rack::Session::Cookie session: invalid format"]
416
-
417
- @cookie = c.split('--', 2)[0][13..-1]
418
- @cookie = Rack::Utils.unescape(@cookie).unpack('m')[0]
419
- @cookie[2] = "^"
420
- @cookie = [@cookie].pack('m')
421
- cookie = String.new
422
- cookie << 'rack.session=' << @cookie << '--' << OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, '1', @cookie)
423
- @cookie = cookie
424
- body('/g/foo').must_equal 'nil'
425
- errors.must_equal ["Error decoding Rack::Session::Cookie session: not base64 encoded marshal dump"]
426
-
427
- @cookie = c
428
- _, h, b = req('/g/foo')
429
- h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/; HttpOnly(; SameSite=Lax)?\nrack\.session=; path=\/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
430
- b.must_equal ['{"a"=>"bar"}']
431
-
432
- @app.plugin :sessions, :cookie_options=>{:path=>'/foo'}, :upgrade_from_rack_session_cookie_options=>{}
433
- @cookie = c
434
- _, h, b = req('/g/foo')
435
- h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/foo; HttpOnly(; SameSite=Lax)?\nrack\.session=; path=\/foo; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
436
- b.must_equal ['{"a"=>"bar"}']
437
-
438
- @app.plugin :sessions, :upgrade_from_rack_session_cookie_options=>{:path=>'/baz'}
439
- @cookie = c
440
- _, h, b = req('/g/foo')
441
- h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/foo; HttpOnly(; SameSite=Lax)?\nrack\.session=; path=\/baz; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
442
- b.must_equal ['{"a"=>"bar"}']
443
-
444
- @app.plugin :sessions, :upgrade_from_rack_session_cookie_key=>'quux.session'
445
- @cookie = c.sub(/\Arack/, 'quux')
446
- _, h, b = req('/g/foo')
447
- h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/foo; HttpOnly(; SameSite=Lax)?\nquux\.session=; path=\/baz; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
448
- b.must_equal ['{"a"=>"bar"}']
449
- end
450
- end
451
- end
452
- end