rack 1.1.6 → 1.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYING +1 -1
- data/HISTORY.md +375 -0
- data/KNOWN-ISSUES +23 -0
- data/README.rdoc +312 -0
- data/Rakefile +124 -0
- data/SPEC +125 -32
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +150 -0
- data/contrib/rack_logo.svg +1 -1
- data/contrib/rdoc.css +412 -0
- data/example/protectedlobster.rb +1 -1
- data/lib/rack/auth/abstract/handler.rb +4 -4
- data/lib/rack/auth/abstract/request.rb +7 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/md5.rb +7 -3
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/auth/digest/params.rb +7 -9
- data/lib/rack/auth/digest/request.rb +10 -9
- data/lib/rack/backports/uri/common_18.rb +56 -0
- data/lib/rack/backports/uri/common_192.rb +52 -0
- data/lib/rack/backports/uri/common_193.rb +29 -0
- data/lib/rack/body_proxy.rb +39 -0
- data/lib/rack/builder.rb +106 -22
- data/lib/rack/cascade.rb +17 -6
- data/lib/rack/chunked.rb +44 -24
- data/lib/rack/commonlogger.rb +36 -13
- data/lib/rack/conditionalget.rb +49 -17
- data/lib/rack/config.rb +5 -0
- data/lib/rack/content_length.rb +14 -6
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +73 -15
- data/lib/rack/directory.rb +18 -8
- data/lib/rack/etag.rb +59 -9
- data/lib/rack/file.rb +106 -44
- data/lib/rack/handler/cgi.rb +11 -11
- data/lib/rack/handler/fastcgi.rb +18 -6
- data/lib/rack/handler/lsws.rb +2 -4
- data/lib/rack/handler/mongrel.rb +22 -6
- data/lib/rack/handler/scgi.rb +16 -8
- data/lib/rack/handler/thin.rb +19 -4
- data/lib/rack/handler/webrick.rb +72 -19
- data/lib/rack/handler.rb +47 -14
- data/lib/rack/head.rb +10 -2
- data/lib/rack/lint.rb +260 -75
- data/lib/rack/lobster.rb +13 -8
- data/lib/rack/lock.rb +13 -3
- data/lib/rack/logger.rb +0 -2
- data/lib/rack/methodoverride.rb +27 -8
- data/lib/rack/mime.rb +625 -167
- data/lib/rack/mock.rb +78 -53
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +253 -0
- data/lib/rack/multipart/uploaded_file.rb +34 -0
- data/lib/rack/multipart.rb +34 -0
- data/lib/rack/nulllogger.rb +21 -2
- data/lib/rack/recursive.rb +10 -5
- data/lib/rack/reloader.rb +3 -2
- data/lib/rack/request.rb +201 -74
- data/lib/rack/response.rb +41 -28
- data/lib/rack/rewindable_input.rb +15 -11
- data/lib/rack/runtime.rb +16 -3
- data/lib/rack/sendfile.rb +47 -29
- data/lib/rack/server.rb +223 -47
- data/lib/rack/session/abstract/id.rb +289 -30
- data/lib/rack/session/cookie.rb +133 -44
- data/lib/rack/session/memcache.rb +30 -56
- data/lib/rack/session/pool.rb +19 -43
- data/lib/rack/showexceptions.rb +53 -15
- data/lib/rack/showstatus.rb +14 -7
- data/lib/rack/static.rb +124 -12
- data/lib/rack/tempfile_reaper.rb +22 -0
- data/lib/rack/urlmap.rb +49 -15
- data/lib/rack/utils/okjson.rb +600 -0
- data/lib/rack/utils.rb +363 -361
- data/lib/rack.rb +17 -23
- data/rack.gemspec +11 -20
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +5 -0
- data/test/builder/line.ru +1 -0
- data/test/builder/options.ru +2 -0
- data/test/cgi/assets/folder/test.js +1 -0
- data/test/cgi/assets/fonts/font.eot +1 -0
- data/test/cgi/assets/images/image.png +1 -0
- data/test/cgi/assets/index.html +1 -0
- data/test/cgi/assets/javascripts/app.js +1 -0
- data/test/cgi/assets/stylesheets/app.css +1 -0
- data/test/cgi/lighttpd.conf +26 -0
- data/test/cgi/rackup_stub.rb +6 -0
- data/test/cgi/sample_rackup.ru +5 -0
- data/test/cgi/test +9 -0
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.fcgi +8 -0
- data/test/cgi/test.ru +5 -0
- data/test/gemloader.rb +10 -0
- data/test/multipart/bad_robots +259 -0
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/empty +10 -0
- data/test/multipart/fail_16384_nofile +814 -0
- data/test/multipart/file1.txt +1 -0
- data/test/multipart/filename_and_modification_param +7 -0
- data/test/multipart/filename_and_no_name +6 -0
- data/test/multipart/filename_with_escaped_quotes +6 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/test/multipart/filename_with_null_byte +7 -0
- data/test/multipart/filename_with_percent_escaped_quotes +6 -0
- data/test/multipart/filename_with_unescaped_percentages +6 -0
- data/test/multipart/filename_with_unescaped_percentages2 +6 -0
- data/test/multipart/filename_with_unescaped_percentages3 +6 -0
- data/test/multipart/filename_with_unescaped_quotes +6 -0
- data/test/multipart/ie +6 -0
- data/test/multipart/invalid_character +6 -0
- data/test/multipart/mixed_files +21 -0
- data/test/multipart/nested +10 -0
- data/test/multipart/none +9 -0
- data/test/multipart/semicolon +6 -0
- data/test/multipart/text +15 -0
- data/test/multipart/three_files_three_fields +31 -0
- data/test/multipart/webkit +32 -0
- data/test/rackup/config.ru +31 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
- data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
- data/test/spec_body_proxy.rb +85 -0
- data/test/spec_builder.rb +223 -0
- data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
- data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
- data/test/spec_chunked.rb +101 -0
- data/test/spec_commonlogger.rb +93 -0
- data/test/spec_conditionalget.rb +102 -0
- data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
- data/test/spec_content_length.rb +85 -0
- data/test/spec_content_type.rb +45 -0
- data/test/spec_deflater.rb +339 -0
- data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
- data/test/spec_etag.rb +107 -0
- data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
- data/test/spec_file.rb +221 -0
- data/test/spec_handler.rb +72 -0
- data/test/spec_head.rb +45 -0
- data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
- data/test/spec_lobster.rb +58 -0
- data/test/spec_lock.rb +164 -0
- data/test/spec_logger.rb +23 -0
- data/test/spec_methodoverride.rb +95 -0
- data/test/spec_mime.rb +51 -0
- data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
- data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
- data/test/spec_multipart.rb +600 -0
- data/test/spec_nulllogger.rb +20 -0
- data/test/spec_recursive.rb +72 -0
- data/test/spec_request.rb +1227 -0
- data/test/spec_response.rb +407 -0
- data/test/spec_rewindable_input.rb +118 -0
- data/test/spec_runtime.rb +49 -0
- data/test/spec_sendfile.rb +130 -0
- data/test/spec_server.rb +167 -0
- data/test/spec_session_abstract_id.rb +53 -0
- data/test/spec_session_cookie.rb +410 -0
- data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
- data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
- data/test/spec_showexceptions.rb +85 -0
- data/test/spec_showstatus.rb +103 -0
- data/test/spec_static.rb +145 -0
- data/test/spec_tempfile_reaper.rb +63 -0
- data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
- data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
- data/test/spec_utils.rb +647 -0
- data/test/spec_version.rb +17 -0
- data/test/spec_webrick.rb +184 -0
- data/test/static/another/index.html +1 -0
- data/test/static/index.html +1 -0
- data/test/testrequest.rb +78 -0
- data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +220 -239
- data/RDOX +0 -0
- data/README +0 -592
- data/lib/rack/adapter/camping.rb +0 -22
- data/test/spec_auth.rb +0 -57
- data/test/spec_rack_builder.rb +0 -84
- data/test/spec_rack_camping.rb +0 -55
- data/test/spec_rack_chunked.rb +0 -62
- data/test/spec_rack_commonlogger.rb +0 -61
- data/test/spec_rack_conditionalget.rb +0 -41
- data/test/spec_rack_content_length.rb +0 -43
- data/test/spec_rack_content_type.rb +0 -30
- data/test/spec_rack_deflater.rb +0 -127
- data/test/spec_rack_etag.rb +0 -17
- data/test/spec_rack_file.rb +0 -75
- data/test/spec_rack_handler.rb +0 -43
- data/test/spec_rack_head.rb +0 -30
- data/test/spec_rack_lobster.rb +0 -45
- data/test/spec_rack_lock.rb +0 -38
- data/test/spec_rack_logger.rb +0 -21
- data/test/spec_rack_methodoverride.rb +0 -60
- data/test/spec_rack_nulllogger.rb +0 -13
- data/test/spec_rack_recursive.rb +0 -77
- data/test/spec_rack_request.rb +0 -594
- data/test/spec_rack_response.rb +0 -221
- data/test/spec_rack_rewindable_input.rb +0 -118
- data/test/spec_rack_runtime.rb +0 -35
- data/test/spec_rack_sendfile.rb +0 -86
- data/test/spec_rack_session_cookie.rb +0 -92
- data/test/spec_rack_showexceptions.rb +0 -21
- data/test/spec_rack_showstatus.rb +0 -72
- data/test/spec_rack_static.rb +0 -37
- data/test/spec_rack_utils.rb +0 -557
- data/test/spec_rack_webrick.rb +0 -130
- data/test/spec_rackup.rb +0 -164
data/lib/rack/reloader.rb
CHANGED
|
@@ -26,6 +26,7 @@ module Rack
|
|
|
26
26
|
@last = (Time.now - cooldown)
|
|
27
27
|
@cache = {}
|
|
28
28
|
@mtimes = {}
|
|
29
|
+
@reload_mutex = Mutex.new
|
|
29
30
|
|
|
30
31
|
extend backend
|
|
31
32
|
end
|
|
@@ -33,7 +34,7 @@ module Rack
|
|
|
33
34
|
def call(env)
|
|
34
35
|
if @cooldown and Time.now > @last + @cooldown
|
|
35
36
|
if Thread.list.size > 1
|
|
36
|
-
|
|
37
|
+
@reload_mutex.synchronize{ reload! }
|
|
37
38
|
else
|
|
38
39
|
reload!
|
|
39
40
|
end
|
|
@@ -101,7 +102,7 @@ module Rack
|
|
|
101
102
|
return unless file
|
|
102
103
|
stat = ::File.stat(file)
|
|
103
104
|
return file, stat if stat.file?
|
|
104
|
-
rescue Errno::ENOENT, Errno::ENOTDIR
|
|
105
|
+
rescue Errno::ENOENT, Errno::ENOTDIR, Errno::ESRCH
|
|
105
106
|
@cache.delete(file) and false
|
|
106
107
|
end
|
|
107
108
|
end
|
data/lib/rack/request.rb
CHANGED
|
@@ -8,10 +8,6 @@ module Rack
|
|
|
8
8
|
# req = Rack::Request.new(env)
|
|
9
9
|
# req.post?
|
|
10
10
|
# req.params["data"]
|
|
11
|
-
#
|
|
12
|
-
# The environment hash passed will store a reference to the Request object
|
|
13
|
-
# instantiated so that it will only instantiate if an instance of the Request
|
|
14
|
-
# object doesn't already exist.
|
|
15
11
|
|
|
16
12
|
class Request
|
|
17
13
|
# The environment of the request.
|
|
@@ -22,14 +18,17 @@ module Rack
|
|
|
22
18
|
end
|
|
23
19
|
|
|
24
20
|
def body; @env["rack.input"] end
|
|
25
|
-
def
|
|
26
|
-
def
|
|
27
|
-
def path_info; @env["PATH_INFO"].to_s end
|
|
28
|
-
def port; @env["SERVER_PORT"].to_i end
|
|
21
|
+
def script_name; @env[SCRIPT_NAME].to_s end
|
|
22
|
+
def path_info; @env[PATH_INFO].to_s end
|
|
29
23
|
def request_method; @env["REQUEST_METHOD"] end
|
|
30
|
-
def query_string; @env[
|
|
24
|
+
def query_string; @env[QUERY_STRING].to_s end
|
|
31
25
|
def content_length; @env['CONTENT_LENGTH'] end
|
|
32
|
-
|
|
26
|
+
|
|
27
|
+
def content_type
|
|
28
|
+
content_type = @env['CONTENT_TYPE']
|
|
29
|
+
content_type.nil? || content_type.empty? ? nil : content_type
|
|
30
|
+
end
|
|
31
|
+
|
|
33
32
|
def session; @env['rack.session'] ||= {} end
|
|
34
33
|
def session_options; @env['rack.session.options'] ||= {} end
|
|
35
34
|
def logger; @env['rack.logger'] end
|
|
@@ -51,9 +50,9 @@ module Rack
|
|
|
51
50
|
# { 'charset' => 'utf-8' }
|
|
52
51
|
def media_type_params
|
|
53
52
|
return {} if content_type.nil?
|
|
54
|
-
content_type.split(/\s*[;,]\s*/)[1..-1].
|
|
53
|
+
Hash[*content_type.split(/\s*[;,]\s*/)[1..-1].
|
|
55
54
|
collect { |s| s.split('=', 2) }.
|
|
56
|
-
|
|
55
|
+
map { |k,v| [k.downcase, strip_doublequotes(v)] }.flatten]
|
|
57
56
|
end
|
|
58
57
|
|
|
59
58
|
# The character set of the request body if a "charset" media type
|
|
@@ -64,6 +63,24 @@ module Rack
|
|
|
64
63
|
media_type_params['charset']
|
|
65
64
|
end
|
|
66
65
|
|
|
66
|
+
def scheme
|
|
67
|
+
if @env['HTTPS'] == 'on'
|
|
68
|
+
'https'
|
|
69
|
+
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
|
|
70
|
+
'https'
|
|
71
|
+
elsif @env['HTTP_X_FORWARDED_SCHEME']
|
|
72
|
+
@env['HTTP_X_FORWARDED_SCHEME']
|
|
73
|
+
elsif @env['HTTP_X_FORWARDED_PROTO']
|
|
74
|
+
@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
|
|
75
|
+
else
|
|
76
|
+
@env["rack.url_scheme"]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def ssl?
|
|
81
|
+
scheme == 'https'
|
|
82
|
+
end
|
|
83
|
+
|
|
67
84
|
def host_with_port
|
|
68
85
|
if forwarded = @env["HTTP_X_FORWARDED_HOST"]
|
|
69
86
|
forwarded.split(/,\s?/).last
|
|
@@ -72,19 +89,59 @@ module Rack
|
|
|
72
89
|
end
|
|
73
90
|
end
|
|
74
91
|
|
|
92
|
+
def port
|
|
93
|
+
if port = host_with_port.split(/:/)[1]
|
|
94
|
+
port.to_i
|
|
95
|
+
elsif port = @env['HTTP_X_FORWARDED_PORT']
|
|
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
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
75
106
|
def host
|
|
76
107
|
# Remove port number.
|
|
77
|
-
host_with_port.to_s.
|
|
108
|
+
host_with_port.to_s.sub(/:\d+\z/, '')
|
|
78
109
|
end
|
|
79
110
|
|
|
80
111
|
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
|
|
81
112
|
def path_info=(s); @env["PATH_INFO"] = s.to_s end
|
|
82
113
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
|
|
114
|
+
|
|
115
|
+
# Checks the HTTP request method (or verb) to see if it was of type DELETE
|
|
116
|
+
def delete?; request_method == "DELETE" end
|
|
117
|
+
|
|
118
|
+
# Checks the HTTP request method (or verb) to see if it was of type GET
|
|
119
|
+
def get?; request_method == GET end
|
|
120
|
+
|
|
121
|
+
# Checks the HTTP request method (or verb) to see if it was of type HEAD
|
|
122
|
+
def head?; request_method == HEAD end
|
|
123
|
+
|
|
124
|
+
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
|
|
125
|
+
def options?; request_method == "OPTIONS" end
|
|
126
|
+
|
|
127
|
+
# Checks the HTTP request method (or verb) to see if it was of type LINK
|
|
128
|
+
def link?; request_method == "LINK" end
|
|
129
|
+
|
|
130
|
+
# Checks the HTTP request method (or verb) to see if it was of type PATCH
|
|
131
|
+
def patch?; request_method == "PATCH" end
|
|
132
|
+
|
|
133
|
+
# Checks the HTTP request method (or verb) to see if it was of type POST
|
|
134
|
+
def post?; request_method == "POST" end
|
|
135
|
+
|
|
136
|
+
# Checks the HTTP request method (or verb) to see if it was of type PUT
|
|
137
|
+
def put?; request_method == "PUT" end
|
|
138
|
+
|
|
139
|
+
# Checks the HTTP request method (or verb) to see if it was of type TRACE
|
|
140
|
+
def trace?; request_method == "TRACE" end
|
|
141
|
+
|
|
142
|
+
# Checks the HTTP request method (or verb) to see if it was of type UNLINK
|
|
143
|
+
def unlink?; request_method == "UNLINK" end
|
|
144
|
+
|
|
88
145
|
|
|
89
146
|
# The set of form-data media-types. Requests that do not indicate
|
|
90
147
|
# one of the media types presents in this list will not be eligible
|
|
@@ -102,6 +159,10 @@ module Rack
|
|
|
102
159
|
'multipart/mixed'
|
|
103
160
|
]
|
|
104
161
|
|
|
162
|
+
# Default ports depending on scheme. Used to decide whether or not
|
|
163
|
+
# to include the port in a generated URI.
|
|
164
|
+
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
|
165
|
+
|
|
105
166
|
# Determine whether the request body contains form-data by checking
|
|
106
167
|
# the request Content-Type for one of the media-types:
|
|
107
168
|
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
|
@@ -112,7 +173,7 @@ module Rack
|
|
|
112
173
|
# Content-Type header is provided and the request_method is POST.
|
|
113
174
|
def form_data?
|
|
114
175
|
type = media_type
|
|
115
|
-
meth = env["rack.methodoverride.original_method"] || env[
|
|
176
|
+
meth = env["rack.methodoverride.original_method"] || env[REQUEST_METHOD]
|
|
116
177
|
(meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
|
|
117
178
|
end
|
|
118
179
|
|
|
@@ -122,38 +183,40 @@ module Rack
|
|
|
122
183
|
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
|
|
123
184
|
end
|
|
124
185
|
|
|
125
|
-
# Returns the data
|
|
186
|
+
# Returns the data received in the query string.
|
|
126
187
|
def GET
|
|
127
188
|
if @env["rack.request.query_string"] == query_string
|
|
128
189
|
@env["rack.request.query_hash"]
|
|
129
190
|
else
|
|
191
|
+
p = parse_query({ :query => query_string, :separator => '&;' })
|
|
130
192
|
@env["rack.request.query_string"] = query_string
|
|
131
|
-
@env["rack.request.query_hash"] =
|
|
193
|
+
@env["rack.request.query_hash"] = p
|
|
132
194
|
end
|
|
133
195
|
end
|
|
134
196
|
|
|
135
|
-
# Returns the data
|
|
197
|
+
# Returns the data received in the request body.
|
|
136
198
|
#
|
|
137
199
|
# This method support both application/x-www-form-urlencoded and
|
|
138
200
|
# multipart/form-data.
|
|
139
201
|
def POST
|
|
140
202
|
if @env["rack.input"].nil?
|
|
141
203
|
raise "Missing rack.input"
|
|
142
|
-
elsif @env["rack.request.form_input"].
|
|
204
|
+
elsif @env["rack.request.form_input"].equal? @env["rack.input"]
|
|
143
205
|
@env["rack.request.form_hash"]
|
|
144
206
|
elsif form_data? || parseable_data?
|
|
145
|
-
@env["rack.request.form_input"] = @env["rack.input"]
|
|
146
207
|
unless @env["rack.request.form_hash"] = parse_multipart(env)
|
|
147
208
|
form_vars = @env["rack.input"].read
|
|
148
209
|
|
|
149
210
|
# Fix for Safari Ajax postings that always append \0
|
|
150
|
-
form_vars.sub!(/\0\z/, '')
|
|
211
|
+
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
|
212
|
+
form_vars.slice!(-1) if form_vars[-1] == ?\0
|
|
151
213
|
|
|
152
214
|
@env["rack.request.form_vars"] = form_vars
|
|
153
|
-
@env["rack.request.form_hash"] = parse_query(form_vars)
|
|
215
|
+
@env["rack.request.form_hash"] = parse_query({ :query => form_vars, :separator => '&' })
|
|
154
216
|
|
|
155
217
|
@env["rack.input"].rewind
|
|
156
218
|
end
|
|
219
|
+
@env["rack.request.form_input"] = @env["rack.input"]
|
|
157
220
|
@env["rack.request.form_hash"]
|
|
158
221
|
else
|
|
159
222
|
{}
|
|
@@ -161,10 +224,45 @@ module Rack
|
|
|
161
224
|
end
|
|
162
225
|
|
|
163
226
|
# The union of GET and POST data.
|
|
227
|
+
#
|
|
228
|
+
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
|
164
229
|
def params
|
|
165
|
-
self.GET.
|
|
166
|
-
rescue EOFError
|
|
167
|
-
self.GET
|
|
230
|
+
@params ||= self.GET.merge(self.POST)
|
|
231
|
+
rescue EOFError
|
|
232
|
+
self.GET.dup
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
|
|
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
|
|
245
|
+
end
|
|
246
|
+
if self.POST.has_key?(k)
|
|
247
|
+
found = true
|
|
248
|
+
self.POST[k] = v
|
|
249
|
+
end
|
|
250
|
+
unless found
|
|
251
|
+
self.GET[k] = v
|
|
252
|
+
end
|
|
253
|
+
@params = nil
|
|
254
|
+
nil
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Destructively delete a parameter, whether it's in GET or POST. Returns the value of the deleted parameter.
|
|
258
|
+
#
|
|
259
|
+
# If the parameter is in both GET and POST, the POST value takes precedence since that's how #params works.
|
|
260
|
+
#
|
|
261
|
+
# env['rack.input'] is not touched.
|
|
262
|
+
def delete_param(k)
|
|
263
|
+
v = [ self.POST.delete(k), self.GET.delete(k) ].compact.first
|
|
264
|
+
@params = nil
|
|
265
|
+
v
|
|
168
266
|
end
|
|
169
267
|
|
|
170
268
|
# shortcut for request.params[key]
|
|
@@ -173,6 +271,8 @@ module Rack
|
|
|
173
271
|
end
|
|
174
272
|
|
|
175
273
|
# shortcut for request.params[key] = value
|
|
274
|
+
#
|
|
275
|
+
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
|
176
276
|
def []=(key, value)
|
|
177
277
|
params[key.to_s] = value
|
|
178
278
|
end
|
|
@@ -182,9 +282,9 @@ module Rack
|
|
|
182
282
|
keys.map{|key| params[key] }
|
|
183
283
|
end
|
|
184
284
|
|
|
185
|
-
# the referer of the client
|
|
285
|
+
# the referer of the client
|
|
186
286
|
def referer
|
|
187
|
-
@env['HTTP_REFERER']
|
|
287
|
+
@env['HTTP_REFERER']
|
|
188
288
|
end
|
|
189
289
|
alias referrer referer
|
|
190
290
|
|
|
@@ -193,42 +293,36 @@ module Rack
|
|
|
193
293
|
end
|
|
194
294
|
|
|
195
295
|
def cookies
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
end
|
|
296
|
+
hash = @env["rack.request.cookie_hash"] ||= {}
|
|
297
|
+
string = @env["HTTP_COOKIE"]
|
|
298
|
+
|
|
299
|
+
return hash if string == @env["rack.request.cookie_string"]
|
|
300
|
+
hash.clear
|
|
301
|
+
|
|
302
|
+
# According to RFC 2109:
|
|
303
|
+
# If multiple cookies satisfy the criteria above, they are ordered in
|
|
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
|
|
213
311
|
end
|
|
214
312
|
|
|
215
313
|
def xhr?
|
|
216
314
|
@env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
|
|
217
315
|
end
|
|
218
316
|
|
|
317
|
+
def base_url
|
|
318
|
+
url = "#{scheme}://#{host}"
|
|
319
|
+
url << ":#{port}" if port != DEFAULT_PORTS[scheme]
|
|
320
|
+
url
|
|
321
|
+
end
|
|
322
|
+
|
|
219
323
|
# Tries to return a remake of the original request URL as a string.
|
|
220
324
|
def url
|
|
221
|
-
|
|
222
|
-
url << host
|
|
223
|
-
|
|
224
|
-
if scheme == "https" && port != 443 ||
|
|
225
|
-
scheme == "http" && port != 80
|
|
226
|
-
url << ":#{port}"
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
url << fullpath
|
|
230
|
-
|
|
231
|
-
url
|
|
325
|
+
base_url + fullpath
|
|
232
326
|
end
|
|
233
327
|
|
|
234
328
|
def path
|
|
@@ -240,32 +334,65 @@ module Rack
|
|
|
240
334
|
end
|
|
241
335
|
|
|
242
336
|
def accept_encoding
|
|
243
|
-
@env["HTTP_ACCEPT_ENCODING"]
|
|
244
|
-
|
|
337
|
+
parse_http_accept_header(@env["HTTP_ACCEPT_ENCODING"])
|
|
338
|
+
end
|
|
245
339
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
340
|
+
def accept_language
|
|
341
|
+
parse_http_accept_header(@env["HTTP_ACCEPT_LANGUAGE"])
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def trusted_proxy?(ip)
|
|
345
|
+
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
|
|
252
346
|
end
|
|
253
347
|
|
|
254
348
|
def ip
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
349
|
+
remote_addrs = split_ip_addresses(@env['REMOTE_ADDR'])
|
|
350
|
+
remote_addrs = reject_trusted_ip_addresses(remote_addrs)
|
|
351
|
+
|
|
352
|
+
return remote_addrs.first if remote_addrs.any?
|
|
353
|
+
|
|
354
|
+
forwarded_ips = split_ip_addresses(@env['HTTP_X_FORWARDED_FOR'])
|
|
355
|
+
|
|
356
|
+
return reject_trusted_ip_addresses(forwarded_ips).last || @env["REMOTE_ADDR"]
|
|
260
357
|
end
|
|
261
358
|
|
|
262
359
|
protected
|
|
360
|
+
def split_ip_addresses(ip_addresses)
|
|
361
|
+
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def reject_trusted_ip_addresses(ip_addresses)
|
|
365
|
+
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
|
366
|
+
end
|
|
367
|
+
|
|
263
368
|
def parse_query(qs)
|
|
264
|
-
|
|
369
|
+
d = '&'
|
|
370
|
+
qs, d = qs[:query], qs[:separator] if Hash === qs
|
|
371
|
+
Utils.parse_nested_query(qs, d)
|
|
265
372
|
end
|
|
266
373
|
|
|
267
374
|
def parse_multipart(env)
|
|
268
|
-
|
|
375
|
+
Rack::Multipart.parse_multipart(env)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def parse_http_accept_header(header)
|
|
379
|
+
header.to_s.split(/\s*,\s*/).map do |part|
|
|
380
|
+
attribute, parameters = part.split(/\s*;\s*/, 2)
|
|
381
|
+
quality = 1.0
|
|
382
|
+
if parameters and /\Aq=([\d.]+)/ =~ parameters
|
|
383
|
+
quality = $1.to_f
|
|
384
|
+
end
|
|
385
|
+
[attribute, quality]
|
|
386
|
+
end
|
|
269
387
|
end
|
|
388
|
+
|
|
389
|
+
private
|
|
390
|
+
def strip_doublequotes(s)
|
|
391
|
+
if s[0] == ?" && s[-1] == ?"
|
|
392
|
+
s[1..-2]
|
|
393
|
+
else
|
|
394
|
+
s
|
|
395
|
+
end
|
|
396
|
+
end
|
|
270
397
|
end
|
|
271
398
|
end
|
data/lib/rack/response.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
require 'rack/request'
|
|
2
2
|
require 'rack/utils'
|
|
3
|
+
require 'rack/body_proxy'
|
|
4
|
+
require 'time'
|
|
3
5
|
|
|
4
6
|
module Rack
|
|
5
7
|
# Rack::Response provides a convenient interface to create a Rack
|
|
@@ -11,21 +13,23 @@ module Rack
|
|
|
11
13
|
# You can use Response#write to iteratively generate your response,
|
|
12
14
|
# but note that this is buffered by Rack::Response until you call
|
|
13
15
|
# +finish+. +finish+ however can take a block inside which calls to
|
|
14
|
-
# +write+ are
|
|
16
|
+
# +write+ are synchronous with the Rack response.
|
|
15
17
|
#
|
|
16
18
|
# Your application's +call+ should end returning Response#finish.
|
|
17
19
|
|
|
18
20
|
class Response
|
|
19
21
|
attr_accessor :length
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
CHUNKED = 'chunked'.freeze
|
|
24
|
+
TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
|
|
25
|
+
def initialize(body=[], status=200, header={})
|
|
22
26
|
@status = status.to_i
|
|
23
|
-
@header = Utils::HeaderHash.new(
|
|
24
|
-
merge(header))
|
|
27
|
+
@header = Utils::HeaderHash.new.merge(header)
|
|
25
28
|
|
|
26
|
-
@
|
|
27
|
-
@
|
|
28
|
-
@
|
|
29
|
+
@chunked = CHUNKED == @header[TRANSFER_ENCODING]
|
|
30
|
+
@writer = lambda { |x| @body << x }
|
|
31
|
+
@block = nil
|
|
32
|
+
@length = 0
|
|
29
33
|
|
|
30
34
|
@body = []
|
|
31
35
|
|
|
@@ -69,14 +73,17 @@ module Rack
|
|
|
69
73
|
def finish(&block)
|
|
70
74
|
@block = block
|
|
71
75
|
|
|
72
|
-
if [204, 304].include?(status.to_i)
|
|
73
|
-
header.delete
|
|
76
|
+
if [204, 205, 304].include?(status.to_i)
|
|
77
|
+
header.delete CONTENT_TYPE
|
|
78
|
+
header.delete CONTENT_LENGTH
|
|
79
|
+
close
|
|
74
80
|
[status.to_i, header, []]
|
|
75
81
|
else
|
|
76
|
-
[status.to_i, header, self]
|
|
82
|
+
[status.to_i, header, BodyProxy.new(self){}]
|
|
77
83
|
end
|
|
78
84
|
end
|
|
79
85
|
alias to_a finish # For *response
|
|
86
|
+
alias to_ary finish # For implicit-splat on Ruby 1.9.2
|
|
80
87
|
|
|
81
88
|
def each(&callback)
|
|
82
89
|
@body.each(&callback)
|
|
@@ -90,10 +97,10 @@ module Rack
|
|
|
90
97
|
#
|
|
91
98
|
def write(str)
|
|
92
99
|
s = str.to_s
|
|
93
|
-
@length += Rack::Utils.bytesize(s)
|
|
100
|
+
@length += Rack::Utils.bytesize(s) unless @chunked
|
|
94
101
|
@writer.call s
|
|
95
102
|
|
|
96
|
-
header[
|
|
103
|
+
header[CONTENT_LENGTH] = @length.to_s unless @chunked
|
|
97
104
|
str
|
|
98
105
|
end
|
|
99
106
|
|
|
@@ -108,20 +115,26 @@ module Rack
|
|
|
108
115
|
alias headers header
|
|
109
116
|
|
|
110
117
|
module Helpers
|
|
111
|
-
def invalid?;
|
|
112
|
-
|
|
113
|
-
def informational?;
|
|
114
|
-
def successful?;
|
|
115
|
-
def redirection?;
|
|
116
|
-
def client_error?;
|
|
117
|
-
def server_error?;
|
|
118
|
-
|
|
119
|
-
def ok?;
|
|
120
|
-
def
|
|
121
|
-
def
|
|
122
|
-
|
|
123
|
-
def
|
|
124
|
-
def
|
|
118
|
+
def invalid?; status < 100 || status >= 600; end
|
|
119
|
+
|
|
120
|
+
def informational?; status >= 100 && status < 200; end
|
|
121
|
+
def successful?; status >= 200 && status < 300; end
|
|
122
|
+
def redirection?; status >= 300 && status < 400; end
|
|
123
|
+
def client_error?; status >= 400 && status < 500; end
|
|
124
|
+
def server_error?; status >= 500 && status < 600; end
|
|
125
|
+
|
|
126
|
+
def ok?; status == 200; end
|
|
127
|
+
def created?; status == 201; end
|
|
128
|
+
def accepted?; status == 202; end
|
|
129
|
+
def bad_request?; status == 400; end
|
|
130
|
+
def unauthorized?; status == 401; end
|
|
131
|
+
def forbidden?; status == 403; end
|
|
132
|
+
def not_found?; status == 404; end
|
|
133
|
+
def method_not_allowed?; status == 405; end
|
|
134
|
+
def i_m_a_teapot?; status == 418; end
|
|
135
|
+
def unprocessable?; status == 422; end
|
|
136
|
+
|
|
137
|
+
def redirect?; [301, 302, 303, 307].include? status; end
|
|
125
138
|
|
|
126
139
|
# Headers
|
|
127
140
|
attr_reader :headers, :original_headers
|
|
@@ -131,11 +144,11 @@ module Rack
|
|
|
131
144
|
end
|
|
132
145
|
|
|
133
146
|
def content_type
|
|
134
|
-
headers[
|
|
147
|
+
headers[CONTENT_TYPE]
|
|
135
148
|
end
|
|
136
149
|
|
|
137
150
|
def content_length
|
|
138
|
-
cl = headers[
|
|
151
|
+
cl = headers[CONTENT_LENGTH]
|
|
139
152
|
cl ? cl.to_i : cl
|
|
140
153
|
end
|
|
141
154
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
1
2
|
require 'tempfile'
|
|
3
|
+
require 'rack/utils'
|
|
2
4
|
|
|
3
5
|
module Rack
|
|
4
6
|
# Class which can make any IO object rewindable, including non-rewindable ones. It does
|
|
@@ -16,27 +18,27 @@ module Rack
|
|
|
16
18
|
@rewindable_io = nil
|
|
17
19
|
@unlinked = false
|
|
18
20
|
end
|
|
19
|
-
|
|
21
|
+
|
|
20
22
|
def gets
|
|
21
23
|
make_rewindable unless @rewindable_io
|
|
22
24
|
@rewindable_io.gets
|
|
23
25
|
end
|
|
24
|
-
|
|
26
|
+
|
|
25
27
|
def read(*args)
|
|
26
28
|
make_rewindable unless @rewindable_io
|
|
27
29
|
@rewindable_io.read(*args)
|
|
28
30
|
end
|
|
29
|
-
|
|
31
|
+
|
|
30
32
|
def each(&block)
|
|
31
33
|
make_rewindable unless @rewindable_io
|
|
32
34
|
@rewindable_io.each(&block)
|
|
33
35
|
end
|
|
34
|
-
|
|
36
|
+
|
|
35
37
|
def rewind
|
|
36
38
|
make_rewindable unless @rewindable_io
|
|
37
39
|
@rewindable_io.rewind
|
|
38
40
|
end
|
|
39
|
-
|
|
41
|
+
|
|
40
42
|
# Closes this RewindableInput object without closing the originally
|
|
41
43
|
# wrapped IO oject. Cleans up any temporary resources that this RewindableInput
|
|
42
44
|
# has created.
|
|
@@ -52,9 +54,9 @@ module Rack
|
|
|
52
54
|
@rewindable_io = nil
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
private
|
|
57
|
-
|
|
59
|
+
|
|
58
60
|
# Ruby's Tempfile class has a bug. Subclass it and fix it.
|
|
59
61
|
class Tempfile < ::Tempfile
|
|
60
62
|
def _close
|
|
@@ -75,16 +77,18 @@ module Rack
|
|
|
75
77
|
@rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
|
|
76
78
|
@rewindable_io.binmode
|
|
77
79
|
if filesystem_has_posix_semantics?
|
|
78
|
-
|
|
80
|
+
# Use ::File.unlink as 1.9.1 Tempfile has a bug where unlink closes the file!
|
|
81
|
+
::File.unlink @rewindable_io.path
|
|
82
|
+
raise 'Unlink failed. IO closed.' if @rewindable_io.closed?
|
|
79
83
|
@unlinked = true
|
|
80
84
|
end
|
|
81
|
-
|
|
85
|
+
|
|
82
86
|
buffer = ""
|
|
83
87
|
while @io.read(1024 * 4, buffer)
|
|
84
88
|
entire_buffer_written_out = false
|
|
85
89
|
while !entire_buffer_written_out
|
|
86
90
|
written = @rewindable_io.write(buffer)
|
|
87
|
-
entire_buffer_written_out = written == buffer
|
|
91
|
+
entire_buffer_written_out = written == Rack::Utils.bytesize(buffer)
|
|
88
92
|
if !entire_buffer_written_out
|
|
89
93
|
buffer.slice!(0 .. written - 1)
|
|
90
94
|
end
|
|
@@ -92,7 +96,7 @@ module Rack
|
|
|
92
96
|
end
|
|
93
97
|
@rewindable_io.rewind
|
|
94
98
|
end
|
|
95
|
-
|
|
99
|
+
|
|
96
100
|
def filesystem_has_posix_semantics?
|
|
97
101
|
RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/
|
|
98
102
|
end
|
data/lib/rack/runtime.rb
CHANGED
|
@@ -12,16 +12,29 @@ module Rack
|
|
|
12
12
|
@header_name << "-#{name}" if name
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
FORMAT_STRING = "%0.6f"
|
|
15
16
|
def call(env)
|
|
16
|
-
start_time =
|
|
17
|
+
start_time = clock_time
|
|
17
18
|
status, headers, body = @app.call(env)
|
|
18
|
-
request_time =
|
|
19
|
+
request_time = clock_time - start_time
|
|
19
20
|
|
|
20
21
|
if !headers.has_key?(@header_name)
|
|
21
|
-
headers[@header_name] =
|
|
22
|
+
headers[@header_name] = FORMAT_STRING % request_time
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
[status, headers, body]
|
|
25
26
|
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
|
31
|
+
def clock_time
|
|
32
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
def clock_time
|
|
36
|
+
Time.now.to_f
|
|
37
|
+
end
|
|
38
|
+
end
|
|
26
39
|
end
|
|
27
40
|
end
|