diff --git a/Gemfile b/Gemfile index 7546bc43c807f..43d2b165d293c 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,9 @@ gem "json", ">= 2.0.0", "!=2.7.0" # Workaround until Ruby ships with cgi version 0.3.6 or higher. gem "cgi", ">= 0.3.6", require: false +# Workaround until all supported Ruby versions ship with uri version 0.13.1 or higher. +gem "uri", ">= 0.13.1", require: false + gem "prism" group :lint do diff --git a/Gemfile.lock b/Gemfile.lock index 55cd2439bb76e..312a997fbe7d1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,6 +96,7 @@ PATH minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) rails (8.0.0.alpha) actioncable (= 8.0.0.alpha) actionmailbox (= 8.0.0.alpha) @@ -596,6 +597,7 @@ GEM concurrent-ruby (~> 1.0) uber (0.1.0) unicode-display_width (2.5.0) + uri (0.13.1) useragent (0.16.10) w3c_validators (1.3.7) json (>= 1.8) @@ -701,6 +703,7 @@ DEPENDENCIES trilogy (>= 2.7.0) turbo-rails tzinfo-data + uri (>= 0.13.1) useragent w3c_validators (~> 1.3.6) wdm (>= 0.1.0) diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index 75287996109f8..c06a0053c8d76 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -101,7 +101,7 @@ def normalize_filter(filter) { controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ } elsif filter[:grep] grep_pattern = Regexp.new(filter[:grep]) - path = URI::DEFAULT_PARSER.escape(filter[:grep]) + path = URI::RFC2396_PARSER.escape(filter[:grep]) normalized_path = ("/" + path).squeeze("/") { diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 1b3b0203ec1f5..0a90de854d6dd 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -2033,7 +2033,7 @@ def add_route(action, controller, options, _path, to, via, formatted, anchor, op name_for_action(options.delete(:as), action) end - path = Mapping.normalize_path URI::DEFAULT_PARSER.escape(path), formatted + path = Mapping.normalize_path URI::RFC2396_PARSER.escape(path), formatted ast = Journey::Parser.parse path mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index e4669afc8b11c..5ed211bd46c9d 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -929,7 +929,7 @@ def recognize_path_with_request(req, path, extras, raise_on_missing: true) params.each do |key, value| if value.is_a?(String) value = value.dup.force_encoding(Encoding::BINARY) - params[key] = URI::DEFAULT_PARSER.unescape(value) + params[key] = URI::RFC2396_PARSER.unescape(value) end end req.path_parameters = params diff --git a/actionpack/test/controller/parameter_encoding_test.rb b/actionpack/test/controller/parameter_encoding_test.rb index ab15c5dccd468..ab59cecbb85fe 100644 --- a/actionpack/test/controller/parameter_encoding_test.rb +++ b/actionpack/test/controller/parameter_encoding_test.rb @@ -56,7 +56,7 @@ class ParameterEncodingTest < ActionController::TestCase end test "does not raise an error when passed a param declared as ASCII-8BIT that contains invalid bytes" do - get :test_skip_parameter_encoding, params: { "bar" => URI::DEFAULT_PARSER.escape("bar\xE2baz".b) } + get :test_skip_parameter_encoding, params: { "bar" => URI::RFC2396_PARSER.escape("bar\xE2baz".b) } assert_response :success assert_equal "ASCII-8BIT", @response.body diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 119a40e273de3..4e150f5277407 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -2179,11 +2179,11 @@ def test_extras end def test_unicode_path - assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI::DEFAULT_PARSER.escape("こんにちは/世界"), method: :get)) + assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI::RFC2396_PARSER.escape("こんにちは/世界"), method: :get)) end def test_downcased_unicode_path - assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI::DEFAULT_PARSER.escape("こんにちは/世界").downcase, method: :get)) + assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI::RFC2396_PARSER.escape("こんにちは/世界").downcase, method: :get)) end private diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 70c5be9d112d7..a2ab335b8cfdc 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -556,14 +556,14 @@ def current_page?(options = nil, check_parameters: false, **options_as_kwargs) options ||= options_as_kwargs check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters) - url_string = URI::DEFAULT_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY) + url_string = URI::RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY) # We ignore any extra parameters in the request_uri if the # submitted URL doesn't have any either. This lets the function # work with things like ?order=asc # the behavior can be disabled with check_parameters: true request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path - request_uri = URI::DEFAULT_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY) + request_uri = URI::RFC2396_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY) if %r{^\w+://}.match?(url_string) request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}" diff --git a/activerecord/lib/active_record/database_configurations/connection_url_resolver.rb b/activerecord/lib/active_record/database_configurations/connection_url_resolver.rb index 6333e819f9f35..fe0315b0d2381 100644 --- a/activerecord/lib/active_record/database_configurations/connection_url_resolver.rb +++ b/activerecord/lib/active_record/database_configurations/connection_url_resolver.rb @@ -45,7 +45,7 @@ def to_hash attr_reader :uri def uri_parser - @uri_parser ||= URI::Parser.new + @uri_parser ||= URI::RFC2396_Parser.new end # Converts the query parameters of the URI into a hash. diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index 9f9bfc68af054..04856617f87a4 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -44,4 +44,5 @@ Gem::Specification.new do |s| s.add_dependency "bigdecimal" s.add_dependency "logger", ">= 1.4.2" s.add_dependency "securerandom", ">= 0.3" + s.add_dependency "uri", ">= 0.13.1" end diff --git a/activesupport/lib/active_support/testing/strict_warnings.rb b/activesupport/lib/active_support/testing/strict_warnings.rb index 83cd048a21f74..1d535b5558fd3 100644 --- a/activesupport/lib/active_support/testing/strict_warnings.rb +++ b/activesupport/lib/active_support/testing/strict_warnings.rb @@ -15,9 +15,6 @@ class WarningError < StandardError; end /Ignoring .*\.yml because it has expired/, /Failed to validate the schema cache because/, - # TODO: Can be removed if https://github.com/ruby/uri/issues/118 is addressed - /URI::RFC3986_PARSER/, - # TODO: We need to decide what to do with this. /Status code :unprocessable_entity is deprecated/ ) diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb index fec6a6d84a343..2279b133e57f0 100644 --- a/railties/lib/rails/info_controller.rb +++ b/railties/lib/rails/info_controller.rb @@ -20,7 +20,7 @@ def properties def routes if query = params[:query] - query = URI::DEFAULT_PARSER.escape query + query = URI::RFC2396_PARSER.escape query render json: { exact: matching_routes(query: query, exact_match: true), @@ -61,7 +61,7 @@ def matching_routes(query:, exact_match:) match ||= (query === route_wrapper.verb) unless match - controller_action = URI::DEFAULT_PARSER.escape(route_wrapper.reqs) + controller_action = URI::RFC2396_PARSER.escape(route_wrapper.reqs) match = exact_match ? (query === controller_action) : controller_action.include?(query) end diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index 4e5e98859ec31..a8f6b1d5eff46 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -324,7 +324,7 @@ class User < ActiveRecord::Base; raise 'should not be reached'; end # Load app env app "development" - get "/assets/#{URI::DEFAULT_PARSER.escape(asset_path)}" + get "/assets/#{URI::RFC2396_PARSER.escape(asset_path)}" assert_match "not an image really", last_response.body assert_file_exists("#{app_path}/public/assets/#{asset_path}") end