rack 1.0.1 → 2.2.21

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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +829 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +355 -0
  6. data/Rakefile +64 -98
  7. data/SPEC.rdoc +292 -0
  8. data/bin/rackup +3 -174
  9. data/contrib/rack.png +0 -0
  10. data/contrib/rack.svg +150 -0
  11. data/contrib/rack_logo.svg +164 -111
  12. data/contrib/rdoc.css +412 -0
  13. data/example/lobster.ru +2 -0
  14. data/example/protectedlobster.rb +4 -2
  15. data/example/protectedlobster.ru +3 -1
  16. data/lib/rack/auth/abstract/handler.rb +7 -5
  17. data/lib/rack/auth/abstract/request.rb +12 -2
  18. data/lib/rack/auth/basic.rb +6 -4
  19. data/lib/rack/auth/digest/md5.rb +20 -13
  20. data/lib/rack/auth/digest/nonce.rb +6 -4
  21. data/lib/rack/auth/digest/params.rb +11 -12
  22. data/lib/rack/auth/digest/request.rb +15 -12
  23. data/lib/rack/body_proxy.rb +45 -0
  24. data/lib/rack/builder.rb +213 -19
  25. data/lib/rack/cascade.rb +50 -18
  26. data/lib/rack/chunked.rb +95 -27
  27. data/lib/rack/common_logger.rb +84 -0
  28. data/lib/rack/conditional_get.rb +83 -0
  29. data/lib/rack/config.rb +22 -0
  30. data/lib/rack/content_length.rb +19 -10
  31. data/lib/rack/content_type.rb +12 -5
  32. data/lib/rack/core_ext/regexp.rb +14 -0
  33. data/lib/rack/deflater.rb +90 -42
  34. data/lib/rack/directory.rb +118 -72
  35. data/lib/rack/etag.rb +77 -0
  36. data/lib/rack/events.rb +153 -0
  37. data/lib/rack/file.rb +4 -85
  38. data/lib/rack/files.rb +218 -0
  39. data/lib/rack/handler/cgi.rb +22 -24
  40. data/lib/rack/handler/fastcgi.rb +34 -24
  41. data/lib/rack/handler/lsws.rb +16 -15
  42. data/lib/rack/handler/scgi.rb +38 -29
  43. data/lib/rack/handler/thin.rb +23 -7
  44. data/lib/rack/handler/webrick.rb +95 -37
  45. data/lib/rack/handler.rb +49 -14
  46. data/lib/rack/head.rb +19 -13
  47. data/lib/rack/lint.rb +466 -217
  48. data/lib/rack/lobster.rb +19 -14
  49. data/lib/rack/lock.rb +24 -8
  50. data/lib/rack/logger.rb +20 -0
  51. data/lib/rack/media_type.rb +53 -0
  52. data/lib/rack/method_override.rb +52 -0
  53. data/lib/rack/mime.rb +652 -171
  54. data/lib/rack/mock.rb +203 -64
  55. data/lib/rack/multipart/generator.rb +97 -0
  56. data/lib/rack/multipart/parser.rb +440 -0
  57. data/lib/rack/multipart/uploaded_file.rb +41 -0
  58. data/lib/rack/multipart.rb +64 -0
  59. data/lib/rack/null_logger.rb +39 -0
  60. data/lib/rack/query_parser.rb +266 -0
  61. data/lib/rack/recursive.rb +18 -11
  62. data/lib/rack/reloader.rb +13 -5
  63. data/lib/rack/request.rb +607 -191
  64. data/lib/rack/response.rb +245 -110
  65. data/lib/rack/rewindable_input.rb +15 -21
  66. data/lib/rack/runtime.rb +34 -0
  67. data/lib/rack/sendfile.rb +192 -0
  68. data/lib/rack/server.rb +466 -0
  69. data/lib/rack/session/abstract/id.rb +449 -68
  70. data/lib/rack/session/cookie.rb +159 -47
  71. data/lib/rack/session/memcache.rb +4 -103
  72. data/lib/rack/session/pool.rb +39 -49
  73. data/lib/rack/show_exceptions.rb +390 -0
  74. data/lib/rack/{showstatus.rb → show_status.rb} +21 -14
  75. data/lib/rack/static.rb +164 -14
  76. data/lib/rack/tempfile_reaper.rb +22 -0
  77. data/lib/rack/urlmap.rb +60 -18
  78. data/lib/rack/utils.rb +508 -277
  79. data/lib/rack/version.rb +29 -0
  80. data/lib/rack.rb +85 -34
  81. data/rack.gemspec +43 -51
  82. metadata +119 -207
  83. data/KNOWN-ISSUES +0 -18
  84. data/RDOX +0 -428
  85. data/README +0 -364
  86. data/SPEC +0 -164
  87. data/lib/rack/adapter/camping.rb +0 -22
  88. data/lib/rack/auth/openid.rb +0 -480
  89. data/lib/rack/commonlogger.rb +0 -61
  90. data/lib/rack/conditionalget.rb +0 -47
  91. data/lib/rack/handler/evented_mongrel.rb +0 -8
  92. data/lib/rack/handler/mongrel.rb +0 -87
  93. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  94. data/lib/rack/methodoverride.rb +0 -27
  95. data/lib/rack/showexceptions.rb +0 -349
  96. data/test/cgi/lighttpd.conf +0 -20
  97. data/test/cgi/test +0 -9
  98. data/test/cgi/test.fcgi +0 -8
  99. data/test/cgi/test.ru +0 -7
  100. data/test/multipart/binary +0 -0
  101. data/test/multipart/empty +0 -10
  102. data/test/multipart/ie +0 -6
  103. data/test/multipart/nested +0 -10
  104. data/test/multipart/none +0 -9
  105. data/test/multipart/semicolon +0 -6
  106. data/test/multipart/text +0 -10
  107. data/test/spec_rack_auth_basic.rb +0 -73
  108. data/test/spec_rack_auth_digest.rb +0 -226
  109. data/test/spec_rack_auth_openid.rb +0 -84
  110. data/test/spec_rack_builder.rb +0 -84
  111. data/test/spec_rack_camping.rb +0 -51
  112. data/test/spec_rack_cascade.rb +0 -50
  113. data/test/spec_rack_cgi.rb +0 -89
  114. data/test/spec_rack_chunked.rb +0 -62
  115. data/test/spec_rack_commonlogger.rb +0 -32
  116. data/test/spec_rack_conditionalget.rb +0 -41
  117. data/test/spec_rack_content_length.rb +0 -43
  118. data/test/spec_rack_content_type.rb +0 -30
  119. data/test/spec_rack_deflater.rb +0 -127
  120. data/test/spec_rack_directory.rb +0 -61
  121. data/test/spec_rack_fastcgi.rb +0 -89
  122. data/test/spec_rack_file.rb +0 -75
  123. data/test/spec_rack_handler.rb +0 -43
  124. data/test/spec_rack_head.rb +0 -30
  125. data/test/spec_rack_lint.rb +0 -521
  126. data/test/spec_rack_lobster.rb +0 -45
  127. data/test/spec_rack_lock.rb +0 -38
  128. data/test/spec_rack_methodoverride.rb +0 -60
  129. data/test/spec_rack_mock.rb +0 -157
  130. data/test/spec_rack_mongrel.rb +0 -189
  131. data/test/spec_rack_recursive.rb +0 -77
  132. data/test/spec_rack_request.rb +0 -504
  133. data/test/spec_rack_response.rb +0 -218
  134. data/test/spec_rack_rewindable_input.rb +0 -118
  135. data/test/spec_rack_session_cookie.rb +0 -82
  136. data/test/spec_rack_session_memcache.rb +0 -240
  137. data/test/spec_rack_session_pool.rb +0 -172
  138. data/test/spec_rack_showexceptions.rb +0 -21
  139. data/test/spec_rack_showstatus.rb +0 -72
  140. data/test/spec_rack_static.rb +0 -37
  141. data/test/spec_rack_thin.rb +0 -91
  142. data/test/spec_rack_urlmap.rb +0 -185
  143. data/test/spec_rack_utils.rb +0 -387
  144. data/test/spec_rack_webrick.rb +0 -130
  145. data/test/testrequest.rb +0 -57
  146. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  147. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ class QueryParser
5
+ (require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
6
+
7
+ DEFAULT_SEP = /[&;] */n
8
+ COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
9
+
10
+ # ParameterTypeError is the error that is raised when incoming structural
11
+ # parameters (parsed by parse_nested_query) contain conflicting types.
12
+ class ParameterTypeError < TypeError; end
13
+
14
+ # InvalidParameterError is the error that is raised when incoming structural
15
+ # parameters (parsed by parse_nested_query) contain invalid format or byte
16
+ # sequence.
17
+ class InvalidParameterError < ArgumentError; end
18
+
19
+ # QueryLimitError is for errors raised when the query provided exceeds one
20
+ # of the query parser limits.
21
+ class QueryLimitError < RangeError
22
+ end
23
+
24
+ # ParamsTooDeepError is the old name for the error that is raised when params
25
+ # are recursively nested over the specified limit. Make it the same as
26
+ # as QueryLimitError, so that code that rescues ParamsTooDeepError error
27
+ # to handle bad query strings also now handles other limits.
28
+ ParamsTooDeepError = QueryLimitError
29
+
30
+ def self.make_default(key_space_limit, param_depth_limit, **options)
31
+ new(Params, key_space_limit, param_depth_limit, **options)
32
+ end
33
+
34
+ attr_reader :key_space_limit, :param_depth_limit
35
+
36
+ env_int = lambda do |key, val|
37
+ if str_val = ENV[key]
38
+ begin
39
+ val = Integer(str_val, 10)
40
+ rescue ArgumentError
41
+ raise ArgumentError, "non-integer value provided for environment variable #{key}"
42
+ end
43
+ end
44
+
45
+ val
46
+ end
47
+
48
+ BYTESIZE_LIMIT = env_int.call("RACK_QUERY_PARSER_BYTESIZE_LIMIT", 4194304)
49
+ private_constant :BYTESIZE_LIMIT
50
+
51
+ PARAMS_LIMIT = env_int.call("RACK_QUERY_PARSER_PARAMS_LIMIT", 4096)
52
+ private_constant :PARAMS_LIMIT
53
+
54
+ attr_reader :bytesize_limit
55
+
56
+ def initialize(params_class, key_space_limit, param_depth_limit, bytesize_limit: BYTESIZE_LIMIT, params_limit: PARAMS_LIMIT)
57
+ @params_class = params_class
58
+ @key_space_limit = key_space_limit
59
+ @param_depth_limit = param_depth_limit
60
+ @bytesize_limit = bytesize_limit
61
+ @params_limit = params_limit
62
+ end
63
+
64
+ # Stolen from Mongrel, with some small modifications:
65
+ # Parses a query string by breaking it up at the '&'
66
+ # and ';' characters. You can also use this to parse
67
+ # cookies by changing the characters used in the second
68
+ # parameter (which defaults to '&;').
69
+ def parse_query(qs, d = nil, &unescaper)
70
+ unescaper ||= method(:unescape)
71
+
72
+ params = make_params
73
+
74
+ check_query_string(qs, d).split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
75
+ next if p.empty?
76
+ k, v = p.split('=', 2).map!(&unescaper)
77
+
78
+ if cur = params[k]
79
+ if cur.class == Array
80
+ params[k] << v
81
+ else
82
+ params[k] = [cur, v]
83
+ end
84
+ else
85
+ params[k] = v
86
+ end
87
+ end
88
+
89
+ return params.to_h
90
+ end
91
+
92
+ # parse_nested_query expands a query string into structural types. Supported
93
+ # types are Arrays, Hashes and basic value types. It is possible to supply
94
+ # query strings with parameters of conflicting types, in this case a
95
+ # ParameterTypeError is raised. Users are encouraged to return a 400 in this
96
+ # case.
97
+ def parse_nested_query(qs, d = nil)
98
+ params = make_params
99
+
100
+ unless qs.nil? || qs.empty?
101
+ check_query_string(qs, d).split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
102
+ k, v = p.split('=', 2).map! { |s| unescape(s) }
103
+
104
+ normalize_params(params, k, v, param_depth_limit)
105
+ end
106
+ end
107
+
108
+ return params.to_h
109
+ rescue ArgumentError => e
110
+ raise InvalidParameterError, e.message, e.backtrace
111
+ end
112
+
113
+ # normalize_params recursively expands parameters into structural types. If
114
+ # the structural types represented by two different parameter names are in
115
+ # conflict, a ParameterTypeError is raised.
116
+ def normalize_params(params, name, v, depth)
117
+ raise ParamsTooDeepError if depth <= 0
118
+
119
+ name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
120
+ k = $1 || ''
121
+ after = $' || ''
122
+
123
+ if k.empty?
124
+ if !v.nil? && name == "[]"
125
+ return Array(v)
126
+ else
127
+ return
128
+ end
129
+ end
130
+
131
+ if after == ''
132
+ params[k] = v
133
+ elsif after == "["
134
+ params[name] = v
135
+ elsif after == "[]"
136
+ params[k] ||= []
137
+ raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
138
+ params[k] << v
139
+ elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
140
+ child_key = $1
141
+ params[k] ||= []
142
+ raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
143
+ if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
144
+ normalize_params(params[k].last, child_key, v, depth - 1)
145
+ else
146
+ params[k] << normalize_params(make_params, child_key, v, depth - 1)
147
+ end
148
+ else
149
+ params[k] ||= make_params
150
+ raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
151
+ params[k] = normalize_params(params[k], after, v, depth - 1)
152
+ end
153
+
154
+ params
155
+ end
156
+
157
+ def make_params
158
+ @params_class.new @key_space_limit
159
+ end
160
+
161
+ def new_space_limit(key_space_limit)
162
+ self.class.new @params_class, key_space_limit, param_depth_limit
163
+ end
164
+
165
+ def new_depth_limit(param_depth_limit)
166
+ self.class.new @params_class, key_space_limit, param_depth_limit
167
+ end
168
+
169
+ private
170
+
171
+ def params_hash_type?(obj)
172
+ obj.kind_of?(@params_class)
173
+ end
174
+
175
+ def params_hash_has_key?(hash, key)
176
+ return false if /\[\]/.match?(key)
177
+
178
+ key.split(/[\[\]]+/).inject(hash) do |h, part|
179
+ next h if part == ''
180
+ return false unless params_hash_type?(h) && h.key?(part)
181
+ h[part]
182
+ end
183
+
184
+ true
185
+ end
186
+
187
+ def check_query_string(qs, sep)
188
+ if qs
189
+ if qs.bytesize > @bytesize_limit
190
+ raise QueryLimitError, "total query size exceeds limit (#{@bytesize_limit})"
191
+ end
192
+
193
+ if (param_count = qs.count(sep.is_a?(String) ? sep : '&;')) >= @params_limit
194
+ raise QueryLimitError, "total number of query parameters (#{param_count+1}) exceeds limit (#{@params_limit})"
195
+ end
196
+
197
+ qs
198
+ else
199
+ ''
200
+ end
201
+ end
202
+
203
+ def unescape(string)
204
+ Utils.unescape(string)
205
+ end
206
+
207
+ class Params
208
+ def initialize(limit)
209
+ @limit = limit
210
+ @size = 0
211
+ @params = {}
212
+ end
213
+
214
+ def [](key)
215
+ @params[key]
216
+ end
217
+
218
+ def []=(key, value)
219
+ @size += key.size if key && !@params.key?(key)
220
+ raise ParamsTooDeepError, 'exceeded available parameter key space' if @size > @limit
221
+ @params[key] = value
222
+ end
223
+
224
+ def key?(key)
225
+ @params.key?(key)
226
+ end
227
+
228
+ # Recursively unwraps nested `Params` objects and constructs an object
229
+ # of the same shape, but using the objects' internal representations
230
+ # (Ruby hashes) in place of the objects. The result is a hash consisting
231
+ # purely of Ruby primitives.
232
+ #
233
+ # Mutation warning!
234
+ #
235
+ # 1. This method mutates the internal representation of the `Params`
236
+ # objects in order to save object allocations.
237
+ #
238
+ # 2. The value you get back is a reference to the internal hash
239
+ # representation, not a copy.
240
+ #
241
+ # 3. Because the `Params` object's internal representation is mutable
242
+ # through the `#[]=` method, it is not thread safe. The result of
243
+ # getting the hash representation while another thread is adding a
244
+ # key to it is non-deterministic.
245
+ #
246
+ def to_h
247
+ @params.each do |key, value|
248
+ case value
249
+ when self
250
+ # Handle circular references gracefully.
251
+ @params[key] = @params
252
+ when Params
253
+ @params[key] = value.to_h
254
+ when Array
255
+ value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
256
+ else
257
+ # Ignore anything that is not a `Params` object or
258
+ # a collection that can contain one.
259
+ end
260
+ end
261
+ @params
262
+ end
263
+ alias_method :to_params_hash, :to_h
264
+ end
265
+ end
266
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
 
3
5
  module Rack
@@ -10,15 +12,15 @@ module Rack
10
12
  class ForwardRequest < Exception
11
13
  attr_reader :url, :env
12
14
 
13
- def initialize(url, env={})
15
+ def initialize(url, env = {})
14
16
  @url = URI(url)
15
17
  @env = env
16
18
 
17
- @env["PATH_INFO"] = @url.path
18
- @env["QUERY_STRING"] = @url.query if @url.query
19
- @env["HTTP_HOST"] = @url.host if @url.host
20
- @env["HTTP_PORT"] = @url.port if @url.port
21
- @env["rack.url_scheme"] = @url.scheme if @url.scheme
19
+ @env[PATH_INFO] = @url.path
20
+ @env[QUERY_STRING] = @url.query if @url.query
21
+ @env[HTTP_HOST] = @url.host if @url.host
22
+ @env[HTTP_PORT] = @url.port if @url.port
23
+ @env[RACK_URL_SCHEME] = @url.scheme if @url.scheme
22
24
 
23
25
  super "forwarding to #{url}"
24
26
  end
@@ -35,8 +37,12 @@ module Rack
35
37
  end
36
38
 
37
39
  def call(env)
38
- @script_name = env["SCRIPT_NAME"]
39
- @app.call(env.merge('rack.recursive.include' => method(:include)))
40
+ dup._call(env)
41
+ end
42
+
43
+ def _call(env)
44
+ @script_name = env[SCRIPT_NAME]
45
+ @app.call(env.merge(RACK_RECURSIVE_INCLUDE => method(:include)))
40
46
  rescue ForwardRequest => req
41
47
  call(env.merge(req.env))
42
48
  end
@@ -47,10 +53,11 @@ module Rack
47
53
  raise ArgumentError, "can only include below #{@script_name}, not #{path}"
48
54
  end
49
55
 
50
- env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name,
51
- "REQUEST_METHOD" => "GET",
56
+ env = env.merge(PATH_INFO => path,
57
+ SCRIPT_NAME => @script_name,
58
+ REQUEST_METHOD => GET,
52
59
  "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "",
53
- "rack.input" => StringIO.new(""))
60
+ RACK_INPUT => StringIO.new(""))
54
61
  @app.call(env)
55
62
  end
56
63
  end
data/lib/rack/reloader.rb CHANGED
@@ -1,5 +1,8 @@
1
- # Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
2
- # All files in this distribution are subject to the terms of the Ruby license.
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2009-2018 Michael Fellinger <m.fellinger@gmail.com>
4
+ # Rack::Reloader is subject to the terms of an MIT-style license.
5
+ # See MIT-LICENSE or https://opensource.org/licenses/MIT.
3
6
 
4
7
  require 'pathname'
5
8
 
@@ -19,12 +22,15 @@ module Rack
19
22
  # It is performing a check/reload cycle at the start of every request, but
20
23
  # also respects a cool down time, during which nothing will be done.
21
24
  class Reloader
25
+ (require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
26
+
22
27
  def initialize(app, cooldown = 10, backend = Stat)
23
28
  @app = app
24
29
  @cooldown = cooldown
25
30
  @last = (Time.now - cooldown)
26
31
  @cache = {}
27
32
  @mtimes = {}
33
+ @reload_mutex = Mutex.new
28
34
 
29
35
  extend backend
30
36
  end
@@ -32,7 +38,7 @@ module Rack
32
38
  def call(env)
33
39
  if @cooldown and Time.now > @last + @cooldown
34
40
  if Thread.list.size > 1
35
- Thread.exclusive{ reload! }
41
+ @reload_mutex.synchronize{ reload! }
36
42
  else
37
43
  reload!
38
44
  end
@@ -67,7 +73,7 @@ module Rack
67
73
  paths = ['./', *$LOAD_PATH].uniq
68
74
 
69
75
  files.map{|file|
70
- next if file =~ /\.(so|bundle)$/ # cannot reload compiled files
76
+ next if /\.(so|bundle)$/.match?(file) # cannot reload compiled files
71
77
 
72
78
  found, stat = figure_path(file, paths)
73
79
  next unless found && stat && mtime = stat.mtime
@@ -92,13 +98,15 @@ module Rack
92
98
  found, stat = safe_stat(path)
93
99
  return ::File.expand_path(found), stat if found
94
100
  end
101
+
102
+ return false, false
95
103
  end
96
104
 
97
105
  def safe_stat(file)
98
106
  return unless file
99
107
  stat = ::File.stat(file)
100
108
  return file, stat if stat.file?
101
- rescue Errno::ENOENT, Errno::ENOTDIR
109
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::ESRCH
102
110
  @cache.delete(file) and false
103
111
  end
104
112
  end