rack 1.5.5 → 1.6.0.beta

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/KNOWN-ISSUES +14 -0
  3. data/README.rdoc +10 -6
  4. data/Rakefile +3 -4
  5. data/SPEC +59 -23
  6. data/lib/rack.rb +2 -1
  7. data/lib/rack/auth/abstract/request.rb +1 -1
  8. data/lib/rack/auth/basic.rb +1 -1
  9. data/lib/rack/auth/digest/md5.rb +1 -1
  10. data/lib/rack/backports/uri/common_18.rb +1 -1
  11. data/lib/rack/builder.rb +19 -4
  12. data/lib/rack/cascade.rb +2 -2
  13. data/lib/rack/chunked.rb +12 -1
  14. data/lib/rack/commonlogger.rb +13 -5
  15. data/lib/rack/conditionalget.rb +14 -2
  16. data/lib/rack/content_length.rb +5 -1
  17. data/lib/rack/deflater.rb +52 -13
  18. data/lib/rack/directory.rb +8 -2
  19. data/lib/rack/etag.rb +14 -6
  20. data/lib/rack/file.rb +10 -14
  21. data/lib/rack/handler.rb +2 -0
  22. data/lib/rack/handler/fastcgi.rb +4 -1
  23. data/lib/rack/handler/mongrel.rb +8 -2
  24. data/lib/rack/handler/scgi.rb +4 -1
  25. data/lib/rack/handler/thin.rb +8 -2
  26. data/lib/rack/handler/webrick.rb +46 -6
  27. data/lib/rack/head.rb +7 -2
  28. data/lib/rack/lint.rb +73 -25
  29. data/lib/rack/lobster.rb +8 -3
  30. data/lib/rack/methodoverride.rb +14 -3
  31. data/lib/rack/mime.rb +1 -15
  32. data/lib/rack/mock.rb +15 -7
  33. data/lib/rack/multipart.rb +2 -2
  34. data/lib/rack/multipart/parser.rb +107 -53
  35. data/lib/rack/multipart/uploaded_file.rb +2 -2
  36. data/lib/rack/nulllogger.rb +21 -2
  37. data/lib/rack/request.rb +38 -24
  38. data/lib/rack/response.rb +5 -0
  39. data/lib/rack/sendfile.rb +10 -5
  40. data/lib/rack/server.rb +45 -17
  41. data/lib/rack/session/abstract/id.rb +7 -6
  42. data/lib/rack/session/cookie.rb +17 -7
  43. data/lib/rack/session/memcache.rb +4 -4
  44. data/lib/rack/session/pool.rb +3 -6
  45. data/lib/rack/showexceptions.rb +20 -11
  46. data/lib/rack/showstatus.rb +1 -1
  47. data/lib/rack/static.rb +27 -30
  48. data/lib/rack/tempfile_reaper.rb +22 -0
  49. data/lib/rack/urlmap.rb +17 -3
  50. data/lib/rack/utils.rb +78 -47
  51. data/lib/rack/utils/okjson.rb +90 -91
  52. data/rack.gemspec +3 -3
  53. data/test/multipart/filename_and_no_name +6 -0
  54. data/test/multipart/invalid_character +6 -0
  55. data/test/spec_builder.rb +13 -4
  56. data/test/spec_chunked.rb +16 -0
  57. data/test/spec_commonlogger.rb +36 -0
  58. data/test/spec_content_length.rb +3 -1
  59. data/test/spec_deflater.rb +283 -148
  60. data/test/spec_etag.rb +11 -2
  61. data/test/spec_file.rb +11 -3
  62. data/test/spec_head.rb +2 -0
  63. data/test/spec_lobster.rb +1 -1
  64. data/test/spec_mock.rb +8 -0
  65. data/test/spec_multipart.rb +111 -49
  66. data/test/spec_request.rb +109 -25
  67. data/test/spec_response.rb +30 -0
  68. data/test/spec_server.rb +20 -5
  69. data/test/spec_session_cookie.rb +45 -2
  70. data/test/spec_session_memcache.rb +1 -1
  71. data/test/spec_showexceptions.rb +29 -36
  72. data/test/spec_showstatus.rb +19 -0
  73. data/test/spec_tempfile_reaper.rb +63 -0
  74. data/test/spec_urlmap.rb +23 -0
  75. data/test/spec_utils.rb +60 -10
  76. data/test/spec_webrick.rb +41 -0
  77. metadata +12 -9
  78. data/test/cgi/lighttpd.errors +0 -1
  79. data/test/multipart/three_files_three_fields +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ddc7522db220a6e7fef72e5720df45c3e78a9b54
4
- data.tar.gz: c0b77fe5d69c8bcfbd315494f00541e4e72d4f35
3
+ metadata.gz: b7e9fdbcce0cd16bdeda0ee39e9aa066e2dbcb17
4
+ data.tar.gz: 1be24bd36bc380d25cb5ef7bff5bf2abab269412
5
5
  SHA512:
6
- metadata.gz: 5c9e7ad0586b57f002aab11b31758620c0c314ff77998500d67a6bf1b7eb6c6edcd74630a0642ea71a78da8866a281c9f74a3f540fd6a9296e1bb4cf7ea3dcbe
7
- data.tar.gz: 51fb9d24391b01c03a9dcc0c1b7cf453d7d3320be8bc97ce9bf4a65656cbe820ae1edef3d587b3595ed64c6dabe3e31d86effb92b79a3461a78dbdeb874375d7
6
+ metadata.gz: 1f4730c383cd5346a255fe4a179470653370f345ccf508389bf4149c744908b195ec3676eb564f5bf434e261d698230c7ba38dd7d192ddd022c1df9d9c0f7bf7
7
+ data.tar.gz: 42f1f9f7239286710e4b3b58f142cd673fe66e56b9e42ec45c2c7a3406079118f2dd1684c165404d9f77e482129335fa315ef179fefd0711c0f59156c4ca7a31
@@ -28,3 +28,17 @@
28
28
 
29
29
  Since lighttpd 1.4.23, you also can use the "fix-root-scriptname" flag
30
30
  in fastcgi.server.
31
+
32
+ = Known conflicts regarding parameter parsing
33
+
34
+ * Many users have differing opinions about parameter parsing. The current
35
+ parameter parsers in Rack are based on a combination of the HTTP and CGI
36
+ specs, and are intended to round-trip encoding and decoding. There are some
37
+ choices that may be viewed as deficiencies, specifically:
38
+ - Rack does not create implicit arrays for multiple instances of a parameter
39
+ - Rack returns nil when a value is not given
40
+ - Rack does not support multi-type keys in parameters
41
+ These issues or choices, will not be fixed before 2.0, if at all. They are
42
+ very major breaking changes. Users are free to write alternative parameter
43
+ parsers, and their own Request and Response wrappers. Moreover, users are
44
+ encouraged to do so.
@@ -1,4 +1,4 @@
1
- = Rack, a modular Ruby webserver interface {<img src="https://secure.travis-ci.org/rack/rack.png" alt="Build Status" />}[http://travis-ci.org/rack/rack] {<img src="https://gemnasium.com/rack/rack.png" alt="Dependency Status" />}[https://gemnasium.com/rack/rack]
1
+ = Rack, a modular Ruby webserver interface {<img src="https://secure.travis-ci.org/rack/rack.svg" alt="Build Status" />}[http://travis-ci.org/rack/rack] {<img src="https://gemnasium.com/rack/rack.svg" alt="Dependency Status" />}[https://gemnasium.com/rack/rack]
2
2
 
3
3
  Rack provides a minimal, modular and adaptable interface for developing
4
4
  web applications in Ruby. By wrapping HTTP requests and responses in
@@ -33,6 +33,7 @@ These web servers include Rack handlers in their distributions:
33
33
  * Unicorn
34
34
  * unixrack
35
35
  * uWSGI
36
+ * yahns
36
37
  * Zbatery
37
38
 
38
39
  Any valid Rack app will run the same on all these handlers, without
@@ -528,8 +529,6 @@ run on port 11211) and memcache-client installed.
528
529
  * Fix a race condition that could result in overwritten pidfiles
529
530
  * Various documentation additions
530
531
 
531
- * Prevent extremely deep parameters from being parsed. CVE-2015-3225
532
-
533
532
  == Contact
534
533
 
535
534
  Please post bugs, suggestions and patches to
@@ -557,12 +556,17 @@ The Rack Core Team, consisting of
557
556
  * Christian Neukirchen (chneukirchen)
558
557
  * James Tucker (raggi)
559
558
  * Josh Peek (josh)
559
+ * José Valim (josevalim)
560
560
  * Michael Fellinger (manveru)
561
- * Ryan Tomayko (rtomayko)
562
- * Scytrin dai Kinthra (scytrin)
563
561
  * Aaron Patterson (tenderlove)
562
+ * Santiago Pastorino (spastorino)
564
563
  * Konstantin Haase (rkh)
565
564
 
565
+ and the Rack Alumnis
566
+
567
+ * Ryan Tomayko (rtomayko)
568
+ * Scytrin dai Kinthra (scytrin)
569
+
566
570
  would like to thank:
567
571
 
568
572
  * Adrian Madrid, for the LiteSpeed handler.
@@ -616,7 +620,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
616
620
 
617
621
  == Links
618
622
 
619
- Rack:: <http://rack.github.com/>
623
+ Rack:: <http://rack.github.io/>
620
624
  Official Rack repositories:: <http://github.com/rack>
621
625
  Rack Bug Tracking:: <http://github.com/rack/rack/issues>
622
626
  rack-devel mailing list:: <http://groups.google.com/group/rack-devel>
data/Rakefile CHANGED
@@ -6,7 +6,6 @@ task :default => [:test]
6
6
  desc "Install gem dependencies"
7
7
  task :deps do
8
8
  require 'rubygems'
9
- require 'rbconfig'
10
9
  spec = Gem::Specification.load('rack.gemspec')
11
10
  spec.dependencies.each do |dep|
12
11
  reqs = dep.requirements_list
@@ -33,7 +32,7 @@ task :officialrelease do
33
32
  end
34
33
 
35
34
  task :officialrelease_really => %w[SPEC dist gem] do
36
- sh "shasum #{release}.tar.gz #{release}.gem"
35
+ sh "sha1sum #{release}.tar.gz #{release}.gem"
37
36
  end
38
37
 
39
38
  def release
@@ -42,8 +41,8 @@ end
42
41
 
43
42
  desc "Make binaries executable"
44
43
  task :chmod do
45
- Dir["bin/*"].each { |binary| File.chmod(0775, binary) }
46
- Dir["test/cgi/test*"].each { |binary| File.chmod(0775, binary) }
44
+ Dir["bin/*"].each { |binary| File.chmod(0755, binary) }
45
+ Dir["test/cgi/test*"].each { |binary| File.chmod(0755, binary) }
47
46
  end
48
47
 
49
48
  desc "Generate a ChangeLog"
data/SPEC CHANGED
@@ -40,7 +40,17 @@ below.
40
40
  <tt>QUERY_STRING</tt>:: The portion of the request URL that
41
41
  follows the <tt>?</tt>, if any. May be
42
42
  empty, but is always required!
43
- <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL. <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
43
+ <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>::
44
+ When combined with <tt>SCRIPT_NAME</tt> and
45
+ <tt>PATH_INFO</tt>, these variables can be
46
+ used to complete the URL. Note, however,
47
+ that <tt>HTTP_HOST</tt>, if present,
48
+ should be used in preference to
49
+ <tt>SERVER_NAME</tt> for reconstructing
50
+ the request URL.
51
+ <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt>
52
+ can never be empty strings, and so
53
+ are always required.
44
54
  <tt>HTTP_</tt> Variables:: Variables corresponding to the
45
55
  client-supplied HTTP request
46
56
  headers (i.e., variables whose
@@ -49,24 +59,47 @@ below.
49
59
  variables should correspond with
50
60
  the presence or absence of the
51
61
  appropriate HTTP header in the
52
- request. See <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
53
- RFC3875 section 4.1.18</a> for specific behavior.
62
+ request. See
63
+ <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
64
+ RFC3875 section 4.1.18</a> for
65
+ specific behavior.
54
66
  In addition to this, the Rack environment must include these
55
67
  Rack-specific variables:
56
- <tt>rack.version</tt>:: The Array representing this version of Rack. See Rack::VERSION, that corresponds to the version of this SPEC.
57
- <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
68
+ <tt>rack.version</tt>:: The Array representing this version of Rack
69
+ See Rack::VERSION, that corresponds to
70
+ the version of this SPEC.
71
+ <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the
72
+ request URL.
58
73
  <tt>rack.input</tt>:: See below, the input stream.
59
74
  <tt>rack.errors</tt>:: See below, the error stream.
60
- <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
61
- <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
62
- <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
63
- <tt>rack.hijack?</tt>:: present and true if the server supports connection hijacking. See below, hijacking.
64
- <tt>rack.hijack</tt>:: an object responding to #call that must be called at least once before using rack.hijack_io. It is recommended #call return rack.hijack_io as well as setting it in env if necessary.
65
- <tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack has received #call, this will contain an object resembling an IO. See hijacking.
75
+ <tt>rack.multithread</tt>:: true if the application object may be
76
+ simultaneously invoked by another thread
77
+ in the same process, false otherwise.
78
+ <tt>rack.multiprocess</tt>:: true if an equivalent application object
79
+ may be simultaneously invoked by another
80
+ process, false otherwise.
81
+ <tt>rack.run_once</tt>:: true if the server expects
82
+ (but does not guarantee!) that the
83
+ application will only be invoked this one
84
+ time during the life of its containing
85
+ process. Normally, this will only be true
86
+ for a server based on CGI
87
+ (or something similar).
88
+ <tt>rack.hijack?</tt>:: present and true if the server supports
89
+ connection hijacking. See below, hijacking.
90
+ <tt>rack.hijack</tt>:: an object responding to #call that must be
91
+ called at least once before using
92
+ rack.hijack_io.
93
+ It is recommended #call return rack.hijack_io
94
+ as well as setting it in env if necessary.
95
+ <tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack
96
+ has received #call, this will contain
97
+ an object resembling an IO. See hijacking.
66
98
  Additional environment specifications have approved to
67
99
  standardized middleware APIs. None of these are required to
68
100
  be implemented by the server.
69
- <tt>rack.session</tt>:: A hash like interface for storing request session data.
101
+ <tt>rack.session</tt>:: A hash like interface for storing
102
+ request session data.
70
103
  The store must implement:
71
104
  store(key, value) (aliased as []=);
72
105
  fetch(key, default = nil) (aliased as []);
@@ -110,15 +143,18 @@ must be opened in binary mode, for Ruby 1.9 compatibility.
110
143
  The input stream must respond to +gets+, +each+, +read+ and +rewind+.
111
144
  * +gets+ must be called without arguments and return a string,
112
145
  or +nil+ on EOF.
113
- * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
114
- If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must
115
- be a String and may not be nil. If +length+ is given and not nil, then this method
116
- reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
117
- then this method reads all data until EOF.
118
- When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
119
- if +length+ is not given or is nil.
120
- If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
121
- newly created String object.
146
+ * +read+ behaves like IO#read.
147
+ Its signature is <tt>read([length, [buffer]])</tt>.
148
+ If given, +length+ must be a non-negative Integer (>= 0) or +nil+,
149
+ and +buffer+ must be a String and may not be nil.
150
+ If +length+ is given and not nil, then this method reads at most
151
+ +length+ bytes from the input stream.
152
+ If +length+ is not given or nil, then this method reads
153
+ all data until EOF.
154
+ When EOF is reached, this method returns nil if +length+ is given
155
+ and not nil, or "" if +length+ is not given or is nil.
156
+ If +buffer+ is given, then the read data will be placed
157
+ into +buffer+ instead of a newly created String object.
122
158
  * +each+ must be called without arguments and only yield Strings.
123
159
  * +rewind+ must be called without arguments. It rewinds the input
124
160
  stream back to the beginning. It must not raise Errno::ESPIPE:
@@ -207,7 +243,7 @@ but only contain keys that consist of
207
243
  letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter.
208
244
  The values of the header must be Strings,
209
245
  consisting of lines (for multiple header values, e.g. multiple
210
- <tt>Set-Cookie</tt> values) seperated by "\n".
246
+ <tt>Set-Cookie</tt> values) separated by "\\n".
211
247
  The lines must not contain characters below 037.
212
248
  === The Content-Type
213
249
  There must not be a <tt>Content-Type</tt>, when the +Status+ is 1xx,
@@ -222,7 +258,7 @@ The Body itself should not be an instance of String, as this will
222
258
  break in Ruby 1.9.
223
259
  If the Body responds to +close+, it will be called after iteration. If
224
260
  the body is replaced by a middleware after action, the original body
225
- must be closed first, if it repsonds to close.
261
+ must be closed first, if it responds to close.
226
262
  If the Body responds to +to_path+, it must return a String
227
263
  identifying the location of a file whose contents are identical
228
264
  to that produced by calling +each+; this may be used by the
@@ -20,7 +20,7 @@ module Rack
20
20
 
21
21
  # Return the Rack release as a dotted string.
22
22
  def self.release
23
- "1.5.5"
23
+ "1.5"
24
24
  end
25
25
 
26
26
  autoload :Builder, "rack/builder"
@@ -53,6 +53,7 @@ module Rack
53
53
  autoload :ShowExceptions, "rack/showexceptions"
54
54
  autoload :ShowStatus, "rack/showstatus"
55
55
  autoload :Static, "rack/static"
56
+ autoload :TempfileReaper, "rack/tempfile_reaper"
56
57
  autoload :URLMap, "rack/urlmap"
57
58
  autoload :Utils, "rack/utils"
58
59
  autoload :Multipart, "rack/multipart"
@@ -21,7 +21,7 @@ module Rack
21
21
  end
22
22
 
23
23
  def scheme
24
- @scheme ||= parts.first.downcase
24
+ @scheme ||= parts.first && parts.first.downcase
25
25
  end
26
26
 
27
27
  def params
@@ -41,7 +41,7 @@ module Rack
41
41
 
42
42
  class Request < Auth::AbstractRequest
43
43
  def basic?
44
- !parts.first.nil? && "basic" == scheme
44
+ "basic" == scheme
45
45
  end
46
46
 
47
47
  def credentials
@@ -96,7 +96,7 @@ module Rack
96
96
 
97
97
  def valid_digest?(auth)
98
98
  pw = @authenticator.call(auth.username)
99
- pw && digest(auth, pw) == auth.response
99
+ pw && Rack::Utils.secure_compare(digest(auth, pw), auth.response)
100
100
  end
101
101
 
102
102
  def md5(data)
@@ -46,7 +46,7 @@ module URI
46
46
 
47
47
  # Decode given +str+ of URL-encoded form data.
48
48
  #
49
- # This decods + to SP.
49
+ # This decodes + to SP.
50
50
  #
51
51
  # See URI.encode_www_form_component, URI.decode_www_form
52
52
  def self.decode_www_form_component(str, enc=nil)
@@ -51,7 +51,7 @@ module Rack
51
51
  end
52
52
 
53
53
  def initialize(default_app = nil,&block)
54
- @use, @map, @run = [], nil, default_app
54
+ @use, @map, @run, @warmup = [], nil, default_app, nil
55
55
  instance_eval(&block) if block_given?
56
56
  end
57
57
 
@@ -73,7 +73,7 @@ module Rack
73
73
  # end
74
74
  #
75
75
  # use Middleware
76
- # run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }
76
+ # run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
77
77
  #
78
78
  # All requests through to this application will first be processed by the middleware class.
79
79
  # The +call+ method in this example sets an additional environment key which then can be
@@ -104,6 +104,19 @@ module Rack
104
104
  @run = app
105
105
  end
106
106
 
107
+ # Takes a lambda or block that is used to warm-up the application.
108
+ #
109
+ # warmup do |app|
110
+ # client = Rack::MockRequest.new(app)
111
+ # client.get('/')
112
+ # end
113
+ #
114
+ # use SomeMiddleware
115
+ # run MyApp
116
+ def warmup(prc=nil, &block)
117
+ @warmup = prc || block
118
+ end
119
+
107
120
  # Creates a route within the application.
108
121
  #
109
122
  # Rack::Builder.app do
@@ -131,7 +144,9 @@ module Rack
131
144
  def to_app
132
145
  app = @map ? generate_map(@run, @map) : @run
133
146
  fail "missing run or map statement" unless app
134
- @use.reverse.inject(app) { |a,e| e[a] }
147
+ app = @use.reverse.inject(app) { |a,e| e[a] }
148
+ @warmup.call(app) if @warmup
149
+ app
135
150
  end
136
151
 
137
152
  def call(env)
@@ -142,7 +157,7 @@ module Rack
142
157
 
143
158
  def generate_map(default_app, mapping)
144
159
  mapped = default_app ? {'/' => default_app} : {}
145
- mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b) }
160
+ mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
146
161
  URLMap.new(mapped)
147
162
  end
148
163
  end
@@ -38,12 +38,12 @@ module Rack
38
38
  result
39
39
  end
40
40
 
41
- def add app
41
+ def add(app)
42
42
  @has_app[app] = true
43
43
  @apps << app
44
44
  end
45
45
 
46
- def include? app
46
+ def include?(app)
47
47
  @has_app.include? app
48
48
  end
49
49
 
@@ -39,11 +39,22 @@ module Rack
39
39
  @app = app
40
40
  end
41
41
 
42
+ # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
43
+ # a version (nor response headers)
44
+ def chunkable_version?(ver)
45
+ case ver
46
+ when "HTTP/1.0", nil, "HTTP/0.9"
47
+ false
48
+ else
49
+ true
50
+ end
51
+ end
52
+
42
53
  def call(env)
43
54
  status, headers, body = @app.call(env)
44
55
  headers = HeaderHash.new(headers)
45
56
 
46
- if env['HTTP_VERSION'] == 'HTTP/1.0' ||
57
+ if ! chunkable_version?(env['HTTP_VERSION']) ||
47
58
  STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
48
59
  headers['Content-Length'] ||
49
60
  headers['Transfer-Encoding']
@@ -10,7 +10,7 @@ module Rack
10
10
  # an instance of Rack::NullLogger.
11
11
  #
12
12
  # +logger+ can be any class, including the standard library Logger, and is
13
- # expected to have a +write+ method, which accepts the CommonLogger::FORMAT.
13
+ # expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
14
14
  # According to the SPEC, the error stream must also respond to +puts+
15
15
  # (which takes a single argument that responds to +to_s+), and +flush+
16
16
  # (which is called without arguments in order to make the error appear for
@@ -18,7 +18,7 @@ module Rack
18
18
  class CommonLogger
19
19
  # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
20
20
  #
21
- # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
21
+ # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
22
  #
23
23
  # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
24
24
  FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
@@ -42,11 +42,10 @@ module Rack
42
42
  now = Time.now
43
43
  length = extract_content_length(header)
44
44
 
45
- logger = @logger || env['rack.errors']
46
- logger.write FORMAT % [
45
+ msg = FORMAT % [
47
46
  env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
48
47
  env["REMOTE_USER"] || "-",
49
- now.strftime("%d/%b/%Y %H:%M:%S"),
48
+ now.strftime("%d/%b/%Y:%H:%M:%S %z"),
50
49
  env["REQUEST_METHOD"],
51
50
  env["PATH_INFO"],
52
51
  env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
@@ -54,6 +53,15 @@ module Rack
54
53
  status.to_s[0..3],
55
54
  length,
56
55
  now - began_at ]
56
+
57
+ logger = @logger || env['rack.errors']
58
+ # Standard library logger doesn't support write but it supports << which actually
59
+ # calls to write on the log device without formatting
60
+ if logger.respond_to?(:write)
61
+ logger.write(msg)
62
+ else
63
+ logger << msg
64
+ end
57
65
  end
58
66
 
59
67
  def extract_content_length(headers)
@@ -28,7 +28,10 @@ module Rack
28
28
  status = 304
29
29
  headers.delete('Content-Type')
30
30
  headers.delete('Content-Length')
31
- body = []
31
+ original_body = body
32
+ body = Rack::BodyProxy.new([]) do
33
+ original_body.close if original_body.respond_to?(:close)
34
+ end
32
35
  end
33
36
  [status, headers, body]
34
37
  else
@@ -61,7 +64,16 @@ module Rack
61
64
  end
62
65
 
63
66
  def to_rfc2822(since)
64
- Time.rfc2822(since) rescue nil
67
+ # shortest possible valid date is the obsolete: 1 Nov 97 09:55 A
68
+ # anything shorter is invalid, this avoids exceptions for common cases
69
+ # most common being the empty string
70
+ if since && since.length >= 16
71
+ # NOTE: there is no trivial way to write this in a non execption way
72
+ # _rfc2822 returns a hash but is not that usable
73
+ Time.rfc2822(since) rescue nil
74
+ else
75
+ nil
76
+ end
65
77
  end
66
78
  end
67
79
  end