edgar-rack 1.2.1

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 (138) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +401 -0
  4. data/Rakefile +101 -0
  5. data/SPEC +171 -0
  6. data/bin/rackup +4 -0
  7. data/contrib/rack_logo.svg +111 -0
  8. data/example/lobster.ru +4 -0
  9. data/example/protectedlobster.rb +14 -0
  10. data/example/protectedlobster.ru +8 -0
  11. data/lib/rack.rb +81 -0
  12. data/lib/rack/auth/abstract/handler.rb +37 -0
  13. data/lib/rack/auth/abstract/request.rb +43 -0
  14. data/lib/rack/auth/basic.rb +58 -0
  15. data/lib/rack/auth/digest/md5.rb +124 -0
  16. data/lib/rack/auth/digest/nonce.rb +51 -0
  17. data/lib/rack/auth/digest/params.rb +53 -0
  18. data/lib/rack/auth/digest/request.rb +40 -0
  19. data/lib/rack/builder.rb +80 -0
  20. data/lib/rack/cascade.rb +41 -0
  21. data/lib/rack/chunked.rb +52 -0
  22. data/lib/rack/commonlogger.rb +49 -0
  23. data/lib/rack/conditionalget.rb +63 -0
  24. data/lib/rack/config.rb +15 -0
  25. data/lib/rack/content_length.rb +29 -0
  26. data/lib/rack/content_type.rb +23 -0
  27. data/lib/rack/deflater.rb +96 -0
  28. data/lib/rack/directory.rb +157 -0
  29. data/lib/rack/etag.rb +59 -0
  30. data/lib/rack/file.rb +118 -0
  31. data/lib/rack/handler.rb +88 -0
  32. data/lib/rack/handler/cgi.rb +61 -0
  33. data/lib/rack/handler/evented_mongrel.rb +8 -0
  34. data/lib/rack/handler/fastcgi.rb +90 -0
  35. data/lib/rack/handler/lsws.rb +61 -0
  36. data/lib/rack/handler/mongrel.rb +90 -0
  37. data/lib/rack/handler/scgi.rb +59 -0
  38. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  39. data/lib/rack/handler/thin.rb +17 -0
  40. data/lib/rack/handler/webrick.rb +73 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +567 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +44 -0
  45. data/lib/rack/logger.rb +18 -0
  46. data/lib/rack/methodoverride.rb +27 -0
  47. data/lib/rack/mime.rb +210 -0
  48. data/lib/rack/mock.rb +185 -0
  49. data/lib/rack/nulllogger.rb +18 -0
  50. data/lib/rack/recursive.rb +61 -0
  51. data/lib/rack/reloader.rb +109 -0
  52. data/lib/rack/request.rb +307 -0
  53. data/lib/rack/response.rb +151 -0
  54. data/lib/rack/rewindable_input.rb +104 -0
  55. data/lib/rack/runtime.rb +27 -0
  56. data/lib/rack/sendfile.rb +139 -0
  57. data/lib/rack/server.rb +289 -0
  58. data/lib/rack/session/abstract/id.rb +348 -0
  59. data/lib/rack/session/cookie.rb +152 -0
  60. data/lib/rack/session/memcache.rb +93 -0
  61. data/lib/rack/session/pool.rb +79 -0
  62. data/lib/rack/showexceptions.rb +378 -0
  63. data/lib/rack/showstatus.rb +113 -0
  64. data/lib/rack/static.rb +53 -0
  65. data/lib/rack/urlmap.rb +55 -0
  66. data/lib/rack/utils.rb +698 -0
  67. data/rack.gemspec +39 -0
  68. data/test/cgi/lighttpd.conf +25 -0
  69. data/test/cgi/rackup_stub.rb +6 -0
  70. data/test/cgi/sample_rackup.ru +5 -0
  71. data/test/cgi/test +9 -0
  72. data/test/cgi/test.fcgi +8 -0
  73. data/test/cgi/test.ru +5 -0
  74. data/test/gemloader.rb +6 -0
  75. data/test/multipart/bad_robots +259 -0
  76. data/test/multipart/binary +0 -0
  77. data/test/multipart/empty +10 -0
  78. data/test/multipart/fail_16384_nofile +814 -0
  79. data/test/multipart/file1.txt +1 -0
  80. data/test/multipart/filename_and_modification_param +7 -0
  81. data/test/multipart/filename_with_escaped_quotes +6 -0
  82. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  83. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  84. data/test/multipart/filename_with_unescaped_quotes +6 -0
  85. data/test/multipart/ie +6 -0
  86. data/test/multipart/nested +10 -0
  87. data/test/multipart/none +9 -0
  88. data/test/multipart/semicolon +6 -0
  89. data/test/multipart/text +15 -0
  90. data/test/rackup/config.ru +31 -0
  91. data/test/spec_auth_basic.rb +70 -0
  92. data/test/spec_auth_digest.rb +241 -0
  93. data/test/spec_builder.rb +123 -0
  94. data/test/spec_cascade.rb +45 -0
  95. data/test/spec_cgi.rb +102 -0
  96. data/test/spec_chunked.rb +60 -0
  97. data/test/spec_commonlogger.rb +56 -0
  98. data/test/spec_conditionalget.rb +86 -0
  99. data/test/spec_config.rb +23 -0
  100. data/test/spec_content_length.rb +36 -0
  101. data/test/spec_content_type.rb +29 -0
  102. data/test/spec_deflater.rb +125 -0
  103. data/test/spec_directory.rb +57 -0
  104. data/test/spec_etag.rb +75 -0
  105. data/test/spec_fastcgi.rb +107 -0
  106. data/test/spec_file.rb +92 -0
  107. data/test/spec_handler.rb +49 -0
  108. data/test/spec_head.rb +30 -0
  109. data/test/spec_lint.rb +515 -0
  110. data/test/spec_lobster.rb +43 -0
  111. data/test/spec_lock.rb +142 -0
  112. data/test/spec_logger.rb +28 -0
  113. data/test/spec_methodoverride.rb +58 -0
  114. data/test/spec_mock.rb +241 -0
  115. data/test/spec_mongrel.rb +182 -0
  116. data/test/spec_nulllogger.rb +12 -0
  117. data/test/spec_recursive.rb +69 -0
  118. data/test/spec_request.rb +774 -0
  119. data/test/spec_response.rb +245 -0
  120. data/test/spec_rewindable_input.rb +118 -0
  121. data/test/spec_runtime.rb +39 -0
  122. data/test/spec_sendfile.rb +83 -0
  123. data/test/spec_server.rb +8 -0
  124. data/test/spec_session_abstract_id.rb +43 -0
  125. data/test/spec_session_cookie.rb +171 -0
  126. data/test/spec_session_memcache.rb +289 -0
  127. data/test/spec_session_pool.rb +200 -0
  128. data/test/spec_showexceptions.rb +87 -0
  129. data/test/spec_showstatus.rb +79 -0
  130. data/test/spec_static.rb +48 -0
  131. data/test/spec_thin.rb +86 -0
  132. data/test/spec_urlmap.rb +213 -0
  133. data/test/spec_utils.rb +678 -0
  134. data/test/spec_webrick.rb +141 -0
  135. data/test/testrequest.rb +78 -0
  136. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  137. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  138. metadata +329 -0
@@ -0,0 +1 @@
1
+ contents
@@ -0,0 +1,7 @@
1
+ --AaB03x
2
+ Content-Type: image/jpeg
3
+ Content-Disposition: attachment; name="files"; filename=genome.jpeg; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
4
+ Content-Description: a complete map of the human genome
5
+
6
+ contents
7
+ --AaB03x--
@@ -0,0 +1,6 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="files"; filename="escape \"quotes"
3
+ Content-Type: application/octet-stream
4
+
5
+ contents
6
+ --AaB03x--
@@ -0,0 +1,7 @@
1
+ --AaB03x
2
+ Content-Type: image/jpeg
3
+ Content-Disposition: attachment; name="files"; filename=""human" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
4
+ Content-Description: a complete map of the human genome
5
+
6
+ contents
7
+ --AaB03x--
@@ -0,0 +1,6 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="files"; filename="escape %22quotes"
3
+ Content-Type: application/octet-stream
4
+
5
+ contents
6
+ --AaB03x--
@@ -0,0 +1,6 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="files"; filename="escape "quotes"
3
+ Content-Type: application/octet-stream
4
+
5
+ contents
6
+ --AaB03x--
@@ -0,0 +1,6 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="files"; filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"
3
+ Content-Type: text/plain
4
+
5
+ contents
6
+ --AaB03x--
@@ -0,0 +1,10 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="foo[submit-name]"
3
+
4
+ Larry
5
+ --AaB03x
6
+ Content-Disposition: form-data; name="foo[files]"; filename="file1.txt"
7
+ Content-Type: text/plain
8
+
9
+ contents
10
+ --AaB03x--
@@ -0,0 +1,9 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="submit-name"
3
+
4
+ Larry
5
+ --AaB03x
6
+ Content-Disposition: form-data; name="files"; filename=""
7
+
8
+
9
+ --AaB03x--
@@ -0,0 +1,6 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="files"; filename="fi;le1.txt"
3
+ Content-Type: text/plain
4
+
5
+ contents
6
+ --AaB03x--
@@ -0,0 +1,15 @@
1
+ --AaB03x
2
+ Content-Disposition: form-data; name="submit-name"
3
+
4
+ Larry
5
+ --AaB03x
6
+ Content-Disposition: form-data; name="submit-name-with-content"
7
+ Content-Type: text/plain
8
+
9
+ Berry
10
+ --AaB03x
11
+ Content-Disposition: form-data; name="files"; filename="file1.txt"
12
+ Content-Type: text/plain
13
+
14
+ contents
15
+ --AaB03x--
@@ -0,0 +1,31 @@
1
+ require "#{File.dirname(__FILE__)}/../testrequest"
2
+
3
+ $stderr = File.open("#{File.dirname(__FILE__)}/log_output", "w")
4
+
5
+ class EnvMiddleware
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ # provides a way to test that lint is present
12
+ if env["PATH_INFO"] == "/broken_lint"
13
+ return [200, {}, ["Broken Lint"]]
14
+ # provides a way to kill the process without knowing the pid
15
+ elsif env["PATH_INFO"] == "/die"
16
+ exit!
17
+ end
18
+
19
+ env["test.$DEBUG"] = $DEBUG
20
+ env["test.$EVAL"] = BUKKIT if defined?(BUKKIT)
21
+ env["test.$VERBOSE"] = $VERBOSE
22
+ env["test.$LOAD_PATH"] = $LOAD_PATH
23
+ env["test.stderr"] = File.expand_path($stderr.path)
24
+ env["test.Ping"] = defined?(Ping)
25
+ env["test.pid"] = Process.pid
26
+ @app.call(env)
27
+ end
28
+ end
29
+
30
+ use EnvMiddleware
31
+ run TestRequest.new
@@ -0,0 +1,70 @@
1
+ require 'rack/auth/basic'
2
+ require 'rack/mock'
3
+
4
+ describe Rack::Auth::Basic do
5
+ def realm
6
+ 'WallysWorld'
7
+ end
8
+
9
+ def unprotected_app
10
+ lambda { |env| [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ] }
11
+ end
12
+
13
+ def protected_app
14
+ app = Rack::Auth::Basic.new(unprotected_app) { |username, password| 'Boss' == username }
15
+ app.realm = realm
16
+ app
17
+ end
18
+
19
+ before do
20
+ @request = Rack::MockRequest.new(protected_app)
21
+ end
22
+
23
+ def request_with_basic_auth(username, password, &block)
24
+ request 'HTTP_AUTHORIZATION' => 'Basic ' + ["#{username}:#{password}"].pack("m*"), &block
25
+ end
26
+
27
+ def request(headers = {})
28
+ yield @request.get('/', headers)
29
+ end
30
+
31
+ def assert_basic_auth_challenge(response)
32
+ response.should.be.a.client_error
33
+ response.status.should.equal 401
34
+ response.should.include 'WWW-Authenticate'
35
+ response.headers['WWW-Authenticate'].should =~ /Basic realm="#{Regexp.escape(realm)}"/
36
+ response.body.should.be.empty
37
+ end
38
+
39
+ should 'challenge correctly when no credentials are specified' do
40
+ request do |response|
41
+ assert_basic_auth_challenge response
42
+ end
43
+ end
44
+
45
+ should 'rechallenge if incorrect credentials are specified' do
46
+ request_with_basic_auth 'joe', 'password' do |response|
47
+ assert_basic_auth_challenge response
48
+ end
49
+ end
50
+
51
+ should 'return application output if correct credentials are specified' do
52
+ request_with_basic_auth 'Boss', 'password' do |response|
53
+ response.status.should.equal 200
54
+ response.body.to_s.should.equal 'Hi Boss'
55
+ end
56
+ end
57
+
58
+ should 'return 400 Bad Request if different auth scheme used' do
59
+ request 'HTTP_AUTHORIZATION' => 'Digest params' do |response|
60
+ response.should.be.a.client_error
61
+ response.status.should.equal 400
62
+ response.should.not.include 'WWW-Authenticate'
63
+ end
64
+ end
65
+
66
+ it 'takes realm as optional constructor arg' do
67
+ app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
68
+ realm.should == app.realm
69
+ end
70
+ end
@@ -0,0 +1,241 @@
1
+ require 'rack/auth/digest/md5'
2
+ require 'rack/mock'
3
+
4
+ describe Rack::Auth::Digest::MD5 do
5
+ def realm
6
+ 'WallysWorld'
7
+ end
8
+
9
+ def unprotected_app
10
+ lambda do |env|
11
+ friend = Rack::Utils.parse_query(env["QUERY_STRING"])["friend"]
12
+ [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}#{friend ? " and #{friend}" : ''}"] ]
13
+ end
14
+ end
15
+
16
+ def protected_app
17
+ app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
18
+ { 'Alice' => 'correct-password' }[username]
19
+ end
20
+ app.realm = realm
21
+ app.opaque = 'this-should-be-secret'
22
+ app
23
+ end
24
+
25
+ def protected_app_with_hashed_passwords
26
+ app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
27
+ username == 'Alice' ? Digest::MD5.hexdigest("Alice:#{realm}:correct-password") : nil
28
+ end
29
+ app.realm = realm
30
+ app.opaque = 'this-should-be-secret'
31
+ app.passwords_hashed = true
32
+ app
33
+ end
34
+
35
+ def partially_protected_app
36
+ Rack::URLMap.new({
37
+ '/' => unprotected_app,
38
+ '/protected' => protected_app
39
+ })
40
+ end
41
+
42
+ def protected_app_with_method_override
43
+ Rack::MethodOverride.new(protected_app)
44
+ end
45
+
46
+ before do
47
+ @request = Rack::MockRequest.new(protected_app)
48
+ end
49
+
50
+ def request(method, path, headers = {}, &block)
51
+ response = @request.request(method, path, headers)
52
+ block.call(response) if block
53
+ return response
54
+ end
55
+
56
+ class MockDigestRequest
57
+ def initialize(params)
58
+ @params = params
59
+ end
60
+ def method_missing(sym)
61
+ if @params.has_key? k = sym.to_s
62
+ return @params[k]
63
+ end
64
+ super
65
+ end
66
+ def method
67
+ @params['method']
68
+ end
69
+ def response(password)
70
+ Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
71
+ end
72
+ end
73
+
74
+ def request_with_digest_auth(method, path, username, password, options = {}, &block)
75
+ request_options = {}
76
+ request_options[:input] = options.delete(:input) if options.include? :input
77
+
78
+ response = request(method, path, request_options)
79
+
80
+ return response unless response.status == 401
81
+
82
+ if wait = options.delete(:wait)
83
+ sleep wait
84
+ end
85
+
86
+ challenge = response['WWW-Authenticate'].split(' ', 2).last
87
+
88
+ params = Rack::Auth::Digest::Params.parse(challenge)
89
+
90
+ params['username'] = username
91
+ params['nc'] = '00000001'
92
+ params['cnonce'] = 'nonsensenonce'
93
+ params['uri'] = path
94
+
95
+ params['method'] = method
96
+
97
+ params.update options
98
+
99
+ params['response'] = MockDigestRequest.new(params).response(password)
100
+
101
+ request(method, path, request_options.merge('HTTP_AUTHORIZATION' => "Digest #{params}"), &block)
102
+ end
103
+
104
+ def assert_digest_auth_challenge(response)
105
+ response.should.be.a.client_error
106
+ response.status.should.equal 401
107
+ response.should.include 'WWW-Authenticate'
108
+ response.headers['WWW-Authenticate'].should =~ /^Digest /
109
+ response.body.should.be.empty
110
+ end
111
+
112
+ def assert_bad_request(response)
113
+ response.should.be.a.client_error
114
+ response.status.should.equal 400
115
+ response.should.not.include 'WWW-Authenticate'
116
+ end
117
+
118
+ should 'challenge when no credentials are specified' do
119
+ request 'GET', '/' do |response|
120
+ assert_digest_auth_challenge response
121
+ end
122
+ end
123
+
124
+ should 'return application output if correct credentials given' do
125
+ request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
126
+ response.status.should.equal 200
127
+ response.body.to_s.should.equal 'Hi Alice'
128
+ end
129
+ end
130
+
131
+ should 'return application output if correct credentials given (hashed passwords)' do
132
+ @request = Rack::MockRequest.new(protected_app_with_hashed_passwords)
133
+
134
+ request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
135
+ response.status.should.equal 200
136
+ response.body.to_s.should.equal 'Hi Alice'
137
+ end
138
+ end
139
+
140
+ should 'rechallenge if incorrect username given' do
141
+ request_with_digest_auth 'GET', '/', 'Bob', 'correct-password' do |response|
142
+ assert_digest_auth_challenge response
143
+ end
144
+ end
145
+
146
+ should 'rechallenge if incorrect password given' do
147
+ request_with_digest_auth 'GET', '/', 'Alice', 'wrong-password' do |response|
148
+ assert_digest_auth_challenge response
149
+ end
150
+ end
151
+
152
+ should 'rechallenge with stale parameter if nonce is stale' do
153
+ begin
154
+ Rack::Auth::Digest::Nonce.time_limit = 1
155
+
156
+ request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 2 do |response|
157
+ assert_digest_auth_challenge response
158
+ response.headers['WWW-Authenticate'].should =~ /\bstale=true\b/
159
+ end
160
+ ensure
161
+ Rack::Auth::Digest::Nonce.time_limit = nil
162
+ end
163
+ end
164
+
165
+ should 'return 400 Bad Request if incorrect qop given' do
166
+ request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'qop' => 'auth-int' do |response|
167
+ assert_bad_request response
168
+ end
169
+ end
170
+
171
+ should 'return 400 Bad Request if incorrect uri given' do
172
+ request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'uri' => '/foo' do |response|
173
+ assert_bad_request response
174
+ end
175
+ end
176
+
177
+ should 'return 400 Bad Request if different auth scheme used' do
178
+ request 'GET', '/', 'HTTP_AUTHORIZATION' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' do |response|
179
+ assert_bad_request response
180
+ end
181
+ end
182
+
183
+ should 'not require credentials for unprotected path' do
184
+ @request = Rack::MockRequest.new(partially_protected_app)
185
+ request 'GET', '/' do |response|
186
+ response.should.be.ok
187
+ end
188
+ end
189
+
190
+ should 'challenge when no credentials are specified for protected path' do
191
+ @request = Rack::MockRequest.new(partially_protected_app)
192
+ request 'GET', '/protected' do |response|
193
+ assert_digest_auth_challenge response
194
+ end
195
+ end
196
+
197
+ should 'return application output if correct credentials given for protected path' do
198
+ @request = Rack::MockRequest.new(partially_protected_app)
199
+ request_with_digest_auth 'GET', '/protected', 'Alice', 'correct-password' do |response|
200
+ response.status.should.equal 200
201
+ response.body.to_s.should.equal 'Hi Alice'
202
+ end
203
+ end
204
+
205
+ should 'return application output when used with a query string and path as uri' do
206
+ @request = Rack::MockRequest.new(partially_protected_app)
207
+ request_with_digest_auth 'GET', '/protected?friend=Mike', 'Alice', 'correct-password' do |response|
208
+ response.status.should.equal 200
209
+ response.body.to_s.should.equal 'Hi Alice and Mike'
210
+ end
211
+ end
212
+
213
+ should 'return application output when used with a query string and fullpath as uri' do
214
+ @request = Rack::MockRequest.new(partially_protected_app)
215
+ qs_uri = '/protected?friend=Mike'
216
+ request_with_digest_auth 'GET', qs_uri, 'Alice', 'correct-password', 'uri' => qs_uri do |response|
217
+ response.status.should.equal 200
218
+ response.body.to_s.should.equal 'Hi Alice and Mike'
219
+ end
220
+ end
221
+
222
+ should 'return application output if correct credentials given for POST' do
223
+ request_with_digest_auth 'POST', '/', 'Alice', 'correct-password' do |response|
224
+ response.status.should.equal 200
225
+ response.body.to_s.should.equal 'Hi Alice'
226
+ end
227
+ end
228
+
229
+ should 'return application output if correct credentials given for PUT (using method override of POST)' do
230
+ @request = Rack::MockRequest.new(protected_app_with_method_override)
231
+ request_with_digest_auth 'POST', '/', 'Alice', 'correct-password', :input => "_method=put" do |response|
232
+ response.status.should.equal 200
233
+ response.body.to_s.should.equal 'Hi Alice'
234
+ end
235
+ end
236
+
237
+ it 'takes realm as optional constructor arg' do
238
+ app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true }
239
+ realm.should == app.realm
240
+ end
241
+ end
@@ -0,0 +1,123 @@
1
+ require 'rack/builder'
2
+ require 'rack/mock'
3
+ require 'rack/showexceptions'
4
+ require 'rack/urlmap'
5
+
6
+ class NothingMiddleware
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+ def call(env)
11
+ @@env = env
12
+ response = @app.call(env)
13
+ response
14
+ end
15
+ def self.env
16
+ @@env
17
+ end
18
+ end
19
+
20
+ describe Rack::Builder do
21
+ it "supports mapping" do
22
+ app = Rack::Builder.new do
23
+ map '/' do |outer_env|
24
+ run lambda { |inner_env| [200, {}, ['root']] }
25
+ end
26
+ map '/sub' do
27
+ run lambda { |inner_env| [200, {}, ['sub']] }
28
+ end
29
+ end.to_app
30
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
31
+ Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
32
+ end
33
+
34
+ it "doesn't dupe env even when mapping" do
35
+ app = Rack::Builder.new do
36
+ use NothingMiddleware
37
+ map '/' do |outer_env|
38
+ run lambda { |inner_env|
39
+ inner_env['new_key'] = 'new_value'
40
+ [200, {}, ['root']]
41
+ }
42
+ end
43
+ end.to_app
44
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
45
+ NothingMiddleware.env['new_key'].should.equal 'new_value'
46
+ end
47
+
48
+ it "chains apps by default" do
49
+ app = Rack::Builder.new do
50
+ use Rack::ShowExceptions
51
+ run lambda { |env| raise "bzzzt" }
52
+ end.to_app
53
+
54
+ Rack::MockRequest.new(app).get("/").should.be.server_error
55
+ Rack::MockRequest.new(app).get("/").should.be.server_error
56
+ Rack::MockRequest.new(app).get("/").should.be.server_error
57
+ end
58
+
59
+ it "has implicit #to_app" do
60
+ app = Rack::Builder.new do
61
+ use Rack::ShowExceptions
62
+ run lambda { |env| raise "bzzzt" }
63
+ end
64
+
65
+ Rack::MockRequest.new(app).get("/").should.be.server_error
66
+ Rack::MockRequest.new(app).get("/").should.be.server_error
67
+ Rack::MockRequest.new(app).get("/").should.be.server_error
68
+ end
69
+
70
+ it "supports blocks on use" do
71
+ app = Rack::Builder.new do
72
+ use Rack::ShowExceptions
73
+ use Rack::Auth::Basic do |username, password|
74
+ 'secret' == password
75
+ end
76
+
77
+ run lambda { |env| [200, {}, ['Hi Boss']] }
78
+ end
79
+
80
+ response = Rack::MockRequest.new(app).get("/")
81
+ response.should.be.client_error
82
+ response.status.should.equal 401
83
+
84
+ # with auth...
85
+ response = Rack::MockRequest.new(app).get("/",
86
+ 'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
87
+ response.status.should.equal 200
88
+ response.body.to_s.should.equal 'Hi Boss'
89
+ end
90
+
91
+ it "has explicit #to_app" do
92
+ app = Rack::Builder.app do
93
+ use Rack::ShowExceptions
94
+ run lambda { |env| raise "bzzzt" }
95
+ end
96
+
97
+ Rack::MockRequest.new(app).get("/").should.be.server_error
98
+ Rack::MockRequest.new(app).get("/").should.be.server_error
99
+ Rack::MockRequest.new(app).get("/").should.be.server_error
100
+ end
101
+
102
+ should "initialize apps once" do
103
+ app = Rack::Builder.new do
104
+ class AppClass
105
+ def initialize
106
+ @called = 0
107
+ end
108
+ def call(env)
109
+ raise "bzzzt" if @called > 0
110
+ @called += 1
111
+ [200, {'Content-Type' => 'text/plain'}, ['OK']]
112
+ end
113
+ end
114
+
115
+ use Rack::ShowExceptions
116
+ run AppClass.new
117
+ end
118
+
119
+ Rack::MockRequest.new(app).get("/").status.should.equal 200
120
+ Rack::MockRequest.new(app).get("/").should.be.server_error
121
+ end
122
+
123
+ end