rack 2.1.3 → 2.2.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +626 -1
- data/CONTRIBUTING.md +136 -0
- data/README.rdoc +83 -39
- data/Rakefile +14 -7
- data/{SPEC → SPEC.rdoc} +35 -6
- data/lib/rack.rb +7 -16
- data/lib/rack/auth/abstract/request.rb +0 -2
- data/lib/rack/auth/basic.rb +3 -3
- data/lib/rack/auth/digest/md5.rb +4 -4
- data/lib/rack/auth/digest/request.rb +3 -3
- data/lib/rack/body_proxy.rb +13 -9
- data/lib/rack/builder.rb +77 -8
- data/lib/rack/cascade.rb +23 -8
- data/lib/rack/chunked.rb +48 -23
- data/lib/rack/common_logger.rb +25 -18
- data/lib/rack/conditional_get.rb +18 -16
- data/lib/rack/content_length.rb +6 -7
- data/lib/rack/content_type.rb +3 -4
- data/lib/rack/deflater.rb +45 -35
- data/lib/rack/directory.rb +77 -59
- data/lib/rack/etag.rb +2 -3
- data/lib/rack/events.rb +15 -18
- data/lib/rack/file.rb +1 -1
- data/lib/rack/files.rb +96 -56
- data/lib/rack/handler/cgi.rb +1 -4
- data/lib/rack/handler/fastcgi.rb +1 -3
- data/lib/rack/handler/lsws.rb +1 -3
- data/lib/rack/handler/scgi.rb +1 -3
- data/lib/rack/handler/thin.rb +1 -3
- data/lib/rack/handler/webrick.rb +12 -5
- data/lib/rack/head.rb +0 -2
- data/lib/rack/lint.rb +57 -14
- data/lib/rack/lobster.rb +3 -5
- data/lib/rack/lock.rb +0 -1
- data/lib/rack/mock.rb +22 -4
- data/lib/rack/multipart.rb +1 -1
- data/lib/rack/multipart/generator.rb +11 -6
- data/lib/rack/multipart/parser.rb +7 -15
- data/lib/rack/multipart/uploaded_file.rb +13 -7
- data/lib/rack/query_parser.rb +7 -8
- data/lib/rack/recursive.rb +1 -1
- data/lib/rack/reloader.rb +1 -3
- data/lib/rack/request.rb +182 -76
- data/lib/rack/response.rb +62 -19
- data/lib/rack/rewindable_input.rb +0 -1
- data/lib/rack/runtime.rb +3 -3
- data/lib/rack/sendfile.rb +0 -3
- data/lib/rack/server.rb +9 -8
- data/lib/rack/session/abstract/id.rb +21 -18
- data/lib/rack/session/cookie.rb +1 -3
- data/lib/rack/session/pool.rb +1 -1
- data/lib/rack/show_exceptions.rb +6 -8
- data/lib/rack/show_status.rb +5 -7
- data/lib/rack/static.rb +13 -6
- data/lib/rack/tempfile_reaper.rb +0 -2
- data/lib/rack/urlmap.rb +1 -4
- data/lib/rack/utils.rb +63 -55
- data/lib/rack/version.rb +29 -0
- data/rack.gemspec +31 -29
- metadata +14 -15
data/lib/rack/etag.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative '../rack'
|
4
4
|
require 'digest/sha2'
|
5
5
|
|
6
6
|
module Rack
|
@@ -57,8 +57,7 @@ module Rack
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def skip_caching?(headers)
|
60
|
-
(
|
61
|
-
headers.key?(ETAG_STRING) || headers.key?('Last-Modified')
|
60
|
+
headers.key?(ETAG_STRING) || headers.key?('Last-Modified')
|
62
61
|
end
|
63
62
|
|
64
63
|
def digest_body(body)
|
data/lib/rack/events.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack/response'
|
4
|
-
require 'rack/body_proxy'
|
5
|
-
|
6
3
|
module Rack
|
7
4
|
### This middleware provides hooks to certain places in the request /
|
8
5
|
# response lifecycle. This is so that middleware that don't need to filter
|
@@ -59,26 +56,26 @@ module Rack
|
|
59
56
|
|
60
57
|
class Events
|
61
58
|
module Abstract
|
62
|
-
def on_start
|
59
|
+
def on_start(req, res)
|
63
60
|
end
|
64
61
|
|
65
|
-
def on_commit
|
62
|
+
def on_commit(req, res)
|
66
63
|
end
|
67
64
|
|
68
|
-
def on_send
|
65
|
+
def on_send(req, res)
|
69
66
|
end
|
70
67
|
|
71
|
-
def on_finish
|
68
|
+
def on_finish(req, res)
|
72
69
|
end
|
73
70
|
|
74
|
-
def on_error
|
71
|
+
def on_error(req, res, e)
|
75
72
|
end
|
76
73
|
end
|
77
74
|
|
78
75
|
class EventedBodyProxy < Rack::BodyProxy # :nodoc:
|
79
76
|
attr_reader :request, :response
|
80
77
|
|
81
|
-
def initialize
|
78
|
+
def initialize(body, request, response, handlers, &block)
|
82
79
|
super(body, &block)
|
83
80
|
@request = request
|
84
81
|
@response = response
|
@@ -94,7 +91,7 @@ module Rack
|
|
94
91
|
class BufferedResponse < Rack::Response::Raw # :nodoc:
|
95
92
|
attr_reader :body
|
96
93
|
|
97
|
-
def initialize
|
94
|
+
def initialize(status, headers, body)
|
98
95
|
super(status, headers)
|
99
96
|
@body = body
|
100
97
|
end
|
@@ -102,12 +99,12 @@ module Rack
|
|
102
99
|
def to_a; [status, headers, body]; end
|
103
100
|
end
|
104
101
|
|
105
|
-
def initialize
|
102
|
+
def initialize(app, handlers)
|
106
103
|
@app = app
|
107
104
|
@handlers = handlers
|
108
105
|
end
|
109
106
|
|
110
|
-
def call
|
107
|
+
def call(env)
|
111
108
|
request = make_request env
|
112
109
|
on_start request, nil
|
113
110
|
|
@@ -129,27 +126,27 @@ module Rack
|
|
129
126
|
|
130
127
|
private
|
131
128
|
|
132
|
-
def on_error
|
129
|
+
def on_error(request, response, e)
|
133
130
|
@handlers.reverse_each { |handler| handler.on_error request, response, e }
|
134
131
|
end
|
135
132
|
|
136
|
-
def on_commit
|
133
|
+
def on_commit(request, response)
|
137
134
|
@handlers.reverse_each { |handler| handler.on_commit request, response }
|
138
135
|
end
|
139
136
|
|
140
|
-
def on_start
|
137
|
+
def on_start(request, response)
|
141
138
|
@handlers.each { |handler| handler.on_start request, nil }
|
142
139
|
end
|
143
140
|
|
144
|
-
def on_finish
|
141
|
+
def on_finish(request, response)
|
145
142
|
@handlers.reverse_each { |handler| handler.on_finish request, response }
|
146
143
|
end
|
147
144
|
|
148
|
-
def make_request
|
145
|
+
def make_request(env)
|
149
146
|
Rack::Request.new env
|
150
147
|
end
|
151
148
|
|
152
|
-
def make_response
|
149
|
+
def make_response(status, headers, body)
|
153
150
|
BufferedResponse.new status, headers, body
|
154
151
|
end
|
155
152
|
end
|
data/lib/rack/file.rb
CHANGED
data/lib/rack/files.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'time'
|
4
|
-
require 'rack/utils'
|
5
|
-
require 'rack/mime'
|
6
|
-
require 'rack/request'
|
7
|
-
require 'rack/head'
|
8
4
|
|
9
5
|
module Rack
|
10
6
|
# Rack::Files serves files below the +root+ directory given, according to the
|
@@ -18,6 +14,15 @@ module Rack
|
|
18
14
|
class Files
|
19
15
|
ALLOWED_VERBS = %w[GET HEAD OPTIONS]
|
20
16
|
ALLOW_HEADER = ALLOWED_VERBS.join(', ')
|
17
|
+
MULTIPART_BOUNDARY = 'AaB03x'
|
18
|
+
|
19
|
+
# @todo remove in 3.0
|
20
|
+
def self.method_added(name)
|
21
|
+
if name == :response_body
|
22
|
+
raise "#{self.class}\#response_body is no longer supported."
|
23
|
+
end
|
24
|
+
super
|
25
|
+
end
|
21
26
|
|
22
27
|
attr_reader :root
|
23
28
|
|
@@ -48,7 +53,11 @@ module Rack
|
|
48
53
|
available = begin
|
49
54
|
::File.file?(path) && ::File.readable?(path)
|
50
55
|
rescue SystemCallError
|
56
|
+
# Not sure in what conditions this exception can occur, but this
|
57
|
+
# is a safe way to handle such an error.
|
58
|
+
# :nocov:
|
51
59
|
false
|
60
|
+
# :nocov:
|
52
61
|
end
|
53
62
|
|
54
63
|
if available
|
@@ -70,75 +79,116 @@ module Rack
|
|
70
79
|
headers[CONTENT_TYPE] = mime_type if mime_type
|
71
80
|
|
72
81
|
# Set custom headers
|
73
|
-
|
74
|
-
|
75
|
-
response = [ 200, headers ]
|
82
|
+
headers.merge!(@headers) if @headers
|
76
83
|
|
84
|
+
status = 200
|
77
85
|
size = filesize path
|
78
86
|
|
79
|
-
range = nil
|
80
87
|
ranges = Rack::Utils.get_byte_ranges(request.get_header('HTTP_RANGE'), size)
|
81
|
-
if ranges.nil?
|
82
|
-
# No ranges
|
83
|
-
|
84
|
-
response[0] = 200
|
85
|
-
range = 0..size - 1
|
88
|
+
if ranges.nil?
|
89
|
+
# No ranges:
|
90
|
+
ranges = [0..size - 1]
|
86
91
|
elsif ranges.empty?
|
87
92
|
# Unsatisfiable. Return error, and file size:
|
88
93
|
response = fail(416, "Byte range unsatisfiable")
|
89
94
|
response[1]["Content-Range"] = "bytes */#{size}"
|
90
95
|
return response
|
91
|
-
|
92
|
-
# Partial content
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
96
|
+
elsif ranges.size >= 1
|
97
|
+
# Partial content
|
98
|
+
partial_content = true
|
99
|
+
|
100
|
+
if ranges.size == 1
|
101
|
+
range = ranges[0]
|
102
|
+
headers["Content-Range"] = "bytes #{range.begin}-#{range.end}/#{size}"
|
103
|
+
else
|
104
|
+
headers[CONTENT_TYPE] = "multipart/byteranges; boundary=#{MULTIPART_BOUNDARY}"
|
105
|
+
end
|
106
|
+
|
107
|
+
status = 206
|
108
|
+
body = BaseIterator.new(path, ranges, mime_type: mime_type, size: size)
|
109
|
+
size = body.bytesize
|
97
110
|
end
|
98
111
|
|
99
|
-
|
112
|
+
headers[CONTENT_LENGTH] = size.to_s
|
100
113
|
|
101
|
-
|
102
|
-
|
103
|
-
|
114
|
+
if request.head?
|
115
|
+
body = []
|
116
|
+
elsif !partial_content
|
117
|
+
body = Iterator.new(path, ranges, mime_type: mime_type, size: size)
|
118
|
+
end
|
119
|
+
|
120
|
+
[status, headers, body]
|
104
121
|
end
|
105
122
|
|
106
|
-
class
|
107
|
-
attr_reader :path, :
|
108
|
-
alias :to_path :path
|
123
|
+
class BaseIterator
|
124
|
+
attr_reader :path, :ranges, :options
|
109
125
|
|
110
|
-
def initialize
|
111
|
-
@path
|
112
|
-
@
|
126
|
+
def initialize(path, ranges, options)
|
127
|
+
@path = path
|
128
|
+
@ranges = ranges
|
129
|
+
@options = options
|
113
130
|
end
|
114
131
|
|
115
132
|
def each
|
116
133
|
::File.open(path, "rb") do |file|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
yield part
|
134
|
+
ranges.each do |range|
|
135
|
+
yield multipart_heading(range) if multipart?
|
136
|
+
|
137
|
+
each_range_part(file, range) do |part|
|
138
|
+
yield part
|
139
|
+
end
|
125
140
|
end
|
141
|
+
|
142
|
+
yield "\r\n--#{MULTIPART_BOUNDARY}--\r\n" if multipart?
|
126
143
|
end
|
127
144
|
end
|
128
145
|
|
146
|
+
def bytesize
|
147
|
+
size = ranges.inject(0) do |sum, range|
|
148
|
+
sum += multipart_heading(range).bytesize if multipart?
|
149
|
+
sum += range.size
|
150
|
+
end
|
151
|
+
size += "\r\n--#{MULTIPART_BOUNDARY}--\r\n".bytesize if multipart?
|
152
|
+
size
|
153
|
+
end
|
154
|
+
|
129
155
|
def close; end
|
130
|
-
end
|
131
156
|
|
132
|
-
|
157
|
+
private
|
133
158
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
159
|
+
def multipart?
|
160
|
+
ranges.size > 1
|
161
|
+
end
|
162
|
+
|
163
|
+
def multipart_heading(range)
|
164
|
+
<<-EOF
|
165
|
+
\r
|
166
|
+
--#{MULTIPART_BOUNDARY}\r
|
167
|
+
Content-Type: #{options[:mime_type]}\r
|
168
|
+
Content-Range: bytes #{range.begin}-#{range.end}/#{options[:size]}\r
|
169
|
+
\r
|
170
|
+
EOF
|
171
|
+
end
|
172
|
+
|
173
|
+
def each_range_part(file, range)
|
174
|
+
file.seek(range.begin)
|
175
|
+
remaining_len = range.end - range.begin + 1
|
176
|
+
while remaining_len > 0
|
177
|
+
part = file.read([8192, remaining_len].min)
|
178
|
+
break unless part
|
179
|
+
remaining_len -= part.length
|
180
|
+
|
181
|
+
yield part
|
182
|
+
end
|
139
183
|
end
|
140
184
|
end
|
141
185
|
|
186
|
+
class Iterator < BaseIterator
|
187
|
+
alias :to_path :path
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
142
192
|
def fail(status, body, headers = {})
|
143
193
|
body += "\n"
|
144
194
|
|
@@ -154,25 +204,15 @@ module Rack
|
|
154
204
|
end
|
155
205
|
|
156
206
|
# The MIME type for the contents of the file located at @path
|
157
|
-
def mime_type
|
207
|
+
def mime_type(path, default_mime)
|
158
208
|
Mime.mime_type(::File.extname(path), default_mime)
|
159
209
|
end
|
160
210
|
|
161
|
-
def filesize
|
162
|
-
# If response_body is present, use its size.
|
163
|
-
return response_body.bytesize if response_body
|
164
|
-
|
211
|
+
def filesize(path)
|
165
212
|
# We check via File::size? whether this file provides size info
|
166
213
|
# via stat (e.g. /proc files often don't), otherwise we have to
|
167
214
|
# figure it out by reading the whole file into memory.
|
168
215
|
::File.size?(path) || ::File.read(path).bytesize
|
169
216
|
end
|
170
|
-
|
171
|
-
# By default, the response body for file requests is nil.
|
172
|
-
# In this case, the response body will be generated later
|
173
|
-
# from the file at @path
|
174
|
-
def response_body
|
175
|
-
nil
|
176
|
-
end
|
177
217
|
end
|
178
218
|
end
|
data/lib/rack/handler/cgi.rb
CHANGED
data/lib/rack/handler/fastcgi.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'fcgi'
|
4
4
|
require 'socket'
|
5
|
-
require 'rack/content_length'
|
6
|
-
require 'rack/rewindable_input'
|
7
5
|
|
8
6
|
if defined? FCGI::Stream
|
9
7
|
class FCGI::Stream
|
@@ -20,7 +18,7 @@ end
|
|
20
18
|
module Rack
|
21
19
|
module Handler
|
22
20
|
class FastCGI
|
23
|
-
def self.run(app, options
|
21
|
+
def self.run(app, **options)
|
24
22
|
if options[:File]
|
25
23
|
STDIN.reopen(UNIXServer.new(options[:File]))
|
26
24
|
elsif options[:Port]
|
data/lib/rack/handler/lsws.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'lsapi'
|
4
|
-
require 'rack/content_length'
|
5
|
-
require 'rack/rewindable_input'
|
6
4
|
|
7
5
|
module Rack
|
8
6
|
module Handler
|
9
7
|
class LSWS
|
10
|
-
def self.run(app, options
|
8
|
+
def self.run(app, **options)
|
11
9
|
while LSAPI.accept != nil
|
12
10
|
serve app
|
13
11
|
end
|
data/lib/rack/handler/scgi.rb
CHANGED
@@ -2,15 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'scgi'
|
4
4
|
require 'stringio'
|
5
|
-
require 'rack/content_length'
|
6
|
-
require 'rack/chunked'
|
7
5
|
|
8
6
|
module Rack
|
9
7
|
module Handler
|
10
8
|
class SCGI < ::SCGI::Processor
|
11
9
|
attr_accessor :app
|
12
10
|
|
13
|
-
def self.run(app, options
|
11
|
+
def self.run(app, **options)
|
14
12
|
options[:Socket] = UNIXServer.new(options[:File]) if options[:File]
|
15
13
|
new(options.merge(app: app,
|
16
14
|
host: options[:Host],
|
data/lib/rack/handler/thin.rb
CHANGED
@@ -4,13 +4,11 @@ require "thin"
|
|
4
4
|
require "thin/server"
|
5
5
|
require "thin/logging"
|
6
6
|
require "thin/backends/tcp_server"
|
7
|
-
require "rack/content_length"
|
8
|
-
require "rack/chunked"
|
9
7
|
|
10
8
|
module Rack
|
11
9
|
module Handler
|
12
10
|
class Thin
|
13
|
-
def self.run(app, options
|
11
|
+
def self.run(app, **options)
|
14
12
|
environment = ENV['RACK_ENV'] || 'development'
|
15
13
|
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
|
16
14
|
|
data/lib/rack/handler/webrick.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'webrick'
|
4
4
|
require 'stringio'
|
5
|
-
require 'rack/content_length'
|
6
5
|
|
7
6
|
# This monkey patch allows for applications to perform their own chunking
|
8
7
|
# through WEBrick::HTTPResponse if rack is set to true.
|
@@ -24,12 +23,18 @@ end
|
|
24
23
|
module Rack
|
25
24
|
module Handler
|
26
25
|
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
|
27
|
-
def self.run(app, options
|
26
|
+
def self.run(app, **options)
|
28
27
|
environment = ENV['RACK_ENV'] || 'development'
|
29
28
|
default_host = environment == 'development' ? 'localhost' : nil
|
30
29
|
|
31
|
-
options[:BindAddress]
|
30
|
+
if !options[:BindAddress] || options[:Host]
|
31
|
+
options[:BindAddress] = options.delete(:Host) || default_host
|
32
|
+
end
|
32
33
|
options[:Port] ||= 8080
|
34
|
+
if options[:SSLEnable]
|
35
|
+
require 'webrick/https'
|
36
|
+
end
|
37
|
+
|
33
38
|
@server = ::WEBrick::HTTPServer.new(options)
|
34
39
|
@server.mount "/", Rack::Handler::WEBrick, app
|
35
40
|
yield @server if block_given?
|
@@ -47,8 +52,10 @@ module Rack
|
|
47
52
|
end
|
48
53
|
|
49
54
|
def self.shutdown
|
50
|
-
@server
|
51
|
-
|
55
|
+
if @server
|
56
|
+
@server.shutdown
|
57
|
+
@server = nil
|
58
|
+
end
|
52
59
|
end
|
53
60
|
|
54
61
|
def initialize(server, app)
|