rocketio 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -5
  3. data/.pryrc +2 -0
  4. data/.travis.yml +3 -0
  5. data/README.md +22 -5
  6. data/Rakefile +7 -1
  7. data/bin/console +14 -0
  8. data/bin/setup +7 -0
  9. data/lib/rocketio.rb +131 -3
  10. data/lib/rocketio/application.rb +31 -0
  11. data/lib/rocketio/controller.rb +288 -0
  12. data/lib/rocketio/controller/authentication.rb +141 -0
  13. data/lib/rocketio/controller/authorization.rb +53 -0
  14. data/lib/rocketio/controller/cookies.rb +59 -0
  15. data/lib/rocketio/controller/error_handlers.rb +89 -0
  16. data/lib/rocketio/controller/filters.rb +119 -0
  17. data/lib/rocketio/controller/flash.rb +21 -0
  18. data/lib/rocketio/controller/helpers.rb +438 -0
  19. data/lib/rocketio/controller/middleware.rb +32 -0
  20. data/lib/rocketio/controller/render.rb +148 -0
  21. data/lib/rocketio/controller/render/engine.rb +76 -0
  22. data/lib/rocketio/controller/render/layout.rb +27 -0
  23. data/lib/rocketio/controller/render/layouts.rb +85 -0
  24. data/lib/rocketio/controller/render/templates.rb +83 -0
  25. data/lib/rocketio/controller/request.rb +115 -0
  26. data/lib/rocketio/controller/response.rb +84 -0
  27. data/lib/rocketio/controller/sessions.rb +64 -0
  28. data/lib/rocketio/controller/token_auth.rb +118 -0
  29. data/lib/rocketio/controller/websocket.rb +21 -0
  30. data/lib/rocketio/error_templates/404.html +3 -0
  31. data/lib/rocketio/error_templates/409.html +7 -0
  32. data/lib/rocketio/error_templates/500.html +3 -0
  33. data/lib/rocketio/error_templates/501.html +6 -0
  34. data/lib/rocketio/error_templates/layout.html +1 -0
  35. data/lib/rocketio/exceptions.rb +4 -0
  36. data/lib/rocketio/router.rb +65 -0
  37. data/lib/rocketio/util.rb +122 -0
  38. data/lib/rocketio/version.rb +2 -2
  39. data/rocketio.gemspec +21 -17
  40. data/test/aliases_test.rb +54 -0
  41. data/test/authentication_test.rb +307 -0
  42. data/test/authorization_test.rb +91 -0
  43. data/test/cache_control_test.rb +268 -0
  44. data/test/content_type_test.rb +124 -0
  45. data/test/cookies_test.rb +49 -0
  46. data/test/error_handlers_test.rb +125 -0
  47. data/test/etag_test.rb +445 -0
  48. data/test/filters_test.rb +177 -0
  49. data/test/halt_test.rb +73 -0
  50. data/test/helpers_test.rb +171 -0
  51. data/test/middleware_test.rb +57 -0
  52. data/test/redirect_test.rb +135 -0
  53. data/test/render/engine_test.rb +71 -0
  54. data/test/render/get.erb +1 -0
  55. data/test/render/items.erb +1 -0
  56. data/test/render/layout.erb +1 -0
  57. data/test/render/layout_test.rb +104 -0
  58. data/test/render/layouts/master.erb +1 -0
  59. data/test/render/layouts_test.rb +145 -0
  60. data/test/render/master.erb +1 -0
  61. data/test/render/post.erb +1 -0
  62. data/test/render/put.erb +1 -0
  63. data/test/render/render_test.rb +101 -0
  64. data/test/render/setup.rb +14 -0
  65. data/test/render/templates/a/get.erb +1 -0
  66. data/test/render/templates/master.erb +1 -0
  67. data/test/render/templates_test.rb +146 -0
  68. data/test/request_test.rb +105 -0
  69. data/test/response_test.rb +119 -0
  70. data/test/routes_test.rb +70 -0
  71. data/test/sendfile_test.rb +209 -0
  72. data/test/sessions_test.rb +176 -0
  73. data/test/setup.rb +59 -0
  74. metadata +144 -9
  75. data/LICENSE.txt +0 -22
@@ -0,0 +1,91 @@
1
+ require 'setup'
2
+
3
+ spec :AuthorizationTest do
4
+ context :inheritance do
5
+ it 'inherits token auth procedures from superclass' do
6
+ a = mock_controller {
7
+ token_auth {|t| t == 'x'}
8
+ }
9
+ b = mock_controller(a)
10
+ app(b)
11
+ get
12
+ assert(last_response.status) == 401
13
+ token_authorize 'x'
14
+ get
15
+ assert(last_response.status) == 501
16
+ end
17
+
18
+ it 'directly overrides token auth inherited from superclass' do
19
+ a = mock_controller {
20
+ token_auth {|t| t == 'x'}
21
+ }
22
+ b = mock_controller(a) {
23
+ token_auth {|t| t == 'y'}
24
+ }
25
+ app(b)
26
+ get
27
+ assert(last_response.status) == 401
28
+ token_authorize 'y'
29
+ get
30
+ assert(last_response.status) == 501
31
+ end
32
+
33
+ it 'uses `inherit` to override token auth inherited from superclass' do
34
+ a = mock_controller {
35
+ token_auth {|t| t == 'x'}
36
+ }
37
+ b = mock_controller(a) {
38
+ token_auth {|t| t == 'y'}
39
+ }
40
+ c = mock_controller(a) {
41
+ inherit :token_auth, from: b
42
+ }
43
+ app(c)
44
+ get
45
+ assert(last_response.status) == 401
46
+ token_authorize 'y'
47
+ get
48
+ assert(last_response.status) == 501
49
+ end
50
+
51
+ it 'inherits token auth procedures via `inherit`' do
52
+ a = mock_controller {
53
+ token_auth {|t| t == 'x'}
54
+ }
55
+ b = mock_controller {
56
+ inherit :token_auth, from: a
57
+ }
58
+ app(b)
59
+ get
60
+ assert(last_response.status) == 401
61
+ token_authorize 'x'
62
+ get
63
+ assert(last_response.status) == 501
64
+ end
65
+ end
66
+
67
+ context 'protect all request methods' do
68
+ before do
69
+ app mock_controller {
70
+ token_auth {|t| t == 'st'}
71
+ define_method(:get) {}
72
+ }
73
+ end
74
+
75
+ it 'return "401 Unauthorized" if token missing' do
76
+ get
77
+ assert(last_response.status) == 401
78
+ end
79
+
80
+ it 'return "401 Unauthorized" if token is wrong' do
81
+ get
82
+ assert(last_response.status) == 401
83
+ end
84
+
85
+ it 'return "200 Ok" when correct token provided' do
86
+ token_auth 'st'
87
+ get
88
+ assert(last_response.status) == 200
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,268 @@
1
+ # Copyright (c) 2007, 2008, 2009 Blake Mizerany
2
+ # Copyright (c) 2010, 2011, 2012, 2013, 2014 Konstantin Haase
3
+ # Copyright (c) 2015 Slee Woo
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation
7
+ # files (the "Software"), to deal in the Software without
8
+ # restriction, including without limitation the rights to use,
9
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following
12
+ # conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ # OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require 'setup'
27
+
28
+ spec 'cache_control' do
29
+ before do
30
+ a = mock_controller('/foo') {
31
+ def get
32
+ cache_control :public, :no_cache, :max_age => 60.0
33
+ 'Hello World'
34
+ end
35
+ }
36
+ b = mock_controller('/bar') {
37
+ def get
38
+ cache_control :public, :no_cache
39
+ 'Hello World'
40
+ end
41
+ }
42
+ app mock_app(a, b)
43
+ end
44
+
45
+ it 'sets the Cache-Control header' do
46
+ get '/foo'
47
+ assert(last_response['Cache-Control'].split(', ')) == ['public', 'no-cache', 'max-age=60']
48
+ end
49
+
50
+ it 'last argument does not have to be a hash' do
51
+ get '/bar'
52
+ assert(last_response['Cache-Control'].split(', ')) == ['public', 'no-cache']
53
+ end
54
+ end
55
+
56
+ spec 'expires' do
57
+ before do
58
+ foo = mock_controller('/foo') {
59
+ def get
60
+ expires 60, :public, :no_cache
61
+ 'Hello World'
62
+ end
63
+ }
64
+ bar = mock_controller('/bar') {
65
+ def get; expires Time.now end
66
+ }
67
+ baz = mock_controller('/baz') {
68
+ def get; expires Time.at(0) end
69
+ }
70
+ blah = mock_controller('/blah') {
71
+ def get
72
+ obj = Object.new
73
+ def obj.method_missing(*a, &b) 60.send(*a, &b) end
74
+ def obj.is_a?(thing) 60.is_a?(thing) end
75
+ expires obj, :public, :no_cache
76
+ 'Hello World'
77
+ end
78
+ }
79
+ boom = mock_controller('/boom') {
80
+ def get; expires '9999' end
81
+ }
82
+ app mock_app(foo, bar, baz, blah, boom)
83
+ end
84
+
85
+ it 'sets the Cache-Control header' do
86
+ get '/foo'
87
+ assert(last_response['Cache-Control'].split(', ')) == ['public', 'no-cache', 'max-age=60']
88
+ end
89
+
90
+ it 'sets the Expires header' do
91
+ get '/foo'
92
+ refute(last_response['Expires']).nil?
93
+ end
94
+
95
+ it 'allows passing Time.now objects' do
96
+ get '/bar'
97
+ refute(last_response['Expires']).nil?
98
+ end
99
+
100
+ it 'allows passing Time.at objects' do
101
+ get '/baz'
102
+ assert(last_response['Expires']) == 'Thu, 01 Jan 1970 00:00:00 GMT'
103
+ end
104
+
105
+ it 'accepts values pretending to be a Numeric (like ActiveSupport::Duration)' do
106
+ get '/blah'
107
+ assert(last_response['Cache-Control'].split(', ')) == ['public', 'no-cache', 'max-age=60']
108
+ end
109
+
110
+ it 'fails when Time.parse raises an ArgumentError' do
111
+ get '/boom'
112
+ assert(last_response.status) == 500
113
+ end
114
+ end
115
+
116
+ spec 'last_modified' do
117
+ it 'ignores nil' do
118
+ app mock_controller {
119
+ def get; last_modified nil; ''; end
120
+ }
121
+
122
+ get
123
+ assert(last_response['Last-Modified']).nil?
124
+ end
125
+
126
+ it 'does not change a status other than 200' do
127
+ app mock_controller {
128
+ def get
129
+ response.status = 299
130
+ last_modified Time.at(0)
131
+ 'ok'
132
+ end
133
+ }
134
+
135
+ env['HTTP_IF_MODIFIED_SINCE'] = 'Sun, 26 Sep 2030 23:43:52 GMT'
136
+ get
137
+ assert(last_response.status) == 299
138
+ assert(last_response.body) == 'ok'
139
+ end
140
+
141
+ [
142
+ Time.now,
143
+ DateTime.now,
144
+ Date.today,
145
+ Time.now.to_i,
146
+ Struct.new(:to_time).new(Time.now)
147
+ ].each do |last_modified_time|
148
+ context "with #{last_modified_time.class.name}" do
149
+ before do
150
+ app mock_controller {
151
+ define_method :get do
152
+ last_modified(last_modified_time)
153
+ 'Boo!'
154
+ end
155
+ }
156
+ @last_modified_time = mock_controller.initialize_controller.time_for(last_modified_time)
157
+ end
158
+
159
+ context "when there's no If-Modified-Since header" do
160
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
161
+ get
162
+ assert(last_response['Last-Modified']) == @last_modified_time.httpdate
163
+ end
164
+
165
+ it 'conditional GET misses and returns a body' do
166
+ get
167
+ assert(last_response.status) == 200
168
+ assert(last_response.body) == 'Boo!'
169
+ end
170
+ end
171
+
172
+ context "when there's an invalid If-Modified-Since header" do
173
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
174
+ env['HTTP_IF_MODIFIED_SINCE'] = 'a really weird date'
175
+ get
176
+ assert(last_response['Last-Modified']) == @last_modified_time.httpdate
177
+ end
178
+
179
+ it 'conditional GET misses and returns a body' do
180
+ env['HTTP_IF_MODIFIED_SINCE'] = 'a really weird date'
181
+ get
182
+ assert(last_response.status) == 200
183
+ assert(last_response.body) == 'Boo!'
184
+ end
185
+ end
186
+
187
+ context "when the resource has been modified since the If-Modified-Since header date" do
188
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
189
+ env['HTTP_IF_MODIFIED_SINCE'] = (@last_modified_time - 1).httpdate
190
+ get
191
+ assert(last_response['Last-Modified']) == @last_modified_time.httpdate
192
+ end
193
+
194
+ it 'conditional GET misses and returns a body' do
195
+ env['HTTP_IF_MODIFIED_SINCE'] = (@last_modified_time - 1).httpdate
196
+ get
197
+ assert(last_response.status) == 200
198
+ assert(last_response.body) == 'Boo!'
199
+ end
200
+
201
+ it 'does not rely on string comparison' do
202
+ app mock_controller {
203
+ def get
204
+ last_modified "Mon, 18 Oct 2010 20:57:11 GMT"
205
+ "foo"
206
+ end
207
+ }
208
+
209
+ env['HTTP_IF_MODIFIED_SINCE'] = 'Sun, 26 Sep 2010 23:43:52 GMT'
210
+ get
211
+ assert(last_response.status) == 200
212
+ assert(last_response.body) == 'foo'
213
+
214
+ env['HTTP_IF_MODIFIED_SINCE'] = 'Sun, 26 Sep 2030 23:43:52 GMT'
215
+ get
216
+ assert(last_response.status) == 304
217
+ assert(last_response.body) == ''
218
+ end
219
+ end
220
+
221
+ context "when the resource has been modified on the exact If-Modified-Since header date" do
222
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
223
+ env['HTTP_IF_MODIFIED_SINCE'] = @last_modified_time.httpdate
224
+ get
225
+ assert(last_response['Last-Modified']) == @last_modified_time.httpdate
226
+ end
227
+
228
+ it 'conditional GET matches and halts' do
229
+ env['HTTP_IF_MODIFIED_SINCE'] = @last_modified_time.httpdate
230
+ get
231
+ assert(last_response.status) == 304
232
+ assert(last_response.body) == ''
233
+ end
234
+ end
235
+
236
+ context "when the resource hasn't been modified since the If-Modified-Since header date" do
237
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
238
+ env['HTTP_IF_MODIFIED_SINCE'] = (@last_modified_time + 1).httpdate
239
+ get
240
+ assert(last_response['Last-Modified']) == @last_modified_time.httpdate
241
+ end
242
+
243
+ it 'conditional GET matches and halts' do
244
+ env['HTTP_IF_MODIFIED_SINCE'] = (@last_modified_time + 1).httpdate
245
+ get
246
+ assert(last_response.status) == 304
247
+ assert(last_response.body) == ''
248
+ end
249
+ end
250
+
251
+ context "If-Unmodified-Since" do
252
+ it 'results in 200 if resource has not been modified' do
253
+ env['HTTP_IF_UNMODIFIED_SINCE'] = 'Sun, 26 Sep 2030 23:43:52 GMT'
254
+ get
255
+ assert(last_response.status) == 200
256
+ assert(last_response.body) == 'Boo!'
257
+ end
258
+
259
+ it 'results in 412 if resource has been modified' do
260
+ env['HTTP_IF_UNMODIFIED_SINCE'] = Time.at(0).httpdate
261
+ get
262
+ assert(last_response.status) == 412
263
+ assert(last_response.body) == ''
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,124 @@
1
+ # Copyright (c) 2007, 2008, 2009 Blake Mizerany
2
+ # Copyright (c) 2010, 2011, 2012, 2013, 2014 Konstantin Haase
3
+ # Copyright (c) 2015 Slee Woo
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation
7
+ # files (the "Software"), to deal in the Software without
8
+ # restriction, including without limitation the rights to use,
9
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following
12
+ # conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ # OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require 'setup'
27
+
28
+ spec :ContentType do
29
+
30
+ it 'sets the Content-Type header' do
31
+ app mock_controller {
32
+ define_method :get do
33
+ content_type 'text/plain'
34
+ 'Hello World'
35
+ end
36
+ }
37
+
38
+ get
39
+ assert(last_response['Content-Type']) == 'text/plain'
40
+ assert(last_response.body) == 'Hello World'
41
+ end
42
+
43
+ it 'takes media type parameters (like charset=)' do
44
+ app mock_controller {
45
+ define_method :get do
46
+ content_type 'text/html', charset: 'latin1'
47
+ "<h1>Hello, World</h1>"
48
+ end
49
+ }
50
+
51
+ get
52
+ assert(last_response).ok?
53
+ assert(last_response['Content-Type']) == 'text/html; charset=latin1'
54
+ assert(last_response.body) == "<h1>Hello, World</h1>"
55
+ end
56
+
57
+ it 'updates charset for default content type' do
58
+ app mock_controller {
59
+ def get
60
+ charset('latin1')
61
+ "<h1>Hello, World</h1>"
62
+ end
63
+ }
64
+
65
+ get
66
+ assert(last_response).ok?
67
+ assert(last_response['Content-Type']) == 'text/html; charset=latin1'
68
+ assert(last_response.body) == "<h1>Hello, World</h1>"
69
+ end
70
+
71
+ it 'updates charset without alter actual mimetype nor params' do
72
+ app mock_controller {
73
+ def get
74
+ content_type('.json', level: 1)
75
+ charset('utf-8')
76
+ 'ok'
77
+ end
78
+ }
79
+
80
+ get
81
+ assert(last_response).ok?
82
+ assert(last_response['Content-Type']) == 'application/json; level=1, charset=utf-8'
83
+ end
84
+
85
+ it "looks up symbols in Rack's mime types dictionary" do
86
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
87
+ app mock_controller('/foo.xml') {
88
+ define_method :get do
89
+ content_type :foo
90
+ "I AM FOO"
91
+ end
92
+ }
93
+
94
+ get '/foo.xml'
95
+ assert(last_response).ok?
96
+ assert(last_response['Content-Type']) == 'application/foo'
97
+ assert(last_response.body) == 'I AM FOO'
98
+ end
99
+
100
+ it 'fails when no mime type is registered for the argument provided' do
101
+ app mock_controller {
102
+ def get
103
+ content_type :bizzle
104
+ "I AM FOO"
105
+ end
106
+ }
107
+ get
108
+ assert(last_response.status) == 500
109
+ assert(last_response.body) =~ /Unknown media type: :bizzle/
110
+ end
111
+
112
+
113
+ it 'handles already present params' do
114
+ app mock_controller {
115
+ def get
116
+ content_type 'foo/bar;level=1', :charset => 'utf-8'
117
+ 'ok'
118
+ end
119
+ }
120
+
121
+ get
122
+ assert(last_response['Content-Type']) == 'foo/bar; level=1, charset=utf-8'
123
+ end
124
+ end