diff --git a/app/lib/request.rb b/app/lib/request.rb index 4858aa4bc2..dd65b481d8 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -65,6 +65,7 @@ class Request # and 5s timeout on the TLS handshake, meaning the worst case should take # about 15s in total TIMEOUT = { connect_timeout: 5, read_timeout: 10, write_timeout: 10, read_deadline: 30 }.freeze + SAFE_PRESERVED_CHARS = '+,' include RoutingHelper @@ -72,7 +73,7 @@ class Request raise ArgumentError if url.blank? @verb = verb - @url = Addressable::URI.parse(url).normalize + @url = normalize_preserving_url_encodings(url, SAFE_PRESERVED_CHARS) @http_client = options.delete(:http_client) @allow_local = options.delete(:allow_local) @options = { @@ -148,6 +149,34 @@ class Request private + # Using code from https://github.com/sporkmonger/addressable/blob/3450895887d0a1770660d8831d1b6fcfed9bd9d6/lib/addressable/uri.rb#L1609-L1635 + # to preserve some URL Encodings while normalizing + def normalize_preserving_url_encodings(url, preserved_chars = SAFE_PRESERVED_CHARS, *flags) + original_uri = Addressable::URI.parse(url) + normalized_uri = original_uri.normalize + + if original_uri.query + modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup + modified_query_class.sub!('\\&', '').sub!('\\;', '') + + pairs = original_uri.query.split('&', -1) + pairs.delete_if(&:empty?).uniq! if flags.include?(:compacted) + pairs.sort! if flags.include?(:sorted) + + normalized_query = pairs.map do |pair| + Addressable::URI.normalize_component( + pair, + modified_query_class, + preserved_chars + ) + end.join('&') + + normalized_uri.query = normalized_query == '' ? nil : normalized_query + end + + normalized_uri + end + def set_common_headers! @headers['User-Agent'] = Mastodon::Version.user_agent @headers['Host'] = @url.host