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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Auth
3
5
  module Digest
@@ -17,7 +19,7 @@ module Rack
17
19
  end
18
20
 
19
21
  def self.split_header_value(str)
20
- str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] }
22
+ str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)
21
23
  end
22
24
 
23
25
  def initialize
@@ -38,16 +40,15 @@ module Rack
38
40
 
39
41
  def to_s
40
42
  map do |k, v|
41
- "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v))
43
+ "#{k}=#{(UNQUOTED.include?(k) ? v.to_s : quote(v))}"
42
44
  end.join(', ')
43
45
  end
44
46
 
45
47
  def quote(str) # From WEBrick::HTTPUtils
46
- '"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
48
+ '"' + str.gsub(/[\\\"]/o, "\\\1") + '"'
47
49
  end
48
50
 
49
51
  end
50
52
  end
51
53
  end
52
54
  end
53
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/auth/abstract/request'
2
4
  require 'rack/auth/digest/params'
3
5
  require 'rack/auth/digest/nonce'
@@ -7,11 +9,11 @@ module Rack
7
9
  module Digest
8
10
  class Request < Auth::AbstractRequest
9
11
  def method
10
- @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD']
12
+ @env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] || @env[REQUEST_METHOD]
11
13
  end
12
14
 
13
15
  def digest?
14
- :digest == scheme
16
+ "digest" == scheme
15
17
  end
16
18
 
17
19
  def correct_uri?
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  class BodyProxy
3
5
  def initialize(body, &block)
4
- @body, @block, @closed = body, block, false
6
+ @body = body
7
+ @block = block
8
+ @closed = false
5
9
  end
6
10
 
7
- def respond_to?(*args)
8
- return false if args.first.to_s =~ /^to_ary$/
9
- super or @body.respond_to?(*args)
11
+ def respond_to?(method_name, include_all = false)
12
+ super or @body.respond_to?(method_name, include_all)
10
13
  end
11
14
 
12
15
  def close
@@ -27,13 +30,12 @@ module Rack
27
30
  # We are applying this special case for #each only. Future bugs of this
28
31
  # class will be handled by requesting users to patch their ruby
29
32
  # implementation, to save adding too many methods in this class.
30
- def each(*args, &block)
31
- @body.each(*args, &block)
33
+ def each
34
+ @body.each { |body| yield body }
32
35
  end
33
36
 
34
- def method_missing(*args, &block)
35
- super if args.first.to_s =~ /^to_ary$/
36
- @body.__send__(*args, &block)
37
+ def method_missing(method_name, *args, &block)
38
+ @body.__send__(method_name, *args, &block)
37
39
  end
38
40
  end
39
41
  end
data/lib/rack/builder.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Builder implements a small DSL to iteratively construct Rack
3
5
  # applications.
@@ -25,29 +27,47 @@ module Rack
25
27
  #
26
28
  # run app
27
29
  #
28
- # +use+ adds a middleware to the stack, +run+ dispatches to an application.
30
+ # +use+ adds middleware to the stack, +run+ dispatches to an application.
29
31
  # You can use +map+ to construct a Rack::URLMap in a convenient way.
30
32
 
31
33
  class Builder
34
+
35
+ # https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
36
+ UTF_8_BOM = '\xef\xbb\xbf'
37
+
32
38
  def self.parse_file(config, opts = Server::Options.new)
33
- options = {}
34
- if config =~ /\.ru$/
35
- cfgfile = ::File.read(config)
36
- if cfgfile[/^#\\(.*)/] && opts
37
- options = opts.parse! $1.split(/\s+/)
38
- end
39
- cfgfile.sub!(/^__END__\n.*\Z/m, '')
40
- app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app",
41
- TOPLEVEL_BINDING, config, 0
39
+ if config.end_with?('.ru')
40
+ return self.load_file(config, opts)
42
41
  else
43
42
  require config
44
- app = Object.const_get(::File.basename(config, '.rb').capitalize)
43
+ app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
44
+ return app, {}
45
+ end
46
+ end
47
+
48
+ def self.load_file(path, opts = Server::Options.new)
49
+ options = {}
50
+
51
+ cfgfile = ::File.read(path)
52
+ cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
53
+
54
+ if cfgfile[/^#\\(.*)/] && opts
55
+ options = opts.parse! $1.split(/\s+/)
45
56
  end
57
+
58
+ cfgfile.sub!(/^__END__\n.*\Z/m, '')
59
+ app = new_from_string cfgfile, path
60
+
46
61
  return app, options
47
62
  end
48
63
 
49
- def initialize(default_app = nil,&block)
50
- @use, @map, @run = [], nil, default_app
64
+ def self.new_from_string(builder_script, file = "(rackup)")
65
+ eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
66
+ TOPLEVEL_BINDING, file, 0
67
+ end
68
+
69
+ def initialize(default_app = nil, &block)
70
+ @use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
51
71
  instance_eval(&block) if block_given?
52
72
  end
53
73
 
@@ -55,7 +75,7 @@ module Rack
55
75
  self.new(default_app, &block).to_app
56
76
  end
57
77
 
58
- # Specifies a middleware to use in a stack.
78
+ # Specifies middleware to use in a stack.
59
79
  #
60
80
  # class Middleware
61
81
  # def initialize(app)
@@ -69,7 +89,7 @@ module Rack
69
89
  # end
70
90
  #
71
91
  # use Middleware
72
- # run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }
92
+ # run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
73
93
  #
74
94
  # All requests through to this application will first be processed by the middleware class.
75
95
  # The +call+ method in this example sets an additional environment key which then can be
@@ -77,10 +97,11 @@ module Rack
77
97
  def use(middleware, *args, &block)
78
98
  if @map
79
99
  mapping, @map = @map, nil
80
- @use << proc { |app| generate_map app, mapping }
100
+ @use << proc { |app| generate_map(app, mapping) }
81
101
  end
82
102
  @use << proc { |app| middleware.new(app, *args, &block) }
83
103
  end
104
+ ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
84
105
 
85
106
  # Takes an argument that is an object that responds to #call and returns a Rack response.
86
107
  # The simplest form of this is a lambda object:
@@ -92,7 +113,7 @@ module Rack
92
113
  # class Heartbeat
93
114
  # def self.call(env)
94
115
  # [200, { "Content-Type" => "text/plain" }, ["OK"]]
95
- # end
116
+ # end
96
117
  # end
97
118
  #
98
119
  # run Heartbeat
@@ -100,6 +121,19 @@ module Rack
100
121
  @run = app
101
122
  end
102
123
 
124
+ # Takes a lambda or block that is used to warm-up the application.
125
+ #
126
+ # warmup do |app|
127
+ # client = Rack::MockRequest.new(app)
128
+ # client.get('/')
129
+ # end
130
+ #
131
+ # use SomeMiddleware
132
+ # run MyApp
133
+ def warmup(prc = nil, &block)
134
+ @warmup = prc || block
135
+ end
136
+
103
137
  # Creates a route within the application.
104
138
  #
105
139
  # Rack::Builder.app do
@@ -124,10 +158,19 @@ module Rack
124
158
  @map[path] = block
125
159
  end
126
160
 
161
+ # Freeze the app (set using run) and all middleware instances when building the application
162
+ # in to_app.
163
+ def freeze_app
164
+ @freeze_app = true
165
+ end
166
+
127
167
  def to_app
128
168
  app = @map ? generate_map(@run, @map) : @run
129
169
  fail "missing run or map statement" unless app
130
- @use.reverse.inject(app) { |a,e| e[a] }
170
+ app.freeze if @freeze_app
171
+ app = @use.reverse.inject(app) { |a, e| e[a].tap { |x| x.freeze if @freeze_app } }
172
+ @warmup.call(app) if @warmup
173
+ app
131
174
  end
132
175
 
133
176
  def call(env)
@@ -137,8 +180,8 @@ module Rack
137
180
  private
138
181
 
139
182
  def generate_map(default_app, mapping)
140
- mapped = default_app ? {'/' => default_app} : {}
141
- mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b) }
183
+ mapped = default_app ? { '/' => default_app } : {}
184
+ mapping.each { |r, b| mapped[r] = self.class.new(default_app, &b).to_app }
142
185
  URLMap.new(mapped)
143
186
  end
144
187
  end
data/lib/rack/cascade.rb CHANGED
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
- # Rack::Cascade tries an request on several apps, and returns the
3
- # first response that is not 404 (or in a list of configurable
4
+ # Rack::Cascade tries a request on several apps, and returns the
5
+ # first response that is not 404 or 405 (or in a list of configurable
4
6
  # status codes).
5
7
 
6
8
  class Cascade
7
- NotFound = [404, {"Content-Type" => "text/plain"}, []]
9
+ NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
8
10
 
9
11
  attr_reader :apps
10
12
 
11
- def initialize(apps, catch=[404, 405])
12
- @apps = []; @has_app = {}
13
+ def initialize(apps, catch = [404, 405])
14
+ @apps = []
13
15
  apps.each { |app| add app }
14
16
 
15
17
  @catch = {}
@@ -38,13 +40,12 @@ module Rack
38
40
  result
39
41
  end
40
42
 
41
- def add app
42
- @has_app[app] = true
43
+ def add(app)
43
44
  @apps << app
44
45
  end
45
46
 
46
- def include? app
47
- @has_app.include? app
47
+ def include?(app)
48
+ @apps.include?(app)
48
49
  end
49
50
 
50
51
  alias_method :<<, :add
data/lib/rack/chunked.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  module Rack
@@ -10,7 +12,7 @@ module Rack
10
12
  # A body wrapper that emits chunked responses
11
13
  class Body
12
14
  TERM = "\r\n"
13
- TAIL = "0#{TERM}#{TERM}"
15
+ TAIL = "0#{TERM}"
14
16
 
15
17
  include Rack::Utils
16
18
 
@@ -18,40 +20,72 @@ module Rack
18
20
  @body = body
19
21
  end
20
22
 
21
- def each
23
+ def each(&block)
22
24
  term = TERM
23
25
  @body.each do |chunk|
24
- size = bytesize(chunk)
26
+ size = chunk.bytesize
25
27
  next if size == 0
26
28
 
27
- chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding)
29
+ chunk = chunk.b
28
30
  yield [size.to_s(16), term, chunk, term].join
29
31
  end
30
32
  yield TAIL
33
+ insert_trailers(&block)
34
+ yield TERM
31
35
  end
32
36
 
33
37
  def close
34
38
  @body.close if @body.respond_to?(:close)
35
39
  end
40
+
41
+ private
42
+
43
+ def insert_trailers(&block)
44
+ end
45
+ end
46
+
47
+ class TrailerBody < Body
48
+ private
49
+
50
+ def insert_trailers(&block)
51
+ @body.trailers.each_pair do |k, v|
52
+ yield "#{k}: #{v}\r\n"
53
+ end
54
+ end
36
55
  end
37
56
 
38
57
  def initialize(app)
39
58
  @app = app
40
59
  end
41
60
 
61
+ # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
62
+ # a version (nor response headers)
63
+ def chunkable_version?(ver)
64
+ case ver
65
+ when 'HTTP/1.0', nil, 'HTTP/0.9'
66
+ false
67
+ else
68
+ true
69
+ end
70
+ end
71
+
42
72
  def call(env)
43
73
  status, headers, body = @app.call(env)
44
74
  headers = HeaderHash.new(headers)
45
75
 
46
- if env['HTTP_VERSION'] == 'HTTP/1.0' ||
47
- STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
48
- headers['Content-Length'] ||
49
- headers['Transfer-Encoding']
76
+ if ! chunkable_version?(env[SERVER_PROTOCOL]) ||
77
+ STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
78
+ headers[CONTENT_LENGTH] ||
79
+ headers[TRANSFER_ENCODING]
50
80
  [status, headers, body]
51
81
  else
52
- headers.delete('Content-Length')
53
- headers['Transfer-Encoding'] = 'chunked'
54
- [status, headers, Body.new(body)]
82
+ headers.delete(CONTENT_LENGTH)
83
+ headers[TRANSFER_ENCODING] = 'chunked'
84
+ if headers['Trailer']
85
+ [status, headers, TrailerBody.new(body)]
86
+ else
87
+ [status, headers, Body.new(body)]
88
+ end
55
89
  end
56
90
  end
57
91
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/body_proxy'
2
4
 
3
5
  module Rack
@@ -10,7 +12,7 @@ module Rack
10
12
  # an instance of Rack::NullLogger.
11
13
  #
12
14
  # +logger+ can be any class, including the standard library Logger, and is
13
- # expected to have a +write+ method, which accepts the CommonLogger::FORMAT.
15
+ # expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
14
16
  # According to the SPEC, the error stream must also respond to +puts+
15
17
  # (which takes a single argument that responds to +to_s+), and +flush+
16
18
  # (which is called without arguments in order to make the error appear for
@@ -18,18 +20,18 @@ module Rack
18
20
  class CommonLogger
19
21
  # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
20
22
  #
21
- # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
23
+ # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
24
  #
23
25
  # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
24
26
  FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
25
27
 
26
- def initialize(app, logger=nil)
28
+ def initialize(app, logger = nil)
27
29
  @app = app
28
30
  @logger = logger
29
31
  end
30
32
 
31
33
  def call(env)
32
- began_at = Time.now
34
+ began_at = Utils.clock_time
33
35
  status, header, body = @app.call(env)
34
36
  header = Utils::HeaderHash.new(header)
35
37
  body = BodyProxy.new(body) { log(env, status, header, began_at) }
@@ -39,26 +41,33 @@ module Rack
39
41
  private
40
42
 
41
43
  def log(env, status, header, began_at)
42
- now = Time.now
43
44
  length = extract_content_length(header)
44
45
 
45
- logger = @logger || env['rack.errors']
46
- logger.write FORMAT % [
46
+ msg = FORMAT % [
47
47
  env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
48
48
  env["REMOTE_USER"] || "-",
49
- now.strftime("%d/%b/%Y %H:%M:%S"),
50
- env["REQUEST_METHOD"],
51
- env["PATH_INFO"],
52
- env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
53
- env["HTTP_VERSION"],
49
+ Time.now.strftime("%d/%b/%Y:%H:%M:%S %z"),
50
+ env[REQUEST_METHOD],
51
+ env[PATH_INFO],
52
+ env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
53
+ env[SERVER_PROTOCOL],
54
54
  status.to_s[0..3],
55
55
  length,
56
- now - began_at ]
56
+ Utils.clock_time - began_at ]
57
+
58
+ logger = @logger || env[RACK_ERRORS]
59
+ # Standard library logger doesn't support write but it supports << which actually
60
+ # calls to write on the log device without formatting
61
+ if logger.respond_to?(:write)
62
+ logger.write(msg)
63
+ else
64
+ logger << msg
65
+ end
57
66
  end
58
67
 
59
68
  def extract_content_length(headers)
60
- value = headers['Content-Length'] or return '-'
61
- value.to_s == '0' ? '-' : value
69
+ value = headers[CONTENT_LENGTH]
70
+ !value || value.to_s == '0' ? '-' : value
62
71
  end
63
72
  end
64
73
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  module Rack
@@ -13,22 +15,25 @@ module Rack
13
15
  # a conditional GET matches.
14
16
  #
15
17
  # Adapted from Michael Klishin's Merb implementation:
16
- # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb
18
+ # https://github.com/wycats/merb/blob/master/merb-core/lib/merb-core/rack/middleware/conditional_get.rb
17
19
  class ConditionalGet
18
20
  def initialize(app)
19
21
  @app = app
20
22
  end
21
23
 
22
24
  def call(env)
23
- case env['REQUEST_METHOD']
25
+ case env[REQUEST_METHOD]
24
26
  when "GET", "HEAD"
25
27
  status, headers, body = @app.call(env)
26
28
  headers = Utils::HeaderHash.new(headers)
27
29
  if status == 200 && fresh?(env, headers)
28
30
  status = 304
29
- headers.delete('Content-Type')
30
- headers.delete('Content-Length')
31
- body = []
31
+ headers.delete(CONTENT_TYPE)
32
+ headers.delete(CONTENT_LENGTH)
33
+ original_body = body
34
+ body = Rack::BodyProxy.new([]) do
35
+ original_body.close if original_body.respond_to?(:close)
36
+ end
32
37
  end
33
38
  [status, headers, body]
34
39
  else
@@ -61,7 +66,16 @@ module Rack
61
66
  end
62
67
 
63
68
  def to_rfc2822(since)
64
- Time.rfc2822(since) rescue nil
69
+ # shortest possible valid date is the obsolete: 1 Nov 97 09:55 A
70
+ # anything shorter is invalid, this avoids exceptions for common cases
71
+ # most common being the empty string
72
+ if since && since.length >= 16
73
+ # NOTE: there is no trivial way to write this in a non exception way
74
+ # _rfc2822 returns a hash but is not that usable
75
+ Time.rfc2822(since) rescue nil
76
+ else
77
+ nil
78
+ end
65
79
  end
66
80
  end
67
81
  end
data/lib/rack/config.rb CHANGED
@@ -1,6 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Config modifies the environment using the block given during
3
5
  # initialization.
6
+ #
7
+ # Example:
8
+ # use Rack::Config do |env|
9
+ # env['my-key'] = 'some-value'
10
+ # end
4
11
  class Config
5
12
  def initialize(app, &block)
6
13
  @app = app
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
4
+ require 'rack/body_proxy'
2
5
 
3
6
  module Rack
4
7
 
@@ -14,17 +17,20 @@ module Rack
14
17
  status, headers, body = @app.call(env)
15
18
  headers = HeaderHash.new(headers)
16
19
 
17
- if !STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
18
- !headers['Content-Length'] &&
19
- !headers['Transfer-Encoding'] &&
20
+ if !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
21
+ !headers[CONTENT_LENGTH] &&
22
+ !headers[TRANSFER_ENCODING] &&
20
23
  body.respond_to?(:to_ary)
21
24
 
22
25
  obody = body
23
26
  body, length = [], 0
24
- obody.each { |part| body << part; length += bytesize(part) }
25
- obody.close if obody.respond_to?(:close)
27
+ obody.each { |part| body << part; length += part.bytesize }
28
+
29
+ body = BodyProxy.new(body) do
30
+ obody.close if obody.respond_to?(:close)
31
+ end
26
32
 
27
- headers['Content-Length'] = length.to_s
33
+ headers[CONTENT_LENGTH] = length.to_s
28
34
  end
29
35
 
30
36
  [status, headers, body]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  module Rack
@@ -19,8 +21,8 @@ module Rack
19
21
  status, headers, body = @app.call(env)
20
22
  headers = Utils::HeaderHash.new(headers)
21
23
 
22
- unless STATUS_WITH_NO_ENTITY_BODY.include?(status)
23
- headers['Content-Type'] ||= @content_type
24
+ unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
25
+ headers[CONTENT_TYPE] ||= @content_type
24
26
  end
25
27
 
26
28
  [status, headers, body]
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Regexp has `match?` since Ruby 2.4
4
+ # so to support Ruby < 2.4 we need to define this method
5
+
6
+ module Rack
7
+ module RegexpExtensions
8
+ refine Regexp do
9
+ def match?(string, pos = 0)
10
+ !!match(string, pos)
11
+ end
12
+ end unless //.respond_to?(:match?)
13
+ end
14
+ end