omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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)