lack 2.0.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.
- checksums.yaml +7 -0
- data/bin/rackup +5 -0
- data/lib/rack.rb +26 -0
- data/lib/rack/body_proxy.rb +39 -0
- data/lib/rack/builder.rb +166 -0
- data/lib/rack/handler.rb +63 -0
- data/lib/rack/handler/webrick.rb +120 -0
- data/lib/rack/mime.rb +661 -0
- data/lib/rack/mock.rb +198 -0
- data/lib/rack/multipart.rb +31 -0
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +239 -0
- data/lib/rack/multipart/uploaded_file.rb +34 -0
- data/lib/rack/request.rb +394 -0
- data/lib/rack/response.rb +160 -0
- data/lib/rack/server.rb +258 -0
- data/lib/rack/server/options.rb +121 -0
- data/lib/rack/utils.rb +653 -0
- data/lib/rack/version.rb +3 -0
- data/spec/spec_helper.rb +1 -0
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +5 -0
- data/test/builder/line.ru +1 -0
- data/test/builder/options.ru +2 -0
- data/test/multipart/bad_robots +259 -0
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/empty +10 -0
- data/test/multipart/fail_16384_nofile +814 -0
- data/test/multipart/file1.txt +1 -0
- data/test/multipart/filename_and_modification_param +7 -0
- data/test/multipart/filename_and_no_name +6 -0
- data/test/multipart/filename_with_escaped_quotes +6 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/test/multipart/filename_with_percent_escaped_quotes +6 -0
- data/test/multipart/filename_with_unescaped_percentages +6 -0
- data/test/multipart/filename_with_unescaped_percentages2 +6 -0
- data/test/multipart/filename_with_unescaped_percentages3 +6 -0
- data/test/multipart/filename_with_unescaped_quotes +6 -0
- data/test/multipart/ie +6 -0
- data/test/multipart/invalid_character +6 -0
- data/test/multipart/mixed_files +21 -0
- data/test/multipart/nested +10 -0
- data/test/multipart/none +9 -0
- data/test/multipart/semicolon +6 -0
- data/test/multipart/text +15 -0
- data/test/multipart/webkit +32 -0
- data/test/rackup/config.ru +31 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/spec_body_proxy.rb +69 -0
- data/test/spec_builder.rb +223 -0
- data/test/spec_chunked.rb +101 -0
- data/test/spec_file.rb +221 -0
- data/test/spec_handler.rb +59 -0
- data/test/spec_head.rb +45 -0
- data/test/spec_lint.rb +522 -0
- data/test/spec_mime.rb +51 -0
- data/test/spec_mock.rb +277 -0
- data/test/spec_multipart.rb +547 -0
- data/test/spec_recursive.rb +72 -0
- data/test/spec_request.rb +1199 -0
- data/test/spec_response.rb +343 -0
- data/test/spec_rewindable_input.rb +118 -0
- data/test/spec_sendfile.rb +130 -0
- data/test/spec_server.rb +167 -0
- data/test/spec_utils.rb +635 -0
- data/test/spec_webrick.rb +184 -0
- data/test/testrequest.rb +78 -0
- data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +240 -0
@@ -0,0 +1 @@
|
|
1
|
+
contents
|
data/test/multipart/ie
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
--AaB03x
|
2
|
+
Content-Disposition: form-data; name="foo"
|
3
|
+
|
4
|
+
bar
|
5
|
+
--AaB03x
|
6
|
+
Content-Disposition: form-data; name="files"
|
7
|
+
Content-Type: multipart/mixed, boundary=BbC04y
|
8
|
+
|
9
|
+
--BbC04y
|
10
|
+
Content-Disposition: attachment; filename="file.txt"
|
11
|
+
Content-Type: text/plain
|
12
|
+
|
13
|
+
contents
|
14
|
+
--BbC04y
|
15
|
+
Content-Disposition: attachment; filename="flowers.jpg"
|
16
|
+
Content-Type: image/jpeg
|
17
|
+
Content-Transfer-Encoding: binary
|
18
|
+
|
19
|
+
contents
|
20
|
+
--BbC04y--
|
21
|
+
--AaB03x--
|
data/test/multipart/none
ADDED
data/test/multipart/text
ADDED
@@ -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,32 @@
|
|
1
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
2
|
+
Content-Disposition: form-data; name="_method"
|
3
|
+
|
4
|
+
put
|
5
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
6
|
+
Content-Disposition: form-data; name="profile[blog]"
|
7
|
+
|
8
|
+
|
9
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
10
|
+
Content-Disposition: form-data; name="profile[public_email]"
|
11
|
+
|
12
|
+
|
13
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
14
|
+
Content-Disposition: form-data; name="profile[interests]"
|
15
|
+
|
16
|
+
|
17
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
18
|
+
Content-Disposition: form-data; name="profile[bio]"
|
19
|
+
|
20
|
+
hello
|
21
|
+
|
22
|
+
"quote"
|
23
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
24
|
+
Content-Disposition: form-data; name="media"; filename=""
|
25
|
+
Content-Type: application/octet-stream
|
26
|
+
|
27
|
+
|
28
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR
|
29
|
+
Content-Disposition: form-data; name="commit"
|
30
|
+
|
31
|
+
Save
|
32
|
+
------WebKitFormBoundaryWLHCs9qmcJJoyjKR--
|
@@ -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,69 @@
|
|
1
|
+
require 'rack/body_proxy'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
describe Rack::BodyProxy do
|
5
|
+
should 'call each on the wrapped body' do
|
6
|
+
called = false
|
7
|
+
proxy = Rack::BodyProxy.new(['foo']) { }
|
8
|
+
proxy.each do |str|
|
9
|
+
called = true
|
10
|
+
str.should.equal 'foo'
|
11
|
+
end
|
12
|
+
called.should.equal true
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'call close on the wrapped body' do
|
16
|
+
body = StringIO.new
|
17
|
+
proxy = Rack::BodyProxy.new(body) { }
|
18
|
+
proxy.close
|
19
|
+
body.should.be.closed
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'only call close on the wrapped body if it responds to close' do
|
23
|
+
body = []
|
24
|
+
proxy = Rack::BodyProxy.new(body) { }
|
25
|
+
proc { proxy.close }.should.not.raise
|
26
|
+
end
|
27
|
+
|
28
|
+
should 'call the passed block on close' do
|
29
|
+
called = false
|
30
|
+
proxy = Rack::BodyProxy.new([]) { called = true }
|
31
|
+
called.should.equal false
|
32
|
+
proxy.close
|
33
|
+
called.should.equal true
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'call the passed block on close even if there is an exception' do
|
37
|
+
object = Object.new
|
38
|
+
def object.close() raise "No!" end
|
39
|
+
called = false
|
40
|
+
|
41
|
+
begin
|
42
|
+
proxy = Rack::BodyProxy.new(object) { called = true }
|
43
|
+
called.should.equal false
|
44
|
+
proxy.close
|
45
|
+
rescue RuntimeError => e
|
46
|
+
end
|
47
|
+
|
48
|
+
raise "Expected exception to have been raised" unless e
|
49
|
+
called.should.equal true
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'not close more than one time' do
|
53
|
+
count = 0
|
54
|
+
proxy = Rack::BodyProxy.new([]) { count += 1; raise "Block invoked more than 1 time!" if count > 1 }
|
55
|
+
2.times { proxy.close }
|
56
|
+
count.should.equal 1
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'be closed when the callback is triggered' do
|
60
|
+
closed = false
|
61
|
+
proxy = Rack::BodyProxy.new([]) { closed = proxy.closed? }
|
62
|
+
proxy.close
|
63
|
+
closed.should.equal true
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'provide an #each method' do
|
67
|
+
Rack::BodyProxy.method_defined?(:each).should.equal true
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'rack/builder'
|
2
|
+
require 'rack/lint'
|
3
|
+
require 'rack/mock'
|
4
|
+
require 'rack/showexceptions'
|
5
|
+
require 'rack/urlmap'
|
6
|
+
|
7
|
+
class NothingMiddleware
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
def call(env)
|
12
|
+
@@env = env
|
13
|
+
response = @app.call(env)
|
14
|
+
response
|
15
|
+
end
|
16
|
+
def self.env
|
17
|
+
@@env
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe Rack::Builder do
|
22
|
+
def builder(&block)
|
23
|
+
Rack::Lint.new Rack::Builder.new(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def builder_to_app(&block)
|
27
|
+
Rack::Lint.new Rack::Builder.new(&block).to_app
|
28
|
+
end
|
29
|
+
|
30
|
+
it "supports mapping" do
|
31
|
+
app = builder_to_app do
|
32
|
+
map '/' do |outer_env|
|
33
|
+
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
|
34
|
+
end
|
35
|
+
map '/sub' do
|
36
|
+
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
40
|
+
Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
|
41
|
+
end
|
42
|
+
|
43
|
+
it "doesn't dupe env even when mapping" do
|
44
|
+
app = builder_to_app do
|
45
|
+
use NothingMiddleware
|
46
|
+
map '/' do |outer_env|
|
47
|
+
run lambda { |inner_env|
|
48
|
+
inner_env['new_key'] = 'new_value'
|
49
|
+
[200, {"Content-Type" => "text/plain"}, ['root']]
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
54
|
+
NothingMiddleware.env['new_key'].should.equal 'new_value'
|
55
|
+
end
|
56
|
+
|
57
|
+
it "chains apps by default" do
|
58
|
+
app = builder_to_app do
|
59
|
+
use Rack::ShowExceptions
|
60
|
+
run lambda { |env| raise "bzzzt" }
|
61
|
+
end
|
62
|
+
|
63
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
64
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
65
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
66
|
+
end
|
67
|
+
|
68
|
+
it "has implicit #to_app" do
|
69
|
+
app = builder do
|
70
|
+
use Rack::ShowExceptions
|
71
|
+
run lambda { |env| raise "bzzzt" }
|
72
|
+
end
|
73
|
+
|
74
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
75
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
76
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
77
|
+
end
|
78
|
+
|
79
|
+
it "supports blocks on use" do
|
80
|
+
app = builder do
|
81
|
+
use Rack::ShowExceptions
|
82
|
+
use Rack::Auth::Basic do |username, password|
|
83
|
+
'secret' == password
|
84
|
+
end
|
85
|
+
|
86
|
+
run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
|
87
|
+
end
|
88
|
+
|
89
|
+
response = Rack::MockRequest.new(app).get("/")
|
90
|
+
response.should.be.client_error
|
91
|
+
response.status.should.equal 401
|
92
|
+
|
93
|
+
# with auth...
|
94
|
+
response = Rack::MockRequest.new(app).get("/",
|
95
|
+
'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
|
96
|
+
response.status.should.equal 200
|
97
|
+
response.body.to_s.should.equal 'Hi Boss'
|
98
|
+
end
|
99
|
+
|
100
|
+
it "has explicit #to_app" do
|
101
|
+
app = builder do
|
102
|
+
use Rack::ShowExceptions
|
103
|
+
run lambda { |env| raise "bzzzt" }
|
104
|
+
end
|
105
|
+
|
106
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
107
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
108
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
109
|
+
end
|
110
|
+
|
111
|
+
it "can mix map and run for endpoints" do
|
112
|
+
app = builder do
|
113
|
+
map '/sub' do
|
114
|
+
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
|
115
|
+
end
|
116
|
+
run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
|
117
|
+
end
|
118
|
+
|
119
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
|
120
|
+
Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
|
121
|
+
end
|
122
|
+
|
123
|
+
it "accepts middleware-only map blocks" do
|
124
|
+
app = builder do
|
125
|
+
map('/foo') { use Rack::ShowExceptions }
|
126
|
+
run lambda { |env| raise "bzzzt" }
|
127
|
+
end
|
128
|
+
|
129
|
+
proc { Rack::MockRequest.new(app).get("/") }.should.raise(RuntimeError)
|
130
|
+
Rack::MockRequest.new(app).get("/foo").should.be.server_error
|
131
|
+
end
|
132
|
+
|
133
|
+
it "yields the generated app to a block for warmup" do
|
134
|
+
warmed_up_app = nil
|
135
|
+
|
136
|
+
app = Rack::Builder.new do
|
137
|
+
warmup { |a| warmed_up_app = a }
|
138
|
+
run lambda { |env| [200, {}, []] }
|
139
|
+
end.to_app
|
140
|
+
|
141
|
+
warmed_up_app.should.equal app
|
142
|
+
end
|
143
|
+
|
144
|
+
should "initialize apps once" do
|
145
|
+
app = builder do
|
146
|
+
class AppClass
|
147
|
+
def initialize
|
148
|
+
@called = 0
|
149
|
+
end
|
150
|
+
def call(env)
|
151
|
+
raise "bzzzt" if @called > 0
|
152
|
+
@called += 1
|
153
|
+
[200, {'Content-Type' => 'text/plain'}, ['OK']]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
use Rack::ShowExceptions
|
158
|
+
run AppClass.new
|
159
|
+
end
|
160
|
+
|
161
|
+
Rack::MockRequest.new(app).get("/").status.should.equal 200
|
162
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
163
|
+
end
|
164
|
+
|
165
|
+
it "allows use after run" do
|
166
|
+
app = builder do
|
167
|
+
run lambda { |env| raise "bzzzt" }
|
168
|
+
use Rack::ShowExceptions
|
169
|
+
end
|
170
|
+
|
171
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
172
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
173
|
+
Rack::MockRequest.new(app).get("/").should.be.server_error
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'complains about a missing run' do
|
177
|
+
proc do
|
178
|
+
Rack::Lint.new Rack::Builder.app { use Rack::ShowExceptions }
|
179
|
+
end.should.raise(RuntimeError)
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "parse_file" do
|
183
|
+
def config_file(name)
|
184
|
+
File.join(File.dirname(__FILE__), 'builder', name)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "parses commented options" do
|
188
|
+
app, options = Rack::Builder.parse_file config_file('options.ru')
|
189
|
+
options[:debug].should.be.true
|
190
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
191
|
+
end
|
192
|
+
|
193
|
+
it "removes __END__ before evaluating app" do
|
194
|
+
app, _ = Rack::Builder.parse_file config_file('end.ru')
|
195
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
196
|
+
end
|
197
|
+
|
198
|
+
it "supports multi-line comments" do
|
199
|
+
lambda {
|
200
|
+
Rack::Builder.parse_file config_file('comment.ru')
|
201
|
+
}.should.not.raise(SyntaxError)
|
202
|
+
end
|
203
|
+
|
204
|
+
it "requires anything not ending in .ru" do
|
205
|
+
$: << File.dirname(__FILE__)
|
206
|
+
app, * = Rack::Builder.parse_file 'builder/anything'
|
207
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
208
|
+
$:.pop
|
209
|
+
end
|
210
|
+
|
211
|
+
it "sets __LINE__ correctly" do
|
212
|
+
app, _ = Rack::Builder.parse_file config_file('line.ru')
|
213
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe 'new_from_string' do
|
218
|
+
it "builds a rack app from string" do
|
219
|
+
app, = Rack::Builder.new_from_string "run lambda{|env| [200, {'Content-Type' => 'text/plane'}, ['OK']] }"
|
220
|
+
Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|