actionpack 4.2.10 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +553 -401
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/base.rb +28 -38
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
- data/lib/abstract_controller/caching.rb +62 -0
- data/lib/abstract_controller/callbacks.rb +52 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/error.rb +4 -0
- data/lib/abstract_controller/helpers.rb +4 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +28 -18
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/abstract_controller.rb +6 -2
- data/lib/action_controller/api/api_rendering.rb +14 -0
- data/lib/action_controller/api.rb +147 -0
- data/lib/action_controller/base.rb +10 -13
- data/lib/action_controller/caching.rb +13 -58
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +3 -10
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +106 -34
- data/lib/action_controller/metal/cookies.rb +1 -3
- data/lib/action_controller/metal/data_streaming.rb +11 -32
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +10 -10
- data/lib/action_controller/metal/head.rb +14 -8
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +44 -35
- data/lib/action_controller/metal/implicit_render.rb +61 -6
- data/lib/action_controller/metal/instrumentation.rb +5 -5
- data/lib/action_controller/metal/live.rb +66 -88
- data/lib/action_controller/metal/mime_responds.rb +27 -42
- data/lib/action_controller/metal/params_wrapper.rb +8 -8
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +85 -40
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
- data/lib/action_controller/metal/rescue.rb +3 -12
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +293 -90
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/metal.rb +88 -63
- data/lib/action_controller/renderer.rb +111 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +288 -368
- data/lib/action_controller.rb +12 -9
- data/lib/action_dispatch/http/cache.rb +73 -34
- data/lib/action_dispatch/http/filter_parameters.rb +15 -11
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +44 -13
- data/lib/action_dispatch/http/mime_negotiation.rb +41 -23
- data/lib/action_dispatch/http/mime_type.rb +126 -90
- data/lib/action_dispatch/http/mime_types.rb +3 -4
- data/lib/action_dispatch/http/parameter_filter.rb +18 -8
- data/lib/action_dispatch/http/parameters.rb +54 -41
- data/lib/action_dispatch/http/request.rb +149 -82
- data/lib/action_dispatch/http/response.rb +206 -102
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +39 -28
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser_extras.rb +4 -0
- data/lib/action_dispatch/journey/path/pattern.rb +38 -42
- data/lib/action_dispatch/journey/route.rb +74 -19
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/router.rb +5 -9
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +86 -43
- data/lib/action_dispatch/middleware/callbacks.rb +10 -1
- data/lib/action_dispatch/middleware/cookies.rb +189 -135
- data/lib/action_dispatch/middleware/debug_exceptions.rb +124 -49
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
- data/lib/action_dispatch/middleware/executor.rb +19 -0
- data/lib/action_dispatch/middleware/flash.rb +66 -45
- data/lib/action_dispatch/middleware/params_parser.rb +32 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +14 -58
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +115 -36
- data/lib/action_dispatch/middleware/stack.rb +44 -40
- data/lib/action_dispatch/middleware/static.rb +51 -35
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
- data/lib/action_dispatch/railtie.rb +2 -2
- data/lib/action_dispatch/request/session.rb +69 -33
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing/inspector.rb +32 -43
- data/lib/action_dispatch/routing/mapper.rb +491 -338
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +145 -238
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/routing.rb +17 -13
- data/lib/action_dispatch/testing/assertion_response.rb +45 -0
- data/lib/action_dispatch/testing/assertions/response.rb +38 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +11 -10
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +368 -97
- data/lib/action_dispatch/testing/test_process.rb +5 -6
- data/lib/action_dispatch/testing/test_request.rb +22 -31
- data/lib/action_dispatch/testing/test_response.rb +7 -4
- data/lib/action_dispatch.rb +3 -1
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +30 -34
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
- /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
-
require 'active_support/core_ext/string/filters'
|
3
|
-
require 'active_support/deprecation'
|
4
2
|
require 'action_dispatch/http/filter_redirect'
|
3
|
+
require 'action_dispatch/http/cache'
|
5
4
|
require 'monitor'
|
6
5
|
|
7
6
|
module ActionDispatch # :nodoc:
|
@@ -34,46 +33,62 @@ module ActionDispatch # :nodoc:
|
|
34
33
|
# end
|
35
34
|
# end
|
36
35
|
class Response
|
36
|
+
class Header < DelegateClass(Hash) # :nodoc:
|
37
|
+
def initialize(response, header)
|
38
|
+
@response = response
|
39
|
+
super(header)
|
40
|
+
end
|
41
|
+
|
42
|
+
def []=(k,v)
|
43
|
+
if @response.sending? || @response.sent?
|
44
|
+
raise ActionDispatch::IllegalStateError, 'header already sent'
|
45
|
+
end
|
46
|
+
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def merge(other)
|
51
|
+
self.class.new @response, __getobj__.merge(other)
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_hash
|
55
|
+
__getobj__.dup
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
37
59
|
# The request that the response is responding to.
|
38
60
|
attr_accessor :request
|
39
61
|
|
40
62
|
# The HTTP status code.
|
41
63
|
attr_reader :status
|
42
64
|
|
43
|
-
|
65
|
+
# Get headers for this response.
|
66
|
+
attr_reader :header
|
44
67
|
|
45
|
-
# Get and set headers for this response.
|
46
|
-
attr_accessor :header
|
47
|
-
|
48
|
-
alias_method :headers=, :header=
|
49
68
|
alias_method :headers, :header
|
50
69
|
|
51
70
|
delegate :[], :[]=, :to => :@header
|
52
|
-
delegate :each, :to => :@stream
|
53
|
-
|
54
|
-
# Sets the HTTP response's content MIME type. For example, in the controller
|
55
|
-
# you could write this:
|
56
|
-
#
|
57
|
-
# response.content_type = "text/plain"
|
58
|
-
#
|
59
|
-
# If a character set has been defined for this response (see charset=) then
|
60
|
-
# the character set information will also be included in the content type
|
61
|
-
# information.
|
62
|
-
attr_reader :content_type
|
63
71
|
|
64
|
-
|
65
|
-
|
66
|
-
|
72
|
+
def each(&block)
|
73
|
+
sending!
|
74
|
+
x = @stream.each(&block)
|
75
|
+
sent!
|
76
|
+
x
|
77
|
+
end
|
67
78
|
|
68
79
|
CONTENT_TYPE = "Content-Type".freeze
|
69
80
|
SET_COOKIE = "Set-Cookie".freeze
|
70
81
|
LOCATION = "Location".freeze
|
71
|
-
NO_CONTENT_CODES = [204, 304]
|
82
|
+
NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
|
72
83
|
|
73
84
|
cattr_accessor(:default_charset) { "utf-8" }
|
74
85
|
cattr_accessor(:default_headers)
|
75
86
|
|
76
87
|
include Rack::Response::Helpers
|
88
|
+
# Aliasing these off because AD::Http::Cache::Response defines them
|
89
|
+
alias :_cache_control :cache_control
|
90
|
+
alias :_cache_control= :cache_control=
|
91
|
+
|
77
92
|
include ActionDispatch::Http::FilterRedirect
|
78
93
|
include ActionDispatch::Http::Cache::Response
|
79
94
|
include MonitorMixin
|
@@ -83,20 +98,33 @@ module ActionDispatch # :nodoc:
|
|
83
98
|
@response = response
|
84
99
|
@buf = buf
|
85
100
|
@closed = false
|
101
|
+
@str_body = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def body
|
105
|
+
@str_body ||= begin
|
106
|
+
buf = ''
|
107
|
+
each { |chunk| buf << chunk }
|
108
|
+
buf
|
109
|
+
end
|
86
110
|
end
|
87
111
|
|
88
112
|
def write(string)
|
89
113
|
raise IOError, "closed stream" if closed?
|
90
114
|
|
115
|
+
@str_body = nil
|
91
116
|
@response.commit!
|
92
117
|
@buf.push string
|
93
118
|
end
|
94
119
|
|
95
120
|
def each(&block)
|
96
|
-
@
|
97
|
-
|
98
|
-
|
99
|
-
|
121
|
+
if @str_body
|
122
|
+
return enum_for(:each) unless block_given?
|
123
|
+
|
124
|
+
yield @str_body
|
125
|
+
else
|
126
|
+
each_chunk(&block)
|
127
|
+
end
|
100
128
|
end
|
101
129
|
|
102
130
|
def abort
|
@@ -110,39 +138,48 @@ module ActionDispatch # :nodoc:
|
|
110
138
|
def closed?
|
111
139
|
@closed
|
112
140
|
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def each_chunk(&block)
|
145
|
+
@buf.each(&block) # extract into own method
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
|
150
|
+
header = merge_default_headers(header, default_headers)
|
151
|
+
new status, header, body
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.merge_default_headers(original, default)
|
155
|
+
default.respond_to?(:merge) ? default.merge(original) : original
|
113
156
|
end
|
114
157
|
|
115
158
|
# The underlying body, as a streamable object.
|
116
159
|
attr_reader :stream
|
117
160
|
|
118
|
-
def initialize(status = 200, header = {}, body = []
|
161
|
+
def initialize(status = 200, header = {}, body = [])
|
119
162
|
super()
|
120
163
|
|
121
|
-
|
122
|
-
header = merge_default_headers(header, default_headers)
|
164
|
+
@header = Header.new(self, header)
|
123
165
|
|
124
|
-
self.body, self.
|
166
|
+
self.body, self.status = body, status
|
125
167
|
|
126
|
-
@sending_file = false
|
127
|
-
@blank = false
|
128
168
|
@cv = new_cond
|
129
169
|
@committed = false
|
130
170
|
@sending = false
|
131
171
|
@sent = false
|
132
|
-
@content_type = nil
|
133
|
-
@charset = nil
|
134
|
-
|
135
|
-
if content_type = self[CONTENT_TYPE]
|
136
|
-
type, charset = content_type.split(/;\s*charset=/)
|
137
|
-
@content_type = Mime::Type.lookup(type)
|
138
|
-
@charset = charset || self.class.default_charset
|
139
|
-
end
|
140
172
|
|
141
173
|
prepare_cache_control!
|
142
174
|
|
143
175
|
yield self if block_given?
|
144
176
|
end
|
145
177
|
|
178
|
+
def has_header?(key); headers.key? key; end
|
179
|
+
def get_header(key); headers[key]; end
|
180
|
+
def set_header(key, v); headers[key] = v; end
|
181
|
+
def delete_header(key); headers.delete key; end
|
182
|
+
|
146
183
|
def await_commit
|
147
184
|
synchronize do
|
148
185
|
@cv.wait_until { @committed }
|
@@ -187,7 +224,49 @@ module ActionDispatch # :nodoc:
|
|
187
224
|
|
188
225
|
# Sets the HTTP content type.
|
189
226
|
def content_type=(content_type)
|
190
|
-
|
227
|
+
header_info = parse_content_type
|
228
|
+
set_content_type content_type.to_s, header_info.charset || self.class.default_charset
|
229
|
+
end
|
230
|
+
|
231
|
+
# Sets the HTTP response's content MIME type. For example, in the controller
|
232
|
+
# you could write this:
|
233
|
+
#
|
234
|
+
# response.content_type = "text/plain"
|
235
|
+
#
|
236
|
+
# If a character set has been defined for this response (see charset=) then
|
237
|
+
# the character set information will also be included in the content type
|
238
|
+
# information.
|
239
|
+
|
240
|
+
def content_type
|
241
|
+
parse_content_type.mime_type
|
242
|
+
end
|
243
|
+
|
244
|
+
def sending_file=(v)
|
245
|
+
if true == v
|
246
|
+
self.charset = false
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Sets the HTTP character set. In case of nil parameter
|
251
|
+
# it sets the charset to utf-8.
|
252
|
+
#
|
253
|
+
# response.charset = 'utf-16' # => 'utf-16'
|
254
|
+
# response.charset = nil # => 'utf-8'
|
255
|
+
def charset=(charset)
|
256
|
+
header_info = parse_content_type
|
257
|
+
if false == charset
|
258
|
+
set_header CONTENT_TYPE, header_info.mime_type
|
259
|
+
else
|
260
|
+
content_type = header_info.mime_type
|
261
|
+
set_content_type content_type, charset || self.class.default_charset
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# The charset of the response. HTML wants to know the encoding of the
|
266
|
+
# content you're giving them, so we need to send that along.
|
267
|
+
def charset
|
268
|
+
header_info = parse_content_type
|
269
|
+
header_info.charset || self.class.default_charset
|
191
270
|
end
|
192
271
|
|
193
272
|
# The response code of the request.
|
@@ -216,17 +295,15 @@ module ActionDispatch # :nodoc:
|
|
216
295
|
# Returns the content of the response as a string. This contains the contents
|
217
296
|
# of any calls to <tt>render</tt>.
|
218
297
|
def body
|
219
|
-
|
220
|
-
each { |part| strings << part.to_s }
|
221
|
-
strings.join
|
298
|
+
@stream.body
|
222
299
|
end
|
223
300
|
|
224
|
-
|
301
|
+
def write(string)
|
302
|
+
@stream.write string
|
303
|
+
end
|
225
304
|
|
226
305
|
# Allows you to manually set or override the response body.
|
227
306
|
def body=(body)
|
228
|
-
@blank = true if body == EMPTY
|
229
|
-
|
230
307
|
if body.respond_to?(:to_path)
|
231
308
|
@stream = body
|
232
309
|
else
|
@@ -236,31 +313,49 @@ module ActionDispatch # :nodoc:
|
|
236
313
|
end
|
237
314
|
end
|
238
315
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
316
|
+
# Avoid having to pass an open file handle as the response body.
|
317
|
+
# Rack::Sendfile will usually intercept the response and uses
|
318
|
+
# the path directly, so there is no reason to open the file.
|
319
|
+
class FileBody #:nodoc:
|
320
|
+
attr_reader :to_path
|
321
|
+
|
322
|
+
def initialize(path)
|
323
|
+
@to_path = path
|
324
|
+
end
|
244
325
|
|
245
|
-
|
246
|
-
|
326
|
+
def body
|
327
|
+
File.binread(to_path)
|
328
|
+
end
|
329
|
+
|
330
|
+
# Stream the file's contents if Rack::Sendfile isn't present.
|
331
|
+
def each
|
332
|
+
File.open(to_path, 'rb') do |file|
|
333
|
+
while chunk = file.read(16384)
|
334
|
+
yield chunk
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
247
338
|
end
|
248
339
|
|
249
|
-
|
250
|
-
|
340
|
+
# Send the file stored at +path+ as the response body.
|
341
|
+
def send_file(path)
|
342
|
+
commit!
|
343
|
+
@stream = FileBody.new(path)
|
251
344
|
end
|
252
345
|
|
253
|
-
|
254
|
-
|
255
|
-
headers[LOCATION]
|
346
|
+
def reset_body!
|
347
|
+
@stream = build_buffer(self, [])
|
256
348
|
end
|
257
|
-
alias_method :redirect_url, :location
|
258
349
|
|
259
|
-
|
260
|
-
|
261
|
-
|
350
|
+
def body_parts
|
351
|
+
parts = []
|
352
|
+
@stream.each { |x| parts << x }
|
353
|
+
parts
|
262
354
|
end
|
263
355
|
|
356
|
+
# The location header we'll be responding with.
|
357
|
+
alias_method :redirect_url, :location
|
358
|
+
|
264
359
|
def close
|
265
360
|
stream.close if stream.respond_to?(:close)
|
266
361
|
end
|
@@ -277,34 +372,21 @@ module ActionDispatch # :nodoc:
|
|
277
372
|
end
|
278
373
|
|
279
374
|
# Turns the Response into a Rack-compatible array of the status, headers,
|
280
|
-
# and body. Allows
|
375
|
+
# and body. Allows explicit splatting:
|
281
376
|
#
|
282
377
|
# status, headers, body = *response
|
283
378
|
def to_a
|
379
|
+
commit!
|
284
380
|
rack_response @status, @header.to_hash
|
285
381
|
end
|
286
382
|
alias prepare! to_a
|
287
383
|
|
288
|
-
# Be super clear that a response object is not an Array. Defining this
|
289
|
-
# would make implicit splatting work, but it also makes adding responses
|
290
|
-
# as arrays work, and "flattening" responses, cascading to the rack body!
|
291
|
-
# Not sensible behavior.
|
292
|
-
def to_ary
|
293
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
294
|
-
`ActionDispatch::Response#to_ary` no longer performs implicit conversion
|
295
|
-
to an array. Please use `response.to_a` instead, or a splat like `status,
|
296
|
-
headers, body = *response`.
|
297
|
-
MSG
|
298
|
-
|
299
|
-
to_a
|
300
|
-
end
|
301
|
-
|
302
384
|
# Returns the response cookies, converted to a Hash of (name => value) pairs
|
303
385
|
#
|
304
386
|
# assert_equal 'AuthorOfNewPage', r.cookies['author']
|
305
387
|
def cookies
|
306
388
|
cookies = {}
|
307
|
-
if header =
|
389
|
+
if header = get_header(SET_COOKIE)
|
308
390
|
header = header.split("\n") if header.respond_to?(:to_str)
|
309
391
|
header.each do |cookie|
|
310
392
|
if pair = cookie.split(';').first
|
@@ -318,14 +400,43 @@ module ActionDispatch # :nodoc:
|
|
318
400
|
|
319
401
|
private
|
320
402
|
|
403
|
+
ContentTypeHeader = Struct.new :mime_type, :charset
|
404
|
+
NullContentTypeHeader = ContentTypeHeader.new nil, nil
|
405
|
+
|
406
|
+
def parse_content_type
|
407
|
+
content_type = get_header CONTENT_TYPE
|
408
|
+
if content_type
|
409
|
+
type, charset = content_type.split(/;\s*charset=/)
|
410
|
+
type = nil if type.empty?
|
411
|
+
ContentTypeHeader.new(type, charset)
|
412
|
+
else
|
413
|
+
NullContentTypeHeader
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def set_content_type(content_type, charset)
|
418
|
+
type = (content_type || '').dup
|
419
|
+
type << "; charset=#{charset}" if charset
|
420
|
+
set_header CONTENT_TYPE, type
|
421
|
+
end
|
422
|
+
|
321
423
|
def before_committed
|
424
|
+
return if committed?
|
425
|
+
assign_default_content_type_and_charset!
|
426
|
+
handle_conditional_get!
|
427
|
+
handle_no_content!
|
322
428
|
end
|
323
429
|
|
324
430
|
def before_sending
|
325
|
-
|
431
|
+
# Normally we've already committed by now, but it's possible
|
432
|
+
# (e.g., if the controller action tries to read back its own
|
433
|
+
# response) to get here before that. In that case, we must force
|
434
|
+
# an "early" commit: we're about to freeze the headers, so this is
|
435
|
+
# our last chance.
|
436
|
+
commit! unless committed?
|
326
437
|
|
327
|
-
|
328
|
-
|
438
|
+
headers.freeze
|
439
|
+
request.commit_cookie_jar! unless committed?
|
329
440
|
end
|
330
441
|
|
331
442
|
def build_buffer(response, body)
|
@@ -336,20 +447,12 @@ module ActionDispatch # :nodoc:
|
|
336
447
|
body.respond_to?(:each) ? body : [body]
|
337
448
|
end
|
338
449
|
|
339
|
-
def assign_default_content_type_and_charset!
|
340
|
-
return if
|
341
|
-
|
342
|
-
@content_type ||= Mime::HTML
|
343
|
-
@charset ||= self.class.default_charset unless @charset == false
|
450
|
+
def assign_default_content_type_and_charset!
|
451
|
+
return if content_type
|
344
452
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
headers[CONTENT_TYPE] = type
|
349
|
-
end
|
350
|
-
|
351
|
-
def append_charset?
|
352
|
-
!@sending_file && @charset != false
|
453
|
+
ct = parse_content_type
|
454
|
+
set_content_type(ct.mime_type || Mime[:html].to_s,
|
455
|
+
ct.charset || self.class.default_charset)
|
353
456
|
end
|
354
457
|
|
355
458
|
class RackBody
|
@@ -388,14 +491,15 @@ module ActionDispatch # :nodoc:
|
|
388
491
|
end
|
389
492
|
end
|
390
493
|
|
391
|
-
def
|
392
|
-
assign_default_content_type_and_charset!(header)
|
393
|
-
handle_conditional_get!
|
394
|
-
|
395
|
-
header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
|
396
|
-
|
494
|
+
def handle_no_content!
|
397
495
|
if NO_CONTENT_CODES.include?(@status)
|
398
|
-
header.delete CONTENT_TYPE
|
496
|
+
@header.delete CONTENT_TYPE
|
497
|
+
@header.delete 'Content-Length'
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
def rack_response(status, header)
|
502
|
+
if NO_CONTENT_CODES.include?(status)
|
399
503
|
[status, header, []]
|
400
504
|
else
|
401
505
|
[status, header, RackBody.new(self)]
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
-
require 'active_support/core_ext/hash/slice'
|
3
2
|
|
4
3
|
module ActionDispatch
|
5
4
|
module Http
|
@@ -12,10 +11,22 @@ module ActionDispatch
|
|
12
11
|
self.tld_length = 1
|
13
12
|
|
14
13
|
class << self
|
14
|
+
# Returns the domain part of a host given the domain level.
|
15
|
+
#
|
16
|
+
# # Top-level domain example
|
17
|
+
# extract_domain('www.example.com', 1) # => "example.com"
|
18
|
+
# # Second-level domain example
|
19
|
+
# extract_domain('dev.www.example.co.uk', 2) # => "example.co.uk"
|
15
20
|
def extract_domain(host, tld_length)
|
16
21
|
extract_domain_from(host, tld_length) if named_host?(host)
|
17
22
|
end
|
18
23
|
|
24
|
+
# Returns the subdomains of a host as an Array given the domain level.
|
25
|
+
#
|
26
|
+
# # Top-level domain example
|
27
|
+
# extract_subdomains('www.example.com', 1) # => ["www"]
|
28
|
+
# # Second-level domain example
|
29
|
+
# extract_subdomains('dev.www.example.co.uk', 2) # => ["dev", "www"]
|
19
30
|
def extract_subdomains(host, tld_length)
|
20
31
|
if named_host?(host)
|
21
32
|
extract_subdomains_from(host, tld_length)
|
@@ -24,6 +35,12 @@ module ActionDispatch
|
|
24
35
|
end
|
25
36
|
end
|
26
37
|
|
38
|
+
# Returns the subdomains of a host as a String given the domain level.
|
39
|
+
#
|
40
|
+
# # Top-level domain example
|
41
|
+
# extract_subdomain('www.example.com', 1) # => "www"
|
42
|
+
# # Second-level domain example
|
43
|
+
# extract_subdomain('dev.www.example.co.uk', 2) # => "dev.www"
|
27
44
|
def extract_subdomain(host, tld_length)
|
28
45
|
extract_subdomains(host, tld_length).join('.')
|
29
46
|
end
|
@@ -49,7 +66,7 @@ module ActionDispatch
|
|
49
66
|
end
|
50
67
|
|
51
68
|
def path_for(options)
|
52
|
-
path = options[:script_name].to_s.chomp("/")
|
69
|
+
path = options[:script_name].to_s.chomp("/".freeze)
|
53
70
|
path << options[:path] if options.key?(:path)
|
54
71
|
|
55
72
|
add_trailing_slash(path) if options[:trailing_slash]
|
@@ -64,7 +81,8 @@ module ActionDispatch
|
|
64
81
|
def add_params(path, params)
|
65
82
|
params = { params: params } unless params.is_a?(Hash)
|
66
83
|
params.reject! { |_,v| v.to_param.nil? }
|
67
|
-
|
84
|
+
query = params.to_query
|
85
|
+
path << "?#{query}" unless query.empty?
|
68
86
|
end
|
69
87
|
|
70
88
|
def add_anchor(path, anchor)
|
@@ -166,43 +184,97 @@ module ActionDispatch
|
|
166
184
|
end
|
167
185
|
end
|
168
186
|
|
169
|
-
def initialize
|
187
|
+
def initialize
|
170
188
|
super
|
171
189
|
@protocol = nil
|
172
190
|
@port = nil
|
173
191
|
end
|
174
192
|
|
175
193
|
# Returns the complete URL used for this request.
|
194
|
+
#
|
195
|
+
# class Request < Rack::Request
|
196
|
+
# include ActionDispatch::Http::URL
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# req = Request.new 'HTTP_HOST' => 'example.com'
|
200
|
+
# req.url # => "http://example.com"
|
176
201
|
def url
|
177
202
|
protocol + host_with_port + fullpath
|
178
203
|
end
|
179
204
|
|
180
205
|
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
|
206
|
+
#
|
207
|
+
# class Request < Rack::Request
|
208
|
+
# include ActionDispatch::Http::URL
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# req = Request.new 'HTTP_HOST' => 'example.com'
|
212
|
+
# req.protocol # => "http://"
|
213
|
+
#
|
214
|
+
# req = Request.new 'HTTP_HOST' => 'example.com', 'HTTPS' => 'on'
|
215
|
+
# req.protocol # => "https://"
|
181
216
|
def protocol
|
182
217
|
@protocol ||= ssl? ? 'https://' : 'http://'
|
183
218
|
end
|
184
219
|
|
185
220
|
# Returns the \host for this request, such as "example.com".
|
221
|
+
#
|
222
|
+
# class Request < Rack::Request
|
223
|
+
# include ActionDispatch::Http::URL
|
224
|
+
# end
|
225
|
+
#
|
226
|
+
# req = Request.new 'HTTP_HOST' => 'example.com'
|
227
|
+
# req.raw_host_with_port # => "example.com"
|
228
|
+
#
|
229
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
230
|
+
# req.raw_host_with_port # => "example.com:8080"
|
186
231
|
def raw_host_with_port
|
187
|
-
if forwarded =
|
232
|
+
if forwarded = x_forwarded_host.presence
|
188
233
|
forwarded.split(/,\s?/).last
|
189
234
|
else
|
190
|
-
|
235
|
+
get_header('HTTP_HOST') || "#{server_name || server_addr}:#{get_header('SERVER_PORT')}"
|
191
236
|
end
|
192
237
|
end
|
193
238
|
|
194
239
|
# Returns the host for this request, such as example.com.
|
240
|
+
#
|
241
|
+
# class Request < Rack::Request
|
242
|
+
# include ActionDispatch::Http::URL
|
243
|
+
# end
|
244
|
+
#
|
245
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
246
|
+
# req.host # => "example.com"
|
195
247
|
def host
|
196
|
-
raw_host_with_port.sub(/:\d+$/, '')
|
248
|
+
raw_host_with_port.sub(/:\d+$/, ''.freeze)
|
197
249
|
end
|
198
250
|
|
199
251
|
# Returns a \host:\port string for this request, such as "example.com" or
|
200
252
|
# "example.com:8080".
|
253
|
+
#
|
254
|
+
# class Request < Rack::Request
|
255
|
+
# include ActionDispatch::Http::URL
|
256
|
+
# end
|
257
|
+
#
|
258
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:80'
|
259
|
+
# req.host_with_port # => "example.com"
|
260
|
+
#
|
261
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
262
|
+
# req.host_with_port # => "example.com:8080"
|
201
263
|
def host_with_port
|
202
264
|
"#{host}#{port_string}"
|
203
265
|
end
|
204
266
|
|
205
267
|
# Returns the port number of this request as an integer.
|
268
|
+
#
|
269
|
+
# class Request < Rack::Request
|
270
|
+
# include ActionDispatch::Http::URL
|
271
|
+
# end
|
272
|
+
#
|
273
|
+
# req = Request.new 'HTTP_HOST' => 'example.com'
|
274
|
+
# req.port # => 80
|
275
|
+
#
|
276
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
277
|
+
# req.port # => 8080
|
206
278
|
def port
|
207
279
|
@port ||= begin
|
208
280
|
if raw_host_with_port =~ /:(\d+)$/
|
@@ -214,6 +286,13 @@ module ActionDispatch
|
|
214
286
|
end
|
215
287
|
|
216
288
|
# Returns the standard \port number for this request's protocol.
|
289
|
+
#
|
290
|
+
# class Request < Rack::Request
|
291
|
+
# include ActionDispatch::Http::URL
|
292
|
+
# end
|
293
|
+
#
|
294
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
295
|
+
# req.standard_port # => 80
|
217
296
|
def standard_port
|
218
297
|
case protocol
|
219
298
|
when 'https://' then 443
|
@@ -222,24 +301,54 @@ module ActionDispatch
|
|
222
301
|
end
|
223
302
|
|
224
303
|
# Returns whether this request is using the standard port
|
304
|
+
#
|
305
|
+
# class Request < Rack::Request
|
306
|
+
# include ActionDispatch::Http::URL
|
307
|
+
# end
|
308
|
+
#
|
309
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:80'
|
310
|
+
# req.standard_port? # => true
|
311
|
+
#
|
312
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
313
|
+
# req.standard_port? # => false
|
225
314
|
def standard_port?
|
226
315
|
port == standard_port
|
227
316
|
end
|
228
317
|
|
229
318
|
# Returns a number \port suffix like 8080 if the \port number of this request
|
230
319
|
# is not the default HTTP \port 80 or HTTPS \port 443.
|
320
|
+
#
|
321
|
+
# class Request < Rack::Request
|
322
|
+
# include ActionDispatch::Http::URL
|
323
|
+
# end
|
324
|
+
#
|
325
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:80'
|
326
|
+
# req.optional_port # => nil
|
327
|
+
#
|
328
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
329
|
+
# req.optional_port # => 8080
|
231
330
|
def optional_port
|
232
331
|
standard_port? ? nil : port
|
233
332
|
end
|
234
333
|
|
235
334
|
# Returns a string \port suffix, including colon, like ":8080" if the \port
|
236
335
|
# number of this request is not the default HTTP \port 80 or HTTPS \port 443.
|
336
|
+
#
|
337
|
+
# class Request < Rack::Request
|
338
|
+
# include ActionDispatch::Http::URL
|
339
|
+
# end
|
340
|
+
#
|
341
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:80'
|
342
|
+
# req.port_string # => ""
|
343
|
+
#
|
344
|
+
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
|
345
|
+
# req.port_string # => ":8080"
|
237
346
|
def port_string
|
238
347
|
standard_port? ? '' : ":#{port}"
|
239
348
|
end
|
240
349
|
|
241
350
|
def server_port
|
242
|
-
|
351
|
+
get_header('SERVER_PORT').to_i
|
243
352
|
end
|
244
353
|
|
245
354
|
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
|