omg-actionpack 8.0.0.alpha1

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.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +691 -0
  65. data/lib/action_controller.rb +80 -0
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "abstract_controller"
6
+ require "action_dispatch"
7
+ require "action_controller/deprecator"
8
+ require "action_controller/metal/strong_parameters"
9
+ require "action_controller/metal/exceptions"
10
+
11
+ # # Action Controller
12
+ #
13
+ # Action Controller is a module of Action Pack.
14
+ #
15
+ # Action Controller provides a base controller class that can be subclassed to
16
+ # implement filters and actions to handle requests. The result of an action is
17
+ # typically content generated from views.
18
+ module ActionController
19
+ extend ActiveSupport::Autoload
20
+
21
+ autoload :API
22
+ autoload :Base
23
+ autoload :Metal
24
+ autoload :Renderer
25
+ autoload :FormBuilder
26
+
27
+ eager_autoload do
28
+ autoload :Caching
29
+ end
30
+
31
+ autoload_under "metal" do
32
+ autoload :AllowBrowser
33
+ autoload :ConditionalGet
34
+ autoload :ContentSecurityPolicy
35
+ autoload :Cookies
36
+ autoload :DataStreaming
37
+ autoload :DefaultHeaders
38
+ autoload :EtagWithTemplateDigest
39
+ autoload :EtagWithFlash
40
+ autoload :PermissionsPolicy
41
+ autoload :Flash
42
+ autoload :Head
43
+ autoload :Helpers
44
+ autoload :HttpAuthentication
45
+ autoload :BasicImplicitRender
46
+ autoload :ImplicitRender
47
+ autoload :Instrumentation
48
+ autoload :Live
49
+ autoload :Logging
50
+ autoload :MimeResponds
51
+ autoload :ParamsWrapper
52
+ autoload :RateLimiting
53
+ autoload :Redirecting
54
+ autoload :Renderers
55
+ autoload :Rendering
56
+ autoload :RequestForgeryProtection
57
+ autoload :Rescue
58
+ autoload :Streaming
59
+ autoload :StrongParameters
60
+ autoload :ParameterEncoding
61
+ autoload :Testing
62
+ autoload :UrlFor
63
+ end
64
+
65
+ autoload_under "api" do
66
+ autoload :ApiRendering
67
+ end
68
+
69
+ autoload_at "action_controller/test_case" do
70
+ autoload :TestCase
71
+ autoload :TestRequest
72
+ autoload :TemplateAssertions
73
+ end
74
+ end
75
+
76
+ # Common Active Support usage in Action Controller
77
+ require "active_support/core_ext/module/attribute_accessors"
78
+ require "active_support/core_ext/module/attr_internal"
79
+ require "active_support/core_ext/name_error"
80
+ require "active_support/inflector"
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "rack/version"
6
+
7
+ module ActionDispatch
8
+ module Constants
9
+ # Response Header keys for Rack 2.x and 3.x
10
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
11
+ VARY = "Vary"
12
+ CONTENT_ENCODING = "Content-Encoding"
13
+ CONTENT_SECURITY_POLICY = "Content-Security-Policy"
14
+ CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
15
+ LOCATION = "Location"
16
+ FEATURE_POLICY = "Feature-Policy"
17
+ X_REQUEST_ID = "X-Request-Id"
18
+ X_CASCADE = "X-Cascade"
19
+ SERVER_TIMING = "Server-Timing"
20
+ STRICT_TRANSPORT_SECURITY = "Strict-Transport-Security"
21
+ else
22
+ VARY = "vary"
23
+ CONTENT_ENCODING = "content-encoding"
24
+ CONTENT_SECURITY_POLICY = "content-security-policy"
25
+ CONTENT_SECURITY_POLICY_REPORT_ONLY = "content-security-policy-report-only"
26
+ LOCATION = "location"
27
+ FEATURE_POLICY = "feature-policy"
28
+ X_REQUEST_ID = "x-request-id"
29
+ X_CASCADE = "x-cascade"
30
+ SERVER_TIMING = "server-timing"
31
+ STRICT_TRANSPORT_SECURITY = "strict-transport-security"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ def self.deprecator # :nodoc:
7
+ @deprecator ||= ActiveSupport::Deprecation.new
8
+ end
9
+ end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ module Http
7
+ module Cache
8
+ module Request
9
+ HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE"
10
+ HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH"
11
+
12
+ mattr_accessor :strict_freshness, default: false
13
+
14
+ def if_modified_since
15
+ if since = get_header(HTTP_IF_MODIFIED_SINCE)
16
+ Time.rfc2822(since) rescue nil
17
+ end
18
+ end
19
+
20
+ def if_none_match
21
+ get_header HTTP_IF_NONE_MATCH
22
+ end
23
+
24
+ def if_none_match_etags
25
+ if_none_match ? if_none_match.split(",").each(&:strip!) : []
26
+ end
27
+
28
+ def not_modified?(modified_at)
29
+ if_modified_since && modified_at && if_modified_since >= modified_at
30
+ end
31
+
32
+ def etag_matches?(etag)
33
+ if etag
34
+ validators = if_none_match_etags
35
+ validators.include?(etag) || validators.include?("*")
36
+ end
37
+ end
38
+
39
+ # Check response freshness (`Last-Modified` and `ETag`) against request
40
+ # `If-Modified-Since` and `If-None-Match` conditions.
41
+ # If both headers are supplied, based on configuration, either `ETag` is preferred over `Last-Modified`
42
+ # or both are considered equally. You can adjust the preference with
43
+ # `config.action_dispatch.strict_freshness`.
44
+ # Reference: http://tools.ietf.org/html/rfc7232#section-6
45
+ def fresh?(response)
46
+ if Request.strict_freshness
47
+ if if_none_match
48
+ etag_matches?(response.etag)
49
+ elsif if_modified_since
50
+ not_modified?(response.last_modified)
51
+ else
52
+ false
53
+ end
54
+ else
55
+ last_modified = if_modified_since
56
+ etag = if_none_match
57
+
58
+ return false unless last_modified || etag
59
+
60
+ success = true
61
+ success &&= not_modified?(response.last_modified) if last_modified
62
+ success &&= etag_matches?(response.etag) if etag
63
+ success
64
+ end
65
+ end
66
+ end
67
+
68
+ module Response
69
+ attr_reader :cache_control
70
+
71
+ def last_modified
72
+ if last = get_header(LAST_MODIFIED)
73
+ Time.httpdate(last)
74
+ end
75
+ end
76
+
77
+ def last_modified?
78
+ has_header? LAST_MODIFIED
79
+ end
80
+
81
+ def last_modified=(utc_time)
82
+ set_header LAST_MODIFIED, utc_time.httpdate
83
+ end
84
+
85
+ def date
86
+ if date_header = get_header(DATE)
87
+ Time.httpdate(date_header)
88
+ end
89
+ end
90
+
91
+ def date?
92
+ has_header? DATE
93
+ end
94
+
95
+ def date=(utc_time)
96
+ set_header DATE, utc_time.httpdate
97
+ end
98
+
99
+ # This method sets a weak ETag validator on the response so browsers and proxies
100
+ # may cache the response, keyed on the ETag. On subsequent requests, the
101
+ # `If-None-Match` header is set to the cached ETag. If it matches the current
102
+ # ETag, we can return a `304 Not Modified` response with no body, letting the
103
+ # browser or proxy know that their cache is current. Big savings in request time
104
+ # and network bandwidth.
105
+ #
106
+ # Weak ETags are considered to be semantically equivalent but not byte-for-byte
107
+ # identical. This is perfect for browser caching of HTML pages where we don't
108
+ # care about exact equality, just what the user is viewing.
109
+ #
110
+ # Strong ETags are considered byte-for-byte identical. They allow a browser or
111
+ # proxy cache to support `Range` requests, useful for paging through a PDF file
112
+ # or scrubbing through a video. Some CDNs only support strong ETags and will
113
+ # ignore weak ETags entirely.
114
+ #
115
+ # Weak ETags are what we almost always need, so they're the default. Check out
116
+ # #strong_etag= to provide a strong ETag validator.
117
+ def etag=(weak_validators)
118
+ self.weak_etag = weak_validators
119
+ end
120
+
121
+ def weak_etag=(weak_validators)
122
+ set_header "ETag", generate_weak_etag(weak_validators)
123
+ end
124
+
125
+ def strong_etag=(strong_validators)
126
+ set_header "ETag", generate_strong_etag(strong_validators)
127
+ end
128
+
129
+ def etag?; etag; end
130
+
131
+ # True if an ETag is set, and it's a weak validator (preceded with `W/`).
132
+ def weak_etag?
133
+ etag? && etag.start_with?('W/"')
134
+ end
135
+
136
+ # True if an ETag is set, and it isn't a weak validator (not preceded with
137
+ # `W/`).
138
+ def strong_etag?
139
+ etag? && !weak_etag?
140
+ end
141
+
142
+ private
143
+ DATE = "Date"
144
+ LAST_MODIFIED = "Last-Modified"
145
+ SPECIAL_KEYS = Set.new(%w[extras no-store no-cache max-age public private must-revalidate])
146
+
147
+ def generate_weak_etag(validators)
148
+ "W/#{generate_strong_etag(validators)}"
149
+ end
150
+
151
+ def generate_strong_etag(validators)
152
+ %("#{ActiveSupport::Digest.hexdigest(ActiveSupport::Cache.expand_cache_key(validators))}")
153
+ end
154
+
155
+ def cache_control_segments
156
+ if cache_control = _cache_control
157
+ cache_control.delete(" ").split(",")
158
+ end
159
+ end
160
+
161
+ def cache_control_headers
162
+ cache_control = {}
163
+
164
+ cache_control_segments&.each do |segment|
165
+ directive, argument = segment.split("=", 2)
166
+
167
+ if SPECIAL_KEYS.include? directive
168
+ directive.tr!("-", "_")
169
+ cache_control[directive.to_sym] = argument || true
170
+ else
171
+ cache_control[:extras] ||= []
172
+ cache_control[:extras] << segment
173
+ end
174
+ end
175
+
176
+ cache_control
177
+ end
178
+
179
+ def prepare_cache_control!
180
+ @cache_control = cache_control_headers
181
+ end
182
+
183
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
184
+ NO_STORE = "no-store"
185
+ NO_CACHE = "no-cache"
186
+ PUBLIC = "public"
187
+ PRIVATE = "private"
188
+ MUST_REVALIDATE = "must-revalidate"
189
+ IMMUTABLE = "immutable"
190
+
191
+ def handle_conditional_get!
192
+ # Normally default cache control setting is handled by ETag middleware. But, if
193
+ # an etag is already set, the middleware defaults to `no-cache` unless a default
194
+ # `Cache-Control` value is previously set. So, set a default one here.
195
+ if (etag? || last_modified?) && !self._cache_control
196
+ self._cache_control = DEFAULT_CACHE_CONTROL
197
+ end
198
+ end
199
+
200
+ def merge_and_normalize_cache_control!(cache_control)
201
+ control = cache_control_headers
202
+
203
+ return if control.empty? && cache_control.empty? # Let middleware handle default behavior
204
+
205
+ if cache_control.any?
206
+ # Any caching directive coming from a controller overrides no-cache/no-store in
207
+ # the default Cache-Control header.
208
+ control.delete(:no_cache)
209
+ control.delete(:no_store)
210
+
211
+ if extras = control.delete(:extras)
212
+ cache_control[:extras] ||= []
213
+ cache_control[:extras] += extras
214
+ cache_control[:extras].uniq!
215
+ end
216
+
217
+ control.merge! cache_control
218
+ end
219
+
220
+ options = []
221
+
222
+ if control[:no_store]
223
+ options << PRIVATE if control[:private]
224
+ options << NO_STORE
225
+ elsif control[:no_cache]
226
+ options << PUBLIC if control[:public]
227
+ options << NO_CACHE
228
+ options.concat(control[:extras]) if control[:extras]
229
+ else
230
+ extras = control[:extras]
231
+ max_age = control[:max_age]
232
+ stale_while_revalidate = control[:stale_while_revalidate]
233
+ stale_if_error = control[:stale_if_error]
234
+
235
+ options << "max-age=#{max_age.to_i}" if max_age
236
+ options << (control[:public] ? PUBLIC : PRIVATE)
237
+ options << MUST_REVALIDATE if control[:must_revalidate]
238
+ options << "stale-while-revalidate=#{stale_while_revalidate.to_i}" if stale_while_revalidate
239
+ options << "stale-if-error=#{stale_if_error.to_i}" if stale_if_error
240
+ options << IMMUTABLE if control[:immutable]
241
+ options.concat(extras) if extras
242
+ end
243
+
244
+ self._cache_control = options.join(", ")
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ module Http
7
+ class ContentDisposition # :nodoc:
8
+ def self.format(disposition:, filename:)
9
+ new(disposition: disposition, filename: filename).to_s
10
+ end
11
+
12
+ attr_reader :disposition, :filename
13
+
14
+ def initialize(disposition:, filename:)
15
+ @disposition = disposition
16
+ @filename = filename
17
+ end
18
+
19
+ TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!\#$+.^_`|~-]/
20
+
21
+ def ascii_filename
22
+ 'filename="' + percent_escape(I18n.transliterate(filename), TRADITIONAL_ESCAPED_CHAR) + '"'
23
+ end
24
+
25
+ RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!\#$&+.^_`|~-]/
26
+
27
+ def utf8_filename
28
+ "filename*=UTF-8''" + percent_escape(filename, RFC_5987_ESCAPED_CHAR)
29
+ end
30
+
31
+ def to_s
32
+ if filename
33
+ "#{disposition}; #{ascii_filename}; #{utf8_filename}"
34
+ else
35
+ "#{disposition}"
36
+ end
37
+ end
38
+
39
+ private
40
+ def percent_escape(string, pattern)
41
+ string.gsub(pattern) do |char|
42
+ char.bytes.map { |byte| "%%%02X" % byte }.join
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end