rack 1.4.7 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (183) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +77 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +122 -456
  5. data/Rakefile +32 -31
  6. data/SPEC +119 -29
  7. data/bin/rackup +1 -0
  8. data/contrib/rack_logo.svg +164 -111
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +4 -2
  11. data/example/protectedlobster.ru +3 -1
  12. data/lib/rack/auth/abstract/handler.rb +7 -5
  13. data/lib/rack/auth/abstract/request.rb +8 -6
  14. data/lib/rack/auth/basic.rb +5 -2
  15. data/lib/rack/auth/digest/md5.rb +10 -8
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +5 -4
  18. data/lib/rack/auth/digest/request.rb +4 -2
  19. data/lib/rack/body_proxy.rb +11 -9
  20. data/lib/rack/builder.rb +63 -20
  21. data/lib/rack/cascade.rb +10 -9
  22. data/lib/rack/chunked.rb +45 -11
  23. data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
  24. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
  25. data/lib/rack/config.rb +7 -0
  26. data/lib/rack/content_length.rb +12 -6
  27. data/lib/rack/content_type.rb +4 -2
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +73 -42
  30. data/lib/rack/directory.rb +77 -56
  31. data/lib/rack/etag.rb +25 -13
  32. data/lib/rack/events.rb +156 -0
  33. data/lib/rack/file.rb +4 -143
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler/cgi.rb +18 -17
  36. data/lib/rack/handler/fastcgi.rb +21 -17
  37. data/lib/rack/handler/lsws.rb +14 -12
  38. data/lib/rack/handler/scgi.rb +27 -21
  39. data/lib/rack/handler/thin.rb +19 -5
  40. data/lib/rack/handler/webrick.rb +66 -24
  41. data/lib/rack/handler.rb +29 -19
  42. data/lib/rack/head.rb +21 -14
  43. data/lib/rack/lint.rb +259 -65
  44. data/lib/rack/lobster.rb +17 -10
  45. data/lib/rack/lock.rb +19 -10
  46. data/lib/rack/logger.rb +4 -2
  47. data/lib/rack/media_type.rb +43 -0
  48. data/lib/rack/method_override.rb +52 -0
  49. data/lib/rack/mime.rb +43 -6
  50. data/lib/rack/mock.rb +109 -44
  51. data/lib/rack/multipart/generator.rb +11 -12
  52. data/lib/rack/multipart/parser.rb +302 -115
  53. data/lib/rack/multipart/uploaded_file.rb +4 -3
  54. data/lib/rack/multipart.rb +40 -9
  55. data/lib/rack/null_logger.rb +39 -0
  56. data/lib/rack/query_parser.rb +218 -0
  57. data/lib/rack/recursive.rb +14 -11
  58. data/lib/rack/reloader.rb +12 -5
  59. data/lib/rack/request.rb +484 -270
  60. data/lib/rack/response.rb +196 -77
  61. data/lib/rack/rewindable_input.rb +5 -14
  62. data/lib/rack/runtime.rb +13 -6
  63. data/lib/rack/sendfile.rb +44 -20
  64. data/lib/rack/server.rb +175 -61
  65. data/lib/rack/session/abstract/id.rb +276 -133
  66. data/lib/rack/session/cookie.rb +75 -40
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +24 -18
  69. data/lib/rack/show_exceptions.rb +392 -0
  70. data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
  71. data/lib/rack/static.rb +65 -38
  72. data/lib/rack/tempfile_reaper.rb +24 -0
  73. data/lib/rack/urlmap.rb +40 -15
  74. data/lib/rack/utils.rb +316 -285
  75. data/lib/rack.rb +78 -23
  76. data/rack.gemspec +26 -19
  77. metadata +44 -209
  78. data/KNOWN-ISSUES +0 -30
  79. data/lib/rack/backports/uri/common_18.rb +0 -56
  80. data/lib/rack/backports/uri/common_192.rb +0 -52
  81. data/lib/rack/backports/uri/common_193.rb +0 -29
  82. data/lib/rack/handler/evented_mongrel.rb +0 -8
  83. data/lib/rack/handler/mongrel.rb +0 -100
  84. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  85. data/lib/rack/methodoverride.rb +0 -33
  86. data/lib/rack/nulllogger.rb +0 -18
  87. data/lib/rack/showexceptions.rb +0 -378
  88. data/test/builder/anything.rb +0 -5
  89. data/test/builder/comment.ru +0 -4
  90. data/test/builder/end.ru +0 -5
  91. data/test/builder/line.ru +0 -1
  92. data/test/builder/options.ru +0 -2
  93. data/test/cgi/assets/folder/test.js +0 -1
  94. data/test/cgi/assets/fonts/font.eot +0 -1
  95. data/test/cgi/assets/images/image.png +0 -1
  96. data/test/cgi/assets/index.html +0 -1
  97. data/test/cgi/assets/javascripts/app.js +0 -1
  98. data/test/cgi/assets/stylesheets/app.css +0 -1
  99. data/test/cgi/lighttpd.conf +0 -26
  100. data/test/cgi/lighttpd.errors +0 -1
  101. data/test/cgi/rackup_stub.rb +0 -6
  102. data/test/cgi/sample_rackup.ru +0 -5
  103. data/test/cgi/test +0 -9
  104. data/test/cgi/test+directory/test+file +0 -1
  105. data/test/cgi/test.fcgi +0 -8
  106. data/test/cgi/test.ru +0 -5
  107. data/test/gemloader.rb +0 -10
  108. data/test/multipart/bad_robots +0 -259
  109. data/test/multipart/binary +0 -0
  110. data/test/multipart/content_type_and_no_filename +0 -6
  111. data/test/multipart/empty +0 -10
  112. data/test/multipart/fail_16384_nofile +0 -814
  113. data/test/multipart/file1.txt +0 -1
  114. data/test/multipart/filename_and_modification_param +0 -7
  115. data/test/multipart/filename_with_escaped_quotes +0 -6
  116. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  117. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages +0 -6
  119. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  120. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  121. data/test/multipart/filename_with_unescaped_quotes +0 -6
  122. data/test/multipart/ie +0 -6
  123. data/test/multipart/mixed_files +0 -21
  124. data/test/multipart/nested +0 -10
  125. data/test/multipart/none +0 -9
  126. data/test/multipart/semicolon +0 -6
  127. data/test/multipart/text +0 -15
  128. data/test/multipart/three_files_three_fields +0 -31
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth.rb +0 -57
  133. data/test/spec_auth_basic.rb +0 -81
  134. data/test/spec_auth_digest.rb +0 -259
  135. data/test/spec_body_proxy.rb +0 -69
  136. data/test/spec_builder.rb +0 -207
  137. data/test/spec_cascade.rb +0 -61
  138. data/test/spec_cgi.rb +0 -102
  139. data/test/spec_chunked.rb +0 -87
  140. data/test/spec_commonlogger.rb +0 -57
  141. data/test/spec_conditionalget.rb +0 -102
  142. data/test/spec_config.rb +0 -22
  143. data/test/spec_content_length.rb +0 -86
  144. data/test/spec_content_type.rb +0 -45
  145. data/test/spec_deflater.rb +0 -187
  146. data/test/spec_directory.rb +0 -88
  147. data/test/spec_etag.rb +0 -98
  148. data/test/spec_fastcgi.rb +0 -107
  149. data/test/spec_file.rb +0 -200
  150. data/test/spec_handler.rb +0 -59
  151. data/test/spec_head.rb +0 -48
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -58
  154. data/test/spec_lock.rb +0 -167
  155. data/test/spec_logger.rb +0 -23
  156. data/test/spec_methodoverride.rb +0 -72
  157. data/test/spec_mock.rb +0 -269
  158. data/test/spec_mongrel.rb +0 -182
  159. data/test/spec_multipart.rb +0 -479
  160. data/test/spec_nulllogger.rb +0 -23
  161. data/test/spec_recursive.rb +0 -72
  162. data/test/spec_request.rb +0 -955
  163. data/test/spec_response.rb +0 -313
  164. data/test/spec_rewindable_input.rb +0 -118
  165. data/test/spec_runtime.rb +0 -49
  166. data/test/spec_sendfile.rb +0 -90
  167. data/test/spec_server.rb +0 -121
  168. data/test/spec_session_abstract_id.rb +0 -43
  169. data/test/spec_session_cookie.rb +0 -361
  170. data/test/spec_session_memcache.rb +0 -321
  171. data/test/spec_session_pool.rb +0 -209
  172. data/test/spec_showexceptions.rb +0 -92
  173. data/test/spec_showstatus.rb +0 -84
  174. data/test/spec_static.rb +0 -145
  175. data/test/spec_thin.rb +0 -86
  176. data/test/spec_urlmap.rb +0 -213
  177. data/test/spec_utils.rb +0 -554
  178. data/test/spec_webrick.rb +0 -143
  179. data/test/static/another/index.html +0 -1
  180. data/test/static/index.html +0 -1
  181. data/test/testrequest.rb +0 -78
  182. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  183. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/test/spec_mongrel.rb DELETED
@@ -1,182 +0,0 @@
1
- begin
2
- require 'rack'
3
- require 'rack/handler/mongrel'
4
- require File.expand_path('../testrequest', __FILE__)
5
- require 'timeout'
6
-
7
- Thread.abort_on_exception = true
8
- $tcp_defer_accept_opts = nil
9
- $tcp_cork_opts = nil
10
-
11
- describe Rack::Handler::Mongrel do
12
- extend TestRequest::Helpers
13
-
14
- @server = Mongrel::HttpServer.new(@host='127.0.0.1', @port=9201)
15
- @server.register('/test',
16
- Rack::Handler::Mongrel.new(Rack::Lint.new(TestRequest.new)))
17
- @server.register('/stream',
18
- Rack::Handler::Mongrel.new(Rack::Lint.new(StreamingRequest)))
19
- @acc = @server.run
20
-
21
- should "respond" do
22
- lambda {
23
- GET("/test")
24
- }.should.not.raise
25
- end
26
-
27
- should "be a Mongrel" do
28
- GET("/test")
29
- status.should.equal 200
30
- response["SERVER_SOFTWARE"].should =~ /Mongrel/
31
- response["HTTP_VERSION"].should.equal "HTTP/1.1"
32
- response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
33
- response["SERVER_PORT"].should.equal "9201"
34
- response["SERVER_NAME"].should.equal "127.0.0.1"
35
- end
36
-
37
- should "have rack headers" do
38
- GET("/test")
39
- response["rack.version"].should.equal [1,1]
40
- response["rack.multithread"].should.be.true
41
- response["rack.multiprocess"].should.be.false
42
- response["rack.run_once"].should.be.false
43
- end
44
-
45
- should "have CGI headers on GET" do
46
- GET("/test")
47
- response["REQUEST_METHOD"].should.equal "GET"
48
- response["SCRIPT_NAME"].should.equal "/test"
49
- response["REQUEST_PATH"].should.equal "/test"
50
- response["PATH_INFO"].should.be.equal ""
51
- response["QUERY_STRING"].should.equal ""
52
- response["test.postdata"].should.equal ""
53
-
54
- GET("/test/foo?quux=1")
55
- response["REQUEST_METHOD"].should.equal "GET"
56
- response["SCRIPT_NAME"].should.equal "/test"
57
- response["REQUEST_PATH"].should.equal "/test/foo"
58
- response["PATH_INFO"].should.equal "/foo"
59
- response["QUERY_STRING"].should.equal "quux=1"
60
- end
61
-
62
- should "have CGI headers on POST" do
63
- POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
64
- status.should.equal 200
65
- response["REQUEST_METHOD"].should.equal "POST"
66
- response["SCRIPT_NAME"].should.equal "/test"
67
- response["REQUEST_PATH"].should.equal "/test"
68
- response["QUERY_STRING"].should.equal ""
69
- response["HTTP_X_TEST_HEADER"].should.equal "42"
70
- response["test.postdata"].should.equal "rack-form-data=23"
71
- end
72
-
73
- should "support HTTP auth" do
74
- GET("/test", {:user => "ruth", :passwd => "secret"})
75
- response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
76
- end
77
-
78
- should "set status" do
79
- GET("/test?secret")
80
- status.should.equal 403
81
- response["rack.url_scheme"].should.equal "http"
82
- end
83
-
84
- should "provide a .run" do
85
- block_ran = false
86
- Thread.new {
87
- Rack::Handler::Mongrel.run(lambda {}, {:Host => '127.0.0.1', :Port => 9211}) { |server|
88
- server.should.be.kind_of Mongrel::HttpServer
89
- block_ran = true
90
- }
91
- }
92
- sleep 1
93
- block_ran.should.be.true
94
- end
95
-
96
- should "provide a .run that maps a hash" do
97
- block_ran = false
98
- Thread.new {
99
- map = {'/'=>lambda{},'/foo'=>lambda{}}
100
- Rack::Handler::Mongrel.run(map, :map => true, :Host => '127.0.0.1', :Port => 9221) { |server|
101
- server.should.be.kind_of Mongrel::HttpServer
102
- server.classifier.uris.size.should.equal 2
103
- server.classifier.uris.should.not.include '/arf'
104
- server.classifier.uris.should.include '/'
105
- server.classifier.uris.should.include '/foo'
106
- block_ran = true
107
- }
108
- }
109
- sleep 1
110
- block_ran.should.be.true
111
- end
112
-
113
- should "provide a .run that maps a urlmap" do
114
- block_ran = false
115
- Thread.new {
116
- map = Rack::URLMap.new({'/'=>lambda{},'/bar'=>lambda{}})
117
- Rack::Handler::Mongrel.run(map, {:map => true, :Host => '127.0.0.1', :Port => 9231}) { |server|
118
- server.should.be.kind_of Mongrel::HttpServer
119
- server.classifier.uris.size.should.equal 2
120
- server.classifier.uris.should.not.include '/arf'
121
- server.classifier.uris.should.include '/'
122
- server.classifier.uris.should.include '/bar'
123
- block_ran = true
124
- }
125
- }
126
- sleep 1
127
- block_ran.should.be.true
128
- end
129
-
130
- should "provide a .run that maps a urlmap restricting by host" do
131
- block_ran = false
132
- Thread.new {
133
- map = Rack::URLMap.new({
134
- '/' => lambda{},
135
- '/foo' => lambda{},
136
- '/bar' => lambda{},
137
- 'http://127.0.0.1/' => lambda{},
138
- 'http://127.0.0.1/bar' => lambda{},
139
- 'http://falsehost/arf' => lambda{},
140
- 'http://falsehost/qux' => lambda{}
141
- })
142
- opt = {:map => true, :Port => 9241, :Host => '127.0.0.1'}
143
- Rack::Handler::Mongrel.run(map, opt) { |server|
144
- server.should.be.kind_of Mongrel::HttpServer
145
- server.classifier.uris.should.include '/'
146
- server.classifier.handler_map['/'].size.should.equal 2
147
- server.classifier.uris.should.include '/foo'
148
- server.classifier.handler_map['/foo'].size.should.equal 1
149
- server.classifier.uris.should.include '/bar'
150
- server.classifier.handler_map['/bar'].size.should.equal 2
151
- server.classifier.uris.should.not.include '/qux'
152
- server.classifier.uris.should.not.include '/arf'
153
- server.classifier.uris.size.should.equal 3
154
- block_ran = true
155
- }
156
- }
157
- sleep 1
158
- block_ran.should.be.true
159
- end
160
-
161
- should "stream #each part of the response" do
162
- body = ''
163
- begin
164
- Timeout.timeout(1) do
165
- Net::HTTP.start(@host, @port) do |http|
166
- get = Net::HTTP::Get.new('/stream')
167
- http.request(get) do |response|
168
- response.read_body { |part| body << part }
169
- end
170
- end
171
- end
172
- rescue Timeout::Error
173
- end
174
- body.should.not.be.empty
175
- end
176
-
177
- @acc.raise Mongrel::StopServer
178
- end
179
-
180
- rescue LoadError
181
- warn "Skipping Rack::Handler::Mongrel tests (Mongrel is required). `gem install mongrel` and try again."
182
- end
@@ -1,479 +0,0 @@
1
- require 'rack/utils'
2
- require 'rack/mock'
3
-
4
- describe Rack::Multipart do
5
- def multipart_fixture(name, boundary = "AaB03x")
6
- file = multipart_file(name)
7
- data = File.open(file, 'rb') { |io| io.read }
8
-
9
- type = "multipart/form-data; boundary=#{boundary}"
10
- length = data.respond_to?(:bytesize) ? data.bytesize : data.size
11
-
12
- { "CONTENT_TYPE" => type,
13
- "CONTENT_LENGTH" => length.to_s,
14
- :input => StringIO.new(data) }
15
- end
16
-
17
- def multipart_file(name)
18
- File.join(File.dirname(__FILE__), "multipart", name.to_s)
19
- end
20
-
21
- should "return nil if content type is not multipart" do
22
- env = Rack::MockRequest.env_for("/",
23
- "CONTENT_TYPE" => 'application/x-www-form-urlencoded')
24
- Rack::Multipart.parse_multipart(env).should.equal nil
25
- end
26
-
27
- should "parse multipart content when content type present but filename is not" do
28
- env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
29
- params = Rack::Multipart.parse_multipart(env)
30
- params["text"].should.equal "contents"
31
- end
32
-
33
- should "raise RangeError if the key space is exhausted" do
34
- env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
35
-
36
- old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
37
- begin
38
- lambda { Rack::Multipart.parse_multipart(env) }.should.raise(RangeError)
39
- ensure
40
- Rack::Utils.key_space_limit = old
41
- end
42
- end
43
-
44
- should "parse multipart form webkit style" do
45
- env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
46
- env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
47
- params = Rack::Multipart.parse_multipart(env)
48
- params['profile']['bio'].should.include 'hello'
49
- end
50
-
51
- should "reject insanely long boundaries" do
52
- # using a pipe since a tempfile can use up too much space
53
- rd, wr = IO.pipe
54
-
55
- # we only call rewind once at start, so make sure it succeeds
56
- # and doesn't hit ESPIPE
57
- def rd.rewind; end
58
- wr.sync = true
59
-
60
- # mock out length to make this pipe look like a Tempfile
61
- def rd.length
62
- 1024 * 1024 * 8
63
- end
64
-
65
- # write to a pipe in a background thread, this will write a lot
66
- # unless Rack (properly) shuts down the read end
67
- thr = Thread.new do
68
- begin
69
- wr.write("--AaB03x")
70
-
71
- # make the initial boundary a few gigs long
72
- longer = "0123456789" * 1024 * 1024
73
- (1024 * 1024).times { wr.write(longer) }
74
-
75
- wr.write("\r\n")
76
- wr.write('Content-Disposition: form-data; name="a"; filename="a.txt"')
77
- wr.write("\r\n")
78
- wr.write("Content-Type: text/plain\r\n")
79
- wr.write("\r\na")
80
- wr.write("--AaB03x--\r\n")
81
- wr.close
82
- rescue => err # this is EPIPE if Rack shuts us down
83
- err
84
- end
85
- end
86
-
87
- fixture = {
88
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
89
- "CONTENT_LENGTH" => rd.length.to_s,
90
- :input => rd,
91
- }
92
-
93
- env = Rack::MockRequest.env_for '/', fixture
94
- lambda {
95
- Rack::Multipart.parse_multipart(env)
96
- }.should.raise(EOFError)
97
- rd.close
98
-
99
- err = thr.value
100
- err.should.be.instance_of Errno::EPIPE
101
- wr.close
102
- end
103
-
104
- should "parse multipart upload with text file" do
105
- env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
106
- params = Rack::Multipart.parse_multipart(env)
107
- params["submit-name"].should.equal "Larry"
108
- params["submit-name-with-content"].should.equal "Berry"
109
- params["files"][:type].should.equal "text/plain"
110
- params["files"][:filename].should.equal "file1.txt"
111
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
112
- "name=\"files\"; filename=\"file1.txt\"\r\n" +
113
- "Content-Type: text/plain\r\n"
114
- params["files"][:name].should.equal "files"
115
- params["files"][:tempfile].read.should.equal "contents"
116
- end
117
-
118
- should "parse multipart upload with nested parameters" do
119
- env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
120
- params = Rack::Multipart.parse_multipart(env)
121
- params["foo"]["submit-name"].should.equal "Larry"
122
- params["foo"]["files"][:type].should.equal "text/plain"
123
- params["foo"]["files"][:filename].should.equal "file1.txt"
124
- params["foo"]["files"][:head].should.equal "Content-Disposition: form-data; " +
125
- "name=\"foo[files]\"; filename=\"file1.txt\"\r\n" +
126
- "Content-Type: text/plain\r\n"
127
- params["foo"]["files"][:name].should.equal "foo[files]"
128
- params["foo"]["files"][:tempfile].read.should.equal "contents"
129
- end
130
-
131
- should "parse multipart upload with binary file" do
132
- env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
133
- params = Rack::Multipart.parse_multipart(env)
134
- params["submit-name"].should.equal "Larry"
135
- params["files"][:type].should.equal "image/png"
136
- params["files"][:filename].should.equal "rack-logo.png"
137
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
138
- "name=\"files\"; filename=\"rack-logo.png\"\r\n" +
139
- "Content-Type: image/png\r\n"
140
- params["files"][:name].should.equal "files"
141
- params["files"][:tempfile].read.length.should.equal 26473
142
- end
143
-
144
- should "parse multipart upload with empty file" do
145
- env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
146
- params = Rack::Multipart.parse_multipart(env)
147
- params["submit-name"].should.equal "Larry"
148
- params["files"][:type].should.equal "text/plain"
149
- params["files"][:filename].should.equal "file1.txt"
150
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
151
- "name=\"files\"; filename=\"file1.txt\"\r\n" +
152
- "Content-Type: text/plain\r\n"
153
- params["files"][:name].should.equal "files"
154
- params["files"][:tempfile].read.should.equal ""
155
- end
156
-
157
- should "parse multipart upload with filename with semicolons" do
158
- env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
159
- params = Rack::Multipart.parse_multipart(env)
160
- params["files"][:type].should.equal "text/plain"
161
- params["files"][:filename].should.equal "fi;le1.txt"
162
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
163
- "name=\"files\"; filename=\"fi;le1.txt\"\r\n" +
164
- "Content-Type: text/plain\r\n"
165
- params["files"][:name].should.equal "files"
166
- params["files"][:tempfile].read.should.equal "contents"
167
- end
168
-
169
- should "not include file params if no file was selected" do
170
- env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
171
- params = Rack::Multipart.parse_multipart(env)
172
- params["submit-name"].should.equal "Larry"
173
- params["files"].should.equal nil
174
- params.keys.should.not.include "files"
175
- end
176
-
177
- should "parse multipart/mixed" do
178
- env = Rack::MockRequest.env_for("/", multipart_fixture(:mixed_files))
179
- params = Rack::Utils::Multipart.parse_multipart(env)
180
- params["foo"].should.equal "bar"
181
- params["files"].should.be.instance_of String
182
- params["files"].size.should.equal 252
183
- end
184
-
185
- should "parse IE multipart upload and clean up filename" do
186
- env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
187
- params = Rack::Multipart.parse_multipart(env)
188
- params["files"][:type].should.equal "text/plain"
189
- params["files"][:filename].should.equal "file1.txt"
190
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
191
- "name=\"files\"; " +
192
- 'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' +
193
- "\r\nContent-Type: text/plain\r\n"
194
- params["files"][:name].should.equal "files"
195
- params["files"][:tempfile].read.should.equal "contents"
196
- end
197
-
198
- should "parse filename and modification param" do
199
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param))
200
- params = Rack::Multipart.parse_multipart(env)
201
- params["files"][:type].should.equal "image/jpeg"
202
- params["files"][:filename].should.equal "genome.jpeg"
203
- params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
204
- "Content-Disposition: attachment; " +
205
- "name=\"files\"; " +
206
- "filename=genome.jpeg; " +
207
- "modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
208
- "Content-Description: a complete map of the human genome\r\n"
209
- params["files"][:name].should.equal "files"
210
- params["files"][:tempfile].read.should.equal "contents"
211
- end
212
-
213
- should "parse filename with escaped quotes" do
214
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes))
215
- params = Rack::Multipart.parse_multipart(env)
216
- params["files"][:type].should.equal "application/octet-stream"
217
- params["files"][:filename].should.equal "escape \"quotes"
218
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
219
- "name=\"files\"; " +
220
- "filename=\"escape \\\"quotes\"\r\n" +
221
- "Content-Type: application/octet-stream\r\n"
222
- params["files"][:name].should.equal "files"
223
- params["files"][:tempfile].read.should.equal "contents"
224
- end
225
-
226
- should "parse filename with percent escaped quotes" do
227
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes))
228
- params = Rack::Multipart.parse_multipart(env)
229
- params["files"][:type].should.equal "application/octet-stream"
230
- params["files"][:filename].should.equal "escape \"quotes"
231
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
232
- "name=\"files\"; " +
233
- "filename=\"escape %22quotes\"\r\n" +
234
- "Content-Type: application/octet-stream\r\n"
235
- params["files"][:name].should.equal "files"
236
- params["files"][:tempfile].read.should.equal "contents"
237
- end
238
-
239
- should "parse filename with unescaped quotes" do
240
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
241
- params = Rack::Multipart.parse_multipart(env)
242
- params["files"][:type].should.equal "application/octet-stream"
243
- params["files"][:filename].should.equal "escape \"quotes"
244
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
245
- "name=\"files\"; " +
246
- "filename=\"escape \"quotes\"\r\n" +
247
- "Content-Type: application/octet-stream\r\n"
248
- params["files"][:name].should.equal "files"
249
- params["files"][:tempfile].read.should.equal "contents"
250
- end
251
-
252
- should "parse filename with escaped quotes and modification param" do
253
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
254
- params = Rack::Multipart.parse_multipart(env)
255
- params["files"][:type].should.equal "image/jpeg"
256
- params["files"][:filename].should.equal "\"human\" genome.jpeg"
257
- params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
258
- "Content-Disposition: attachment; " +
259
- "name=\"files\"; " +
260
- "filename=\"\"human\" genome.jpeg\"; " +
261
- "modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
262
- "Content-Description: a complete map of the human genome\r\n"
263
- params["files"][:name].should.equal "files"
264
- params["files"][:tempfile].read.should.equal "contents"
265
- end
266
-
267
- should "parse filename with unescaped percentage characters" do
268
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
269
- params = Rack::Multipart.parse_multipart(env)
270
- files = params["document"]["attachment"]
271
- files[:type].should.equal "image/jpeg"
272
- files[:filename].should.equal "100% of a photo.jpeg"
273
- files[:head].should.equal <<-MULTIPART
274
- Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r
275
- Content-Type: image/jpeg\r
276
- MULTIPART
277
-
278
- files[:name].should.equal "document[attachment]"
279
- files[:tempfile].read.should.equal "contents"
280
- end
281
-
282
- should "parse filename with unescaped percentage characters that look like partial hex escapes" do
283
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
284
- params = Rack::Multipart.parse_multipart(env)
285
- files = params["document"]["attachment"]
286
- files[:type].should.equal "image/jpeg"
287
- files[:filename].should.equal "100%a"
288
- files[:head].should.equal <<-MULTIPART
289
- Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r
290
- Content-Type: image/jpeg\r
291
- MULTIPART
292
-
293
- files[:name].should.equal "document[attachment]"
294
- files[:tempfile].read.should.equal "contents"
295
- end
296
-
297
- should "parse filename with unescaped percentage characters that look like partial hex escapes" do
298
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
299
- params = Rack::Multipart.parse_multipart(env)
300
- files = params["document"]["attachment"]
301
- files[:type].should.equal "image/jpeg"
302
- files[:filename].should.equal "100%"
303
- files[:head].should.equal <<-MULTIPART
304
- Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r
305
- Content-Type: image/jpeg\r
306
- MULTIPART
307
-
308
- files[:name].should.equal "document[attachment]"
309
- files[:tempfile].read.should.equal "contents"
310
- end
311
-
312
- it "rewinds input after parsing upload" do
313
- options = multipart_fixture(:text)
314
- input = options[:input]
315
- env = Rack::MockRequest.env_for("/", options)
316
- params = Rack::Multipart.parse_multipart(env)
317
- params["submit-name"].should.equal "Larry"
318
- params["files"][:filename].should.equal "file1.txt"
319
- input.read.length.should.equal 307
320
- end
321
-
322
- it "builds multipart body" do
323
- files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"))
324
- data = Rack::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
325
-
326
- options = {
327
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
328
- "CONTENT_LENGTH" => data.length.to_s,
329
- :input => StringIO.new(data)
330
- }
331
- env = Rack::MockRequest.env_for("/", options)
332
- params = Rack::Multipart.parse_multipart(env)
333
- params["submit-name"].should.equal "Larry"
334
- params["files"][:filename].should.equal "file1.txt"
335
- params["files"][:tempfile].read.should.equal "contents"
336
- end
337
-
338
- it "builds nested multipart body" do
339
- files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"))
340
- data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
341
-
342
- options = {
343
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
344
- "CONTENT_LENGTH" => data.length.to_s,
345
- :input => StringIO.new(data)
346
- }
347
- env = Rack::MockRequest.env_for("/", options)
348
- params = Rack::Multipart.parse_multipart(env)
349
- params["people"][0]["submit-name"].should.equal "Larry"
350
- params["people"][0]["files"][:filename].should.equal "file1.txt"
351
- params["people"][0]["files"][:tempfile].read.should.equal "contents"
352
- end
353
-
354
- it "can parse fields that end at the end of the buffer" do
355
- input = File.read(multipart_file("bad_robots"))
356
-
357
- req = Rack::Request.new Rack::MockRequest.env_for("/",
358
- "CONTENT_TYPE" => "multipart/form-data, boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
359
- "CONTENT_LENGTH" => input.size,
360
- :input => input)
361
-
362
- req.POST['file.path'].should.equal "/var/tmp/uploads/4/0001728414"
363
- req.POST['addresses'].should.not.equal nil
364
- end
365
-
366
- it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
367
- begin
368
- previous_limit = Rack::Utils.multipart_part_limit
369
- Rack::Utils.multipart_part_limit = 256
370
-
371
- data = File.open(multipart_file("fail_16384_nofile"), 'rb') { |f| f.read }.gsub(/\n/, "\r\n")
372
- options = {
373
- "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
374
- "CONTENT_LENGTH" => data.length.to_s,
375
- :input => StringIO.new(data)
376
- }
377
- env = Rack::MockRequest.env_for("/", options)
378
- params = Rack::Multipart.parse_multipart(env)
379
-
380
- params.should.not.equal nil
381
- params.keys.should.include "AAAAAAAAAAAAAAAAAAA"
382
- params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
383
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new"
384
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2"
385
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id"
386
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
387
- ensure
388
- Rack::Utils.multipart_part_limit = previous_limit
389
- end
390
- end
391
-
392
- should "not reach a multi-part limit" do
393
- begin
394
- previous_limit = Rack::Utils.multipart_part_limit
395
- Rack::Utils.multipart_part_limit = 4
396
-
397
- env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
398
- params = Rack::Multipart.parse_multipart(env)
399
- params['reply'].should.equal 'yes'
400
- params['to'].should.equal 'people'
401
- params['from'].should.equal 'others'
402
- ensure
403
- Rack::Utils.multipart_part_limit = previous_limit
404
- end
405
- end
406
-
407
- should "reach a multipart limit" do
408
- begin
409
- previous_limit = Rack::Utils.multipart_part_limit
410
- Rack::Utils.multipart_part_limit = 3
411
-
412
- env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
413
- lambda { Rack::Multipart.parse_multipart(env) }.should.raise(Rack::Multipart::MultipartLimitError)
414
- ensure
415
- Rack::Utils.multipart_part_limit = previous_limit
416
- end
417
- end
418
-
419
- should "return nil if no UploadedFiles were used" do
420
- data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
421
- data.should.equal nil
422
- end
423
-
424
- should "raise ArgumentError if params is not a Hash" do
425
- lambda { Rack::Multipart.build_multipart("foo=bar") }.
426
- should.raise(ArgumentError).
427
- message.should.equal "value must be a Hash"
428
- end
429
-
430
- it "can parse fields with a content type" do
431
- data = <<-EOF
432
- --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon\r
433
- Content-Disposition: form-data; name="description"\r
434
- Content-Type: text/plain"\r
435
- \r
436
- Very very blue\r
437
- --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon--\r
438
- EOF
439
- options = {
440
- "CONTENT_TYPE" => "multipart/form-data; boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
441
- "CONTENT_LENGTH" => data.length.to_s,
442
- :input => StringIO.new(data)
443
- }
444
- env = Rack::MockRequest.env_for("/", options)
445
- params = Rack::Utils::Multipart.parse_multipart(env)
446
-
447
- params.should.equal({"description"=>"Very very blue"})
448
- end
449
-
450
- should "parse multipart upload with no content-length header" do
451
- env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
452
- env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
453
- env.delete 'CONTENT_LENGTH'
454
- params = Rack::Multipart.parse_multipart(env)
455
- params['profile']['bio'].should.include 'hello'
456
- end
457
-
458
- should "parse very long unquoted multipart file names" do
459
- data = <<-EOF
460
- --AaB03x\r
461
- Content-Type: text/plain\r
462
- Content-Disposition: attachment; name=file; filename=#{'long' * 100}\r
463
- \r
464
- contents\r
465
- --AaB03x--\r
466
- EOF
467
-
468
- options = {
469
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
470
- "CONTENT_LENGTH" => data.length.to_s,
471
- :input => StringIO.new(data)
472
- }
473
- env = Rack::MockRequest.env_for("/", options)
474
- params = Rack::Utils::Multipart.parse_multipart(env)
475
-
476
- params["file"][:filename].should.equal('long' * 100)
477
- end
478
-
479
- end
@@ -1,23 +0,0 @@
1
- require 'enumerator'
2
- require 'rack/lint'
3
- require 'rack/mock'
4
- require 'rack/nulllogger'
5
-
6
- describe Rack::NullLogger do
7
- ::Enumerator = ::Enumerable::Enumerator unless Object.const_defined?(:Enumerator)
8
-
9
- should "act as a noop logger" do
10
- app = lambda { |env|
11
- env['rack.logger'].warn "b00m"
12
- [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]]
13
- }
14
-
15
- logger = Rack::Lint.new(Rack::NullLogger.new(app))
16
-
17
- res = logger.call(Rack::MockRequest.env_for)
18
- res[0..1].should.equal [
19
- 200, {'Content-Type' => 'text/plain'}
20
- ]
21
- Enumerator.new(res[2]).to_a.should.equal ["Hello, World!"]
22
- end
23
- end