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