roda 3.28.0 → 3.29.0

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.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +10 -0
  3. data/README.rdoc +10 -0
  4. data/doc/release_notes/3.29.0.txt +15 -0
  5. data/lib/roda.rb +1 -0
  6. data/lib/roda/plugins/caching.rb +2 -0
  7. data/lib/roda/plugins/common_logger.rb +1 -1
  8. data/lib/roda/plugins/exception_page.rb +7 -1
  9. data/lib/roda/plugins/indifferent_params.rb +2 -0
  10. data/lib/roda/version.rb +1 -1
  11. metadata +4 -214
  12. data/Rakefile +0 -108
  13. data/doc/release_notes/1.0.0.txt +0 -329
  14. data/doc/release_notes/1.1.0.txt +0 -226
  15. data/doc/release_notes/1.2.0.txt +0 -406
  16. data/doc/release_notes/1.3.0.txt +0 -109
  17. data/doc/release_notes/2.0.0.txt +0 -75
  18. data/doc/release_notes/2.1.0.txt +0 -124
  19. data/doc/release_notes/2.10.0.txt +0 -27
  20. data/doc/release_notes/2.11.0.txt +0 -70
  21. data/doc/release_notes/2.12.0.txt +0 -40
  22. data/doc/release_notes/2.13.0.txt +0 -10
  23. data/doc/release_notes/2.14.0.txt +0 -44
  24. data/doc/release_notes/2.15.0.txt +0 -53
  25. data/doc/release_notes/2.16.0.txt +0 -48
  26. data/doc/release_notes/2.17.0.txt +0 -62
  27. data/doc/release_notes/2.18.0.txt +0 -69
  28. data/doc/release_notes/2.19.0.txt +0 -30
  29. data/doc/release_notes/2.2.0.txt +0 -97
  30. data/doc/release_notes/2.20.0.txt +0 -5
  31. data/doc/release_notes/2.21.0.txt +0 -17
  32. data/doc/release_notes/2.22.0.txt +0 -41
  33. data/doc/release_notes/2.23.0.txt +0 -29
  34. data/doc/release_notes/2.24.0.txt +0 -65
  35. data/doc/release_notes/2.25.0.txt +0 -14
  36. data/doc/release_notes/2.26.0.txt +0 -13
  37. data/doc/release_notes/2.27.0.txt +0 -56
  38. data/doc/release_notes/2.28.0.txt +0 -17
  39. data/doc/release_notes/2.29.0.txt +0 -156
  40. data/doc/release_notes/2.3.0.txt +0 -109
  41. data/doc/release_notes/2.4.0.txt +0 -55
  42. data/doc/release_notes/2.5.0.txt +0 -23
  43. data/doc/release_notes/2.5.1.txt +0 -4
  44. data/doc/release_notes/2.6.0.txt +0 -21
  45. data/doc/release_notes/2.7.0.txt +0 -75
  46. data/doc/release_notes/2.8.0.txt +0 -44
  47. data/doc/release_notes/2.9.0.txt +0 -6
  48. data/spec/all.rb +0 -1
  49. data/spec/assets/css/app.scss +0 -1
  50. data/spec/assets/css/no_access.css +0 -1
  51. data/spec/assets/css/raw.css +0 -1
  52. data/spec/assets/js/head/app.js +0 -1
  53. data/spec/composition_spec.rb +0 -31
  54. data/spec/define_roda_method_spec.rb +0 -274
  55. data/spec/env_spec.rb +0 -11
  56. data/spec/freeze_spec.rb +0 -37
  57. data/spec/integration_spec.rb +0 -209
  58. data/spec/matchers_spec.rb +0 -832
  59. data/spec/opts_spec.rb +0 -42
  60. data/spec/plugin/_after_hook_spec.rb +0 -19
  61. data/spec/plugin/all_verbs_spec.rb +0 -29
  62. data/spec/plugin/assets_preloading_spec.rb +0 -98
  63. data/spec/plugin/assets_spec.rb +0 -745
  64. data/spec/plugin/backtracking_array_spec.rb +0 -42
  65. data/spec/plugin/branch_locals_spec.rb +0 -106
  66. data/spec/plugin/caching_spec.rb +0 -337
  67. data/spec/plugin/chunked_spec.rb +0 -201
  68. data/spec/plugin/class_level_routing_spec.rb +0 -164
  69. data/spec/plugin/class_matchers_spec.rb +0 -40
  70. data/spec/plugin/common_logger_spec.rb +0 -85
  71. data/spec/plugin/content_for_spec.rb +0 -162
  72. data/spec/plugin/content_security_policy_spec.rb +0 -175
  73. data/spec/plugin/cookies_spec.rb +0 -51
  74. data/spec/plugin/csrf_spec.rb +0 -111
  75. data/spec/plugin/default_headers_spec.rb +0 -82
  76. data/spec/plugin/default_status_spec.rb +0 -95
  77. data/spec/plugin/delay_build_spec.rb +0 -23
  78. data/spec/plugin/delegate_spec.rb +0 -23
  79. data/spec/plugin/delete_empty_headers_spec.rb +0 -27
  80. data/spec/plugin/direct_call_spec.rb +0 -28
  81. data/spec/plugin/disallow_file_uploads_spec.rb +0 -25
  82. data/spec/plugin/drop_body_spec.rb +0 -24
  83. data/spec/plugin/early_hints_spec.rb +0 -19
  84. data/spec/plugin/empty_root_spec.rb +0 -14
  85. data/spec/plugin/environments_spec.rb +0 -42
  86. data/spec/plugin/error_email_spec.rb +0 -97
  87. data/spec/plugin/error_handler_spec.rb +0 -216
  88. data/spec/plugin/error_mail_spec.rb +0 -93
  89. data/spec/plugin/exception_page_spec.rb +0 -168
  90. data/spec/plugin/flash_spec.rb +0 -121
  91. data/spec/plugin/h_spec.rb +0 -11
  92. data/spec/plugin/halt_spec.rb +0 -119
  93. data/spec/plugin/hash_matcher_spec.rb +0 -27
  94. data/spec/plugin/hash_routes_spec.rb +0 -535
  95. data/spec/plugin/head_spec.rb +0 -52
  96. data/spec/plugin/header_matchers_spec.rb +0 -98
  97. data/spec/plugin/heartbeat_spec.rb +0 -74
  98. data/spec/plugin/hooks_spec.rb +0 -152
  99. data/spec/plugin/indifferent_params_spec.rb +0 -14
  100. data/spec/plugin/json_parser_spec.rb +0 -141
  101. data/spec/plugin/json_spec.rb +0 -83
  102. data/spec/plugin/mail_processor_spec.rb +0 -451
  103. data/spec/plugin/mailer_spec.rb +0 -282
  104. data/spec/plugin/match_affix_spec.rb +0 -43
  105. data/spec/plugin/match_hook_spec.rb +0 -79
  106. data/spec/plugin/middleware_spec.rb +0 -237
  107. data/spec/plugin/middleware_stack_spec.rb +0 -81
  108. data/spec/plugin/module_include_spec.rb +0 -48
  109. data/spec/plugin/multi_route_spec.rb +0 -268
  110. data/spec/plugin/multi_run_spec.rb +0 -87
  111. data/spec/plugin/multi_view_spec.rb +0 -50
  112. data/spec/plugin/multibyte_string_matcher_spec.rb +0 -44
  113. data/spec/plugin/named_templates_spec.rb +0 -96
  114. data/spec/plugin/not_allowed_spec.rb +0 -69
  115. data/spec/plugin/not_found_spec.rb +0 -128
  116. data/spec/plugin/optimized_string_matchers_spec.rb +0 -43
  117. data/spec/plugin/padrino_render_spec.rb +0 -34
  118. data/spec/plugin/param_matchers_spec.rb +0 -69
  119. data/spec/plugin/params_capturing_spec.rb +0 -33
  120. data/spec/plugin/partials_spec.rb +0 -43
  121. data/spec/plugin/pass_spec.rb +0 -29
  122. data/spec/plugin/path_matchers_spec.rb +0 -42
  123. data/spec/plugin/path_rewriter_spec.rb +0 -45
  124. data/spec/plugin/path_spec.rb +0 -222
  125. data/spec/plugin/placeholder_string_matchers_spec.rb +0 -126
  126. data/spec/plugin/precompile_templates_spec.rb +0 -61
  127. data/spec/plugin/public_spec.rb +0 -85
  128. data/spec/plugin/render_each_spec.rb +0 -82
  129. data/spec/plugin/render_locals_spec.rb +0 -114
  130. data/spec/plugin/render_spec.rb +0 -912
  131. data/spec/plugin/request_aref_spec.rb +0 -51
  132. data/spec/plugin/request_headers_spec.rb +0 -39
  133. data/spec/plugin/response_request_spec.rb +0 -43
  134. data/spec/plugin/route_block_args_spec.rb +0 -86
  135. data/spec/plugin/route_csrf_spec.rb +0 -305
  136. data/spec/plugin/run_append_slash_spec.rb +0 -77
  137. data/spec/plugin/run_handler_spec.rb +0 -53
  138. data/spec/plugin/sessions_spec.rb +0 -452
  139. data/spec/plugin/shared_vars_spec.rb +0 -45
  140. data/spec/plugin/sinatra_helpers_spec.rb +0 -537
  141. data/spec/plugin/slash_path_empty_spec.rb +0 -22
  142. data/spec/plugin/static_routing_spec.rb +0 -192
  143. data/spec/plugin/static_spec.rb +0 -30
  144. data/spec/plugin/status_303_spec.rb +0 -28
  145. data/spec/plugin/status_handler_spec.rb +0 -158
  146. data/spec/plugin/streaming_spec.rb +0 -246
  147. data/spec/plugin/strip_path_prefix_spec.rb +0 -24
  148. data/spec/plugin/symbol_matchers_spec.rb +0 -51
  149. data/spec/plugin/symbol_status_spec.rb +0 -25
  150. data/spec/plugin/symbol_views_spec.rb +0 -32
  151. data/spec/plugin/timestamp_public_spec.rb +0 -85
  152. data/spec/plugin/type_routing_spec.rb +0 -348
  153. data/spec/plugin/typecast_params_spec.rb +0 -1370
  154. data/spec/plugin/unescape_path_spec.rb +0 -22
  155. data/spec/plugin/view_options_spec.rb +0 -170
  156. data/spec/plugin_spec.rb +0 -71
  157. data/spec/redirect_spec.rb +0 -41
  158. data/spec/request_spec.rb +0 -97
  159. data/spec/response_spec.rb +0 -199
  160. data/spec/route_spec.rb +0 -39
  161. data/spec/session_middleware_spec.rb +0 -129
  162. data/spec/session_spec.rb +0 -37
  163. data/spec/spec_helper.rb +0 -137
  164. data/spec/version_spec.rb +0 -14
  165. data/spec/views/_test.erb +0 -1
  166. data/spec/views/a.erb +0 -1
  167. data/spec/views/a.rdoc +0 -2
  168. data/spec/views/about.erb +0 -1
  169. data/spec/views/about.str +0 -1
  170. data/spec/views/about/_test.css.gz +0 -0
  171. data/spec/views/about/_test.erb +0 -1
  172. data/spec/views/about/_test.erb.gz +0 -0
  173. data/spec/views/about/comp_test.erb +0 -1
  174. data/spec/views/b.erb +0 -1
  175. data/spec/views/c.erb +0 -1
  176. data/spec/views/comp_layout.erb +0 -1
  177. data/spec/views/comp_test.erb +0 -1
  178. data/spec/views/content-yield.erb +0 -1
  179. data/spec/views/each.str +0 -1
  180. data/spec/views/home.erb +0 -2
  181. data/spec/views/home.str +0 -2
  182. data/spec/views/iv.erb +0 -1
  183. data/spec/views/layout-alternative.erb +0 -2
  184. data/spec/views/layout-yield.erb +0 -3
  185. data/spec/views/layout.erb +0 -2
  186. data/spec/views/layout.str +0 -2
  187. data/spec/views/multiple-layout.erb +0 -1
  188. 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