rack 2.1.4.4 → 2.2.0
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 +598 -15
- data/CONTRIBUTING.md +136 -0
- data/README.rdoc +84 -54
- data/Rakefile +14 -7
- data/{SPEC → SPEC.rdoc} +35 -6
- 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 -21
- 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 -60
- 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 +15 -11
- data/lib/rack/handler/webrick.rb +12 -5
- data/lib/rack/head.rb +0 -2
- data/lib/rack/lint.rb +58 -15
- 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/generator.rb +11 -6
- data/lib/rack/multipart/parser.rb +12 -32
- data/lib/rack/multipart/uploaded_file.rb +13 -7
- data/lib/rack/multipart.rb +5 -4
- 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 +172 -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 +20 -18
- data/lib/rack/session/cookie.rb +2 -3
- data/lib/rack/session/pool.rb +1 -1
- data/lib/rack/show_exceptions.rb +2 -4
- data/lib/rack/show_status.rb +1 -3
- 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 +70 -82
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +7 -16
- data/rack.gemspec +31 -29
- metadata +14 -15
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,24 +4,28 @@ 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
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
if block_given?
|
16
|
+
host = options.delete(:Host) || default_host
|
17
|
+
port = options.delete(:Port) || 8080
|
18
|
+
args = [host, port, app, options]
|
19
|
+
# Thin versions below 0.8.0 do not support additional options
|
20
|
+
args.pop if ::Thin::VERSION::MAJOR < 1 && ::Thin::VERSION::MINOR < 8
|
21
|
+
server = ::Thin::Server.new(*args)
|
22
|
+
yield server
|
23
|
+
server.start
|
24
|
+
else
|
25
|
+
options[:address] = options[:Host] || default_host
|
26
|
+
options[:port] = options[:Port] || 8080
|
27
|
+
::Thin::Controllers::Controller.new(options).start
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
def self.valid_options
|
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)
|
data/lib/rack/head.rb
CHANGED
data/lib/rack/lint.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack/utils'
|
4
3
|
require 'forwardable'
|
5
4
|
|
6
5
|
module Rack
|
@@ -48,13 +47,24 @@ module Rack
|
|
48
47
|
env[RACK_ERRORS] = ErrorWrapper.new(env[RACK_ERRORS])
|
49
48
|
|
50
49
|
## and returns an Array of exactly three values:
|
51
|
-
|
50
|
+
ary = @app.call(env)
|
51
|
+
assert("response #{ary.inspect} is not an Array , but #{ary.class}") {
|
52
|
+
ary.kind_of? Array
|
53
|
+
}
|
54
|
+
assert("response array #{ary.inspect} has #{ary.size} elements instead of 3") {
|
55
|
+
ary.size == 3
|
56
|
+
}
|
57
|
+
|
58
|
+
status, headers, @body = ary
|
52
59
|
## The *status*,
|
53
60
|
check_status status
|
54
61
|
## the *headers*,
|
55
62
|
check_headers headers
|
56
63
|
|
57
|
-
check_hijack_response headers, env
|
64
|
+
hijack_proc = check_hijack_response headers, env
|
65
|
+
if hijack_proc && headers.is_a?(Hash)
|
66
|
+
headers[RACK_HIJACK] = hijack_proc
|
67
|
+
end
|
58
68
|
|
59
69
|
## and the *body*.
|
60
70
|
check_content_type status, headers
|
@@ -65,12 +75,15 @@ module Rack
|
|
65
75
|
|
66
76
|
## == The Environment
|
67
77
|
def check_env(env)
|
68
|
-
## The environment must be an instance of Hash that includes
|
78
|
+
## The environment must be an unfrozen instance of Hash that includes
|
69
79
|
## CGI-like headers. The application is free to modify the
|
70
80
|
## environment.
|
71
81
|
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
|
72
82
|
env.kind_of? Hash
|
73
83
|
}
|
84
|
+
assert("env should not be frozen, but is") {
|
85
|
+
!env.frozen?
|
86
|
+
}
|
74
87
|
|
75
88
|
##
|
76
89
|
## The environment is required to include these variables
|
@@ -104,17 +117,19 @@ module Rack
|
|
104
117
|
## follows the <tt>?</tt>, if any. May be
|
105
118
|
## empty, but is always required!
|
106
119
|
|
107
|
-
## <tt>SERVER_NAME</tt
|
108
|
-
## When combined with <tt>SCRIPT_NAME</tt> and
|
120
|
+
## <tt>SERVER_NAME</tt>:: When combined with <tt>SCRIPT_NAME</tt> and
|
109
121
|
## <tt>PATH_INFO</tt>, these variables can be
|
110
122
|
## used to complete the URL. Note, however,
|
111
123
|
## that <tt>HTTP_HOST</tt>, if present,
|
112
124
|
## should be used in preference to
|
113
125
|
## <tt>SERVER_NAME</tt> for reconstructing
|
114
126
|
## the request URL.
|
115
|
-
## <tt>SERVER_NAME</tt>
|
116
|
-
##
|
117
|
-
|
127
|
+
## <tt>SERVER_NAME</tt> can never be an empty
|
128
|
+
## string, and so is always required.
|
129
|
+
|
130
|
+
## <tt>SERVER_PORT</tt>:: An optional +Integer+ which is the port the
|
131
|
+
## server is running on. Should be specified if
|
132
|
+
## the server is running on a non-standard port.
|
118
133
|
|
119
134
|
## <tt>HTTP_</tt> Variables:: Variables corresponding to the
|
120
135
|
## client-supplied HTTP request
|
@@ -198,6 +213,11 @@ module Rack
|
|
198
213
|
assert("session #{session.inspect} must respond to clear") {
|
199
214
|
session.respond_to?(:clear)
|
200
215
|
}
|
216
|
+
|
217
|
+
## to_hash (returning unfrozen Hash instance);
|
218
|
+
assert("session #{session.inspect} must respond to to_hash and return unfrozen Hash instance") {
|
219
|
+
session.respond_to?(:to_hash) && session.to_hash.kind_of?(Hash) && !session.to_hash.frozen?
|
220
|
+
}
|
201
221
|
end
|
202
222
|
|
203
223
|
## <tt>rack.logger</tt>:: A common object interface for logging messages.
|
@@ -253,13 +273,28 @@ module Rack
|
|
253
273
|
## accepted specifications and must not be used otherwise.
|
254
274
|
##
|
255
275
|
|
256
|
-
%w[REQUEST_METHOD SERVER_NAME
|
257
|
-
QUERY_STRING
|
276
|
+
%w[REQUEST_METHOD SERVER_NAME QUERY_STRING
|
258
277
|
rack.version rack.input rack.errors
|
259
278
|
rack.multithread rack.multiprocess rack.run_once].each { |header|
|
260
279
|
assert("env missing required key #{header}") { env.include? header }
|
261
280
|
}
|
262
281
|
|
282
|
+
## The <tt>SERVER_PORT</tt> must be an Integer if set.
|
283
|
+
assert("env[SERVER_PORT] is not an Integer") do
|
284
|
+
server_port = env["SERVER_PORT"]
|
285
|
+
server_port.nil? || (Integer(server_port) rescue false)
|
286
|
+
end
|
287
|
+
|
288
|
+
## The <tt>SERVER_NAME</tt> must be a valid authority as defined by RFC7540.
|
289
|
+
assert("#{env[SERVER_NAME]} must be a valid authority") do
|
290
|
+
URI.parse("http://#{env[SERVER_NAME]}/") rescue false
|
291
|
+
end
|
292
|
+
|
293
|
+
## The <tt>HTTP_HOST</tt> must be a valid authority as defined by RFC7540.
|
294
|
+
assert("#{env[HTTP_HOST]} must be a valid authority") do
|
295
|
+
URI.parse("http://#{env[HTTP_HOST]}/") rescue false
|
296
|
+
end
|
297
|
+
|
263
298
|
## The environment must not contain the keys
|
264
299
|
## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
|
265
300
|
## (use the versions without <tt>HTTP_</tt>).
|
@@ -270,11 +305,17 @@ module Rack
|
|
270
305
|
}
|
271
306
|
|
272
307
|
## The CGI keys (named without a period) must have String values.
|
308
|
+
## If the string values for CGI keys contain non-ASCII characters,
|
309
|
+
## they should use ASCII-8BIT encoding.
|
273
310
|
env.each { |key, value|
|
274
311
|
next if key.include? "." # Skip extensions
|
275
312
|
assert("env variable #{key} has non-string value #{value.inspect}") {
|
276
313
|
value.kind_of? String
|
277
314
|
}
|
315
|
+
next if value.encoding == Encoding::ASCII_8BIT
|
316
|
+
assert("env variable #{key} has value containing non-ASCII characters and has non-ASCII-8BIT encoding #{value.inspect} encoding: #{value.encoding}") {
|
317
|
+
value.b !~ /[\x80-\xff]/n
|
318
|
+
}
|
278
319
|
}
|
279
320
|
|
280
321
|
## There are the following restrictions:
|
@@ -296,7 +337,7 @@ module Rack
|
|
296
337
|
check_hijack env
|
297
338
|
|
298
339
|
## * The <tt>REQUEST_METHOD</tt> must be a valid token.
|
299
|
-
assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]
|
340
|
+
assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]}") {
|
300
341
|
env[REQUEST_METHOD] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
|
301
342
|
}
|
302
343
|
|
@@ -337,7 +378,7 @@ module Rack
|
|
337
378
|
## When applicable, its external encoding must be "ASCII-8BIT" and it
|
338
379
|
## must be opened in binary mode, for Ruby 1.9 compatibility.
|
339
380
|
assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
|
340
|
-
input.external_encoding
|
381
|
+
input.external_encoding == Encoding::ASCII_8BIT
|
341
382
|
} if input.respond_to?(:external_encoding)
|
342
383
|
assert("rack.input #{input} is not opened in binary mode") {
|
343
384
|
input.binmode?
|
@@ -569,7 +610,7 @@ module Rack
|
|
569
610
|
|
570
611
|
# this check uses headers like a hash, but the spec only requires
|
571
612
|
# headers respond to #each
|
572
|
-
headers = Rack::Utils::HeaderHash
|
613
|
+
headers = Rack::Utils::HeaderHash[headers]
|
573
614
|
|
574
615
|
## In order to do this, an application may set the special header
|
575
616
|
## <tt>rack.hijack</tt> to an object that responds to <tt>call</tt>
|
@@ -593,7 +634,7 @@ module Rack
|
|
593
634
|
headers[RACK_HIJACK].respond_to? :call
|
594
635
|
}
|
595
636
|
original_hijack = headers[RACK_HIJACK]
|
596
|
-
|
637
|
+
proc do |io|
|
597
638
|
original_hijack.call HijackWrapper.new(io)
|
598
639
|
end
|
599
640
|
else
|
@@ -603,6 +644,8 @@ module Rack
|
|
603
644
|
assert('rack.hijack header must not be present if server does not support hijacking') {
|
604
645
|
headers[RACK_HIJACK].nil?
|
605
646
|
}
|
647
|
+
|
648
|
+
nil
|
606
649
|
end
|
607
650
|
end
|
608
651
|
## ==== Conventions
|
data/lib/rack/lobster.rb
CHANGED
@@ -2,9 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'zlib'
|
4
4
|
|
5
|
-
require 'rack/request'
|
6
|
-
require 'rack/response'
|
7
|
-
|
8
5
|
module Rack
|
9
6
|
# Paste has a Pony, Rack has a Lobster!
|
10
7
|
class Lobster
|
@@ -64,9 +61,10 @@ module Rack
|
|
64
61
|
end
|
65
62
|
|
66
63
|
if $0 == __FILE__
|
67
|
-
|
68
|
-
|
64
|
+
# :nocov:
|
65
|
+
require_relative '../rack'
|
69
66
|
Rack::Server.start(
|
70
67
|
app: Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), Port: 9292
|
71
68
|
)
|
69
|
+
# :nocov:
|
72
70
|
end
|
data/lib/rack/lock.rb
CHANGED
data/lib/rack/mock.rb
CHANGED
@@ -2,10 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'uri'
|
4
4
|
require 'stringio'
|
5
|
-
|
6
|
-
require 'rack/lint'
|
7
|
-
require 'rack/utils'
|
8
|
-
require 'rack/response'
|
5
|
+
require_relative '../rack'
|
9
6
|
require 'cgi/cookie'
|
10
7
|
|
11
8
|
module Rack
|
@@ -56,14 +53,24 @@ module Rack
|
|
56
53
|
@app = app
|
57
54
|
end
|
58
55
|
|
56
|
+
# Make a GET request and return a MockResponse. See #request.
|
59
57
|
def get(uri, opts = {}) request(GET, uri, opts) end
|
58
|
+
# Make a POST request and return a MockResponse. See #request.
|
60
59
|
def post(uri, opts = {}) request(POST, uri, opts) end
|
60
|
+
# Make a PUT request and return a MockResponse. See #request.
|
61
61
|
def put(uri, opts = {}) request(PUT, uri, opts) end
|
62
|
+
# Make a PATCH request and return a MockResponse. See #request.
|
62
63
|
def patch(uri, opts = {}) request(PATCH, uri, opts) end
|
64
|
+
# Make a DELETE request and return a MockResponse. See #request.
|
63
65
|
def delete(uri, opts = {}) request(DELETE, uri, opts) end
|
66
|
+
# Make a HEAD request and return a MockResponse. See #request.
|
64
67
|
def head(uri, opts = {}) request(HEAD, uri, opts) end
|
68
|
+
# Make an OPTIONS request and return a MockResponse. See #request.
|
65
69
|
def options(uri, opts = {}) request(OPTIONS, uri, opts) end
|
66
70
|
|
71
|
+
# Make a request using the given request method for the given
|
72
|
+
# uri to the rack application and return a MockResponse.
|
73
|
+
# Options given are passed to MockRequest.env_for.
|
67
74
|
def request(method = GET, uri = "", opts = {})
|
68
75
|
env = self.class.env_for(uri, opts.merge(method: method))
|
69
76
|
|
@@ -88,6 +95,13 @@ module Rack
|
|
88
95
|
end
|
89
96
|
|
90
97
|
# Return the Rack environment used for a request to +uri+.
|
98
|
+
# All options that are strings are added to the returned environment.
|
99
|
+
# Options:
|
100
|
+
# :fatal :: Whether to raise an exception if request outputs to rack.errors
|
101
|
+
# :input :: The rack.input to set
|
102
|
+
# :method :: The HTTP request method to use
|
103
|
+
# :params :: The params to use
|
104
|
+
# :script_name :: The SCRIPT_NAME to set
|
91
105
|
def self.env_for(uri = "", opts = {})
|
92
106
|
uri = parse_uri_rfc2396(uri)
|
93
107
|
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
@@ -157,6 +171,10 @@ module Rack
|
|
157
171
|
# MockRequest.
|
158
172
|
|
159
173
|
class MockResponse < Rack::Response
|
174
|
+
class << self
|
175
|
+
alias [] new
|
176
|
+
end
|
177
|
+
|
160
178
|
# Headers
|
161
179
|
attr_reader :original_headers, :cookies
|
162
180
|
|