rack 1.6.12 → 2.0.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 +5 -5
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +17 -25
- data/Rakefile +6 -14
- data/SPEC +10 -11
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack.rb +70 -21
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +4 -3
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +24 -26
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +40 -40
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +6 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -10
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +272 -158
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +383 -307
- data/lib/rack/response.rb +130 -57
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +30 -23
- data/lib/rack/session/abstract/id.rb +108 -138
- data/lib/rack/session/cookie.rb +26 -28
- data/lib/rack/session/memcache.rb +8 -14
- data/lib/rack/session/pool.rb +14 -21
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +136 -211
- data/rack.gemspec +7 -5
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/filename_with_single_quote +7 -0
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +37 -35
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +85 -49
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +164 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +69 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +22 -37
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +768 -607
- data/test/spec_response.rb +215 -112
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/spec_show_exceptions.rb +80 -0
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +93 -68
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +102 -66
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_showexceptions.rb +0 -98
@@ -11,8 +11,7 @@ module Rack
|
|
11
11
|
raise "#{path} file does not exist" unless ::File.exist?(path)
|
12
12
|
@content_type = content_type
|
13
13
|
@original_filename = ::File.basename(path)
|
14
|
-
@tempfile = Tempfile.new([@original_filename, ::File.extname(path)])
|
15
|
-
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
14
|
+
@tempfile = Tempfile.new([@original_filename, ::File.extname(path)], encoding: Encoding::BINARY)
|
16
15
|
@tempfile.binmode if binary
|
17
16
|
FileUtils.copy_file(path, @tempfile.path)
|
18
17
|
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Rack
|
2
|
+
class QueryParser
|
3
|
+
DEFAULT_SEP = /[&;] */n
|
4
|
+
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
|
5
|
+
|
6
|
+
# ParameterTypeError is the error that is raised when incoming structural
|
7
|
+
# parameters (parsed by parse_nested_query) contain conflicting types.
|
8
|
+
class ParameterTypeError < TypeError; end
|
9
|
+
|
10
|
+
# InvalidParameterError is the error that is raised when incoming structural
|
11
|
+
# parameters (parsed by parse_nested_query) contain invalid format or byte
|
12
|
+
# sequence.
|
13
|
+
class InvalidParameterError < ArgumentError; end
|
14
|
+
|
15
|
+
def self.make_default(key_space_limit, param_depth_limit)
|
16
|
+
new Params, key_space_limit, param_depth_limit
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :key_space_limit, :param_depth_limit
|
20
|
+
|
21
|
+
def initialize(params_class, key_space_limit, param_depth_limit)
|
22
|
+
@params_class = params_class
|
23
|
+
@key_space_limit = key_space_limit
|
24
|
+
@param_depth_limit = param_depth_limit
|
25
|
+
end
|
26
|
+
|
27
|
+
# Stolen from Mongrel, with some small modifications:
|
28
|
+
# Parses a query string by breaking it up at the '&'
|
29
|
+
# and ';' characters. You can also use this to parse
|
30
|
+
# cookies by changing the characters used in the second
|
31
|
+
# parameter (which defaults to '&;').
|
32
|
+
def parse_query(qs, d = nil, &unescaper)
|
33
|
+
unescaper ||= method(:unescape)
|
34
|
+
|
35
|
+
params = make_params
|
36
|
+
|
37
|
+
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
38
|
+
next if p.empty?
|
39
|
+
k, v = p.split('='.freeze, 2).map!(&unescaper)
|
40
|
+
|
41
|
+
if cur = params[k]
|
42
|
+
if cur.class == Array
|
43
|
+
params[k] << v
|
44
|
+
else
|
45
|
+
params[k] = [cur, v]
|
46
|
+
end
|
47
|
+
else
|
48
|
+
params[k] = v
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
return params.to_params_hash
|
53
|
+
end
|
54
|
+
|
55
|
+
# parse_nested_query expands a query string into structural types. Supported
|
56
|
+
# types are Arrays, Hashes and basic value types. It is possible to supply
|
57
|
+
# query strings with parameters of conflicting types, in this case a
|
58
|
+
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
59
|
+
# case.
|
60
|
+
def parse_nested_query(qs, d = nil)
|
61
|
+
return {} if qs.nil? || qs.empty?
|
62
|
+
params = make_params
|
63
|
+
|
64
|
+
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
65
|
+
k, v = p.split('='.freeze, 2).map! { |s| unescape(s) }
|
66
|
+
|
67
|
+
normalize_params(params, k, v, param_depth_limit)
|
68
|
+
end
|
69
|
+
|
70
|
+
return params.to_params_hash
|
71
|
+
rescue ArgumentError => e
|
72
|
+
raise InvalidParameterError, e.message
|
73
|
+
end
|
74
|
+
|
75
|
+
# normalize_params recursively expands parameters into structural types. If
|
76
|
+
# the structural types represented by two different parameter names are in
|
77
|
+
# conflict, a ParameterTypeError is raised.
|
78
|
+
def normalize_params(params, name, v, depth)
|
79
|
+
raise RangeError if depth <= 0
|
80
|
+
|
81
|
+
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
82
|
+
k = $1 || ''.freeze
|
83
|
+
after = $' || ''.freeze
|
84
|
+
|
85
|
+
if k.empty?
|
86
|
+
if !v.nil? && name == "[]".freeze
|
87
|
+
return Array(v)
|
88
|
+
else
|
89
|
+
return
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
if after == ''.freeze
|
94
|
+
params[k] = v
|
95
|
+
elsif after == "[".freeze
|
96
|
+
params[name] = v
|
97
|
+
elsif after == "[]".freeze
|
98
|
+
params[k] ||= []
|
99
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
100
|
+
params[k] << v
|
101
|
+
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
|
102
|
+
child_key = $1
|
103
|
+
params[k] ||= []
|
104
|
+
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
105
|
+
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
106
|
+
normalize_params(params[k].last, child_key, v, depth - 1)
|
107
|
+
else
|
108
|
+
params[k] << normalize_params(make_params, child_key, v, depth - 1)
|
109
|
+
end
|
110
|
+
else
|
111
|
+
params[k] ||= make_params
|
112
|
+
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
113
|
+
params[k] = normalize_params(params[k], after, v, depth - 1)
|
114
|
+
end
|
115
|
+
|
116
|
+
params
|
117
|
+
end
|
118
|
+
|
119
|
+
def make_params
|
120
|
+
@params_class.new @key_space_limit
|
121
|
+
end
|
122
|
+
|
123
|
+
def new_space_limit(key_space_limit)
|
124
|
+
self.class.new @params_class, key_space_limit, param_depth_limit
|
125
|
+
end
|
126
|
+
|
127
|
+
def new_depth_limit(param_depth_limit)
|
128
|
+
self.class.new @params_class, key_space_limit, param_depth_limit
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def params_hash_type?(obj)
|
134
|
+
obj.kind_of?(@params_class)
|
135
|
+
end
|
136
|
+
|
137
|
+
def params_hash_has_key?(hash, key)
|
138
|
+
return false if key =~ /\[\]/
|
139
|
+
|
140
|
+
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
141
|
+
next h if part == ''
|
142
|
+
return false unless params_hash_type?(h) && h.key?(part)
|
143
|
+
h[part]
|
144
|
+
end
|
145
|
+
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
def unescape(s)
|
150
|
+
Utils.unescape(s)
|
151
|
+
end
|
152
|
+
|
153
|
+
class Params
|
154
|
+
def initialize(limit)
|
155
|
+
@limit = limit
|
156
|
+
@size = 0
|
157
|
+
@params = {}
|
158
|
+
end
|
159
|
+
|
160
|
+
def [](key)
|
161
|
+
@params[key]
|
162
|
+
end
|
163
|
+
|
164
|
+
def []=(key, value)
|
165
|
+
@size += key.size if key && !@params.key?(key)
|
166
|
+
raise RangeError, 'exceeded available parameter key space' if @size > @limit
|
167
|
+
@params[key] = value
|
168
|
+
end
|
169
|
+
|
170
|
+
def key?(key)
|
171
|
+
@params.key?(key)
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_params_hash
|
175
|
+
hash = @params
|
176
|
+
hash.keys.each do |key|
|
177
|
+
value = hash[key]
|
178
|
+
if value.kind_of?(self.class)
|
179
|
+
if value.object_id == self.object_id
|
180
|
+
hash[key] = hash
|
181
|
+
else
|
182
|
+
hash[key] = value.to_params_hash
|
183
|
+
end
|
184
|
+
elsif value.kind_of?(Array)
|
185
|
+
value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
hash
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/lib/rack/recursive.rb
CHANGED
@@ -14,11 +14,11 @@ module Rack
|
|
14
14
|
@url = URI(url)
|
15
15
|
@env = env
|
16
16
|
|
17
|
-
@env[PATH_INFO] =
|
18
|
-
@env[QUERY_STRING] =
|
19
|
-
@env[
|
20
|
-
@env["HTTP_PORT"] =
|
21
|
-
@env[
|
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
|
22
22
|
|
23
23
|
super "forwarding to #{url}"
|
24
24
|
end
|
@@ -40,7 +40,7 @@ module Rack
|
|
40
40
|
|
41
41
|
def _call(env)
|
42
42
|
@script_name = env[SCRIPT_NAME]
|
43
|
-
@app.call(env.merge(
|
43
|
+
@app.call(env.merge(RACK_RECURSIVE_INCLUDE => method(:include)))
|
44
44
|
rescue ForwardRequest => req
|
45
45
|
call(env.merge(req.env))
|
46
46
|
end
|
@@ -53,9 +53,9 @@ module Rack
|
|
53
53
|
|
54
54
|
env = env.merge(PATH_INFO => path,
|
55
55
|
SCRIPT_NAME => @script_name,
|
56
|
-
REQUEST_METHOD =>
|
56
|
+
REQUEST_METHOD => GET,
|
57
57
|
"CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "",
|
58
|
-
|
58
|
+
RACK_INPUT => StringIO.new(""))
|
59
59
|
@app.call(env)
|
60
60
|
end
|
61
61
|
end
|
data/lib/rack/request.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rack/utils'
|
2
|
+
require 'rack/media_type'
|
2
3
|
|
3
4
|
module Rack
|
4
5
|
# Rack::Request provides a convenient interface to a Rack
|
@@ -10,371 +11,444 @@ module Rack
|
|
10
11
|
# req.params["data"]
|
11
12
|
|
12
13
|
class Request
|
13
|
-
# The environment of the request.
|
14
|
-
attr_reader :env
|
15
|
-
|
16
|
-
SCHEME_WHITELIST = %w(https http).freeze
|
17
|
-
|
18
14
|
def initialize(env)
|
19
|
-
@
|
15
|
+
@params = nil
|
16
|
+
super(env)
|
20
17
|
end
|
21
18
|
|
22
|
-
def
|
23
|
-
|
24
|
-
def path_info; @env[PATH_INFO].to_s end
|
25
|
-
def request_method; @env["REQUEST_METHOD"] end
|
26
|
-
def query_string; @env[QUERY_STRING].to_s end
|
27
|
-
def content_length; @env['CONTENT_LENGTH'] end
|
28
|
-
|
29
|
-
def content_type
|
30
|
-
content_type = @env['CONTENT_TYPE']
|
31
|
-
content_type.nil? || content_type.empty? ? nil : content_type
|
19
|
+
def params
|
20
|
+
@params ||= super
|
32
21
|
end
|
33
22
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
39
|
-
# without any media type parameters. e.g., when CONTENT_TYPE is
|
40
|
-
# "text/plain;charset=utf-8", the media-type is "text/plain".
|
41
|
-
#
|
42
|
-
# For more information on the use of media types in HTTP, see:
|
43
|
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
44
|
-
def media_type
|
45
|
-
content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
|
23
|
+
def update_param(k, v)
|
24
|
+
super
|
25
|
+
@params = nil
|
46
26
|
end
|
47
27
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# { 'charset' => 'utf-8' }
|
53
|
-
def media_type_params
|
54
|
-
return {} if content_type.nil?
|
55
|
-
Hash[*content_type.split(/\s*[;,]\s*/)[1..-1].
|
56
|
-
collect { |s| s.split('=', 2) }.
|
57
|
-
map { |k,v| [k.downcase, strip_doublequotes(v)] }.flatten]
|
28
|
+
def delete_param(k)
|
29
|
+
v = super
|
30
|
+
@params = nil
|
31
|
+
v
|
58
32
|
end
|
59
33
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
# charset are to be considered ISO-8859-1.
|
64
|
-
def content_charset
|
65
|
-
media_type_params['charset']
|
66
|
-
end
|
34
|
+
module Env
|
35
|
+
# The environment of the request.
|
36
|
+
attr_reader :env
|
67
37
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
|
72
|
-
'https'
|
73
|
-
elsif forwarded_scheme
|
74
|
-
forwarded_scheme
|
75
|
-
else
|
76
|
-
@env["rack.url_scheme"]
|
38
|
+
def initialize(env)
|
39
|
+
@env = env
|
40
|
+
super()
|
77
41
|
end
|
78
|
-
end
|
79
42
|
|
80
|
-
|
81
|
-
|
82
|
-
|
43
|
+
# Predicate method to test to see if `name` has been set as request
|
44
|
+
# specific data
|
45
|
+
def has_header?(name)
|
46
|
+
@env.key? name
|
47
|
+
end
|
83
48
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
else
|
88
|
-
@env['HTTP_HOST'] || "#{@env['SERVER_NAME'] || @env['SERVER_ADDR']}:#{@env['SERVER_PORT']}"
|
49
|
+
# Get a request specific value for `name`.
|
50
|
+
def get_header(name)
|
51
|
+
@env[name]
|
89
52
|
end
|
90
|
-
end
|
91
53
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
port.to_i
|
97
|
-
elsif @env.has_key?("HTTP_X_FORWARDED_HOST")
|
98
|
-
DEFAULT_PORTS[scheme]
|
99
|
-
elsif @env.has_key?("HTTP_X_FORWARDED_PROTO")
|
100
|
-
DEFAULT_PORTS[@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]]
|
101
|
-
else
|
102
|
-
@env["SERVER_PORT"].to_i
|
54
|
+
# If a block is given, it yields to the block if the value hasn't been set
|
55
|
+
# on the request.
|
56
|
+
def fetch_header(name, &block)
|
57
|
+
@env.fetch(name, &block)
|
103
58
|
end
|
104
|
-
end
|
105
59
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
60
|
+
# Loops through each key / value pair in the request specific data.
|
61
|
+
def each_header(&block)
|
62
|
+
@env.each(&block)
|
63
|
+
end
|
110
64
|
|
111
|
-
|
112
|
-
|
65
|
+
# Set a request specific value for `name` to `v`
|
66
|
+
def set_header(name, v)
|
67
|
+
@env[name] = v
|
68
|
+
end
|
69
|
+
|
70
|
+
# Add a header that may have multiple values.
|
71
|
+
#
|
72
|
+
# Example:
|
73
|
+
# request.add_header 'Accept', 'image/png'
|
74
|
+
# request.add_header 'Accept', '*/*'
|
75
|
+
#
|
76
|
+
# assert_equal 'image/png,*/*', request.get_header('Accept')
|
77
|
+
#
|
78
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
79
|
+
def add_header key, v
|
80
|
+
if v.nil?
|
81
|
+
get_header key
|
82
|
+
elsif has_header? key
|
83
|
+
set_header key, "#{get_header key},#{v}"
|
84
|
+
else
|
85
|
+
set_header key, v
|
86
|
+
end
|
87
|
+
end
|
113
88
|
|
89
|
+
# Delete a request specific value for `name`.
|
90
|
+
def delete_header(name)
|
91
|
+
@env.delete name
|
92
|
+
end
|
114
93
|
|
115
|
-
|
116
|
-
|
94
|
+
def initialize_copy(other)
|
95
|
+
@env = other.env.dup
|
96
|
+
end
|
97
|
+
end
|
117
98
|
|
118
|
-
|
119
|
-
|
99
|
+
module Helpers
|
100
|
+
# The set of form-data media-types. Requests that do not indicate
|
101
|
+
# one of the media types presents in this list will not be eligible
|
102
|
+
# for form-data / param parsing.
|
103
|
+
FORM_DATA_MEDIA_TYPES = [
|
104
|
+
'application/x-www-form-urlencoded',
|
105
|
+
'multipart/form-data'
|
106
|
+
]
|
120
107
|
|
121
|
-
|
122
|
-
|
108
|
+
# The set of media-types. Requests that do not indicate
|
109
|
+
# one of the media types presents in this list will not be eligible
|
110
|
+
# for param parsing like soap attachments or generic multiparts
|
111
|
+
PARSEABLE_DATA_MEDIA_TYPES = [
|
112
|
+
'multipart/related',
|
113
|
+
'multipart/mixed'
|
114
|
+
]
|
123
115
|
|
124
|
-
|
125
|
-
|
116
|
+
# Default ports depending on scheme. Used to decide whether or not
|
117
|
+
# to include the port in a generated URI.
|
118
|
+
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
119
|
+
|
120
|
+
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'.freeze
|
121
|
+
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'.freeze
|
122
|
+
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'.freeze
|
123
|
+
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'.freeze
|
124
|
+
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'.freeze
|
125
|
+
|
126
|
+
def body; get_header(RACK_INPUT) end
|
127
|
+
def script_name; get_header(SCRIPT_NAME).to_s end
|
128
|
+
def script_name=(s); set_header(SCRIPT_NAME, s.to_s) end
|
129
|
+
|
130
|
+
def path_info; get_header(PATH_INFO).to_s end
|
131
|
+
def path_info=(s); set_header(PATH_INFO, s.to_s) end
|
132
|
+
|
133
|
+
def request_method; get_header(REQUEST_METHOD) end
|
134
|
+
def query_string; get_header(QUERY_STRING).to_s end
|
135
|
+
def content_length; get_header('CONTENT_LENGTH') end
|
136
|
+
def logger; get_header(RACK_LOGGER) end
|
137
|
+
def user_agent; get_header('HTTP_USER_AGENT') end
|
138
|
+
def multithread?; get_header(RACK_MULTITHREAD) end
|
139
|
+
|
140
|
+
# the referer of the client
|
141
|
+
def referer; get_header('HTTP_REFERER') end
|
142
|
+
alias referrer referer
|
143
|
+
|
144
|
+
def session
|
145
|
+
fetch_header(RACK_SESSION) do |k|
|
146
|
+
set_header RACK_SESSION, default_session
|
147
|
+
end
|
148
|
+
end
|
126
149
|
|
127
|
-
|
128
|
-
|
150
|
+
def session_options
|
151
|
+
fetch_header(RACK_SESSION_OPTIONS) do |k|
|
152
|
+
set_header RACK_SESSION_OPTIONS, {}
|
153
|
+
end
|
154
|
+
end
|
129
155
|
|
130
|
-
|
131
|
-
|
156
|
+
# Checks the HTTP request method (or verb) to see if it was of type DELETE
|
157
|
+
def delete?; request_method == DELETE end
|
132
158
|
|
133
|
-
|
134
|
-
|
159
|
+
# Checks the HTTP request method (or verb) to see if it was of type GET
|
160
|
+
def get?; request_method == GET end
|
135
161
|
|
136
|
-
|
137
|
-
|
162
|
+
# Checks the HTTP request method (or verb) to see if it was of type HEAD
|
163
|
+
def head?; request_method == HEAD end
|
138
164
|
|
139
|
-
|
140
|
-
|
165
|
+
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
|
166
|
+
def options?; request_method == OPTIONS end
|
141
167
|
|
142
|
-
|
143
|
-
|
168
|
+
# Checks the HTTP request method (or verb) to see if it was of type LINK
|
169
|
+
def link?; request_method == LINK end
|
144
170
|
|
171
|
+
# Checks the HTTP request method (or verb) to see if it was of type PATCH
|
172
|
+
def patch?; request_method == PATCH end
|
145
173
|
|
146
|
-
|
147
|
-
|
148
|
-
# for form-data / param parsing.
|
149
|
-
FORM_DATA_MEDIA_TYPES = [
|
150
|
-
'application/x-www-form-urlencoded',
|
151
|
-
'multipart/form-data'
|
152
|
-
]
|
174
|
+
# Checks the HTTP request method (or verb) to see if it was of type POST
|
175
|
+
def post?; request_method == POST end
|
153
176
|
|
154
|
-
|
155
|
-
|
156
|
-
# for param parsing like soap attachments or generic multiparts
|
157
|
-
PARSEABLE_DATA_MEDIA_TYPES = [
|
158
|
-
'multipart/related',
|
159
|
-
'multipart/mixed'
|
160
|
-
]
|
177
|
+
# Checks the HTTP request method (or verb) to see if it was of type PUT
|
178
|
+
def put?; request_method == PUT end
|
161
179
|
|
162
|
-
|
163
|
-
|
164
|
-
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
180
|
+
# Checks the HTTP request method (or verb) to see if it was of type TRACE
|
181
|
+
def trace?; request_method == TRACE end
|
165
182
|
|
166
|
-
|
167
|
-
|
168
|
-
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
169
|
-
# list of form-data media types can be modified through the
|
170
|
-
# +FORM_DATA_MEDIA_TYPES+ array.
|
171
|
-
#
|
172
|
-
# A request body is also assumed to contain form-data when no
|
173
|
-
# Content-Type header is provided and the request_method is POST.
|
174
|
-
def form_data?
|
175
|
-
type = media_type
|
176
|
-
meth = env["rack.methodoverride.original_method"] || env[REQUEST_METHOD]
|
177
|
-
(meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
|
178
|
-
end
|
183
|
+
# Checks the HTTP request method (or verb) to see if it was of type UNLINK
|
184
|
+
def unlink?; request_method == UNLINK end
|
179
185
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
186
|
+
def scheme
|
187
|
+
if get_header(HTTPS) == 'on'
|
188
|
+
'https'
|
189
|
+
elsif get_header(HTTP_X_FORWARDED_SSL) == 'on'
|
190
|
+
'https'
|
191
|
+
elsif get_header(HTTP_X_FORWARDED_SCHEME)
|
192
|
+
get_header(HTTP_X_FORWARDED_SCHEME)
|
193
|
+
elsif get_header(HTTP_X_FORWARDED_PROTO)
|
194
|
+
get_header(HTTP_X_FORWARDED_PROTO).split(',')[0]
|
195
|
+
else
|
196
|
+
get_header(RACK_URL_SCHEME)
|
197
|
+
end
|
198
|
+
end
|
185
199
|
|
186
|
-
|
187
|
-
|
188
|
-
if @env["rack.request.query_string"] == query_string
|
189
|
-
@env["rack.request.query_hash"]
|
190
|
-
else
|
191
|
-
p = parse_query({ :query => query_string, :separator => '&;' })
|
192
|
-
@env["rack.request.query_string"] = query_string
|
193
|
-
@env["rack.request.query_hash"] = p
|
200
|
+
def authority
|
201
|
+
get_header(SERVER_NAME) + ':' + get_header(SERVER_PORT)
|
194
202
|
end
|
195
|
-
end
|
196
203
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
# multipart/form-data.
|
201
|
-
def POST
|
202
|
-
if @env["rack.input"].nil?
|
203
|
-
raise "Missing rack.input"
|
204
|
-
elsif @env["rack.request.form_input"].equal? @env["rack.input"]
|
205
|
-
@env["rack.request.form_hash"]
|
206
|
-
elsif form_data? || parseable_data?
|
207
|
-
unless @env["rack.request.form_hash"] = parse_multipart(env)
|
208
|
-
form_vars = @env["rack.input"].read
|
209
|
-
|
210
|
-
# Fix for Safari Ajax postings that always append \0
|
211
|
-
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
212
|
-
form_vars.slice!(-1) if form_vars[-1] == ?\0
|
213
|
-
|
214
|
-
@env["rack.request.form_vars"] = form_vars
|
215
|
-
@env["rack.request.form_hash"] = parse_query({ :query => form_vars, :separator => '&' })
|
216
|
-
|
217
|
-
@env["rack.input"].rewind
|
204
|
+
def cookies
|
205
|
+
hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |k|
|
206
|
+
set_header(k, {})
|
218
207
|
end
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
208
|
+
string = get_header HTTP_COOKIE
|
209
|
+
|
210
|
+
return hash if string == get_header(RACK_REQUEST_COOKIE_STRING)
|
211
|
+
hash.replace Utils.parse_cookies_header get_header HTTP_COOKIE
|
212
|
+
set_header(RACK_REQUEST_COOKIE_STRING, string)
|
213
|
+
hash
|
223
214
|
end
|
224
|
-
end
|
225
215
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
@params ||= self.GET.merge(self.POST)
|
231
|
-
rescue EOFError
|
232
|
-
self.GET.dup
|
233
|
-
end
|
216
|
+
def content_type
|
217
|
+
content_type = get_header('CONTENT_TYPE')
|
218
|
+
content_type.nil? || content_type.empty? ? nil : content_type
|
219
|
+
end
|
234
220
|
|
235
|
-
|
236
|
-
|
237
|
-
# The parameter is updated wherever it was previous defined, so GET, POST, or both. If it wasn't previously defined, it's inserted into GET.
|
238
|
-
#
|
239
|
-
# env['rack.input'] is not touched.
|
240
|
-
def update_param(k, v)
|
241
|
-
found = false
|
242
|
-
if self.GET.has_key?(k)
|
243
|
-
found = true
|
244
|
-
self.GET[k] = v
|
221
|
+
def xhr?
|
222
|
+
get_header("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest"
|
245
223
|
end
|
246
|
-
|
247
|
-
|
248
|
-
|
224
|
+
|
225
|
+
def host_with_port
|
226
|
+
if forwarded = get_header(HTTP_X_FORWARDED_HOST)
|
227
|
+
forwarded.split(/,\s?/).last
|
228
|
+
else
|
229
|
+
get_header(HTTP_HOST) || "#{get_header(SERVER_NAME) || get_header(SERVER_ADDR)}:#{get_header(SERVER_PORT)}"
|
230
|
+
end
|
249
231
|
end
|
250
|
-
|
251
|
-
|
232
|
+
|
233
|
+
def host
|
234
|
+
# Remove port number.
|
235
|
+
host_with_port.to_s.sub(/:\d+\z/, '')
|
252
236
|
end
|
253
|
-
@params = nil
|
254
|
-
nil
|
255
|
-
end
|
256
237
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
238
|
+
def port
|
239
|
+
if port = host_with_port.split(/:/)[1]
|
240
|
+
port.to_i
|
241
|
+
elsif port = get_header(HTTP_X_FORWARDED_PORT)
|
242
|
+
port.to_i
|
243
|
+
elsif has_header?(HTTP_X_FORWARDED_HOST)
|
244
|
+
DEFAULT_PORTS[scheme]
|
245
|
+
elsif has_header?(HTTP_X_FORWARDED_PROTO)
|
246
|
+
DEFAULT_PORTS[get_header(HTTP_X_FORWARDED_PROTO).split(',')[0]]
|
247
|
+
else
|
248
|
+
get_header(SERVER_PORT).to_i
|
249
|
+
end
|
250
|
+
end
|
267
251
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
end
|
252
|
+
def ssl?
|
253
|
+
scheme == 'https'
|
254
|
+
end
|
272
255
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
def []=(key, value)
|
277
|
-
params[key.to_s] = value
|
278
|
-
end
|
256
|
+
def ip
|
257
|
+
remote_addrs = split_ip_addresses(get_header('REMOTE_ADDR'))
|
258
|
+
remote_addrs = reject_trusted_ip_addresses(remote_addrs)
|
279
259
|
|
280
|
-
|
281
|
-
def values_at(*keys)
|
282
|
-
keys.map{|key| params[key] }
|
283
|
-
end
|
260
|
+
return remote_addrs.first if remote_addrs.any?
|
284
261
|
|
285
|
-
|
286
|
-
def referer
|
287
|
-
@env['HTTP_REFERER']
|
288
|
-
end
|
289
|
-
alias referrer referer
|
262
|
+
forwarded_ips = split_ip_addresses(get_header('HTTP_X_FORWARDED_FOR'))
|
290
263
|
|
291
|
-
|
292
|
-
|
293
|
-
end
|
264
|
+
return reject_trusted_ip_addresses(forwarded_ips).last || get_header("REMOTE_ADDR")
|
265
|
+
end
|
294
266
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
# the Cookie header such that those with more specific Path attributes
|
305
|
-
# precede those with less specific. Ordering with respect to other
|
306
|
-
# attributes (e.g., Domain) is unspecified.
|
307
|
-
cookies = Utils.parse_query(string, ';,') { |s| Rack::Utils.unescape(s) rescue s }
|
308
|
-
cookies.each { |k,v| hash[k] = Array === v ? v.first : v }
|
309
|
-
@env["rack.request.cookie_string"] = string
|
310
|
-
hash
|
311
|
-
end
|
267
|
+
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
268
|
+
# without any media type parameters. e.g., when CONTENT_TYPE is
|
269
|
+
# "text/plain;charset=utf-8", the media-type is "text/plain".
|
270
|
+
#
|
271
|
+
# For more information on the use of media types in HTTP, see:
|
272
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
273
|
+
def media_type
|
274
|
+
MediaType.type(content_type)
|
275
|
+
end
|
312
276
|
|
313
|
-
|
314
|
-
|
315
|
-
|
277
|
+
# The media type parameters provided in CONTENT_TYPE as a Hash, or
|
278
|
+
# an empty Hash if no CONTENT_TYPE or media-type parameters were
|
279
|
+
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
|
280
|
+
# this method responds with the following Hash:
|
281
|
+
# { 'charset' => 'utf-8' }
|
282
|
+
def media_type_params
|
283
|
+
MediaType.params(content_type)
|
284
|
+
end
|
316
285
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
286
|
+
# The character set of the request body if a "charset" media type
|
287
|
+
# parameter was given, or nil if no "charset" was specified. Note
|
288
|
+
# that, per RFC2616, text/* media types that specify no explicit
|
289
|
+
# charset are to be considered ISO-8859-1.
|
290
|
+
def content_charset
|
291
|
+
media_type_params['charset']
|
292
|
+
end
|
322
293
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
294
|
+
# Determine whether the request body contains form-data by checking
|
295
|
+
# the request Content-Type for one of the media-types:
|
296
|
+
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
297
|
+
# list of form-data media types can be modified through the
|
298
|
+
# +FORM_DATA_MEDIA_TYPES+ array.
|
299
|
+
#
|
300
|
+
# A request body is also assumed to contain form-data when no
|
301
|
+
# Content-Type header is provided and the request_method is POST.
|
302
|
+
def form_data?
|
303
|
+
type = media_type
|
304
|
+
meth = get_header(RACK_METHODOVERRIDE_ORIGINAL_METHOD) || get_header(REQUEST_METHOD)
|
305
|
+
(meth == POST && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
|
306
|
+
end
|
327
307
|
|
328
|
-
|
329
|
-
|
330
|
-
|
308
|
+
# Determine whether the request body contains data by checking
|
309
|
+
# the request media_type against registered parse-data media-types
|
310
|
+
def parseable_data?
|
311
|
+
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
|
312
|
+
end
|
331
313
|
|
332
|
-
|
333
|
-
|
334
|
-
|
314
|
+
# Returns the data received in the query string.
|
315
|
+
def GET
|
316
|
+
if get_header(RACK_REQUEST_QUERY_STRING) == query_string
|
317
|
+
get_header(RACK_REQUEST_QUERY_HASH)
|
318
|
+
else
|
319
|
+
query_hash = parse_query(query_string, '&;')
|
320
|
+
set_header(RACK_REQUEST_QUERY_STRING, query_string)
|
321
|
+
set_header(RACK_REQUEST_QUERY_HASH, query_hash)
|
322
|
+
end
|
323
|
+
end
|
335
324
|
|
336
|
-
|
337
|
-
|
338
|
-
|
325
|
+
# Returns the data received in the request body.
|
326
|
+
#
|
327
|
+
# This method support both application/x-www-form-urlencoded and
|
328
|
+
# multipart/form-data.
|
329
|
+
def POST
|
330
|
+
if get_header(RACK_INPUT).nil?
|
331
|
+
raise "Missing rack.input"
|
332
|
+
elsif get_header(RACK_REQUEST_FORM_INPUT) == get_header(RACK_INPUT)
|
333
|
+
get_header(RACK_REQUEST_FORM_HASH)
|
334
|
+
elsif form_data? || parseable_data?
|
335
|
+
unless set_header(RACK_REQUEST_FORM_HASH, parse_multipart)
|
336
|
+
form_vars = get_header(RACK_INPUT).read
|
337
|
+
|
338
|
+
# Fix for Safari Ajax postings that always append \0
|
339
|
+
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
340
|
+
form_vars.slice!(-1) if form_vars[-1] == ?\0
|
341
|
+
|
342
|
+
set_header RACK_REQUEST_FORM_VARS, form_vars
|
343
|
+
set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
|
344
|
+
|
345
|
+
get_header(RACK_INPUT).rewind
|
346
|
+
end
|
347
|
+
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
348
|
+
get_header RACK_REQUEST_FORM_HASH
|
349
|
+
else
|
350
|
+
{}
|
351
|
+
end
|
352
|
+
end
|
339
353
|
|
340
|
-
|
341
|
-
|
342
|
-
|
354
|
+
# The union of GET and POST data.
|
355
|
+
#
|
356
|
+
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
357
|
+
def params
|
358
|
+
self.GET.merge(self.POST)
|
359
|
+
rescue EOFError
|
360
|
+
self.GET.dup
|
361
|
+
end
|
343
362
|
|
344
|
-
|
345
|
-
|
346
|
-
|
363
|
+
# Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
|
364
|
+
#
|
365
|
+
# The parameter is updated wherever it was previous defined, so GET, POST, or both. If it wasn't previously defined, it's inserted into GET.
|
366
|
+
#
|
367
|
+
# <tt>env['rack.input']</tt> is not touched.
|
368
|
+
def update_param(k, v)
|
369
|
+
found = false
|
370
|
+
if self.GET.has_key?(k)
|
371
|
+
found = true
|
372
|
+
self.GET[k] = v
|
373
|
+
end
|
374
|
+
if self.POST.has_key?(k)
|
375
|
+
found = true
|
376
|
+
self.POST[k] = v
|
377
|
+
end
|
378
|
+
unless found
|
379
|
+
self.GET[k] = v
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
# Destructively delete a parameter, whether it's in GET or POST. Returns the value of the deleted parameter.
|
384
|
+
#
|
385
|
+
# If the parameter is in both GET and POST, the POST value takes precedence since that's how #params works.
|
386
|
+
#
|
387
|
+
# <tt>env['rack.input']</tt> is not touched.
|
388
|
+
def delete_param(k)
|
389
|
+
[ self.POST.delete(k), self.GET.delete(k) ].compact.first
|
390
|
+
end
|
391
|
+
|
392
|
+
def base_url
|
393
|
+
url = "#{scheme}://#{host}"
|
394
|
+
url << ":#{port}" if port != DEFAULT_PORTS[scheme]
|
395
|
+
url
|
396
|
+
end
|
347
397
|
|
348
|
-
|
349
|
-
|
350
|
-
|
398
|
+
# Tries to return a remake of the original request URL as a string.
|
399
|
+
def url
|
400
|
+
base_url + fullpath
|
401
|
+
end
|
351
402
|
|
352
|
-
|
403
|
+
def path
|
404
|
+
script_name + path_info
|
405
|
+
end
|
353
406
|
|
354
|
-
|
407
|
+
def fullpath
|
408
|
+
query_string.empty? ? path : "#{path}?#{query_string}"
|
409
|
+
end
|
355
410
|
|
356
|
-
|
357
|
-
|
411
|
+
def accept_encoding
|
412
|
+
parse_http_accept_header(get_header("HTTP_ACCEPT_ENCODING"))
|
413
|
+
end
|
358
414
|
|
359
|
-
|
360
|
-
|
361
|
-
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
|
415
|
+
def accept_language
|
416
|
+
parse_http_accept_header(get_header("HTTP_ACCEPT_LANGUAGE"))
|
362
417
|
end
|
363
418
|
|
364
|
-
def
|
365
|
-
|
419
|
+
def trusted_proxy?(ip)
|
420
|
+
ip =~ /\A127\.0\.0\.1\Z|\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|\A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/i
|
366
421
|
end
|
367
422
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
423
|
+
# shortcut for <tt>request.params[key]</tt>
|
424
|
+
def [](key)
|
425
|
+
if $VERBOSE
|
426
|
+
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead")
|
427
|
+
end
|
428
|
+
|
429
|
+
params[key.to_s]
|
372
430
|
end
|
373
431
|
|
374
|
-
|
375
|
-
|
432
|
+
# shortcut for <tt>request.params[key] = value</tt>
|
433
|
+
#
|
434
|
+
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
435
|
+
def []=(key, value)
|
436
|
+
if $VERBOSE
|
437
|
+
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead")
|
438
|
+
end
|
439
|
+
|
440
|
+
params[key.to_s] = value
|
376
441
|
end
|
377
442
|
|
443
|
+
# like Hash#values_at
|
444
|
+
def values_at(*keys)
|
445
|
+
keys.map { |key| params[key] }
|
446
|
+
end
|
447
|
+
|
448
|
+
private
|
449
|
+
|
450
|
+
def default_session; {}; end
|
451
|
+
|
378
452
|
def parse_http_accept_header(header)
|
379
453
|
header.to_s.split(/\s*,\s*/).map do |part|
|
380
454
|
attribute, parameters = part.split(/\s*;\s*/, 2)
|
@@ -386,26 +460,28 @@ module Rack
|
|
386
460
|
end
|
387
461
|
end
|
388
462
|
|
389
|
-
|
390
|
-
|
391
|
-
if s[0] == ?" && s[-1] == ?"
|
392
|
-
s[1..-2]
|
393
|
-
else
|
394
|
-
s
|
463
|
+
def query_parser
|
464
|
+
Utils.default_query_parser
|
395
465
|
end
|
396
|
-
end
|
397
466
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
467
|
+
def parse_query(qs, d='&')
|
468
|
+
query_parser.parse_nested_query(qs, d)
|
469
|
+
end
|
470
|
+
|
471
|
+
def parse_multipart
|
472
|
+
Rack::Multipart.extract_multipart(self, query_parser)
|
473
|
+
end
|
403
474
|
|
404
|
-
|
405
|
-
|
475
|
+
def split_ip_addresses(ip_addresses)
|
476
|
+
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
|
406
477
|
end
|
407
478
|
|
408
|
-
|
479
|
+
def reject_trusted_ip_addresses(ip_addresses)
|
480
|
+
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
481
|
+
end
|
409
482
|
end
|
483
|
+
|
484
|
+
include Env
|
485
|
+
include Helpers
|
410
486
|
end
|
411
487
|
end
|