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,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+
7
+ module ActionDispatch
8
+ module Http
9
+ module MimeNegotiation
10
+ extend ActiveSupport::Concern
11
+
12
+ class InvalidType < ::Mime::Type::InvalidMimeType; end
13
+
14
+ RESCUABLE_MIME_FORMAT_ERRORS = [
15
+ ActionController::BadRequest,
16
+ ActionDispatch::Http::Parameters::ParseError,
17
+ ]
18
+
19
+ included do
20
+ mattr_accessor :ignore_accept_header, default: false
21
+ end
22
+
23
+ # The MIME type of the HTTP request, such as [Mime](:xml).
24
+ def content_mime_type
25
+ fetch_header("action_dispatch.request.content_type") do |k|
26
+ v = if get_header("CONTENT_TYPE") =~ /^([^,;]*)/
27
+ Mime::Type.lookup($1.strip.downcase)
28
+ else
29
+ nil
30
+ end
31
+ set_header k, v
32
+ rescue ::Mime::Type::InvalidMimeType => e
33
+ raise InvalidType, e.message
34
+ end
35
+ end
36
+
37
+ def has_content_type? # :nodoc:
38
+ get_header "CONTENT_TYPE"
39
+ end
40
+
41
+ # Returns the accepted MIME type for the request.
42
+ def accepts
43
+ fetch_header("action_dispatch.request.accepts") do |k|
44
+ header = get_header("HTTP_ACCEPT").to_s.strip
45
+
46
+ v = if header.empty?
47
+ [content_mime_type]
48
+ else
49
+ Mime::Type.parse(header)
50
+ end
51
+ set_header k, v
52
+ rescue ::Mime::Type::InvalidMimeType => e
53
+ raise InvalidType, e.message
54
+ end
55
+ end
56
+
57
+ # Returns the MIME type for the format used in the request.
58
+ #
59
+ # GET /posts/5.xml | request.format => Mime[:xml]
60
+ # GET /posts/5.xhtml | request.format => Mime[:html]
61
+ # GET /posts/5 | request.format => Mime[:html] or Mime[:js], or request.accepts.first
62
+ #
63
+ def format(_view_path = nil)
64
+ formats.first || Mime::NullType.instance
65
+ end
66
+
67
+ def formats
68
+ fetch_header("action_dispatch.request.formats") do |k|
69
+ v = if params_readable?
70
+ Array(Mime[parameters[:format]])
71
+ elsif use_accept_header && valid_accept_header
72
+ accepts.dup
73
+ elsif extension_format = format_from_path_extension
74
+ [extension_format]
75
+ elsif xhr?
76
+ [Mime[:js]]
77
+ else
78
+ [Mime[:html]]
79
+ end
80
+
81
+ v.select! do |format|
82
+ format.symbol || format.ref == "*/*"
83
+ end
84
+
85
+ set_header k, v
86
+ end
87
+ end
88
+
89
+ # Sets the variant for template.
90
+ def variant=(variant)
91
+ variant = Array(variant)
92
+
93
+ if variant.all?(Symbol)
94
+ @variant = ActiveSupport::ArrayInquirer.new(variant)
95
+ else
96
+ raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols."
97
+ end
98
+ end
99
+
100
+ def variant
101
+ @variant ||= ActiveSupport::ArrayInquirer.new
102
+ end
103
+
104
+ # Sets the format by string extension, which can be used to force custom formats
105
+ # that are not controlled by the extension.
106
+ #
107
+ # class ApplicationController < ActionController::Base
108
+ # before_action :adjust_format_for_iphone
109
+ #
110
+ # private
111
+ # def adjust_format_for_iphone
112
+ # request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
113
+ # end
114
+ # end
115
+ def format=(extension)
116
+ parameters[:format] = extension.to_s
117
+ set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
118
+ end
119
+
120
+ # Sets the formats by string extensions. This differs from #format= by allowing
121
+ # you to set multiple, ordered formats, which is useful when you want to have a
122
+ # fallback.
123
+ #
124
+ # In this example, the `:iphone` format will be used if it's available,
125
+ # otherwise it'll fall back to the `:html` format.
126
+ #
127
+ # class ApplicationController < ActionController::Base
128
+ # before_action :adjust_format_for_iphone_with_html_fallback
129
+ #
130
+ # private
131
+ # def adjust_format_for_iphone_with_html_fallback
132
+ # request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
133
+ # end
134
+ # end
135
+ def formats=(extensions)
136
+ parameters[:format] = extensions.first.to_s
137
+ set_header "action_dispatch.request.formats", extensions.collect { |extension|
138
+ Mime::Type.lookup_by_extension(extension)
139
+ }
140
+ end
141
+
142
+ # Returns the first MIME type that matches the provided array of MIME types.
143
+ def negotiate_mime(order)
144
+ formats.each do |priority|
145
+ if priority == Mime::ALL
146
+ return order.first
147
+ elsif order.include?(priority)
148
+ return priority
149
+ end
150
+ end
151
+
152
+ order.include?(Mime::ALL) ? format : nil
153
+ end
154
+
155
+ def should_apply_vary_header?
156
+ !params_readable? && use_accept_header && valid_accept_header
157
+ end
158
+
159
+ private
160
+ # We use normal content negotiation unless you include **/** in your list, in
161
+ # which case we assume you're a browser and send HTML.
162
+ BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
163
+
164
+ def params_readable?
165
+ parameters[:format]
166
+ rescue *RESCUABLE_MIME_FORMAT_ERRORS
167
+ false
168
+ end
169
+
170
+ def valid_accept_header
171
+ (xhr? && (accept.present? || content_mime_type)) ||
172
+ (accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS))
173
+ end
174
+
175
+ def use_accept_header
176
+ !self.class.ignore_accept_header
177
+ end
178
+
179
+ def format_from_path_extension
180
+ path = get_header("action_dispatch.original_path") || get_header("PATH_INFO")
181
+ if match = path && path.match(/\.(\w+)\z/)
182
+ Mime[match.captures.first]
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "singleton"
6
+
7
+ module Mime
8
+ class Mimes
9
+ attr_reader :symbols
10
+
11
+ include Enumerable
12
+
13
+ def initialize
14
+ @mimes = []
15
+ @symbols = []
16
+ @symbols_set = Set.new
17
+ end
18
+
19
+ def each(&block)
20
+ @mimes.each(&block)
21
+ end
22
+
23
+ def <<(type)
24
+ @mimes << type
25
+ sym_type = type.to_sym
26
+ @symbols << sym_type
27
+ @symbols_set << sym_type
28
+ end
29
+
30
+ def delete_if
31
+ @mimes.delete_if do |x|
32
+ if yield x
33
+ sym_type = x.to_sym
34
+ @symbols.delete(sym_type)
35
+ @symbols_set.delete(sym_type)
36
+ true
37
+ end
38
+ end
39
+ end
40
+
41
+ def valid_symbols?(symbols) # :nodoc
42
+ symbols.all? { |s| @symbols_set.include?(s) }
43
+ end
44
+ end
45
+
46
+ SET = Mimes.new
47
+ EXTENSION_LOOKUP = {}
48
+ LOOKUP = {}
49
+
50
+ class << self
51
+ def [](type)
52
+ return type if type.is_a?(Type)
53
+ Type.lookup_by_extension(type)
54
+ end
55
+
56
+ def symbols
57
+ SET.symbols
58
+ end
59
+
60
+ def valid_symbols?(symbols) # :nodoc:
61
+ SET.valid_symbols?(symbols)
62
+ end
63
+
64
+ def fetch(type, &block)
65
+ return type if type.is_a?(Type)
66
+ EXTENSION_LOOKUP.fetch(type.to_s, &block)
67
+ end
68
+ end
69
+
70
+ # Encapsulates the notion of a MIME type. Can be used at render time, for
71
+ # example, with:
72
+ #
73
+ # class PostsController < ActionController::Base
74
+ # def show
75
+ # @post = Post.find(params[:id])
76
+ #
77
+ # respond_to do |format|
78
+ # format.html
79
+ # format.ics { render body: @post.to_ics, mime_type: Mime::Type.lookup("text/calendar") }
80
+ # format.xml { render xml: @post }
81
+ # end
82
+ # end
83
+ # end
84
+ class Type
85
+ attr_reader :symbol
86
+
87
+ @register_callbacks = []
88
+
89
+ # A simple helper class used in parsing the accept header.
90
+ class AcceptItem # :nodoc:
91
+ attr_accessor :index, :name, :q
92
+ alias :to_s :name
93
+
94
+ def initialize(index, name, q = nil)
95
+ @index = index
96
+ @name = name
97
+ q ||= 0.0 if @name == "*/*" # Default wildcard match to end of list.
98
+ @q = ((q || 1.0).to_f * 100).to_i
99
+ end
100
+
101
+ def <=>(item)
102
+ result = item.q <=> @q
103
+ result = @index <=> item.index if result == 0
104
+ result
105
+ end
106
+ end
107
+
108
+ class AcceptList # :nodoc:
109
+ def self.sort!(list)
110
+ list.sort!
111
+
112
+ text_xml_idx = find_item_by_name list, "text/xml"
113
+ app_xml_idx = find_item_by_name list, Mime[:xml].to_s
114
+
115
+ # Take care of the broken text/xml entry by renaming or deleting it.
116
+ if text_xml_idx && app_xml_idx
117
+ app_xml = list[app_xml_idx]
118
+ text_xml = list[text_xml_idx]
119
+
120
+ app_xml.q = [text_xml.q, app_xml.q].max # Set the q value to the max of the two.
121
+ if app_xml_idx > text_xml_idx # Make sure app_xml is ahead of text_xml in the list.
122
+ list[app_xml_idx], list[text_xml_idx] = text_xml, app_xml
123
+ app_xml_idx, text_xml_idx = text_xml_idx, app_xml_idx
124
+ end
125
+ list.delete_at(text_xml_idx) # Delete text_xml from the list.
126
+ elsif text_xml_idx
127
+ list[text_xml_idx].name = Mime[:xml].to_s
128
+ end
129
+
130
+ # Look for more specific XML-based types and sort them ahead of app/xml.
131
+ if app_xml_idx
132
+ app_xml = list[app_xml_idx]
133
+ idx = app_xml_idx
134
+
135
+ while idx < list.length
136
+ type = list[idx]
137
+ break if type.q < app_xml.q
138
+
139
+ if type.name.end_with? "+xml"
140
+ list[app_xml_idx], list[idx] = list[idx], app_xml
141
+ app_xml_idx = idx
142
+ end
143
+ idx += 1
144
+ end
145
+ end
146
+
147
+ list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
148
+ list
149
+ end
150
+
151
+ def self.find_item_by_name(array, name)
152
+ array.index { |item| item.name == name }
153
+ end
154
+ end
155
+
156
+ class << self
157
+ TRAILING_STAR_REGEXP = /^(text|application)\/\*/
158
+ # all media-type parameters need to be before the q-parameter
159
+ # https://www.rfc-editor.org/rfc/rfc7231#section-5.3.2
160
+ PARAMETER_SEPARATOR_REGEXP = /;\s*q="?/
161
+ ACCEPT_HEADER_REGEXP = /[^,\s"](?:[^,"]|"[^"]*")*/
162
+
163
+ def register_callback(&block)
164
+ @register_callbacks << block
165
+ end
166
+
167
+ def lookup(string)
168
+ return LOOKUP[string] if LOOKUP.key?(string)
169
+
170
+ # fallback to the media-type without parameters if it was not found
171
+ string = string.split(";", 2)[0]&.rstrip
172
+ LOOKUP[string] || Type.new(string)
173
+ end
174
+
175
+ def lookup_by_extension(extension)
176
+ EXTENSION_LOOKUP[extension.to_s]
177
+ end
178
+
179
+ # Registers an alias that's not used on MIME type lookup, but can be referenced
180
+ # directly. Especially useful for rendering different HTML versions depending on
181
+ # the user agent, like an iPhone.
182
+ def register_alias(string, symbol, extension_synonyms = [])
183
+ register(string, symbol, [], extension_synonyms, true)
184
+ end
185
+
186
+ def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
187
+ new_mime = Type.new(string, symbol, mime_type_synonyms)
188
+
189
+ SET << new_mime
190
+
191
+ ([string] + mime_type_synonyms).each { |str| LOOKUP[str] = new_mime } unless skip_lookup
192
+ ([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = new_mime }
193
+
194
+ @register_callbacks.each do |callback|
195
+ callback.call(new_mime)
196
+ end
197
+ new_mime
198
+ end
199
+
200
+ def parse(accept_header)
201
+ if !accept_header.include?(",")
202
+ if (index = accept_header.index(PARAMETER_SEPARATOR_REGEXP))
203
+ accept_header = accept_header[0, index].strip
204
+ end
205
+ return [] if accept_header.blank?
206
+ parse_trailing_star(accept_header) || Array(Mime::Type.lookup(accept_header))
207
+ else
208
+ list, index = [], 0
209
+ accept_header.scan(ACCEPT_HEADER_REGEXP).each do |header|
210
+ params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
211
+
212
+ next unless params
213
+ params.strip!
214
+ next if params.empty?
215
+
216
+ params = parse_trailing_star(params) || [params]
217
+
218
+ params.each do |m|
219
+ list << AcceptItem.new(index, m.to_s, q)
220
+ index += 1
221
+ end
222
+ end
223
+ AcceptList.sort! list
224
+ end
225
+ end
226
+
227
+ def parse_trailing_star(accept_header)
228
+ parse_data_with_trailing_star($1) if accept_header =~ TRAILING_STAR_REGEXP
229
+ end
230
+
231
+ # For an input of `'text'`, returns `[Mime[:json], Mime[:xml], Mime[:ics],
232
+ # Mime[:html], Mime[:css], Mime[:csv], Mime[:js], Mime[:yaml], Mime[:text]]`.
233
+ #
234
+ # For an input of `'application'`, returns `[Mime[:html], Mime[:js], Mime[:xml],
235
+ # Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]]`.
236
+ def parse_data_with_trailing_star(type)
237
+ Mime::SET.select { |m| m.match?(type) }
238
+ end
239
+
240
+ # This method is opposite of register method.
241
+ #
242
+ # To unregister a MIME type:
243
+ #
244
+ # Mime::Type.unregister(:mobile)
245
+ def unregister(symbol)
246
+ symbol = symbol.downcase
247
+ if mime = Mime[symbol]
248
+ SET.delete_if { |v| v.eql?(mime) }
249
+ LOOKUP.delete_if { |_, v| v.eql?(mime) }
250
+ EXTENSION_LOOKUP.delete_if { |_, v| v.eql?(mime) }
251
+ end
252
+ end
253
+ end
254
+
255
+ attr_reader :hash
256
+
257
+ MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
258
+ MIME_PARAMETER_VALUE = "(?:#{MIME_NAME}|\"[^\"\r\\\\]*\")"
259
+ MIME_PARAMETER = "\s*;\s*#{MIME_NAME}(?:=#{MIME_PARAMETER_VALUE})?"
260
+ MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?>#{MIME_PARAMETER})*\s*)\z/
261
+
262
+ class InvalidMimeType < StandardError; end
263
+
264
+ def initialize(string, symbol = nil, synonyms = [])
265
+ unless MIME_REGEXP.match?(string)
266
+ raise InvalidMimeType, "#{string.inspect} is not a valid MIME type"
267
+ end
268
+ @symbol, @synonyms = symbol, synonyms
269
+ @string = string
270
+ @hash = [@string, @synonyms, @symbol].hash
271
+ end
272
+
273
+ def to_s
274
+ @string
275
+ end
276
+
277
+ def to_str
278
+ to_s
279
+ end
280
+
281
+ def to_sym
282
+ @symbol
283
+ end
284
+
285
+ def ref
286
+ symbol || to_s
287
+ end
288
+
289
+ def ===(list)
290
+ if list.is_a?(Array)
291
+ (@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
292
+ else
293
+ super
294
+ end
295
+ end
296
+
297
+ def ==(mime_type)
298
+ return false unless mime_type
299
+ (@synonyms + [ self ]).any? do |synonym|
300
+ synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
301
+ end
302
+ end
303
+
304
+ def eql?(other)
305
+ super || (self.class == other.class &&
306
+ @string == other.string &&
307
+ @synonyms == other.synonyms &&
308
+ @symbol == other.symbol)
309
+ end
310
+
311
+ def =~(mime_type)
312
+ return false unless mime_type
313
+ regexp = Regexp.new(Regexp.quote(mime_type.to_s))
314
+ @synonyms.any? { |synonym| synonym.to_s =~ regexp } || @string =~ regexp
315
+ end
316
+
317
+ def match?(mime_type)
318
+ return false unless mime_type
319
+ regexp = Regexp.new(Regexp.quote(mime_type.to_s))
320
+ @synonyms.any? { |synonym| synonym.to_s.match?(regexp) } || @string.match?(regexp)
321
+ end
322
+
323
+ def html?
324
+ (symbol == :html) || @string.include?("html")
325
+ end
326
+
327
+ def all?; false; end
328
+
329
+ protected
330
+ attr_reader :string, :synonyms
331
+
332
+ private
333
+ def to_ary; end
334
+ def to_a; end
335
+
336
+ def method_missing(method, ...)
337
+ if method.end_with?("?")
338
+ method[0..-2].downcase.to_sym == to_sym
339
+ else
340
+ super
341
+ end
342
+ end
343
+
344
+ def respond_to_missing?(method, include_private = false)
345
+ method.end_with?("?") || super
346
+ end
347
+ end
348
+
349
+ class AllType < Type
350
+ include Singleton
351
+
352
+ def initialize
353
+ super "*/*", nil
354
+ end
355
+
356
+ def all?; true; end
357
+ def html?; true; end
358
+ end
359
+
360
+ # ALL isn't a real MIME type, so we don't register it for lookup with the other
361
+ # concrete types. It's a wildcard match that we use for `respond_to` negotiation
362
+ # internals.
363
+ ALL = AllType.instance
364
+
365
+ class NullType
366
+ include Singleton
367
+
368
+ def nil?
369
+ true
370
+ end
371
+
372
+ def to_s
373
+ ""
374
+ end
375
+
376
+ def ref; end
377
+
378
+ private
379
+ def respond_to_missing?(method, _)
380
+ method.end_with?("?")
381
+ end
382
+
383
+ def method_missing(method, ...)
384
+ false if method.end_with?("?")
385
+ end
386
+ end
387
+ end
388
+
389
+ require "action_dispatch/http/mime_types"
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Build list of Mime types for HTTP responses
4
+ # https://www.iana.org/assignments/media-types/
5
+
6
+ # :markup: markdown
7
+
8
+ Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
9
+ Mime::Type.register "text/plain", :text, [], %w(txt)
10
+ Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
11
+ Mime::Type.register "text/css", :css
12
+ Mime::Type.register "text/calendar", :ics
13
+ Mime::Type.register "text/csv", :csv
14
+ Mime::Type.register "text/vcard", :vcf
15
+ Mime::Type.register "text/vtt", :vtt, %w(vtt)
16
+
17
+ Mime::Type.register "image/png", :png, [], %w(png)
18
+ Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
19
+ Mime::Type.register "image/gif", :gif, [], %w(gif)
20
+ Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
21
+ Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
22
+ Mime::Type.register "image/svg+xml", :svg
23
+ Mime::Type.register "image/webp", :webp, [], %w(webp)
24
+
25
+ Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
26
+
27
+ Mime::Type.register "audio/mpeg", :mp3, [], %w(mp1 mp2 mp3)
28
+ Mime::Type.register "audio/ogg", :ogg, [], %w(oga ogg spx opus)
29
+ Mime::Type.register "audio/aac", :m4a, %w( audio/mp4 ), %w(m4a mpg4 aac)
30
+
31
+ Mime::Type.register "video/webm", :webm, [], %w(webm)
32
+ Mime::Type.register "video/mp4", :mp4, [], %w(mp4 m4v)
33
+
34
+ Mime::Type.register "font/otf", :otf, [], %w(otf)
35
+ Mime::Type.register "font/ttf", :ttf, [], %w(ttf)
36
+ Mime::Type.register "font/woff", :woff, [], %w(woff)
37
+ Mime::Type.register "font/woff2", :woff2, [], %w(woff2)
38
+
39
+ Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
40
+ Mime::Type.register "application/rss+xml", :rss
41
+ Mime::Type.register "application/atom+xml", :atom
42
+ Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml ), %w(yml yaml)
43
+
44
+ Mime::Type.register "multipart/form-data", :multipart_form
45
+ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
46
+
47
+ # https://www.ietf.org/rfc/rfc4627.txt
48
+ # http://www.json.org/JSONRequest.html
49
+ # https://www.ietf.org/rfc/rfc7807.txt
50
+ Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest application/problem+json )
51
+
52
+ Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
53
+ Mime::Type.register "application/zip", :zip, [], %w(zip)
54
+ Mime::Type.register "application/gzip", :gzip, %w(application/x-gzip), %w(gz)