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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +129 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller/asset_paths.rb +14 -0
- data/lib/abstract_controller/base.rb +299 -0
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +265 -0
- data/lib/abstract_controller/collector.rb +44 -0
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +243 -0
- data/lib/abstract_controller/logger.rb +16 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
- data/lib/abstract_controller/rendering.rb +126 -0
- data/lib/abstract_controller/translation.rb +42 -0
- data/lib/abstract_controller/url_for.rb +37 -0
- data/lib/abstract_controller.rb +36 -0
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +155 -0
- data/lib/action_controller/base.rb +332 -0
- data/lib/action_controller/caching.rb +49 -0
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +96 -0
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +341 -0
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +20 -0
- data/lib/action_controller/metal/data_streaming.rb +154 -0
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
- data/lib/action_controller/metal/exceptions.rb +106 -0
- data/lib/action_controller/metal/flash.rb +67 -0
- data/lib/action_controller/metal/head.rb +67 -0
- data/lib/action_controller/metal/helpers.rb +129 -0
- data/lib/action_controller/metal/http_authentication.rb +565 -0
- data/lib/action_controller/metal/implicit_render.rb +67 -0
- data/lib/action_controller/metal/instrumentation.rb +120 -0
- data/lib/action_controller/metal/live.rb +398 -0
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +337 -0
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +312 -0
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +251 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +260 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
- data/lib/action_controller/metal/rescue.rb +33 -0
- data/lib/action_controller/metal/streaming.rb +183 -0
- data/lib/action_controller/metal/strong_parameters.rb +1546 -0
- data/lib/action_controller/metal/testing.rb +25 -0
- data/lib/action_controller/metal/url_for.rb +65 -0
- data/lib/action_controller/metal.rb +339 -0
- data/lib/action_controller/railtie.rb +149 -0
- data/lib/action_controller/railties/helpers.rb +26 -0
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +691 -0
- data/lib/action_controller.rb +80 -0
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +249 -0
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +365 -0
- data/lib/action_dispatch/http/filter_parameters.rb +80 -0
- data/lib/action_dispatch/http/filter_redirect.rb +50 -0
- data/lib/action_dispatch/http/headers.rb +134 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
- data/lib/action_dispatch/http/mime_type.rb +389 -0
- data/lib/action_dispatch/http/mime_types.rb +54 -0
- data/lib/action_dispatch/http/parameters.rb +119 -0
- data/lib/action_dispatch/http/permissions_policy.rb +189 -0
- data/lib/action_dispatch/http/rack_cache.rb +67 -0
- data/lib/action_dispatch/http/request.rb +498 -0
- data/lib/action_dispatch/http/response.rb +556 -0
- data/lib/action_dispatch/http/upload.rb +107 -0
- data/lib/action_dispatch/http/url.rb +344 -0
- data/lib/action_dispatch/journey/formatter.rb +226 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
- data/lib/action_dispatch/journey/nodes/node.rb +208 -0
- data/lib/action_dispatch/journey/parser.rb +103 -0
- data/lib/action_dispatch/journey/path/pattern.rb +209 -0
- data/lib/action_dispatch/journey/route.rb +189 -0
- data/lib/action_dispatch/journey/router/utils.rb +105 -0
- data/lib/action_dispatch/journey/router.rb +151 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +70 -0
- data/lib/action_dispatch/journey/visitors.rb +267 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +38 -0
- data/lib/action_dispatch/middleware/cookies.rb +719 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +318 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
- data/lib/action_dispatch/middleware/reloader.rb +16 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
- data/lib/action_dispatch/middleware/request_id.rb +50 -0
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
- data/lib/action_dispatch/middleware/ssl.rb +180 -0
- data/lib/action_dispatch/middleware/stack.rb +194 -0
- data/lib/action_dispatch/middleware/static.rb +192 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
- data/lib/action_dispatch/railtie.rb +77 -0
- data/lib/action_dispatch/request/session.rb +283 -0
- data/lib/action_dispatch/request/utils.rb +109 -0
- data/lib/action_dispatch/routing/endpoint.rb +19 -0
- data/lib/action_dispatch/routing/inspector.rb +323 -0
- data/lib/action_dispatch/routing/mapper.rb +2372 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
- data/lib/action_dispatch/routing/redirection.rb +218 -0
- data/lib/action_dispatch/routing/route_set.rb +958 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
- data/lib/action_dispatch/routing/url_for.rb +244 -0
- data/lib/action_dispatch/routing.rb +262 -0
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +75 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +114 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
- data/lib/action_dispatch/testing/assertions.rb +25 -0
- data/lib/action_dispatch/testing/integration.rb +694 -0
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +57 -0
- data/lib/action_dispatch/testing/test_request.rb +73 -0
- data/lib/action_dispatch/testing/test_response.rb +58 -0
- data/lib/action_dispatch.rb +147 -0
- data/lib/action_pack/gem_version.rb +19 -0
- data/lib/action_pack/version.rb +12 -0
- data/lib/action_pack.rb +27 -0
- 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)
|