rack 1.6.11 → 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 +675 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +157 -163
- data/Rakefile +38 -32
- data/{SPEC → SPEC.rdoc} +41 -13
- data/bin/rackup +1 -0
- data/contrib/rack_logo.svg +164 -111
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +6 -2
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +5 -4
- data/lib/rack/auth/digest/request.rb +6 -4
- data/lib/rack/body_proxy.rb +21 -15
- data/lib/rack/builder.rb +119 -26
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +70 -22
- data/lib/rack/common_logger.rb +80 -0
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +9 -8
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +60 -70
- data/lib/rack/directory.rb +117 -85
- data/lib/rack/etag.rb +9 -7
- data/lib/rack/events.rb +153 -0
- data/lib/rack/file.rb +4 -149
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +17 -19
- data/lib/rack/handler/fastcgi.rb +17 -18
- data/lib/rack/handler/lsws.rb +14 -14
- data/lib/rack/handler/scgi.rb +22 -21
- data/lib/rack/handler/thin.rb +20 -11
- data/lib/rack/handler/webrick.rb +39 -32
- data/lib/rack/handler.rb +9 -26
- data/lib/rack/head.rb +16 -18
- data/lib/rack/lint.rb +110 -64
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +17 -11
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
- data/lib/rack/mime.rb +27 -6
- data/lib/rack/mock.rb +124 -65
- data/lib/rack/multipart/generator.rb +20 -16
- data/lib/rack/multipart/parser.rb +273 -162
- data/lib/rack/multipart/uploaded_file.rb +15 -8
- data/lib/rack/multipart.rb +39 -8
- data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
- data/lib/rack/query_parser.rb +217 -0
- data/lib/rack/recursive.rb +11 -9
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +543 -305
- data/lib/rack/response.rb +244 -88
- data/lib/rack/rewindable_input.rb +5 -15
- data/lib/rack/runtime.rb +12 -18
- data/lib/rack/sendfile.rb +17 -15
- data/lib/rack/server.rb +125 -47
- data/lib/rack/session/abstract/id.rb +216 -93
- data/lib/rack/session/cookie.rb +47 -31
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +26 -17
- data/lib/rack/show_exceptions.rb +390 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +8 -8
- data/lib/rack/static.rb +48 -11
- data/lib/rack/tempfile_reaper.rb +3 -3
- data/lib/rack/urlmap.rb +26 -19
- data/lib/rack/utils.rb +208 -294
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +76 -33
- data/rack.gemspec +43 -30
- metadata +62 -183
- data/HISTORY.md +0 -375
- 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/commonlogger.rb +0 -72
- 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/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -223
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -101
- data/test/spec_commonlogger.rb +0 -93
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -85
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -339
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -107
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -221
- data/test/spec_handler.rb +0 -72
- data/test/spec_head.rb +0 -45
- data/test/spec_lint.rb +0 -550
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -164
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -111
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -297
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -600
- data/test/spec_nulllogger.rb +0 -20
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -1232
- data/test/spec_response.rb +0 -407
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -130
- data/test/spec_server.rb +0 -167
- data/test/spec_session_abstract_id.rb +0 -53
- data/test/spec_session_cookie.rb +0 -410
- data/test/spec_session_memcache.rb +0 -321
- data/test/spec_session_pool.rb +0 -209
- data/test/spec_showexceptions.rb +0 -98
- data/test/spec_showstatus.rb +0 -103
- data/test/spec_static.rb +0 -145
- data/test/spec_tempfile_reaper.rb +0 -63
- data/test/spec_thin.rb +0 -91
- data/test/spec_urlmap.rb +0 -236
- data/test/spec_utils.rb +0 -647
- data/test/spec_version.rb +0 -17
- data/test/spec_webrick.rb +0 -184
- data/test/static/another/index.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/response.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'rack/body_proxy'
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
4
3
|
require 'time'
|
5
4
|
|
6
5
|
module Rack
|
@@ -8,7 +7,7 @@ module Rack
|
|
8
7
|
# response.
|
9
8
|
#
|
10
9
|
# It allows setting of headers and cookies, and provides useful
|
11
|
-
# defaults (
|
10
|
+
# defaults (an OK response with empty headers and body).
|
12
11
|
#
|
13
12
|
# You can use Response#write to iteratively generate your response,
|
14
13
|
# but note that this is buffered by Rack::Response until you call
|
@@ -16,147 +15,304 @@ module Rack
|
|
16
15
|
# +write+ are synchronous with the Rack response.
|
17
16
|
#
|
18
17
|
# Your application's +call+ should end returning Response#finish.
|
19
|
-
|
20
18
|
class Response
|
21
|
-
|
19
|
+
def self.[](status, headers, body)
|
20
|
+
self.new(body, status, headers)
|
21
|
+
end
|
22
22
|
|
23
|
-
CHUNKED = 'chunked'
|
24
|
-
|
25
|
-
def initialize(body=[], status=200, header={})
|
26
|
-
@status = status.to_i
|
27
|
-
@header = Utils::HeaderHash.new.merge(header)
|
23
|
+
CHUNKED = 'chunked'
|
24
|
+
STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
|
28
25
|
|
29
|
-
|
30
|
-
|
31
|
-
@block = nil
|
32
|
-
@length = 0
|
26
|
+
attr_accessor :length, :status, :body
|
27
|
+
attr_reader :headers
|
33
28
|
|
34
|
-
|
29
|
+
# @deprecated Use {#headers} instead.
|
30
|
+
alias header headers
|
35
31
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
32
|
+
# Initialize the response object with the specified body, status
|
33
|
+
# and headers.
|
34
|
+
#
|
35
|
+
# @param body [nil, #each, #to_str] the response body.
|
36
|
+
# @param status [Integer] the integer status as defined by the
|
37
|
+
# HTTP protocol RFCs.
|
38
|
+
# @param headers [#each] a list of key-value header pairs which
|
39
|
+
# conform to the HTTP protocol RFCs.
|
40
|
+
#
|
41
|
+
# Providing a body which responds to #to_str is legacy behaviour.
|
42
|
+
def initialize(body = nil, status = 200, headers = {})
|
43
|
+
@status = status.to_i
|
44
|
+
@headers = Utils::HeaderHash[headers]
|
48
45
|
|
49
|
-
|
50
|
-
attr_accessor :status, :body
|
46
|
+
@writer = self.method(:append)
|
51
47
|
|
52
|
-
|
53
|
-
header[key]
|
54
|
-
end
|
48
|
+
@block = nil
|
55
49
|
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
# Keep track of whether we have expanded the user supplied body.
|
51
|
+
if body.nil?
|
52
|
+
@body = []
|
53
|
+
@buffered = true
|
54
|
+
@length = 0
|
55
|
+
elsif body.respond_to?(:to_str)
|
56
|
+
@body = [body]
|
57
|
+
@buffered = true
|
58
|
+
@length = body.to_str.bytesize
|
59
|
+
else
|
60
|
+
@body = body
|
61
|
+
@buffered = false
|
62
|
+
@length = 0
|
63
|
+
end
|
59
64
|
|
60
|
-
|
61
|
-
Utils.set_cookie_header!(header, key, value)
|
65
|
+
yield self if block_given?
|
62
66
|
end
|
63
67
|
|
64
|
-
def
|
65
|
-
|
68
|
+
def redirect(target, status = 302)
|
69
|
+
self.status = status
|
70
|
+
self.location = target
|
66
71
|
end
|
67
72
|
|
68
|
-
def
|
69
|
-
|
70
|
-
self["Location"] = target
|
73
|
+
def chunked?
|
74
|
+
CHUNKED == get_header(TRANSFER_ENCODING)
|
71
75
|
end
|
72
76
|
|
77
|
+
# Generate a response array consistent with the requirements of the SPEC.
|
78
|
+
# @return [Array] a 3-tuple suitable of `[status, headers, body]`
|
79
|
+
# which is suitable to be returned from the middleware `#call(env)` method.
|
73
80
|
def finish(&block)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
header.delete CONTENT_TYPE
|
78
|
-
header.delete CONTENT_LENGTH
|
81
|
+
if STATUS_WITH_NO_ENTITY_BODY[status.to_i]
|
82
|
+
delete_header CONTENT_TYPE
|
83
|
+
delete_header CONTENT_LENGTH
|
79
84
|
close
|
80
|
-
[status
|
85
|
+
return [@status, @headers, []]
|
81
86
|
else
|
82
|
-
|
87
|
+
if block_given?
|
88
|
+
@block = block
|
89
|
+
return [@status, @headers, self]
|
90
|
+
else
|
91
|
+
return [@status, @headers, @body]
|
92
|
+
end
|
83
93
|
end
|
84
94
|
end
|
95
|
+
|
85
96
|
alias to_a finish # For *response
|
86
|
-
alias to_ary finish # For implicit-splat on Ruby 1.9.2
|
87
97
|
|
88
98
|
def each(&callback)
|
89
99
|
@body.each(&callback)
|
90
|
-
@
|
91
|
-
|
100
|
+
@buffered = true
|
101
|
+
|
102
|
+
if @block
|
103
|
+
@writer = callback
|
104
|
+
@block.call(self)
|
105
|
+
end
|
92
106
|
end
|
93
107
|
|
94
108
|
# Append to body and update Content-Length.
|
95
109
|
#
|
96
110
|
# NOTE: Do not mix #write and direct #body access!
|
97
111
|
#
|
98
|
-
def write(
|
99
|
-
|
100
|
-
@length += Rack::Utils.bytesize(s) unless @chunked
|
101
|
-
@writer.call s
|
112
|
+
def write(chunk)
|
113
|
+
buffered_body!
|
102
114
|
|
103
|
-
|
104
|
-
str
|
115
|
+
@writer.call(chunk.to_s)
|
105
116
|
end
|
106
117
|
|
107
118
|
def close
|
108
|
-
body.close if body.respond_to?(:close)
|
119
|
+
@body.close if @body.respond_to?(:close)
|
109
120
|
end
|
110
121
|
|
111
122
|
def empty?
|
112
123
|
@block == nil && @body.empty?
|
113
124
|
end
|
114
125
|
|
115
|
-
|
126
|
+
def has_header?(key); headers.key? key; end
|
127
|
+
def get_header(key); headers[key]; end
|
128
|
+
def set_header(key, v); headers[key] = v; end
|
129
|
+
def delete_header(key); headers.delete key; end
|
130
|
+
|
131
|
+
alias :[] :get_header
|
132
|
+
alias :[]= :set_header
|
116
133
|
|
117
134
|
module Helpers
|
118
|
-
def invalid?;
|
119
|
-
|
120
|
-
def informational?;
|
121
|
-
def successful?;
|
122
|
-
def redirection?;
|
123
|
-
def client_error?;
|
124
|
-
def server_error?;
|
125
|
-
|
126
|
-
def ok?;
|
127
|
-
def created?;
|
128
|
-
def accepted?;
|
129
|
-
def
|
130
|
-
def
|
131
|
-
def
|
132
|
-
def
|
133
|
-
def
|
134
|
-
def
|
135
|
-
def
|
136
|
-
|
137
|
-
def
|
138
|
-
|
139
|
-
|
140
|
-
attr_reader :headers, :original_headers
|
135
|
+
def invalid?; status < 100 || status >= 600; end
|
136
|
+
|
137
|
+
def informational?; status >= 100 && status < 200; end
|
138
|
+
def successful?; status >= 200 && status < 300; end
|
139
|
+
def redirection?; status >= 300 && status < 400; end
|
140
|
+
def client_error?; status >= 400 && status < 500; end
|
141
|
+
def server_error?; status >= 500 && status < 600; end
|
142
|
+
|
143
|
+
def ok?; status == 200; end
|
144
|
+
def created?; status == 201; end
|
145
|
+
def accepted?; status == 202; end
|
146
|
+
def no_content?; status == 204; end
|
147
|
+
def moved_permanently?; status == 301; end
|
148
|
+
def bad_request?; status == 400; end
|
149
|
+
def unauthorized?; status == 401; end
|
150
|
+
def forbidden?; status == 403; end
|
151
|
+
def not_found?; status == 404; end
|
152
|
+
def method_not_allowed?; status == 405; end
|
153
|
+
def precondition_failed?; status == 412; end
|
154
|
+
def unprocessable?; status == 422; end
|
155
|
+
|
156
|
+
def redirect?; [301, 302, 303, 307, 308].include? status; end
|
141
157
|
|
142
158
|
def include?(header)
|
143
|
-
|
159
|
+
has_header? header
|
160
|
+
end
|
161
|
+
|
162
|
+
# Add a header that may have multiple values.
|
163
|
+
#
|
164
|
+
# Example:
|
165
|
+
# response.add_header 'Vary', 'Accept-Encoding'
|
166
|
+
# response.add_header 'Vary', 'Cookie'
|
167
|
+
#
|
168
|
+
# assert_equal 'Accept-Encoding,Cookie', response.get_header('Vary')
|
169
|
+
#
|
170
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
171
|
+
def add_header(key, v)
|
172
|
+
if v.nil?
|
173
|
+
get_header key
|
174
|
+
elsif has_header? key
|
175
|
+
set_header key, "#{get_header key},#{v}"
|
176
|
+
else
|
177
|
+
set_header key, v
|
178
|
+
end
|
144
179
|
end
|
145
180
|
|
181
|
+
# Get the content type of the response.
|
146
182
|
def content_type
|
147
|
-
|
183
|
+
get_header CONTENT_TYPE
|
184
|
+
end
|
185
|
+
|
186
|
+
# Set the content type of the response.
|
187
|
+
def content_type=(content_type)
|
188
|
+
set_header CONTENT_TYPE, content_type
|
189
|
+
end
|
190
|
+
|
191
|
+
def media_type
|
192
|
+
MediaType.type(content_type)
|
193
|
+
end
|
194
|
+
|
195
|
+
def media_type_params
|
196
|
+
MediaType.params(content_type)
|
148
197
|
end
|
149
198
|
|
150
199
|
def content_length
|
151
|
-
cl =
|
200
|
+
cl = get_header CONTENT_LENGTH
|
152
201
|
cl ? cl.to_i : cl
|
153
202
|
end
|
154
203
|
|
155
204
|
def location
|
156
|
-
|
205
|
+
get_header "Location"
|
206
|
+
end
|
207
|
+
|
208
|
+
def location=(location)
|
209
|
+
set_header "Location", location
|
210
|
+
end
|
211
|
+
|
212
|
+
def set_cookie(key, value)
|
213
|
+
cookie_header = get_header SET_COOKIE
|
214
|
+
set_header SET_COOKIE, ::Rack::Utils.add_cookie_to_header(cookie_header, key, value)
|
215
|
+
end
|
216
|
+
|
217
|
+
def delete_cookie(key, value = {})
|
218
|
+
set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
|
219
|
+
end
|
220
|
+
|
221
|
+
def set_cookie_header
|
222
|
+
get_header SET_COOKIE
|
223
|
+
end
|
224
|
+
|
225
|
+
def set_cookie_header=(v)
|
226
|
+
set_header SET_COOKIE, v
|
227
|
+
end
|
228
|
+
|
229
|
+
def cache_control
|
230
|
+
get_header CACHE_CONTROL
|
231
|
+
end
|
232
|
+
|
233
|
+
def cache_control=(v)
|
234
|
+
set_header CACHE_CONTROL, v
|
235
|
+
end
|
236
|
+
|
237
|
+
# Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
|
238
|
+
def do_not_cache!
|
239
|
+
set_header CACHE_CONTROL, "no-cache, must-revalidate"
|
240
|
+
set_header EXPIRES, Time.now.httpdate
|
241
|
+
end
|
242
|
+
|
243
|
+
# Specify that the content should be cached.
|
244
|
+
# @param duration [Integer] The number of seconds until the cache expires.
|
245
|
+
# @option directive [String] The cache control directive, one of "public", "private", "no-cache" or "no-store".
|
246
|
+
def cache!(duration = 3600, directive: "public")
|
247
|
+
unless headers[CACHE_CONTROL] =~ /no-cache/
|
248
|
+
set_header CACHE_CONTROL, "#{directive}, max-age=#{duration}"
|
249
|
+
set_header EXPIRES, (Time.now + duration).httpdate
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def etag
|
254
|
+
get_header ETAG
|
255
|
+
end
|
256
|
+
|
257
|
+
def etag=(v)
|
258
|
+
set_header ETAG, v
|
259
|
+
end
|
260
|
+
|
261
|
+
protected
|
262
|
+
|
263
|
+
def buffered_body!
|
264
|
+
return if @buffered
|
265
|
+
|
266
|
+
if @body.is_a?(Array)
|
267
|
+
# The user supplied body was an array:
|
268
|
+
@body = @body.compact
|
269
|
+
@body.each do |part|
|
270
|
+
@length += part.to_s.bytesize
|
271
|
+
end
|
272
|
+
else
|
273
|
+
# Turn the user supplied body into a buffered array:
|
274
|
+
body = @body
|
275
|
+
@body = Array.new
|
276
|
+
|
277
|
+
body.each do |part|
|
278
|
+
@writer.call(part.to_s)
|
279
|
+
end
|
280
|
+
|
281
|
+
body.close if body.respond_to?(:close)
|
282
|
+
end
|
283
|
+
|
284
|
+
@buffered = true
|
285
|
+
end
|
286
|
+
|
287
|
+
def append(chunk)
|
288
|
+
@body << chunk
|
289
|
+
|
290
|
+
unless chunked?
|
291
|
+
@length += chunk.bytesize
|
292
|
+
set_header(CONTENT_LENGTH, @length.to_s)
|
293
|
+
end
|
294
|
+
|
295
|
+
return chunk
|
157
296
|
end
|
158
297
|
end
|
159
298
|
|
160
299
|
include Helpers
|
300
|
+
|
301
|
+
class Raw
|
302
|
+
include Helpers
|
303
|
+
|
304
|
+
attr_reader :headers
|
305
|
+
attr_accessor :status
|
306
|
+
|
307
|
+
def initialize(status, headers)
|
308
|
+
@status = status
|
309
|
+
@headers = headers
|
310
|
+
end
|
311
|
+
|
312
|
+
def has_header?(key); headers.key? key; end
|
313
|
+
def get_header(key); headers[key]; end
|
314
|
+
def set_header(key, v); headers[key] = v; end
|
315
|
+
def delete_header(key); headers.delete key; end
|
316
|
+
end
|
161
317
|
end
|
162
318
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'tempfile'
|
3
|
-
require 'rack/utils'
|
4
5
|
|
5
6
|
module Rack
|
6
7
|
# Class which can make any IO object rewindable, including non-rewindable ones. It does
|
@@ -40,7 +41,7 @@ module Rack
|
|
40
41
|
end
|
41
42
|
|
42
43
|
# Closes this RewindableInput object without closing the originally
|
43
|
-
# wrapped IO
|
44
|
+
# wrapped IO object. Cleans up any temporary resources that this RewindableInput
|
44
45
|
# has created.
|
45
46
|
#
|
46
47
|
# This method may be called multiple times. It does nothing on subsequent calls.
|
@@ -57,15 +58,6 @@ module Rack
|
|
57
58
|
|
58
59
|
private
|
59
60
|
|
60
|
-
# Ruby's Tempfile class has a bug. Subclass it and fix it.
|
61
|
-
class Tempfile < ::Tempfile
|
62
|
-
def _close
|
63
|
-
@tmpfile.close if @tmpfile
|
64
|
-
@data[1] = nil if @data
|
65
|
-
@tmpfile = nil
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
61
|
def make_rewindable
|
70
62
|
# Buffer all data into a tempfile. Since this tempfile is private to this
|
71
63
|
# RewindableInput object, we chmod it so that nobody else can read or write
|
@@ -77,18 +69,16 @@ module Rack
|
|
77
69
|
@rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
|
78
70
|
@rewindable_io.binmode
|
79
71
|
if filesystem_has_posix_semantics?
|
80
|
-
# Use ::File.unlink as 1.9.1 Tempfile has a bug where unlink closes the file!
|
81
|
-
::File.unlink @rewindable_io.path
|
82
72
|
raise 'Unlink failed. IO closed.' if @rewindable_io.closed?
|
83
73
|
@unlinked = true
|
84
74
|
end
|
85
75
|
|
86
|
-
buffer = ""
|
76
|
+
buffer = "".dup
|
87
77
|
while @io.read(1024 * 4, buffer)
|
88
78
|
entire_buffer_written_out = false
|
89
79
|
while !entire_buffer_written_out
|
90
80
|
written = @rewindable_io.write(buffer)
|
91
|
-
entire_buffer_written_out = written ==
|
81
|
+
entire_buffer_written_out = written == buffer.bytesize
|
92
82
|
if !entire_buffer_written_out
|
93
83
|
buffer.slice!(0 .. written - 1)
|
94
84
|
end
|
data/lib/rack/runtime.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
# Sets an "X-Runtime" response header, indicating the response
|
3
5
|
# time of the request, in seconds
|
@@ -6,35 +8,27 @@ module Rack
|
|
6
8
|
# time, or before all the other middlewares to include time for them,
|
7
9
|
# too.
|
8
10
|
class Runtime
|
11
|
+
FORMAT_STRING = "%0.6f" # :nodoc:
|
12
|
+
HEADER_NAME = "X-Runtime" # :nodoc:
|
13
|
+
|
9
14
|
def initialize(app, name = nil)
|
10
15
|
@app = app
|
11
|
-
@header_name =
|
12
|
-
@header_name
|
16
|
+
@header_name = HEADER_NAME
|
17
|
+
@header_name += "-#{name}" if name
|
13
18
|
end
|
14
19
|
|
15
|
-
FORMAT_STRING = "%0.6f"
|
16
20
|
def call(env)
|
17
|
-
start_time = clock_time
|
21
|
+
start_time = Utils.clock_time
|
18
22
|
status, headers, body = @app.call(env)
|
19
|
-
|
23
|
+
headers = Utils::HeaderHash[headers]
|
20
24
|
|
21
|
-
|
25
|
+
request_time = Utils.clock_time - start_time
|
26
|
+
|
27
|
+
unless headers.key?(@header_name)
|
22
28
|
headers[@header_name] = FORMAT_STRING % request_time
|
23
29
|
end
|
24
30
|
|
25
31
|
[status, headers, body]
|
26
32
|
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
|
39
33
|
end
|
40
34
|
end
|
data/lib/rack/sendfile.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'rack/body_proxy'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Rack
|
5
4
|
|
@@ -14,7 +13,7 @@ module Rack
|
|
14
13
|
#
|
15
14
|
# In order to take advantage of this middleware, the response body must
|
16
15
|
# respond to +to_path+ and the request must include an X-Sendfile-Type
|
17
|
-
# header. Rack::
|
16
|
+
# header. Rack::Files and other components implement +to_path+ so there's
|
18
17
|
# rarely anything you need to do in your application. The X-Sendfile-Type
|
19
18
|
# header is typically set in your web servers configuration. The following
|
20
19
|
# sections attempt to document
|
@@ -53,7 +52,7 @@ module Rack
|
|
53
52
|
# that it maps to. The middleware performs a simple substitution on the
|
54
53
|
# resulting path.
|
55
54
|
#
|
56
|
-
# See Also:
|
55
|
+
# See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
|
57
56
|
#
|
58
57
|
# === lighttpd
|
59
58
|
#
|
@@ -99,9 +98,7 @@ module Rack
|
|
99
98
|
# will be matched with case indifference.
|
100
99
|
|
101
100
|
class Sendfile
|
102
|
-
|
103
|
-
|
104
|
-
def initialize(app, variation=nil, mappings=[])
|
101
|
+
def initialize(app, variation = nil, mappings = [])
|
105
102
|
@app = app
|
106
103
|
@variation = variation
|
107
104
|
@mappings = mappings.map do |internal, external|
|
@@ -114,19 +111,20 @@ module Rack
|
|
114
111
|
if body.respond_to?(:to_path)
|
115
112
|
case type = variation(env)
|
116
113
|
when 'X-Accel-Redirect'
|
117
|
-
path =
|
114
|
+
path = ::File.expand_path(body.to_path)
|
118
115
|
if url = map_accel_path(env, path)
|
119
116
|
headers[CONTENT_LENGTH] = '0'
|
120
|
-
|
117
|
+
# '?' must be percent-encoded because it is not query string but a part of path
|
118
|
+
headers[type] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
|
121
119
|
obody = body
|
122
120
|
body = Rack::BodyProxy.new([]) do
|
123
121
|
obody.close if obody.respond_to?(:close)
|
124
122
|
end
|
125
123
|
else
|
126
|
-
env[
|
124
|
+
env[RACK_ERRORS].puts "X-Accel-Mapping header missing"
|
127
125
|
end
|
128
126
|
when 'X-Sendfile', 'X-Lighttpd-Send-File'
|
129
|
-
path =
|
127
|
+
path = ::File.expand_path(body.to_path)
|
130
128
|
headers[CONTENT_LENGTH] = '0'
|
131
129
|
headers[type] = path
|
132
130
|
obody = body
|
@@ -135,7 +133,7 @@ module Rack
|
|
135
133
|
end
|
136
134
|
when '', nil
|
137
135
|
else
|
138
|
-
env[
|
136
|
+
env[RACK_ERRORS].puts "Unknown x-sendfile variation: '#{type}'.\n"
|
139
137
|
end
|
140
138
|
end
|
141
139
|
[status, headers, body]
|
@@ -149,11 +147,15 @@ module Rack
|
|
149
147
|
end
|
150
148
|
|
151
149
|
def map_accel_path(env, path)
|
152
|
-
if mapping = @mappings.find { |internal,_| internal =~ path }
|
150
|
+
if mapping = @mappings.find { |internal, _| internal =~ path }
|
153
151
|
path.sub(*mapping)
|
154
152
|
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
|
155
|
-
|
156
|
-
|
153
|
+
mapping.split(',').map(&:strip).each do |m|
|
154
|
+
internal, external = m.split('=', 2).map(&:strip)
|
155
|
+
new_path = path.sub(/^#{internal}/i, external)
|
156
|
+
return new_path unless path == new_path
|
157
|
+
end
|
158
|
+
path
|
157
159
|
end
|
158
160
|
end
|
159
161
|
end
|