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/lib/rack/server.rb CHANGED
@@ -1,7 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
4
+ require 'fileutils'
5
+
6
+ require_relative 'core_ext/regexp'
2
7
 
3
8
  module Rack
9
+
4
10
  class Server
11
+ using ::Rack::RegexpExtensions
12
+
5
13
  class Options
6
14
  def parse!(args)
7
15
  options = {}
@@ -23,6 +31,9 @@ module Rack
23
31
  opts.on("-w", "--warn", "turn warnings on for your script") {
24
32
  options[:warn] = true
25
33
  }
34
+ opts.on("-q", "--quiet", "turn off logging") {
35
+ options[:quiet] = true
36
+ }
26
37
 
27
38
  opts.on("-I", "--include PATH",
28
39
  "specify $LOAD_PATH (may be used more than once)") { |path|
@@ -36,11 +47,15 @@ module Rack
36
47
 
37
48
  opts.separator ""
38
49
  opts.separator "Rack options:"
39
- opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s|
50
+ opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
51
+ options[:builder] = line
52
+ }
53
+
54
+ opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick)") { |s|
40
55
  options[:server] = s
41
56
  }
42
57
 
43
- opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") { |host|
58
+ opts.on("-o", "--host HOST", "listen on HOST (default: localhost)") { |host|
44
59
  options[:Host] = host
45
60
  }
46
61
 
@@ -62,10 +77,28 @@ module Rack
62
77
  options[:daemonize] = d ? true : false
63
78
  }
64
79
 
65
- opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
80
+ opts.on("-P", "--pid FILE", "file to store PID") { |f|
66
81
  options[:pid] = ::File.expand_path(f)
67
82
  }
68
83
 
84
+ opts.separator ""
85
+ opts.separator "Profiling options:"
86
+
87
+ opts.on("--heap HEAPFILE", "Build the application, then dump the heap to HEAPFILE") do |e|
88
+ options[:heapfile] = e
89
+ end
90
+
91
+ opts.on("--profile PROFILE", "Dump CPU or Memory profile to PROFILE (defaults to a tempfile)") do |e|
92
+ options[:profile_file] = e
93
+ end
94
+
95
+ opts.on("--profile-mode MODE", "Profile mode (cpu|wall|object)") do |e|
96
+ { cpu: true, wall: true, object: true }.fetch(e.to_sym) do
97
+ raise OptionParser::InvalidOption, "unknown profile mode: #{e}"
98
+ end
99
+ options[:profile_mode] = e.to_sym
100
+ end
101
+
69
102
  opts.separator ""
70
103
  opts.separator "Common options:"
71
104
 
@@ -89,21 +122,21 @@ module Rack
89
122
  abort opt_parser.to_s
90
123
  end
91
124
 
92
- options[:config] = args.last if args.last
125
+ options[:config] = args.last if args.last && !args.last.empty?
93
126
  options
94
127
  end
95
128
 
96
129
  def handler_opts(options)
97
130
  begin
98
131
  info = []
99
- server = Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
132
+ server = Rack::Handler.get(options[:server]) || Rack::Handler.default
100
133
  if server && server.respond_to?(:valid_options)
101
134
  info << ""
102
135
  info << "Server-specific options for #{server.name}:"
103
136
 
104
137
  has_options = false
105
138
  server.valid_options.each do |name, description|
106
- next if name.to_s.match(/^(Host|Port)[^a-zA-Z]/) # ignore handler's host and port options, we do our own.
139
+ next if /^(Host|Port)[^a-zA-Z]/.match?(name.to_s) # ignore handler's host and port options, we do our own.
107
140
  info << " -O %-21s %s" % [name, description]
108
141
  has_options = true
109
142
  end
@@ -141,7 +174,9 @@ module Rack
141
174
 
142
175
  # Options may include:
143
176
  # * :app
144
- # a rack application to run (overrides :config)
177
+ # a rack application to run (overrides :config and :builder)
178
+ # * :builder
179
+ # a string to evaluate a Rack::Builder from
145
180
  # * :config
146
181
  # a rackup configuration file path to load (.ru)
147
182
  # * :environment
@@ -162,7 +197,7 @@ module Rack
162
197
  # * :Port
163
198
  # the port to bind to (used by supporting Rack::Handler)
164
199
  # * :AccessLog
165
- # webrick acess log options (or supporting Rack::Handler)
200
+ # webrick access log options (or supporting Rack::Handler)
166
201
  # * :debug
167
202
  # turn on debug output ($DEBUG = true)
168
203
  # * :warn
@@ -171,55 +206,79 @@ module Rack
171
206
  # add given paths to $LOAD_PATH
172
207
  # * :require
173
208
  # require the given libraries
209
+ #
210
+ # Additional options for profiling app initialization include:
211
+ # * :heapfile
212
+ # location for ObjectSpace.dump_all to write the output to
213
+ # * :profile_file
214
+ # location for CPU/Memory (StackProf) profile output (defaults to a tempfile)
215
+ # * :profile_mode
216
+ # StackProf profile mode (cpu|wall|object)
174
217
  def initialize(options = nil)
175
- @options = options
176
- @app = options[:app] if options && options[:app]
218
+ @ignore_options = []
219
+
220
+ if options
221
+ @use_default_options = false
222
+ @options = options
223
+ @app = options[:app] if options[:app]
224
+ else
225
+ argv = defined?(SPEC_ARGV) ? SPEC_ARGV : ARGV
226
+ @use_default_options = true
227
+ @options = parse_options(argv)
228
+ end
177
229
  end
178
230
 
179
231
  def options
180
- @options ||= parse_options(ARGV)
232
+ merged_options = @use_default_options ? default_options.merge(@options) : @options
233
+ merged_options.reject { |k, v| @ignore_options.include?(k) }
181
234
  end
182
235
 
183
236
  def default_options
237
+ environment = ENV['RACK_ENV'] || 'development'
238
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
239
+
184
240
  {
185
- :environment => ENV['RACK_ENV'] || "development",
186
- :pid => nil,
187
- :Port => 9292,
188
- :Host => "0.0.0.0",
189
- :AccessLog => [],
190
- :config => "config.ru"
241
+ environment: environment,
242
+ pid: nil,
243
+ Port: 9292,
244
+ Host: default_host,
245
+ AccessLog: [],
246
+ config: "config.ru"
191
247
  }
192
248
  end
193
249
 
194
250
  def app
195
- @app ||= begin
196
- if !::File.exist? options[:config]
197
- abort "configuration #{options[:config]} not found"
198
- end
199
-
200
- app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
201
- self.options.merge! options
202
- app
203
- end
251
+ @app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
204
252
  end
205
253
 
206
- def self.logging_middleware
207
- lambda { |server|
208
- server.server.name =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr]
209
- }
210
- end
254
+ class << self
255
+ def logging_middleware
256
+ lambda { |server|
257
+ /CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
258
+ }
259
+ end
211
260
 
212
- def self.middleware
213
- @middleware ||= begin
214
- m = Hash.new {|h,k| h[k] = []}
215
- m["deployment"].concat [
261
+ def default_middleware_by_environment
262
+ m = Hash.new {|h, k| h[k] = []}
263
+ m["deployment"] = [
264
+ [Rack::ContentLength],
265
+ logging_middleware,
266
+ [Rack::TempfileReaper]
267
+ ]
268
+ m["development"] = [
216
269
  [Rack::ContentLength],
217
- [Rack::Chunked],
218
- logging_middleware
270
+ logging_middleware,
271
+ [Rack::ShowExceptions],
272
+ [Rack::Lint],
273
+ [Rack::TempfileReaper]
219
274
  ]
220
- m["development"].concat m["deployment"] + [[Rack::ShowExceptions], [Rack::Lint]]
275
+
221
276
  m
222
277
  end
278
+
279
+ def middleware
280
+ default_middleware_by_environment
281
+ end
223
282
  end
224
283
 
225
284
  def middleware
@@ -251,7 +310,9 @@ module Rack
251
310
 
252
311
  # Touch the wrapped app, so that the config.ru is loaded before
253
312
  # daemonization (i.e. before chdir, etc).
254
- wrapped_app
313
+ handle_profiling(options[:heapfile], options[:profile_mode], options[:profile_file]) do
314
+ wrapped_app
315
+ end
255
316
 
256
317
  daemonize_app if options[:daemonize]
257
318
 
@@ -269,21 +330,80 @@ module Rack
269
330
  end
270
331
 
271
332
  def server
272
- @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
333
+ @_server ||= Rack::Handler.get(options[:server])
334
+
335
+ unless @_server
336
+ @_server = Rack::Handler.default
337
+
338
+ # We already speak FastCGI
339
+ @ignore_options = [:File, :Port] if @_server.to_s == 'Rack::Handler::FastCGI'
340
+ end
341
+
342
+ @_server
273
343
  end
274
344
 
275
345
  private
276
- def parse_options(args)
277
- options = default_options
346
+ def build_app_and_options_from_config
347
+ if !::File.exist? options[:config]
348
+ abort "configuration #{options[:config]} not found"
349
+ end
350
+
351
+ app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
352
+ @options.merge!(options) { |key, old, new| old }
353
+ app
354
+ end
278
355
 
356
+ def handle_profiling(heapfile, profile_mode, filename)
357
+ if heapfile
358
+ require "objspace"
359
+ ObjectSpace.trace_object_allocations_start
360
+ yield
361
+ GC.start
362
+ ::File.open(heapfile, "w") { |f| ObjectSpace.dump_all(output: f) }
363
+ exit
364
+ end
365
+
366
+ if profile_mode
367
+ require "stackprof"
368
+ require "tempfile"
369
+
370
+ make_profile_name(filename) do |filename|
371
+ ::File.open(filename, "w") do |f|
372
+ StackProf.run(mode: profile_mode, out: f) do
373
+ yield
374
+ end
375
+ puts "Profile written to: #{filename}"
376
+ end
377
+ end
378
+ exit
379
+ end
380
+
381
+ yield
382
+ end
383
+
384
+ def make_profile_name(filename)
385
+ if filename
386
+ yield filename
387
+ else
388
+ ::Dir::Tmpname.create("profile.dump") do |tmpname, _, _|
389
+ yield tmpname
390
+ end
391
+ end
392
+ end
393
+
394
+ def build_app_from_string
395
+ Rack::Builder.new_from_string(self.options[:builder])
396
+ end
397
+
398
+ def parse_options(args)
279
399
  # Don't evaluate CGI ISINDEX parameters.
280
400
  # http://www.meb.uni-bonn.de/docs/cgi/cl.html
281
- args.clear if ENV.include?("REQUEST_METHOD")
401
+ args.clear if ENV.include?(REQUEST_METHOD)
282
402
 
283
- options.merge! opt_parser.parse!(args)
284
- options[:config] = ::File.expand_path(options[:config])
403
+ @options = opt_parser.parse!(args)
404
+ @options[:config] = ::File.expand_path(options[:config])
285
405
  ENV["RACK_ENV"] = options[:environment]
286
- options
406
+ @options
287
407
  end
288
408
 
289
409
  def opt_parser
@@ -294,8 +414,8 @@ module Rack
294
414
  middleware[options[:environment]].reverse_each do |middleware|
295
415
  middleware = middleware.call(self) if middleware.respond_to?(:call)
296
416
  next unless middleware
297
- klass = middleware.shift
298
- app = klass.new(app, *middleware)
417
+ klass, *args = middleware
418
+ app = klass.new(app, *args)
299
419
  end
300
420
  app
301
421
  end
@@ -305,22 +425,15 @@ module Rack
305
425
  end
306
426
 
307
427
  def daemonize_app
308
- if RUBY_VERSION < "1.9"
309
- exit if fork
310
- Process.setsid
311
- exit if fork
312
- Dir.chdir "/"
313
- STDIN.reopen "/dev/null"
314
- STDOUT.reopen "/dev/null", "a"
315
- STDERR.reopen "/dev/null", "a"
316
- else
317
- Process.daemon
318
- end
428
+ Process.daemon
319
429
  end
320
430
 
321
431
  def write_pid
322
- ::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") }
323
- at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) }
432
+ ::File.open(options[:pid], ::File::CREAT | ::File::EXCL | ::File::WRONLY ){ |f| f.write("#{Process.pid}") }
433
+ at_exit { ::FileUtils.rm_f(options[:pid]) }
434
+ rescue Errno::EEXIST
435
+ check_pid!
436
+ retry
324
437
  end
325
438
 
326
439
  def check_pid!
@@ -348,4 +461,5 @@ module Rack
348
461
  end
349
462
 
350
463
  end
464
+
351
465
  end