actionpack 4.1.16 → 4.2.0.beta1

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.

Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +163 -690
  3. data/README.rdoc +7 -2
  4. data/lib/abstract_controller/base.rb +16 -6
  5. data/lib/abstract_controller/callbacks.rb +28 -51
  6. data/lib/abstract_controller/helpers.rb +0 -3
  7. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  8. data/lib/abstract_controller/rendering.rb +1 -7
  9. data/lib/abstract_controller/url_for.rb +1 -1
  10. data/lib/action_controller.rb +1 -0
  11. data/lib/action_controller/base.rb +2 -1
  12. data/lib/action_controller/caching.rb +1 -1
  13. data/lib/action_controller/caching/fragments.rb +7 -1
  14. data/lib/action_controller/log_subscriber.rb +26 -25
  15. data/lib/action_controller/metal.rb +11 -7
  16. data/lib/action_controller/metal/conditional_get.rb +31 -6
  17. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  18. data/lib/action_controller/metal/force_ssl.rb +1 -1
  19. data/lib/action_controller/metal/head.rb +2 -0
  20. data/lib/action_controller/metal/http_authentication.rb +3 -15
  21. data/lib/action_controller/metal/instrumentation.rb +4 -7
  22. data/lib/action_controller/metal/live.rb +57 -6
  23. data/lib/action_controller/metal/mime_responds.rb +17 -227
  24. data/lib/action_controller/metal/redirecting.rb +14 -8
  25. data/lib/action_controller/metal/renderers.rb +19 -3
  26. data/lib/action_controller/metal/rendering.rb +2 -6
  27. data/lib/action_controller/metal/request_forgery_protection.rb +75 -7
  28. data/lib/action_controller/metal/streaming.rb +1 -1
  29. data/lib/action_controller/metal/strong_parameters.rb +111 -11
  30. data/lib/action_controller/metal/url_for.rb +11 -12
  31. data/lib/action_controller/model_naming.rb +1 -1
  32. data/lib/action_controller/railtie.rb +4 -0
  33. data/lib/action_controller/test_case.rb +87 -75
  34. data/lib/action_dispatch/http/cache.rb +1 -1
  35. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  36. data/lib/action_dispatch/http/headers.rb +43 -9
  37. data/lib/action_dispatch/http/mime_negotiation.rb +10 -4
  38. data/lib/action_dispatch/http/mime_type.rb +2 -16
  39. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  40. data/lib/action_dispatch/http/parameters.rb +11 -26
  41. data/lib/action_dispatch/http/request.rb +30 -10
  42. data/lib/action_dispatch/http/response.rb +52 -17
  43. data/lib/action_dispatch/http/upload.rb +3 -8
  44. data/lib/action_dispatch/http/url.rb +87 -70
  45. data/lib/action_dispatch/journey/formatter.rb +18 -17
  46. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  47. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  48. data/lib/action_dispatch/journey/gtg/transition_table.rb +18 -26
  49. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  50. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  51. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  52. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  53. data/lib/action_dispatch/journey/parser.rb +52 -60
  54. data/lib/action_dispatch/journey/parser.y +11 -10
  55. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  56. data/lib/action_dispatch/journey/route.rb +3 -18
  57. data/lib/action_dispatch/journey/router.rb +34 -65
  58. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  59. data/lib/action_dispatch/journey/routes.rb +0 -4
  60. data/lib/action_dispatch/journey/visitors.rb +81 -92
  61. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  62. data/lib/action_dispatch/middleware/cookies.rb +27 -31
  63. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -3
  64. data/lib/action_dispatch/middleware/exception_wrapper.rb +19 -17
  65. data/lib/action_dispatch/middleware/flash.rb +7 -4
  66. data/lib/action_dispatch/middleware/public_exceptions.rb +13 -8
  67. data/lib/action_dispatch/middleware/remote_ip.rb +3 -3
  68. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  69. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
  71. data/lib/action_dispatch/middleware/static.rb +22 -23
  72. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +22 -18
  73. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +36 -8
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
  75. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
  76. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  77. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  78. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
  79. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
  80. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +119 -63
  81. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  82. data/lib/action_dispatch/routing/inspector.rb +4 -11
  83. data/lib/action_dispatch/routing/mapper.rb +399 -278
  84. data/lib/action_dispatch/routing/polymorphic_routes.rb +190 -78
  85. data/lib/action_dispatch/routing/redirection.rb +10 -12
  86. data/lib/action_dispatch/routing/route_set.rb +224 -177
  87. data/lib/action_dispatch/routing/url_for.rb +9 -4
  88. data/lib/action_dispatch/testing/assertions.rb +11 -7
  89. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  90. data/lib/action_dispatch/testing/assertions/response.rb +2 -7
  91. data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
  92. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  93. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  94. data/lib/action_dispatch/testing/integration.rb +15 -18
  95. data/lib/action_dispatch/testing/test_request.rb +1 -1
  96. data/lib/action_dispatch/testing/test_response.rb +5 -1
  97. data/lib/action_pack/gem_version.rb +3 -3
  98. metadata +57 -15
  99. data/lib/action_controller/metal/responder.rb +0 -297
@@ -2,13 +2,13 @@
2
2
  <html>
3
3
  <head>
4
4
  <title><%= title %></title>
5
- <link rel="stylesheet" href="https://raw.github.com/gist/1706081/af944401f75ea20515a02ddb3fb43d23ecb8c662/reset.css" type="text/css">
5
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.css" type="text/css">
6
6
  <style>
7
7
  <% stylesheets.each do |style| %>
8
8
  <%= style %>
9
9
  <% end %>
10
10
  </style>
11
- <script src="https://raw.github.com/gist/1706081/df464722a05c3c2bec450b7b5c8240d9c31fa52d/d3.min.js" type="text/javascript"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js" type="text/javascript"></script>
12
12
  </head>
13
13
  <body>
14
14
  <div id="wrapper">
@@ -3,6 +3,7 @@ require 'active_support/core_ext/module/attribute_accessors'
3
3
  require 'active_support/core_ext/object/blank'
4
4
  require 'active_support/key_generator'
5
5
  require 'active_support/message_verifier'
6
+ require 'active_support/json'
6
7
 
7
8
  module ActionDispatch
8
9
  class Request < Rack::Request
@@ -70,13 +71,11 @@ module ActionDispatch
70
71
  # restrict to the domain level. If you use a schema like www.example.com
71
72
  # and want to share session with user.example.com set <tt>:domain</tt>
72
73
  # to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
73
- # <tt>:all</tt> or <tt>Array</tt> again when deleting cookies.
74
+ # <tt>:all</tt> again when deleting cookies.
74
75
  #
75
76
  # domain: nil # Does not sets cookie domain. (default)
76
77
  # domain: :all # Allow the cookie for the top most level
77
- # domain and subdomains.
78
- # domain: %w(.example.com .example.org) # Allow the cookie
79
- # for concrete domain names.
78
+ # # domain and subdomains.
80
79
  #
81
80
  # * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
82
81
  # * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
@@ -92,6 +91,7 @@ module ActionDispatch
92
91
  SECRET_TOKEN = "action_dispatch.secret_token".freeze
93
92
  SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
94
93
  COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
94
+ COOKIES_DIGEST = "action_dispatch.cookies_digest".freeze
95
95
 
96
96
  # Cookies can typically store 4096 bytes.
97
97
  MAX_COOKIE_SIZE = 4096
@@ -120,7 +120,7 @@ module ActionDispatch
120
120
  # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
121
121
  # cookie was tampered with by the user (or a 3rd party), nil will be returned.
122
122
  #
123
- # If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
123
+ # If +secrets.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
124
124
  # legacy cookies signed with the old key generator will be transparently upgraded.
125
125
  #
126
126
  # This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
@@ -143,7 +143,7 @@ module ActionDispatch
143
143
  # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
144
144
  # If the cookie was tampered with by the user (or a 3rd party), nil will be returned.
145
145
  #
146
- # If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
146
+ # If +secrets.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
147
147
  # legacy cookies signed with the old key generator will be transparently upgraded.
148
148
  #
149
149
  # This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
@@ -175,10 +175,14 @@ module ActionDispatch
175
175
  end
176
176
  end
177
177
 
178
+ # Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
179
+ # to the Message{Encryptor,Verifier} allows us to handle the
180
+ # (de)serialization step within the cookie jar, which gives us the
181
+ # opportunity to detect and migrate legacy cookies.
178
182
  module VerifyAndUpgradeLegacySignedMessage
179
183
  def initialize(*args)
180
184
  super
181
- @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: NullSerializer)
185
+ @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer)
182
186
  end
183
187
 
184
188
  def verify_and_upgrade_legacy_signed_message(name, signed_message)
@@ -214,7 +218,8 @@ module ActionDispatch
214
218
  secret_token: env[SECRET_TOKEN],
215
219
  secret_key_base: env[SECRET_KEY_BASE],
216
220
  upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
217
- serializer: env[COOKIES_SERIALIZER]
221
+ serializer: env[COOKIES_SERIALIZER],
222
+ digest: env[COOKIES_DIGEST]
218
223
  }
219
224
  end
220
225
 
@@ -291,8 +296,8 @@ module ActionDispatch
291
296
  end
292
297
  end
293
298
 
294
- # Sets the cookie named +name+. The second argument may be the very cookie
295
- # value, or a hash of options as documented above.
299
+ # Sets the cookie named +name+. The second argument may be the cookie's
300
+ # value or a hash of options as documented above.
296
301
  def []=(name, options)
297
302
  if options.is_a?(Hash)
298
303
  options.symbolize_keys!
@@ -387,24 +392,11 @@ module ActionDispatch
387
392
 
388
393
  class JsonSerializer
389
394
  def self.load(value)
390
- JSON.parse(value, quirks_mode: true)
395
+ ActiveSupport::JSON.decode(value)
391
396
  end
392
397
 
393
398
  def self.dump(value)
394
- JSON.generate(value, quirks_mode: true)
395
- end
396
- end
397
-
398
- # Passing the NullSerializer downstream to the Message{Encryptor,Verifier}
399
- # allows us to handle the (de)serialization step within the cookie jar,
400
- # which gives us the opportunity to detect and migrate legacy cookies.
401
- class NullSerializer
402
- def self.load(value)
403
- value
404
- end
405
-
406
- def self.dump(value)
407
- value
399
+ ActiveSupport::JSON.encode(value)
408
400
  end
409
401
  end
410
402
 
@@ -443,6 +435,10 @@ module ActionDispatch
443
435
  serializer
444
436
  end
445
437
  end
438
+
439
+ def digest
440
+ @options[:digest] || 'SHA1'
441
+ end
446
442
  end
447
443
 
448
444
  class SignedCookieJar #:nodoc:
@@ -453,7 +449,7 @@ module ActionDispatch
453
449
  @parent_jar = parent_jar
454
450
  @options = options
455
451
  secret = key_generator.generate_key(@options[:signed_cookie_salt])
456
- @verifier = ActiveSupport::MessageVerifier.new(secret, serializer: NullSerializer)
452
+ @verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
457
453
  end
458
454
 
459
455
  def [](name)
@@ -470,7 +466,7 @@ module ActionDispatch
470
466
  options = { :value => @verifier.generate(serialize(name, options)) }
471
467
  end
472
468
 
473
- raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
469
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
474
470
  @parent_jar[name] = options
475
471
  end
476
472
 
@@ -483,7 +479,7 @@ module ActionDispatch
483
479
  end
484
480
 
485
481
  # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if
486
- # secrets.secret_token and secrets.secret_key_base are both set. It reads
482
+ # config.secret_token and secrets.secret_key_base are both set. It reads
487
483
  # legacy cookies signed with the old dummy key generator and re-saves
488
484
  # them using the new key generator to provide a smooth upgrade path.
489
485
  class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
@@ -510,7 +506,7 @@ module ActionDispatch
510
506
  @options = options
511
507
  secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
512
508
  sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
513
- @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: NullSerializer)
509
+ @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
514
510
  end
515
511
 
516
512
  def [](name)
@@ -528,7 +524,7 @@ module ActionDispatch
528
524
 
529
525
  options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value]))
530
526
 
531
- raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
527
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
532
528
  @parent_jar[name] = options
533
529
  end
534
530
 
@@ -541,7 +537,7 @@ module ActionDispatch
541
537
  end
542
538
 
543
539
  # UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore
544
- # instead of EncryptedCookieJar if secrets.secret_token and secrets.secret_key_base
540
+ # instead of EncryptedCookieJar if config.secret_token and secrets.secret_key_base
545
541
  # are both set. It reads legacy cookies signed with the old dummy key generator and
546
542
  # encrypts and re-saves them using the new key generator to provide a smooth upgrade path.
547
543
  class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc:
@@ -38,9 +38,7 @@ module ActionDispatch
38
38
  template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
39
39
  request: request,
40
40
  exception: wrapper.exception,
41
- application_trace: wrapper.application_trace,
42
- framework_trace: wrapper.framework_trace,
43
- full_trace: wrapper.full_trace,
41
+ traces: traces_from_wrapper(wrapper),
44
42
  routes_inspector: routes_inspector(exception),
45
43
  source_extract: wrapper.source_extract,
46
44
  line_number: wrapper.line_number,
@@ -95,5 +93,36 @@ module ActionDispatch
95
93
  ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
96
94
  end
97
95
  end
96
+
97
+ # Augment the exception traces by providing ids for all unique stack frame
98
+ def traces_from_wrapper(wrapper)
99
+ application_trace = wrapper.application_trace
100
+ framework_trace = wrapper.framework_trace
101
+ full_trace = wrapper.full_trace
102
+
103
+ if application_trace && framework_trace
104
+ id_counter = 0
105
+
106
+ application_trace = application_trace.map do |trace|
107
+ prev = id_counter
108
+ id_counter += 1
109
+ { id: prev, trace: trace }
110
+ end
111
+
112
+ framework_trace = framework_trace.map do |trace|
113
+ prev = id_counter
114
+ id_counter += 1
115
+ { id: prev, trace: trace }
116
+ end
117
+
118
+ full_trace = application_trace + framework_trace
119
+ end
120
+
121
+ {
122
+ "Application Trace" => application_trace,
123
+ "Framework Trace" => framework_trace,
124
+ "Full Trace" => full_trace
125
+ }
126
+ end
98
127
  end
99
128
  end
@@ -6,17 +6,16 @@ module ActionDispatch
6
6
  cattr_accessor :rescue_responses
7
7
  @@rescue_responses = Hash.new(:internal_server_error)
8
8
  @@rescue_responses.merge!(
9
- 'ActionController::RoutingError' => :not_found,
10
- 'AbstractController::ActionNotFound' => :not_found,
11
- 'ActionController::MethodNotAllowed' => :method_not_allowed,
12
- 'ActionController::UnknownHttpMethod' => :method_not_allowed,
13
- 'ActionController::NotImplemented' => :not_implemented,
14
- 'ActionController::UnknownFormat' => :not_acceptable,
15
- 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
16
- 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity,
17
- 'ActionDispatch::ParamsParser::ParseError' => :bad_request,
18
- 'ActionController::BadRequest' => :bad_request,
19
- 'ActionController::ParameterMissing' => :bad_request
9
+ 'ActionController::RoutingError' => :not_found,
10
+ 'AbstractController::ActionNotFound' => :not_found,
11
+ 'ActionController::MethodNotAllowed' => :method_not_allowed,
12
+ 'ActionController::UnknownHttpMethod' => :method_not_allowed,
13
+ 'ActionController::NotImplemented' => :not_implemented,
14
+ 'ActionController::UnknownFormat' => :not_acceptable,
15
+ 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
16
+ 'ActionDispatch::ParamsParser::ParseError' => :bad_request,
17
+ 'ActionController::BadRequest' => :bad_request,
18
+ 'ActionController::ParameterMissing' => :bad_request
20
19
  )
21
20
 
22
21
  cattr_accessor :rescue_templates
@@ -62,12 +61,15 @@ module ActionDispatch
62
61
  end
63
62
 
64
63
  def source_extract
65
- if application_trace && trace = application_trace.first
66
- file, line, _ = trace.split(":")
67
- @file = file
68
- @line_number = line.to_i
69
- source_fragment(@file, @line_number)
70
- end
64
+ exception.backtrace.map do |trace|
65
+ file, line = trace.split(":")
66
+ line_number = line.to_i
67
+ {
68
+ code: source_fragment(file, line_number),
69
+ file: file,
70
+ line_number: line_number
71
+ }
72
+ end if exception.backtrace
71
73
  end
72
74
 
73
75
  private
@@ -10,7 +10,7 @@ module ActionDispatch
10
10
  end
11
11
  end
12
12
 
13
- # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
13
+ # The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
14
14
  # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
15
15
  # action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
16
16
  # then expose the flash to its template. Actually, that exposure is automatically done.
@@ -37,8 +37,11 @@ module ActionDispatch
37
37
  # flash.alert = "You must be logged in"
38
38
  # flash.notice = "Post successfully created"
39
39
  #
40
- # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
41
- # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
40
+ # This example places a string in the flash. And of course, you can put as many as you like at a time too. If you want to pass
41
+ # non-primitive types, you will have to handle that in your application. Example: To show messages with links, you will have to
42
+ # use sanitize helper.
43
+ #
44
+ # Just remember: They'll be gone by the time the next action has been performed.
42
45
  #
43
46
  # See docs on the FlashHash class for more details about the flash.
44
47
  class Flash
@@ -129,7 +132,7 @@ module ActionDispatch
129
132
  end
130
133
 
131
134
  def key?(name)
132
- @flashes.key? name.to_s
135
+ @flashes.key? name
133
136
  end
134
137
 
135
138
  def delete(key)
@@ -1,4 +1,14 @@
1
1
  module ActionDispatch
2
+ # When called, this middleware renders an error page. By default if an HTML
3
+ # response is expected it will render static error pages from the `/public`
4
+ # directory. For example when this middleware receives a 500 response it will
5
+ # render the template found in `/public/500.html`.
6
+ # If an internationalized locale is set, this middleware will attempt to render
7
+ # the template in `/public/500.<locale>.html`. If an internationalized template
8
+ # is not found it will fall back on `/public/500.html`.
9
+ #
10
+ # When a request with a content type other than HTML is made, this middleware
11
+ # will attempt to convert error information into the appropriate response type.
2
12
  class PublicExceptions
3
13
  attr_accessor :public_path
4
14
 
@@ -9,12 +19,8 @@ module ActionDispatch
9
19
  def call(env)
10
20
  status = env["PATH_INFO"][1..-1]
11
21
  request = ActionDispatch::Request.new(env)
22
+ content_type = request.formats.first
12
23
  body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status.to_i, Rack::Utils::HTTP_STATUS_CODES[500]) }
13
- content_type = begin
14
- request.formats.first
15
- rescue ActionController::BadRequest
16
- Mime::HTML
17
- end
18
24
 
19
25
  render(status, content_type, body)
20
26
  end
@@ -36,9 +42,8 @@ module ActionDispatch
36
42
  end
37
43
 
38
44
  def render_html(status)
39
- found = false
40
- path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
41
- path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
45
+ path = "#{public_path}/#{status}.#{I18n.locale}.html"
46
+ path = "#{public_path}/#{status}.html" unless (found = File.exist?(path))
42
47
 
43
48
  if found || File.exist?(path)
44
49
  render_format(status, 'text/html', File.read(path))
@@ -11,7 +11,7 @@ module ActionDispatch
11
11
  # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
12
12
  # requires. Some Rack servers simply drop preceding headers, and only report
13
13
  # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
14
- # If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn)
14
+ # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
15
15
  # then you should test your Rack server to make sure your data is good.
16
16
  #
17
17
  # IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
@@ -31,7 +31,7 @@ module ActionDispatch
31
31
  TRUSTED_PROXIES = %r{
32
32
  ^127\.0\.0\.1$ | # localhost IPv4
33
33
  ^::1$ | # localhost IPv6
34
- ^fc00: | # private IPv6 range fc00
34
+ ^[fF][cCdD] | # private IPv6 range fc00::/7
35
35
  ^10\. | # private IPv4 range 10.x.x.x
36
36
  ^172\.(1[6-9]|2[0-9]|3[0-1])\.| # private IPv4 range 172.16.0.0 .. 172.31.255.255
37
37
  ^192\.168\. # private IPv4 range 192.168.x.x
@@ -118,7 +118,7 @@ module ActionDispatch
118
118
  #
119
119
  # REMOTE_ADDR will be correct if the request is made directly against the
120
120
  # Ruby process, on e.g. Heroku. When the request is proxied by another
121
- # server like HAProxy or Nginx, the IP address that made the original
121
+ # server like HAProxy or NGINX, the IP address that made the original
122
122
  # request will be put in an X-Forwarded-For header. If there are multiple
123
123
  # proxies, that header may contain a list of IPs. Other proxy services
124
124
  # set the Client-Ip header instead, so we check that too.
@@ -5,7 +5,7 @@ module ActionDispatch
5
5
  # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
6
6
  # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
7
7
  #
8
- # The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
8
+ # The unique request id is either based on the X-Request-Id header in the request, which would typically be generated
9
9
  # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
10
10
  # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
11
11
  #
@@ -49,7 +49,7 @@ module ActionDispatch
49
49
  # reasonably sure that your upgrade is otherwise complete. Additionally,
50
50
  # you should take care to make sure you are not relying on the ability to
51
51
  # decode signed cookies generated by your app in external applications or
52
- # Javascript before upgrading.
52
+ # JavaScript before upgrading.
53
53
  #
54
54
  # Note that changing the secret key will invalidate all existing sessions!
55
55
  class CookieStore < Rack::Session::Abstract::ID
@@ -42,6 +42,7 @@ module ActionDispatch
42
42
  wrapper = ExceptionWrapper.new(env, exception)
43
43
  status = wrapper.status_code
44
44
  env["action_dispatch.exception"] = wrapper.exception
45
+ env["action_dispatch.original_path"] = env["PATH_INFO"]
45
46
  env["PATH_INFO"] = "/#{status}"
46
47
  response = @exceptions_app.call(env)
47
48
  response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
@@ -2,6 +2,16 @@ require 'rack/utils'
2
2
  require 'active_support/core_ext/uri'
3
3
 
4
4
  module ActionDispatch
5
+ # This middleware returns a file's contents from disk in the body response.
6
+ # When initialized it can accept an optional 'Cache-Control' header which
7
+ # will be set when a response containing a file's contents is delivered.
8
+ #
9
+ # This middleware will render the file specified in `env["PATH_INFO"]`
10
+ # where the base path is in the +root+ directory. For example if the +root+
11
+ # is set to `public/` then a request with `env["PATH_INFO"]` of
12
+ # `assets/application.js` will return a response with contents of a file
13
+ # located at `public/assets/application.js` if the file exists. If the file
14
+ # does not exist a 404 "File not Found" response will be returned.
5
15
  class FileHandler
6
16
  def initialize(root, cache_control)
7
17
  @root = root.chomp('/')
@@ -14,12 +24,11 @@ module ActionDispatch
14
24
  path = unescape_path(path)
15
25
  return false unless path.valid_encoding?
16
26
 
17
- full_path = path.empty? ? @root : File.join(@root,
18
- clean_path_info(escape_glob_chars(path)))
27
+ full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(path))
19
28
  paths = "#{full_path}#{ext}"
20
29
 
21
30
  matches = Dir[paths]
22
- match = matches.detect { |m| File.file?(m) && File.readable?(m) }
31
+ match = matches.detect { |m| File.file?(m) }
23
32
  if match
24
33
  match.sub!(@compiled_root, '')
25
34
  ::Rack::Utils.escape(match)
@@ -42,29 +51,19 @@ module ActionDispatch
42
51
  end
43
52
 
44
53
  def escape_glob_chars(path)
45
- path.gsub(/[*?{}\[\]\\]/, "\\\\\\&")
46
- end
47
-
48
- private
49
-
50
- PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
51
-
52
- def clean_path_info(path_info)
53
- parts = path_info.split PATH_SEPS
54
-
55
- clean = []
56
-
57
- parts.each do |part|
58
- next if part.empty? || part == '.'
59
- part == '..' ? clean.pop : clean << part
60
- end
61
-
62
- clean.unshift '/' if parts.empty? || parts.first.empty?
63
-
64
- ::File.join(*clean)
54
+ path.gsub(/[*?{}\[\]]/, "\\\\\\&")
65
55
  end
66
56
  end
67
57
 
58
+ # This middleware will attempt to return the contents of a file's body from
59
+ # disk in the response. If a file is not found on disk, the request will be
60
+ # delegated to the application stack. This middleware is commonly initialized
61
+ # to serve assets from a server's `public/` directory.
62
+ #
63
+ # This middleware verifies the path to ensure that only files
64
+ # living in the root directory can be rendered. A request cannot
65
+ # produce a directory traversal using this middleware. Only 'GET' and 'HEAD'
66
+ # requests will result in a file being returned.
68
67
  class Static
69
68
  def initialize(app, path, cache_control=nil)
70
69
  @app = app