From f8138c09d87bb5093948fd4dc4056bb50f6b1596 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 18:32:12 +0200 Subject: [PATCH 01/16] Reproduce caching issue with the JSON::Util::URI.normalize_ref method --- test/uri_util_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index fb662fe8..02397a90 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -203,4 +203,9 @@ def test_ref_addressable_uri_with_scheme_host_and_path assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) end + + def test_normalize_ref_cache + assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI.normalize_ref("#foo", 'http://www.example.com') + assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI.normalize_ref("#foo", 'http://www.example.net') + end end From 7dd8e5aed2b027cfcdbd5d084ef31c3d87d09936 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 18:41:18 +0200 Subject: [PATCH 02/16] Use singleton class to define methods in URI --- lib/json-schema/util/uri.rb | 154 ++++++++++++++++++------------------ test/uri_util_test.rb | 4 +- 2 files changed, 80 insertions(+), 78 deletions(-) diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 94df9b90..d8419e5c 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -5,105 +5,107 @@ module Util module URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero) - def self.normalized_uri(uri, base_path = Dir.pwd) - @normalize_cache ||= {} - normalized_uri = @normalize_cache[uri] - - if !normalized_uri - normalized_uri = parse(uri) - # Check for absolute path - if normalized_uri.relative? - data = normalized_uri - data = File.join(base_path, data) if data.path[0, 1] != '/' - normalized_uri = file_uri(data) - end - @normalize_cache[uri] = normalized_uri.freeze - end - - normalized_uri - end + class << self + def normalized_uri(uri, base_path = Dir.pwd) + @normalize_cache ||= {} + normalized_uri = @normalize_cache[uri] - def self.absolutize_ref(ref, base) - ref_uri = strip_fragment(ref.dup) + if !normalized_uri + normalized_uri = parse(uri) + # Check for absolute path + if normalized_uri.relative? + data = normalized_uri + data = File.join(base_path, data) if data.path[0, 1] != '/' + normalized_uri = file_uri(data) + end + @normalize_cache[uri] = normalized_uri.freeze + end - return ref_uri if ref_uri.absolute? - return parse(base) if ref_uri.path.empty? + normalized_uri + end - uri = strip_fragment(base.dup).join(ref_uri.path) - normalized_uri(uri) - end + def absolutize_ref(ref, base) + ref_uri = strip_fragment(ref.dup) - def self.normalize_ref(ref, base) - ref_uri = parse(ref) - base_uri = parse(base) + return ref_uri if ref_uri.absolute? + return parse(base) if ref_uri.path.empty? - ref_uri.defer_validation do - if ref_uri.relative? - ref_uri.merge!(base_uri) + uri = strip_fragment(base.dup).join(ref_uri.path) + normalized_uri(uri) + end - # Check for absolute path - path, fragment = ref.to_s.split('#') - if path.nil? || path == '' - ref_uri.path = base_uri.path - elsif path[0, 1] == '/' - ref_uri.path = Pathname.new(path).cleanpath.to_s - else - ref_uri.join!(path) + def normalize_ref(ref, base) + ref_uri = parse(ref) + base_uri = parse(base) + + ref_uri.defer_validation do + if ref_uri.relative? + ref_uri.merge!(base_uri) + + # Check for absolute path + path, fragment = ref.to_s.split('#') + if path.nil? || path == '' + ref_uri.path = base_uri.path + elsif path[0, 1] == '/' + ref_uri.path = Pathname.new(path).cleanpath.to_s + else + ref_uri.join!(path) + end + + ref_uri.fragment = fragment end - ref_uri.fragment = fragment + ref_uri.fragment = '' if ref_uri.fragment.nil? || ref_uri.fragment.empty? end - ref_uri.fragment = '' if ref_uri.fragment.nil? || ref_uri.fragment.empty? + ref_uri end - ref_uri - end - - def self.parse(uri) - if uri.is_a?(Addressable::URI) - uri.dup - else - @parse_cache ||= {} - parsed_uri = @parse_cache[uri] - if parsed_uri - parsed_uri.dup + def parse(uri) + if uri.is_a?(Addressable::URI) + uri.dup else - @parse_cache[uri] = Addressable::URI.parse(uri) + @parse_cache ||= {} + parsed_uri = @parse_cache[uri] + if parsed_uri + parsed_uri.dup + else + @parse_cache[uri] = Addressable::URI.parse(uri) + end end + rescue Addressable::URI::InvalidURIError => e + raise JSON::Schema::UriError, e.message end - rescue Addressable::URI::InvalidURIError => e - raise JSON::Schema::UriError, e.message - end - def self.strip_fragment(uri) - parsed_uri = parse(uri) - if parsed_uri.fragment.nil? || parsed_uri.fragment.empty? - parsed_uri - else - parsed_uri.merge(fragment: '') + def strip_fragment(uri) + parsed_uri = parse(uri) + if parsed_uri.fragment.nil? || parsed_uri.fragment.empty? + parsed_uri + else + parsed_uri.merge(fragment: '') + end end - end - def self.file_uri(uri) - parsed_uri = parse(uri) + def file_uri(uri) + parsed_uri = parse(uri) - Addressable::URI.convert_path(parsed_uri.path) - end + Addressable::URI.convert_path(parsed_uri.path) + end - def self.unescape_uri(uri) - Addressable::URI.unescape(uri) - end + def unescape_uri(uri) + Addressable::URI.unescape(uri) + end - def self.unescaped_path(uri) - parsed_uri = parse(uri) + def unescaped_path(uri) + parsed_uri = parse(uri) - Addressable::URI.unescape(parsed_uri.path) - end + Addressable::URI.unescape(parsed_uri.path) + end - def self.clear_cache - @parse_cache = {} - @normalize_cache = {} + def clear_cache + @parse_cache = {} + @normalize_cache = {} + end end end end diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index 02397a90..bc5d0f15 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -205,7 +205,7 @@ def test_ref_addressable_uri_with_scheme_host_and_path end def test_normalize_ref_cache - assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI.normalize_ref("#foo", 'http://www.example.com') - assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI.normalize_ref("#foo", 'http://www.example.net') + assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.com') + assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.net') end end From 299091a26fb794b2046798d26eec8266dc62fd83 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 19:07:46 +0200 Subject: [PATCH 03/16] Add caching to JSON::Util::URI.unescaped_path method I decided not to re-raise the `Addressable::URI::InvalidURIError`, which was originally re-raised as `JSON::Schema::UriError`. This decision was made because the subsequent call to `Addressable::URI.unescape(uri)` does not handle these errors, requiring the user to handle both types of errors. Given that any call to the Addressable gem could potentially lead to an error, and the JSON Schema gem does not consistently wrap them as `JSON::Schema::UriError`, I decided to simplify the code for now. Error handling can be added later if necessary. --- json-schema.gemspec | 1 + lib/json-schema/util/uri.rb | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/json-schema.gemspec b/json-schema.gemspec index 8ad36d09..8dcc04cf 100644 --- a/json-schema.gemspec +++ b/json-schema.gemspec @@ -24,4 +24,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'webmock' s.add_runtime_dependency 'addressable', '>= 2.8' + s.add_runtime_dependency 'concurrent-ruby' end diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index d8419e5c..18308bd7 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -1,10 +1,14 @@ require 'addressable/uri' +require 'concurrent' module JSON module Util module URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero) + @cache_mutex = Mutex.new + @unescaped_path_cache = Concurrent::Map.new + class << self def normalized_uri(uri, base_path = Dir.pwd) @normalize_cache ||= {} @@ -92,20 +96,38 @@ def file_uri(uri) Addressable::URI.convert_path(parsed_uri.path) end + # @return [String] def unescape_uri(uri) Addressable::URI.unescape(uri) end + # @param uri [Addressable::URI, String] + # @return [String] def unescaped_path(uri) - parsed_uri = parse(uri) + unescaped_path_cache.compute_if_absent(uri) do + path = Addressable::URI.parse(uri).path - Addressable::URI.unescape(parsed_uri.path) + unescape_uri(path) + end end def clear_cache - @parse_cache = {} - @normalize_cache = {} + cache_mutex.synchronize do + @unescaped_path_cache = Concurrent::Map.new + @parse_cache = {} + @normalize_cache = {} + end end + + private + + # @!attribute cache_mutex + # @return [Mutex] + attr_reader :cache_mutex + + # @!attribute unescaped_path_cache + # @return [Concurrent::Map] + attr_reader :unescaped_path_cache end end end From f215843a99eed681d544373404280876a483d7da Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 19:28:21 +0200 Subject: [PATCH 04/16] Add caching to JSON::Util::URI.file_uri method --- lib/json-schema/util/uri.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 18308bd7..70011cbc 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -8,6 +8,7 @@ module URI @cache_mutex = Mutex.new @unescaped_path_cache = Concurrent::Map.new + @file_uri_cache = Concurrent::Map.new class << self def normalized_uri(uri, base_path = Dir.pwd) @@ -90,10 +91,14 @@ def strip_fragment(uri) end end + # @param uri [Addressable::URI, String] + # @return [String] def file_uri(uri) - parsed_uri = parse(uri) + file_uri_cache.compute_if_absent(uri) do + path = Addressable::URI.parse(uri).path - Addressable::URI.convert_path(parsed_uri.path) + Addressable::URI.convert_path(path) + end end # @return [String] @@ -128,6 +133,10 @@ def clear_cache # @!attribute unescaped_path_cache # @return [Concurrent::Map] attr_reader :unescaped_path_cache + + # @attribute file_uri_cache + # @return [Concurrent::Map] + attr_reader :file_uri_cache end end end From 8b1b6169f73a8cde2d9bacc103facd7ea35845ad Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 22:35:28 +0200 Subject: [PATCH 05/16] Move URI.unescape_uri to URI2.unescape --- lib/json-schema.rb | 1 + lib/json-schema/attributes/ref.rb | 2 +- lib/json-schema/util/uri.rb | 7 +------ lib/json-schema/util/uri2.rb | 10 ++++++++++ 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 lib/json-schema/util/uri2.rb diff --git a/lib/json-schema.rb b/lib/json-schema.rb index 8d7c1d35..6823e47a 100644 --- a/lib/json-schema.rb +++ b/lib/json-schema.rb @@ -10,6 +10,7 @@ require 'json-schema/util/array_set' require 'json-schema/util/uri' +require 'json-schema/util/uri2' require 'json-schema/schema' require 'json-schema/schema/reader' require 'json-schema/validator' diff --git a/lib/json-schema/attributes/ref.rb b/lib/json-schema/attributes/ref.rb index cfe52666..665b9261 100644 --- a/lib/json-schema/attributes/ref.rb +++ b/lib/json-schema/attributes/ref.rb @@ -32,7 +32,7 @@ def self.get_referenced_uri_and_schema(s, current_schema, validator) if ref_schema # Perform fragment resolution to retrieve the appropriate level for the schema target_schema = ref_schema.schema - fragments = JSON::Util::URI.parse(JSON::Util::URI.unescape_uri(temp_uri)).fragment.split('/') + fragments = JSON::Util::URI.parse(JSON::Util::URI2.unescape(temp_uri)).fragment.split('/') fragment_path = '' fragments.each do |fragment| if fragment && fragment != '' diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 70011cbc..584fe6cf 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -101,18 +101,13 @@ def file_uri(uri) end end - # @return [String] - def unescape_uri(uri) - Addressable::URI.unescape(uri) - end - # @param uri [Addressable::URI, String] # @return [String] def unescaped_path(uri) unescaped_path_cache.compute_if_absent(uri) do path = Addressable::URI.parse(uri).path - unescape_uri(path) + Addressable::URI.unescape(path) end end diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb new file mode 100644 index 00000000..100c1114 --- /dev/null +++ b/lib/json-schema/util/uri2.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'addressable' + +module JSON + module Util + class URI2 < Addressable::URI + end + end +end From 54f71895ab3bdd3b89139a781482cdf81879874b Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 22:42:14 +0200 Subject: [PATCH 06/16] Replace URI.unescaped_path with URI.unescape_path --- lib/json-schema/schema/reader.rb | 2 +- lib/json-schema/util/uri.rb | 16 ---------------- lib/json-schema/util/uri2.rb | 14 ++++++++++++++ lib/json-schema/validator.rb | 2 +- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/json-schema/schema/reader.rb b/lib/json-schema/schema/reader.rb index 6bf6469e..9c3c5768 100644 --- a/lib/json-schema/schema/reader.rb +++ b/lib/json-schema/schema/reader.rb @@ -130,7 +130,7 @@ def read_uri(uri) def read_file(pathname) if accept_file?(pathname) - File.read(JSON::Util::URI.unescaped_path(pathname.to_s)) + File.read(JSON::Util::URI2.unescape_path(pathname.to_s)) else raise JSON::Schema::ReadRefused.new(pathname.to_s, :file) end diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 584fe6cf..c04136f8 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -7,7 +7,6 @@ module URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero) @cache_mutex = Mutex.new - @unescaped_path_cache = Concurrent::Map.new @file_uri_cache = Concurrent::Map.new class << self @@ -101,19 +100,8 @@ def file_uri(uri) end end - # @param uri [Addressable::URI, String] - # @return [String] - def unescaped_path(uri) - unescaped_path_cache.compute_if_absent(uri) do - path = Addressable::URI.parse(uri).path - - Addressable::URI.unescape(path) - end - end - def clear_cache cache_mutex.synchronize do - @unescaped_path_cache = Concurrent::Map.new @parse_cache = {} @normalize_cache = {} end @@ -125,10 +113,6 @@ def clear_cache # @return [Mutex] attr_reader :cache_mutex - # @!attribute unescaped_path_cache - # @return [Concurrent::Map] - attr_reader :unescaped_path_cache - # @attribute file_uri_cache # @return [Concurrent::Map] attr_reader :file_uri_cache diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb index 100c1114..6f1ec438 100644 --- a/lib/json-schema/util/uri2.rb +++ b/lib/json-schema/util/uri2.rb @@ -5,6 +5,20 @@ module JSON module Util class URI2 < Addressable::URI + class << self + # @param uri [String, Addressable::URI + # @return [String] + def unescape_path(uri) + parse(uri).unescape_path + end + end + + # Unencodes any percent encoded characters within a path component. + # + # @return [String] + def unescape_path + self.class.unescape_component(path) + end end end end diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index af9dc316..1f5f5d8c 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -605,7 +605,7 @@ def custom_open(uri) end else begin - File.read(JSON::Util::URI.unescaped_path(uri)) + File.read(JSON::Util::URI2.unescape_path(uri)) rescue SystemCallError => e raise JSON::Schema::JsonLoadError, e.message end From c515f742c2397be3ce1ef7d70be2e9d383ac8489 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 22:50:06 +0200 Subject: [PATCH 07/16] Replace URI.file_uri with URI2.file_uri --- lib/json-schema/schema/reader.rb | 2 +- lib/json-schema/util/uri.rb | 17 +---------------- lib/json-schema/util/uri2.rb | 2 ++ lib/json-schema/validator.rb | 2 +- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/lib/json-schema/schema/reader.rb b/lib/json-schema/schema/reader.rb index 9c3c5768..28452a46 100644 --- a/lib/json-schema/schema/reader.rb +++ b/lib/json-schema/schema/reader.rb @@ -87,7 +87,7 @@ def initialize(options = {}) def read(location) uri = JSON::Util::URI.parse(location.to_s) body = if uri.scheme.nil? || uri.scheme == 'file' - uri = JSON::Util::URI.file_uri(uri) + uri = JSON::Util::URI2.file_uri(uri) read_file(Pathname.new(uri.path).expand_path) else read_uri(uri) diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index c04136f8..fe4208f6 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -7,7 +7,6 @@ module URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero) @cache_mutex = Mutex.new - @file_uri_cache = Concurrent::Map.new class << self def normalized_uri(uri, base_path = Dir.pwd) @@ -20,7 +19,7 @@ def normalized_uri(uri, base_path = Dir.pwd) if normalized_uri.relative? data = normalized_uri data = File.join(base_path, data) if data.path[0, 1] != '/' - normalized_uri = file_uri(data) + normalized_uri = URI2.file_uri(data) end @normalize_cache[uri] = normalized_uri.freeze end @@ -90,16 +89,6 @@ def strip_fragment(uri) end end - # @param uri [Addressable::URI, String] - # @return [String] - def file_uri(uri) - file_uri_cache.compute_if_absent(uri) do - path = Addressable::URI.parse(uri).path - - Addressable::URI.convert_path(path) - end - end - def clear_cache cache_mutex.synchronize do @parse_cache = {} @@ -112,10 +101,6 @@ def clear_cache # @!attribute cache_mutex # @return [Mutex] attr_reader :cache_mutex - - # @attribute file_uri_cache - # @return [Concurrent::Map] - attr_reader :file_uri_cache end end end diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb index 6f1ec438..a8fd3530 100644 --- a/lib/json-schema/util/uri2.rb +++ b/lib/json-schema/util/uri2.rb @@ -6,6 +6,8 @@ module JSON module Util class URI2 < Addressable::URI class << self + alias file_uri convert_path + # @param uri [String, Addressable::URI # @return [String] def unescape_path(uri) diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index 1f5f5d8c..d55f0f61 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -139,7 +139,7 @@ def load_ref_schema(parent_schema, ref) return true if self.class.schema_loaded?(schema_uri) validator = self.class.validator_for_uri(schema_uri, false) - schema_uri = JSON::Util::URI.file_uri(validator.metaschema) if validator + schema_uri = JSON::Util::URI2.file_uri(validator.metaschema) if validator schema = @options[:schema_reader].read(schema_uri) self.class.add_schema(schema) From 88c1a21399ccdc7f9c5bc328e0a270ccb4f3ca98 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Fri, 5 Jul 2024 23:04:52 +0200 Subject: [PATCH 08/16] Replace URI.strip_fragment with URI2.strip_fragment --- lib/json-schema/schema.rb | 2 +- lib/json-schema/util/uri.rb | 19 ++++++++----------- lib/json-schema/util/uri2.rb | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/json-schema/schema.rb b/lib/json-schema/schema.rb index 0a2c2464..550811e9 100644 --- a/lib/json-schema/schema.rb +++ b/lib/json-schema/schema.rb @@ -16,7 +16,7 @@ def initialize(schema, uri, parent_validator = nil) end @uri = temp_uri end - @uri = JSON::Util::URI.strip_fragment(@uri) + @uri = JSON::Util::URI2.strip_fragment(@uri) # If there is a $schema on this schema, use it to determine which validator to use if @schema['$schema'] diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index fe4208f6..33841d06 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -28,12 +28,18 @@ def normalized_uri(uri, base_path = Dir.pwd) end def absolutize_ref(ref, base) - ref_uri = strip_fragment(ref.dup) + parsed_ref = parse(ref.dup) + # Like URI2.strip_fragment but with wired caching inside parse + ref_uri = if parsed_ref.fragment.nil? || parsed_ref.fragment.empty? + parsed_ref + else + parsed_ref.merge(fragment: '') + end return ref_uri if ref_uri.absolute? return parse(base) if ref_uri.path.empty? - uri = strip_fragment(base.dup).join(ref_uri.path) + uri = URI2.strip_fragment(base).join(ref_uri.path) normalized_uri(uri) end @@ -80,15 +86,6 @@ def parse(uri) raise JSON::Schema::UriError, e.message end - def strip_fragment(uri) - parsed_uri = parse(uri) - if parsed_uri.fragment.nil? || parsed_uri.fragment.empty? - parsed_uri - else - parsed_uri.merge(fragment: '') - end - end - def clear_cache cache_mutex.synchronize do @parse_cache = {} diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb index a8fd3530..1b410da8 100644 --- a/lib/json-schema/util/uri2.rb +++ b/lib/json-schema/util/uri2.rb @@ -13,6 +13,13 @@ class << self def unescape_path(uri) parse(uri).unescape_path end + + # Strips the fragment from the URI. + # @param uri [String, Addressable::URI] + # @return [Addressable::URI] + def strip_fragment(uri) + parse(uri).without_fragment + end end # Unencodes any percent encoded characters within a path component. @@ -21,6 +28,16 @@ def unescape_path(uri) def unescape_path self.class.unescape_component(path) end + + # Strips the fragment from the URI. + # @return [Addressable::URI] a new instance of URI without a fragment + def without_fragment + if fragment.nil? || fragment.empty? + dup + else + merge(fragment: '') + end + end end end end From a9b6c7f0bae102b6f9909b9ac0d9f7f5d337cd8d Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Sat, 6 Jul 2024 11:20:00 +0200 Subject: [PATCH 09/16] Replace URI.normalized_uri with URI2.normalize_uri --- lib/json-schema/util/uri.rb | 21 +--------- lib/json-schema/util/uri2.rb | 40 +++++++++++++++++-- lib/json-schema/validator.rb | 10 ++--- test/uri2_util_test.rb | 53 ++++++++++++++++++++++++++ test/uri_util_test.rb | 74 ------------------------------------ 5 files changed, 96 insertions(+), 102 deletions(-) create mode 100644 test/uri2_util_test.rb diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 33841d06..ffdfc6bf 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -9,24 +9,6 @@ module URI @cache_mutex = Mutex.new class << self - def normalized_uri(uri, base_path = Dir.pwd) - @normalize_cache ||= {} - normalized_uri = @normalize_cache[uri] - - if !normalized_uri - normalized_uri = parse(uri) - # Check for absolute path - if normalized_uri.relative? - data = normalized_uri - data = File.join(base_path, data) if data.path[0, 1] != '/' - normalized_uri = URI2.file_uri(data) - end - @normalize_cache[uri] = normalized_uri.freeze - end - - normalized_uri - end - def absolutize_ref(ref, base) parsed_ref = parse(ref.dup) # Like URI2.strip_fragment but with wired caching inside parse @@ -40,7 +22,7 @@ def absolutize_ref(ref, base) return parse(base) if ref_uri.path.empty? uri = URI2.strip_fragment(base).join(ref_uri.path) - normalized_uri(uri) + URI2.normalize_uri(uri) end def normalize_ref(ref, base) @@ -89,7 +71,6 @@ def parse(uri) def clear_cache cache_mutex.synchronize do @parse_cache = {} - @normalize_cache = {} end end diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb index 1b410da8..78b8c24b 100644 --- a/lib/json-schema/util/uri2.rb +++ b/lib/json-schema/util/uri2.rb @@ -6,7 +6,19 @@ module JSON module Util class URI2 < Addressable::URI class << self - alias file_uri convert_path + # @param uri [String, Addressable::URI] + # @return [Addressable::URI, nil] + def parse(uri) + super(uri) + rescue Addressable::URI::InvalidURIError => e + raise JSON::Schema::UriError, e.message + end + + # @param uri [String, Addressable::URI] + # @return [Addressable::URI, nil] + def file_uri(uri) + convert_path(parse(uri).path) + end # @param uri [String, Addressable::URI # @return [String] @@ -18,7 +30,13 @@ def unescape_path(uri) # @param uri [String, Addressable::URI] # @return [Addressable::URI] def strip_fragment(uri) - parse(uri).without_fragment + parse(uri).strip_fragment + end + + # @param uri [String, Addressable::URI] + # @return [Addressable::URI] + def normalize_uri(uri, base_path = Dir.pwd) + parse(uri).normalize_uri(base_path) end end @@ -31,13 +49,29 @@ def unescape_path # Strips the fragment from the URI. # @return [Addressable::URI] a new instance of URI without a fragment - def without_fragment + def strip_fragment if fragment.nil? || fragment.empty? dup else merge(fragment: '') end end + + # Normalizes the URI based on the provided base path. + # + # @param base_path [String] the base path to use for relative URIs. Defaults to the current working directory. + # @return [Addressable::URI] the normalized URI or nil + def normalize_uri(base_path = Dir.pwd) + if relative? + if path[0, 1] == '/' + self.class.file_uri(self) + else + self.class.file_uri(File.join(base_path, self)) + end + else + dup + end + end end end end diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index d55f0f61..3f0609fa 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -322,7 +322,7 @@ def schema_loaded?(schema_uri) end def schema_key_for(uri) - key = Util::URI.normalized_uri(uri).to_s + key = Util::URI2.normalize_uri(uri).to_s key.end_with?('#') ? key : "#{key}#" end @@ -535,7 +535,7 @@ def initialize_schema(schema, default_validator) self.class.add_schema(schema) rescue JSON::Schema::JsonParseError # Build a uri for it - schema_uri = Util::URI.normalized_uri(schema) + schema_uri = Util::URI2.normalize_uri(schema) if !self.class.schema_loaded?(schema_uri) schema = @options[:schema_reader].read(schema_uri) schema = JSON::Schema.stringify(schema) @@ -575,7 +575,7 @@ def initialize_data(data) if @options[:json] data = self.class.parse(data) elsif @options[:uri] - json_uri = Util::URI.normalized_uri(data) + json_uri = Util::URI2.normalize_uri(data) data = self.class.parse(custom_open(json_uri)) elsif data.is_a?(String) begin @@ -584,7 +584,7 @@ def initialize_data(data) data = strict_convert ? data : self.class.parse(data) rescue JSON::Schema::JsonParseError begin - json_uri = Util::URI.normalized_uri(data) + json_uri = Util::URI2.normalize_uri(data) data = self.class.parse(custom_open(json_uri)) rescue JSON::Schema::JsonLoadError, JSON::Schema::UriError # Silently discard the error - use the data as-is @@ -596,7 +596,7 @@ def initialize_data(data) end def custom_open(uri) - uri = Util::URI.normalized_uri(uri) if uri.is_a?(String) + uri = Util::URI2.normalize_uri(uri) if uri.is_a?(String) if uri.absolute? && Util::URI::SUPPORTED_PROTOCOLS.include?(uri.scheme) begin URI.open(uri.to_s).read diff --git a/test/uri2_util_test.rb b/test/uri2_util_test.rb new file mode 100644 index 00000000..64e18057 --- /dev/null +++ b/test/uri2_util_test.rb @@ -0,0 +1,53 @@ +require File.expand_path('../support/test_helper', __FILE__) + +class Uri2UtilTest < Minitest::Test + def test_normalized_uri + str = 'https://www.google.com/search' + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search',) + assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') + end + + def test_normalized_uri_with_empty_fragment + str = 'https://www.google.com/search#' + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search', + fragment: nil,) + assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') + end + + def test_normalized_uri_with_fragment + str = 'https://www.google.com/search#foo' + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search', + fragment: 'foo',) + assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') + end + + def test_normalized_uri_for_absolute_path + str = '/foo/bar.json' + uri = Addressable::URI.new(scheme: 'file', + host: '', + path: '/foo/bar.json',) + assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') + end + + def test_normalized_uri_for_relative_path + str = 'foo/bar.json' + uri = Addressable::URI.new(scheme: 'file', + host: '', + path: '/home/foo/bar.json',) + assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') + end + + def test_normalized_uri_for_file_path_with_host + str = 'file://localhost/foo/bar.json' + uri = Addressable::URI.new(scheme: 'file', + host: 'localhost', + path: '/foo/bar.json',) + assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') + end +end diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index bc5d0f15..26b7d7e0 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -11,56 +11,6 @@ def teardown JSON::Util::URI.clear_cache end - def test_normalized_uri - str = 'https://www.google.com/search' - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search',) - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_with_empty_fragment - str = 'https://www.google.com/search#' - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search', - fragment: nil,) - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_with_fragment - str = 'https://www.google.com/search#foo' - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search', - fragment: 'foo',) - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_for_absolute_path - str = '/foo/bar.json' - uri = Addressable::URI.new(scheme: 'file', - host: '', - path: '/foo/bar.json',) - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_for_relative_path - str = 'foo/bar.json' - uri = Addressable::URI.new(scheme: 'file', - host: '', - path: '/home/foo/bar.json',) - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - - def test_normalized_uri_for_file_path_with_host - str = 'file://localhost/foo/bar.json' - uri = Addressable::URI.new(scheme: 'file', - host: 'localhost', - path: '/foo/bar.json',) - assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') - end - def test_uri_parse str = 'https://www.google.com/search' uri = Addressable::URI.new(scheme: 'https', @@ -76,18 +26,6 @@ def test_invalid_uri_parse end end - def test_normalization_cache - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.normalized_uri('foo') - end - - assert_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - - JSON::Util::URI.clear_cache - - refute_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - end - def test_parse_cache cached_uri = populate_cache_with('www.google.com') do JSON::Util::URI.parse('foo') @@ -100,18 +38,6 @@ def test_parse_cache refute_equal(cached_uri, JSON::Util::URI.parse('foo')) end - def test_validator_clear_cache_for_normalized_uri - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.normalized_uri('foo') - end - - assert_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - - validation_errors({ 'type' => 'string' }, 'foo', clear_cache: true) - - refute_equal(cached_uri, JSON::Util::URI.normalized_uri('foo')) - end - def test_validator_clear_cache_for_parse cached_uri = populate_cache_with('www.google.com') do JSON::Util::URI.parse('foo') From 1089144b54c1b7c0d6803d5d82b940c110bccb5f Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Sat, 6 Jul 2024 17:32:17 +0200 Subject: [PATCH 10/16] Replace URI.normalize_ref with URI2.normalize_ref --- lib/json-schema/attributes/ref.rb | 2 +- lib/json-schema/util/uri.rb | 27 ---------- lib/json-schema/util/uri2.rb | 36 +++++++++++++ test/uri2_util_test.rb | 85 +++++++++++++++++++++++++++++++ test/uri_util_test.rb | 85 ------------------------------- 5 files changed, 122 insertions(+), 113 deletions(-) diff --git a/lib/json-schema/attributes/ref.rb b/lib/json-schema/attributes/ref.rb index 665b9261..f21ff8b0 100644 --- a/lib/json-schema/attributes/ref.rb +++ b/lib/json-schema/attributes/ref.rb @@ -22,7 +22,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options def self.get_referenced_uri_and_schema(s, current_schema, validator) uri, schema = nil, nil - temp_uri = JSON::Util::URI.normalize_ref(s['$ref'], current_schema.uri) + temp_uri = JSON::Util::URI2.normalize_ref(s['$ref'], current_schema.uri) # Grab the parent schema from the schema list schema_key = temp_uri.to_s.split('#')[0] + '#' diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index ffdfc6bf..c240452c 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -25,33 +25,6 @@ def absolutize_ref(ref, base) URI2.normalize_uri(uri) end - def normalize_ref(ref, base) - ref_uri = parse(ref) - base_uri = parse(base) - - ref_uri.defer_validation do - if ref_uri.relative? - ref_uri.merge!(base_uri) - - # Check for absolute path - path, fragment = ref.to_s.split('#') - if path.nil? || path == '' - ref_uri.path = base_uri.path - elsif path[0, 1] == '/' - ref_uri.path = Pathname.new(path).cleanpath.to_s - else - ref_uri.join!(path) - end - - ref_uri.fragment = fragment - end - - ref_uri.fragment = '' if ref_uri.fragment.nil? || ref_uri.fragment.empty? - end - - ref_uri - end - def parse(uri) if uri.is_a?(Addressable::URI) uri.dup diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb index 78b8c24b..2d7d4540 100644 --- a/lib/json-schema/util/uri2.rb +++ b/lib/json-schema/util/uri2.rb @@ -38,6 +38,15 @@ def strip_fragment(uri) def normalize_uri(uri, base_path = Dir.pwd) parse(uri).normalize_uri(base_path) end + + # Normalizes the reference URI based on the provided base URI + # + # @param ref [String, Addressable::URI] + # @param base [String, Addressable::URI] + # @return [Addressable::URI] + def normalize_ref(ref, base) + parse(ref).normalize_ref(base) + end end # Unencodes any percent encoded characters within a path component. @@ -72,6 +81,33 @@ def normalize_uri(base_path = Dir.pwd) dup end end + + # @param base [Addressable::URI, String] + # @return [Addressable::URI] + def normalize_ref(base) + base_uri = self.class.parse(base) + defer_validation do + if relative? + # Check for absolute path + path, fragment = to_s.split('#') + merge!(base_uri) + + if path.nil? || path == '' + self.path = base_uri.path + elsif path[0, 1] == '/' + self.path = Pathname.new(path).cleanpath.to_s + else + join!(path) + end + + self.fragment = fragment + end + + self.fragment = '' if self.fragment.nil? || self.fragment.empty? + end + + self + end end end end diff --git a/test/uri2_util_test.rb b/test/uri2_util_test.rb index 64e18057..bef2a3e5 100644 --- a/test/uri2_util_test.rb +++ b/test/uri2_util_test.rb @@ -50,4 +50,89 @@ def test_normalized_uri_for_file_path_with_host path: '/foo/bar.json',) assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') end + + def test_ref_fragment_path + uri = '#some-thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/foo/#some-thing'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://www.example.com/foo/#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_file_path + uri = '/some/thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri + uri = 'http://foo-bar.com' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_path + uri = 'http://foo-bar.com/some/thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_fragment + uri = 'http://foo-bar.com/some/thing#foo' + base = 'http://www.example.com/hello/#world' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_fragment_and_base_with_no_fragment + uri = 'http://foo-bar.com/some/thing#foo' + base = 'http://www.example.com/hello' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_relative_path + uri = 'hello/world' + base = 'http://www.example.com/foo/#bar' + + # assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_host + uri = Addressable::URI.new(host: 'foo-bar.com') + base = 'http://www.example.com/hello/#world' + + assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com#'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://www.example.com/hello/#world'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_host_and_path + uri = Addressable::URI.new(host: 'foo-bar.com', path: '/hello/world') + base = 'http://www.example.com/a/#b' + + assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('http://www.example.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_scheme_host_and_path + uri = Addressable::URI.new(scheme: 'https', host: 'foo-bar.com', path: '/hello/world') + base = 'http://www.example.com/a/#b' + + assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) + # assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_normalize_ref_cache + assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI2.normalize_ref('#foo', 'http://www.example.com') + assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI2.normalize_ref('#foo', 'http://www.example.net') + end end diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index 26b7d7e0..1edc8c83 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -49,89 +49,4 @@ def test_validator_clear_cache_for_parse refute_equal(cached_uri, JSON::Util::URI.parse('foo')) end - - def test_ref_fragment_path - uri = '#some-thing' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://www.example.com/foo/#some-thing'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/foo/#'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_file_path - uri = '/some/thing' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_uri - uri = 'http://foo-bar.com' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_uri_with_path - uri = 'http://foo-bar.com/some/thing' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_uri_with_fragment - uri = 'http://foo-bar.com/some/thing#foo' - base = 'http://www.example.com/hello/#world' - - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_uri_with_fragment_and_base_with_no_fragment - uri = 'http://foo-bar.com/some/thing#foo' - base = 'http://www.example.com/hello' - - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_relative_path - uri = 'hello/world' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_addressable_uri_with_host - uri = Addressable::URI.new(host: 'foo-bar.com') - base = 'http://www.example.com/hello/#world' - - assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com#'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/hello/#world'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_addressable_uri_with_host_and_path - uri = Addressable::URI.new(host: 'foo-bar.com', path: '/hello/world') - base = 'http://www.example.com/a/#b' - - assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_ref_addressable_uri_with_scheme_host_and_path - uri = Addressable::URI.new(scheme: 'https', host: 'foo-bar.com', path: '/hello/world') - base = 'http://www.example.com/a/#b' - - assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) - end - - def test_normalize_ref_cache - assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.com') - assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.net') - end end From 88339255c7b66ea8aa76f70a2b24653fab4bbf15 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Sat, 6 Jul 2024 18:21:52 +0200 Subject: [PATCH 11/16] Use URI2.absolutize_ref instead of URI.absolutize_ref --- lib/json-schema/util/uri.rb | 16 ---------------- lib/json-schema/util/uri2.rb | 19 +++++++++++++++++-- lib/json-schema/validator.rb | 2 +- test/uri2_util_test.rb | 22 +++++++++++----------- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index c240452c..4e9d59ad 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -9,22 +9,6 @@ module URI @cache_mutex = Mutex.new class << self - def absolutize_ref(ref, base) - parsed_ref = parse(ref.dup) - # Like URI2.strip_fragment but with wired caching inside parse - ref_uri = if parsed_ref.fragment.nil? || parsed_ref.fragment.empty? - parsed_ref - else - parsed_ref.merge(fragment: '') - end - - return ref_uri if ref_uri.absolute? - return parse(base) if ref_uri.path.empty? - - uri = URI2.strip_fragment(base).join(ref_uri.path) - URI2.normalize_uri(uri) - end - def parse(uri) if uri.is_a?(Addressable::URI) uri.dup diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb index 2d7d4540..2291d3ba 100644 --- a/lib/json-schema/util/uri2.rb +++ b/lib/json-schema/util/uri2.rb @@ -47,6 +47,10 @@ def normalize_uri(uri, base_path = Dir.pwd) def normalize_ref(ref, base) parse(ref).normalize_ref(base) end + + def absolutize_ref(ref, base) + parse(ref).absolutize_ref(base) + end end # Unencodes any percent encoded characters within a path component. @@ -60,7 +64,7 @@ def unescape_path # @return [Addressable::URI] a new instance of URI without a fragment def strip_fragment if fragment.nil? || fragment.empty? - dup + self else merge(fragment: '') end @@ -78,7 +82,7 @@ def normalize_uri(base_path = Dir.pwd) self.class.file_uri(File.join(base_path, self)) end else - dup + self end end @@ -108,6 +112,17 @@ def normalize_ref(base) self end + + # @param base [Addressable::URI, String] + # @return [Addressable::URI] + def absolutize_ref(base) + ref = strip_fragment + if ref.absolute? + ref + else + self.class.strip_fragment(base).join(ref.path).normalize_uri + end + end end end end diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index 3f0609fa..e1b22209 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -135,7 +135,7 @@ def validate(data) end def load_ref_schema(parent_schema, ref) - schema_uri = JSON::Util::URI.absolutize_ref(ref, parent_schema.uri) + schema_uri = JSON::Util::URI2.absolutize_ref(ref, parent_schema.uri) return true if self.class.schema_loaded?(schema_uri) validator = self.class.validator_for_uri(schema_uri, false) diff --git a/test/uri2_util_test.rb b/test/uri2_util_test.rb index bef2a3e5..a88ae63b 100644 --- a/test/uri2_util_test.rb +++ b/test/uri2_util_test.rb @@ -56,7 +56,7 @@ def test_ref_fragment_path base = 'http://www.example.com/foo/#bar' assert_equal Addressable::URI.parse('http://www.example.com/foo/#some-thing'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://www.example.com/foo/#'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_file_path @@ -64,7 +64,7 @@ def test_ref_file_path base = 'http://www.example.com/foo/#bar' assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_uri @@ -72,7 +72,7 @@ def test_ref_uri base = 'http://www.example.com/foo/#bar' assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_uri_with_path @@ -80,7 +80,7 @@ def test_ref_uri_with_path base = 'http://www.example.com/foo/#bar' assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_uri_with_fragment @@ -88,7 +88,7 @@ def test_ref_uri_with_fragment base = 'http://www.example.com/hello/#world' assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_uri_with_fragment_and_base_with_no_fragment @@ -96,15 +96,15 @@ def test_ref_uri_with_fragment_and_base_with_no_fragment base = 'http://www.example.com/hello' assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_relative_path uri = 'hello/world' base = 'http://www.example.com/foo/#bar' - # assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_addressable_uri_with_host @@ -112,7 +112,7 @@ def test_ref_addressable_uri_with_host base = 'http://www.example.com/hello/#world' assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com#'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://www.example.com/hello/#world'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/hello/#'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_addressable_uri_with_host_and_path @@ -120,7 +120,7 @@ def test_ref_addressable_uri_with_host_and_path base = 'http://www.example.com/a/#b' assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('http://www.example.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/hello/world'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_ref_addressable_uri_with_scheme_host_and_path @@ -128,7 +128,7 @@ def test_ref_addressable_uri_with_scheme_host_and_path base = 'http://www.example.com/a/#b' assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) - # assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI2.absolutize_ref(uri, base) end def test_normalize_ref_cache From 0cc71ab8f638b39c269fe41cfb1298c37a3b37b1 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Sat, 6 Jul 2024 18:25:50 +0200 Subject: [PATCH 12/16] Use URI2.parse instead of URI2.parse --- lib/json-schema/attributes/formats/uri.rb | 2 +- lib/json-schema/attributes/ref.rb | 2 +- lib/json-schema/schema.rb | 2 +- lib/json-schema/schema/reader.rb | 2 +- lib/json-schema/util/uri.rb | 32 ---------------------- lib/json-schema/validator.rb | 9 +++--- lib/json-schema/validators/draft1.rb | 2 +- lib/json-schema/validators/draft2.rb | 2 +- lib/json-schema/validators/draft3.rb | 2 +- lib/json-schema/validators/draft4.rb | 2 +- lib/json-schema/validators/draft6.rb | 2 +- lib/json-schema/validators/hyper-draft1.rb | 2 +- lib/json-schema/validators/hyper-draft2.rb | 2 +- lib/json-schema/validators/hyper-draft3.rb | 2 +- lib/json-schema/validators/hyper-draft4.rb | 2 +- lib/json-schema/validators/hyper-draft6.rb | 2 +- test/uri_util_test.rb | 32 ++-------------------- 17 files changed, 20 insertions(+), 81 deletions(-) diff --git a/lib/json-schema/attributes/formats/uri.rb b/lib/json-schema/attributes/formats/uri.rb index 7a4a148d..0c5c0d32 100644 --- a/lib/json-schema/attributes/formats/uri.rb +++ b/lib/json-schema/attributes/formats/uri.rb @@ -9,7 +9,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options error_message = "The property '#{build_fragment(fragments)}' must be a valid URI" begin - JSON::Util::URI.parse(data) + JSON::Util::URI2.parse(data) rescue JSON::Schema::UriError validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end diff --git a/lib/json-schema/attributes/ref.rb b/lib/json-schema/attributes/ref.rb index f21ff8b0..66eb35a6 100644 --- a/lib/json-schema/attributes/ref.rb +++ b/lib/json-schema/attributes/ref.rb @@ -32,7 +32,7 @@ def self.get_referenced_uri_and_schema(s, current_schema, validator) if ref_schema # Perform fragment resolution to retrieve the appropriate level for the schema target_schema = ref_schema.schema - fragments = JSON::Util::URI.parse(JSON::Util::URI2.unescape(temp_uri)).fragment.split('/') + fragments = JSON::Util::URI2.parse(JSON::Util::URI2.unescape(temp_uri)).fragment.split('/') fragment_path = '' fragments.each do |fragment| if fragment && fragment != '' diff --git a/lib/json-schema/schema.rb b/lib/json-schema/schema.rb index 550811e9..ff83bd68 100644 --- a/lib/json-schema/schema.rb +++ b/lib/json-schema/schema.rb @@ -10,7 +10,7 @@ def initialize(schema, uri, parent_validator = nil) # If there is an ID on this schema, use it to generate the URI if @schema['id'].is_a?(String) - temp_uri = JSON::Util::URI.parse(@schema['id']) + temp_uri = JSON::Util::URI2.parse(@schema['id']) if temp_uri.relative? temp_uri = uri.join(temp_uri) end diff --git a/lib/json-schema/schema/reader.rb b/lib/json-schema/schema/reader.rb index 28452a46..2e66c1ff 100644 --- a/lib/json-schema/schema/reader.rb +++ b/lib/json-schema/schema/reader.rb @@ -85,7 +85,7 @@ def initialize(options = {}) # @raise [JSON::Schema::ReadFailed] if reading the location was acceptable but the # attempt to retrieve it failed def read(location) - uri = JSON::Util::URI.parse(location.to_s) + uri = JSON::Util::URI2.parse(location.to_s) body = if uri.scheme.nil? || uri.scheme == 'file' uri = JSON::Util::URI2.file_uri(uri) read_file(Pathname.new(uri.path).expand_path) diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 4e9d59ad..48eb87f3 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -5,38 +5,6 @@ module JSON module Util module URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero) - - @cache_mutex = Mutex.new - - class << self - def parse(uri) - if uri.is_a?(Addressable::URI) - uri.dup - else - @parse_cache ||= {} - parsed_uri = @parse_cache[uri] - if parsed_uri - parsed_uri.dup - else - @parse_cache[uri] = Addressable::URI.parse(uri) - end - end - rescue Addressable::URI::InvalidURIError => e - raise JSON::Schema::UriError, e.message - end - - def clear_cache - cache_mutex.synchronize do - @parse_cache = {} - end - end - - private - - # @!attribute cache_mutex - # @return [Mutex] - attr_reader :cache_mutex - end end end end diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index e1b22209..e6a83433 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -300,7 +300,6 @@ def schema_reader=(reader) def clear_cache @@schemas = {} - JSON::Util::URI.clear_cache end def schemas @@ -342,7 +341,7 @@ def default_validator def validator_for_uri(schema_uri, raise_not_found = true) return default_validator unless schema_uri - u = JSON::Util::URI.parse(schema_uri) + u = JSON::Util::URI2.parse(schema_uri) validator = validators["#{u.scheme}://#{u.host}#{u.path}"] if validator.nil? && raise_not_found raise JSON::Schema::SchemaError, "Schema not found: #{schema_uri}" @@ -527,7 +526,7 @@ def initialize_schema(schema, default_validator) if schema.is_a?(String) begin # Build a fake URI for this - schema_uri = JSON::Util::URI.parse(fake_uuid(schema)) + schema_uri = JSON::Util::URI2.parse(fake_uuid(schema)) schema = JSON::Schema.new(JSON::Validator.parse(schema), schema_uri, default_validator) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema @@ -549,14 +548,14 @@ def initialize_schema(schema, default_validator) schema = self.class.schema_for_uri(schema_uri) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema - schema.uri = JSON::Util::URI.parse(fake_uuid(serialize(schema.schema))) + schema.uri = JSON::Util::URI2.parse(fake_uuid(serialize(schema.schema))) self.class.add_schema(schema) end schema end end elsif schema.is_a?(Hash) - schema_uri = JSON::Util::URI.parse(fake_uuid(serialize(schema))) + schema_uri = JSON::Util::URI2.parse(fake_uuid(serialize(schema))) schema = JSON::Schema.stringify(schema) schema = JSON::Schema.new(schema, schema_uri, default_validator) if @options[:list] && @options[:fragment].nil? diff --git a/lib/json-schema/validators/draft1.rb b/lib/json-schema/validators/draft1.rb index 6d1c2a82..682f455c 100644 --- a/lib/json-schema/validators/draft1.rb +++ b/lib/json-schema/validators/draft1.rb @@ -32,7 +32,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-01/schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-01/schema#') @names = ['draft1'] @metaschema_name = 'draft-01.json' end diff --git a/lib/json-schema/validators/draft2.rb b/lib/json-schema/validators/draft2.rb index dd9d56f5..931abbc6 100644 --- a/lib/json-schema/validators/draft2.rb +++ b/lib/json-schema/validators/draft2.rb @@ -33,7 +33,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-02/schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-02/schema#') @names = ['draft2'] @metaschema_name = 'draft-02.json' end diff --git a/lib/json-schema/validators/draft3.rb b/lib/json-schema/validators/draft3.rb index 42acc736..7c16b835 100644 --- a/lib/json-schema/validators/draft3.rb +++ b/lib/json-schema/validators/draft3.rb @@ -37,7 +37,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-03/schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-03/schema#') @names = ['draft3', 'http://json-schema.org/draft-03/schema#'] @metaschema_name = 'draft-03.json' end diff --git a/lib/json-schema/validators/draft4.rb b/lib/json-schema/validators/draft4.rb index a3418dd1..bc40ec67 100644 --- a/lib/json-schema/validators/draft4.rb +++ b/lib/json-schema/validators/draft4.rb @@ -42,7 +42,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-04/schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-04/schema#') @names = ['draft4', 'http://json-schema.org/draft-04/schema#'] @metaschema_name = 'draft-04.json' end diff --git a/lib/json-schema/validators/draft6.rb b/lib/json-schema/validators/draft6.rb index 66eb038e..a2faa206 100644 --- a/lib/json-schema/validators/draft6.rb +++ b/lib/json-schema/validators/draft6.rb @@ -44,7 +44,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-06/schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-06/schema#') @names = ['draft6', 'http://json-schema.org/draft-06/schema#'] @metaschema_name = 'draft-06.json' end diff --git a/lib/json-schema/validators/hyper-draft1.rb b/lib/json-schema/validators/hyper-draft1.rb index 7e6a3189..12902898 100644 --- a/lib/json-schema/validators/hyper-draft1.rb +++ b/lib/json-schema/validators/hyper-draft1.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft1 < Draft1 def initialize super - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-01/hyper-schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-01/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft2.rb b/lib/json-schema/validators/hyper-draft2.rb index ad8b1e0b..6f6d7896 100644 --- a/lib/json-schema/validators/hyper-draft2.rb +++ b/lib/json-schema/validators/hyper-draft2.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft2 < Draft2 def initialize super - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-02/hyper-schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-02/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft3.rb b/lib/json-schema/validators/hyper-draft3.rb index c00da997..b41201cf 100644 --- a/lib/json-schema/validators/hyper-draft3.rb +++ b/lib/json-schema/validators/hyper-draft3.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft3 < Draft3 def initialize super - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-03/hyper-schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-03/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft4.rb b/lib/json-schema/validators/hyper-draft4.rb index 7729d3fe..ad7441b9 100644 --- a/lib/json-schema/validators/hyper-draft4.rb +++ b/lib/json-schema/validators/hyper-draft4.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft4 < Draft4 def initialize super - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-04/hyper-schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-04/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft6.rb b/lib/json-schema/validators/hyper-draft6.rb index 050245bf..1c8a2cf8 100644 --- a/lib/json-schema/validators/hyper-draft6.rb +++ b/lib/json-schema/validators/hyper-draft6.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft6 < Draft6 def initialize super - @uri = JSON::Util::URI.parse('http://json-schema.org/draft-06/hyper-schema#') + @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-06/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index 1edc8c83..b728d6f8 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -7,46 +7,18 @@ def populate_cache_with(str, &blk) cached_uri end - def teardown - JSON::Util::URI.clear_cache - end - def test_uri_parse str = 'https://www.google.com/search' uri = Addressable::URI.new(scheme: 'https', host: 'www.google.com', path: 'search',) - assert_equal uri, JSON::Util::URI.parse(str) + assert_equal uri, JSON::Util::URI2.parse(str) end def test_invalid_uri_parse uri = ':::::::' assert_raises(JSON::Schema::UriError) do - JSON::Util::URI.parse(uri) - end - end - - def test_parse_cache - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.parse('foo') - end - - assert_equal(cached_uri, JSON::Util::URI.parse('foo')) - - JSON::Util::URI.clear_cache - - refute_equal(cached_uri, JSON::Util::URI.parse('foo')) - end - - def test_validator_clear_cache_for_parse - cached_uri = populate_cache_with('www.google.com') do - JSON::Util::URI.parse('foo') + JSON::Util::URI2.parse(uri) end - - assert_equal(cached_uri, JSON::Util::URI.parse('foo')) - - validation_errors({ 'type' => 'string' }, 'foo', clear_cache: true) - - refute_equal(cached_uri, JSON::Util::URI.parse('foo')) end end From 7c5d3232a31073040987622e55b30105b1e24799 Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Sat, 6 Jul 2024 18:28:24 +0200 Subject: [PATCH 13/16] Rename URI2 to URI --- json-schema.gemspec | 1 - lib/json-schema.rb | 1 - lib/json-schema/attributes/formats/uri.rb | 2 +- lib/json-schema/attributes/ref.rb | 4 +- lib/json-schema/schema.rb | 4 +- lib/json-schema/schema/reader.rb | 6 +- lib/json-schema/util/uri.rb | 124 +++++++++++++++++- lib/json-schema/util/uri2.rb | 128 ------------------- lib/json-schema/validator.rb | 24 ++-- lib/json-schema/validators/draft1.rb | 2 +- lib/json-schema/validators/draft2.rb | 2 +- lib/json-schema/validators/draft3.rb | 2 +- lib/json-schema/validators/draft4.rb | 2 +- lib/json-schema/validators/draft6.rb | 2 +- lib/json-schema/validators/hyper-draft1.rb | 2 +- lib/json-schema/validators/hyper-draft2.rb | 2 +- lib/json-schema/validators/hyper-draft3.rb | 2 +- lib/json-schema/validators/hyper-draft4.rb | 2 +- lib/json-schema/validators/hyper-draft6.rb | 2 +- test/uri2_util_test.rb | 138 -------------------- test/uri_util_test.rb | 141 ++++++++++++++++++++- 21 files changed, 287 insertions(+), 306 deletions(-) delete mode 100644 lib/json-schema/util/uri2.rb delete mode 100644 test/uri2_util_test.rb diff --git a/json-schema.gemspec b/json-schema.gemspec index 8dcc04cf..8ad36d09 100644 --- a/json-schema.gemspec +++ b/json-schema.gemspec @@ -24,5 +24,4 @@ Gem::Specification.new do |s| s.add_development_dependency 'webmock' s.add_runtime_dependency 'addressable', '>= 2.8' - s.add_runtime_dependency 'concurrent-ruby' end diff --git a/lib/json-schema.rb b/lib/json-schema.rb index 6823e47a..8d7c1d35 100644 --- a/lib/json-schema.rb +++ b/lib/json-schema.rb @@ -10,7 +10,6 @@ require 'json-schema/util/array_set' require 'json-schema/util/uri' -require 'json-schema/util/uri2' require 'json-schema/schema' require 'json-schema/schema/reader' require 'json-schema/validator' diff --git a/lib/json-schema/attributes/formats/uri.rb b/lib/json-schema/attributes/formats/uri.rb index 0c5c0d32..7a4a148d 100644 --- a/lib/json-schema/attributes/formats/uri.rb +++ b/lib/json-schema/attributes/formats/uri.rb @@ -9,7 +9,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options error_message = "The property '#{build_fragment(fragments)}' must be a valid URI" begin - JSON::Util::URI2.parse(data) + JSON::Util::URI.parse(data) rescue JSON::Schema::UriError validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) end diff --git a/lib/json-schema/attributes/ref.rb b/lib/json-schema/attributes/ref.rb index 66eb35a6..895c38ae 100644 --- a/lib/json-schema/attributes/ref.rb +++ b/lib/json-schema/attributes/ref.rb @@ -22,7 +22,7 @@ def self.validate(current_schema, data, fragments, processor, validator, options def self.get_referenced_uri_and_schema(s, current_schema, validator) uri, schema = nil, nil - temp_uri = JSON::Util::URI2.normalize_ref(s['$ref'], current_schema.uri) + temp_uri = JSON::Util::URI.normalize_ref(s['$ref'], current_schema.uri) # Grab the parent schema from the schema list schema_key = temp_uri.to_s.split('#')[0] + '#' @@ -32,7 +32,7 @@ def self.get_referenced_uri_and_schema(s, current_schema, validator) if ref_schema # Perform fragment resolution to retrieve the appropriate level for the schema target_schema = ref_schema.schema - fragments = JSON::Util::URI2.parse(JSON::Util::URI2.unescape(temp_uri)).fragment.split('/') + fragments = JSON::Util::URI.parse(JSON::Util::URI.unescape(temp_uri)).fragment.split('/') fragment_path = '' fragments.each do |fragment| if fragment && fragment != '' diff --git a/lib/json-schema/schema.rb b/lib/json-schema/schema.rb index ff83bd68..0a2c2464 100644 --- a/lib/json-schema/schema.rb +++ b/lib/json-schema/schema.rb @@ -10,13 +10,13 @@ def initialize(schema, uri, parent_validator = nil) # If there is an ID on this schema, use it to generate the URI if @schema['id'].is_a?(String) - temp_uri = JSON::Util::URI2.parse(@schema['id']) + temp_uri = JSON::Util::URI.parse(@schema['id']) if temp_uri.relative? temp_uri = uri.join(temp_uri) end @uri = temp_uri end - @uri = JSON::Util::URI2.strip_fragment(@uri) + @uri = JSON::Util::URI.strip_fragment(@uri) # If there is a $schema on this schema, use it to determine which validator to use if @schema['$schema'] diff --git a/lib/json-schema/schema/reader.rb b/lib/json-schema/schema/reader.rb index 2e66c1ff..76e389a8 100644 --- a/lib/json-schema/schema/reader.rb +++ b/lib/json-schema/schema/reader.rb @@ -85,9 +85,9 @@ def initialize(options = {}) # @raise [JSON::Schema::ReadFailed] if reading the location was acceptable but the # attempt to retrieve it failed def read(location) - uri = JSON::Util::URI2.parse(location.to_s) + uri = JSON::Util::URI.parse(location.to_s) body = if uri.scheme.nil? || uri.scheme == 'file' - uri = JSON::Util::URI2.file_uri(uri) + uri = JSON::Util::URI.file_uri(uri) read_file(Pathname.new(uri.path).expand_path) else read_uri(uri) @@ -130,7 +130,7 @@ def read_uri(uri) def read_file(pathname) if accept_file?(pathname) - File.read(JSON::Util::URI2.unescape_path(pathname.to_s)) + File.read(JSON::Util::URI.unescape_path(pathname.to_s)) else raise JSON::Schema::ReadRefused.new(pathname.to_s, :file) end diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index 48eb87f3..fad8e709 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -1,10 +1,130 @@ +# frozen_string_literal: true + require 'addressable/uri' -require 'concurrent' module JSON module Util - module URI + class URI < Addressable::URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero) + + class << self + # @param uri [String, Addressable::URI] + # @return [Addressable::URI, nil] + def parse(uri) + super(uri) + rescue Addressable::URI::InvalidURIError => e + raise JSON::Schema::UriError, e.message + end + + # @param uri [String, Addressable::URI] + # @return [Addressable::URI, nil] + def file_uri(uri) + convert_path(parse(uri).path) + end + + # @param uri [String, Addressable::URI + # @return [String] + def unescape_path(uri) + parse(uri).unescape_path + end + + # Strips the fragment from the URI. + # @param uri [String, Addressable::URI] + # @return [Addressable::URI] + def strip_fragment(uri) + parse(uri).strip_fragment + end + + # @param uri [String, Addressable::URI] + # @return [Addressable::URI] + def normalize_uri(uri, base_path = Dir.pwd) + parse(uri).normalize_uri(base_path) + end + + # Normalizes the reference URI based on the provided base URI + # + # @param ref [String, Addressable::URI] + # @param base [String, Addressable::URI] + # @return [Addressable::URI] + def normalize_ref(ref, base) + parse(ref).normalize_ref(base) + end + + def absolutize_ref(ref, base) + parse(ref).absolutize_ref(base) + end + end + + # Unencodes any percent encoded characters within a path component. + # + # @return [String] + def unescape_path + self.class.unescape_component(path) + end + + # Strips the fragment from the URI. + # @return [Addressable::URI] a new instance of URI without a fragment + def strip_fragment + if fragment.nil? || fragment.empty? + self + else + merge(fragment: '') + end + end + + # Normalizes the URI based on the provided base path. + # + # @param base_path [String] the base path to use for relative URIs. Defaults to the current working directory. + # @return [Addressable::URI] the normalized URI or nil + def normalize_uri(base_path = Dir.pwd) + if relative? + if path[0, 1] == '/' + self.class.file_uri(self) + else + self.class.file_uri(File.join(base_path, self)) + end + else + self + end + end + + # @param base [Addressable::URI, String] + # @return [Addressable::URI] + def normalize_ref(base) + base_uri = self.class.parse(base) + defer_validation do + if relative? + # Check for absolute path + path, fragment = to_s.split('#') + merge!(base_uri) + + if path.nil? || path == '' + self.path = base_uri.path + elsif path[0, 1] == '/' + self.path = Pathname.new(path).cleanpath.to_s + else + join!(path) + end + + self.fragment = fragment + end + + self.fragment = '' if self.fragment.nil? || self.fragment.empty? + end + + self + end + + # @param base [Addressable::URI, String] + # @return [Addressable::URI] + def absolutize_ref(base) + ref = strip_fragment + if ref.absolute? + ref + else + self.class.strip_fragment(base).join(ref.path).normalize_uri + end + end end end end diff --git a/lib/json-schema/util/uri2.rb b/lib/json-schema/util/uri2.rb deleted file mode 100644 index 2291d3ba..00000000 --- a/lib/json-schema/util/uri2.rb +++ /dev/null @@ -1,128 +0,0 @@ -# frozen_string_literal: true - -require 'addressable' - -module JSON - module Util - class URI2 < Addressable::URI - class << self - # @param uri [String, Addressable::URI] - # @return [Addressable::URI, nil] - def parse(uri) - super(uri) - rescue Addressable::URI::InvalidURIError => e - raise JSON::Schema::UriError, e.message - end - - # @param uri [String, Addressable::URI] - # @return [Addressable::URI, nil] - def file_uri(uri) - convert_path(parse(uri).path) - end - - # @param uri [String, Addressable::URI - # @return [String] - def unescape_path(uri) - parse(uri).unescape_path - end - - # Strips the fragment from the URI. - # @param uri [String, Addressable::URI] - # @return [Addressable::URI] - def strip_fragment(uri) - parse(uri).strip_fragment - end - - # @param uri [String, Addressable::URI] - # @return [Addressable::URI] - def normalize_uri(uri, base_path = Dir.pwd) - parse(uri).normalize_uri(base_path) - end - - # Normalizes the reference URI based on the provided base URI - # - # @param ref [String, Addressable::URI] - # @param base [String, Addressable::URI] - # @return [Addressable::URI] - def normalize_ref(ref, base) - parse(ref).normalize_ref(base) - end - - def absolutize_ref(ref, base) - parse(ref).absolutize_ref(base) - end - end - - # Unencodes any percent encoded characters within a path component. - # - # @return [String] - def unescape_path - self.class.unescape_component(path) - end - - # Strips the fragment from the URI. - # @return [Addressable::URI] a new instance of URI without a fragment - def strip_fragment - if fragment.nil? || fragment.empty? - self - else - merge(fragment: '') - end - end - - # Normalizes the URI based on the provided base path. - # - # @param base_path [String] the base path to use for relative URIs. Defaults to the current working directory. - # @return [Addressable::URI] the normalized URI or nil - def normalize_uri(base_path = Dir.pwd) - if relative? - if path[0, 1] == '/' - self.class.file_uri(self) - else - self.class.file_uri(File.join(base_path, self)) - end - else - self - end - end - - # @param base [Addressable::URI, String] - # @return [Addressable::URI] - def normalize_ref(base) - base_uri = self.class.parse(base) - defer_validation do - if relative? - # Check for absolute path - path, fragment = to_s.split('#') - merge!(base_uri) - - if path.nil? || path == '' - self.path = base_uri.path - elsif path[0, 1] == '/' - self.path = Pathname.new(path).cleanpath.to_s - else - join!(path) - end - - self.fragment = fragment - end - - self.fragment = '' if self.fragment.nil? || self.fragment.empty? - end - - self - end - - # @param base [Addressable::URI, String] - # @return [Addressable::URI] - def absolutize_ref(base) - ref = strip_fragment - if ref.absolute? - ref - else - self.class.strip_fragment(base).join(ref.path).normalize_uri - end - end - end - end -end diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index e6a83433..63127df4 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -135,11 +135,11 @@ def validate(data) end def load_ref_schema(parent_schema, ref) - schema_uri = JSON::Util::URI2.absolutize_ref(ref, parent_schema.uri) + schema_uri = JSON::Util::URI.absolutize_ref(ref, parent_schema.uri) return true if self.class.schema_loaded?(schema_uri) validator = self.class.validator_for_uri(schema_uri, false) - schema_uri = JSON::Util::URI2.file_uri(validator.metaschema) if validator + schema_uri = JSON::Util::URI.file_uri(validator.metaschema) if validator schema = @options[:schema_reader].read(schema_uri) self.class.add_schema(schema) @@ -321,7 +321,7 @@ def schema_loaded?(schema_uri) end def schema_key_for(uri) - key = Util::URI2.normalize_uri(uri).to_s + key = Util::URI.normalize_uri(uri).to_s key.end_with?('#') ? key : "#{key}#" end @@ -341,7 +341,7 @@ def default_validator def validator_for_uri(schema_uri, raise_not_found = true) return default_validator unless schema_uri - u = JSON::Util::URI2.parse(schema_uri) + u = JSON::Util::URI.parse(schema_uri) validator = validators["#{u.scheme}://#{u.host}#{u.path}"] if validator.nil? && raise_not_found raise JSON::Schema::SchemaError, "Schema not found: #{schema_uri}" @@ -526,7 +526,7 @@ def initialize_schema(schema, default_validator) if schema.is_a?(String) begin # Build a fake URI for this - schema_uri = JSON::Util::URI2.parse(fake_uuid(schema)) + schema_uri = JSON::Util::URI.parse(fake_uuid(schema)) schema = JSON::Schema.new(JSON::Validator.parse(schema), schema_uri, default_validator) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema @@ -534,7 +534,7 @@ def initialize_schema(schema, default_validator) self.class.add_schema(schema) rescue JSON::Schema::JsonParseError # Build a uri for it - schema_uri = Util::URI2.normalize_uri(schema) + schema_uri = Util::URI.normalize_uri(schema) if !self.class.schema_loaded?(schema_uri) schema = @options[:schema_reader].read(schema_uri) schema = JSON::Schema.stringify(schema) @@ -548,14 +548,14 @@ def initialize_schema(schema, default_validator) schema = self.class.schema_for_uri(schema_uri) if @options[:list] && @options[:fragment].nil? schema = schema.to_array_schema - schema.uri = JSON::Util::URI2.parse(fake_uuid(serialize(schema.schema))) + schema.uri = JSON::Util::URI.parse(fake_uuid(serialize(schema.schema))) self.class.add_schema(schema) end schema end end elsif schema.is_a?(Hash) - schema_uri = JSON::Util::URI2.parse(fake_uuid(serialize(schema))) + schema_uri = JSON::Util::URI.parse(fake_uuid(serialize(schema))) schema = JSON::Schema.stringify(schema) schema = JSON::Schema.new(schema, schema_uri, default_validator) if @options[:list] && @options[:fragment].nil? @@ -574,7 +574,7 @@ def initialize_data(data) if @options[:json] data = self.class.parse(data) elsif @options[:uri] - json_uri = Util::URI2.normalize_uri(data) + json_uri = Util::URI.normalize_uri(data) data = self.class.parse(custom_open(json_uri)) elsif data.is_a?(String) begin @@ -583,7 +583,7 @@ def initialize_data(data) data = strict_convert ? data : self.class.parse(data) rescue JSON::Schema::JsonParseError begin - json_uri = Util::URI2.normalize_uri(data) + json_uri = Util::URI.normalize_uri(data) data = self.class.parse(custom_open(json_uri)) rescue JSON::Schema::JsonLoadError, JSON::Schema::UriError # Silently discard the error - use the data as-is @@ -595,7 +595,7 @@ def initialize_data(data) end def custom_open(uri) - uri = Util::URI2.normalize_uri(uri) if uri.is_a?(String) + uri = Util::URI.normalize_uri(uri) if uri.is_a?(String) if uri.absolute? && Util::URI::SUPPORTED_PROTOCOLS.include?(uri.scheme) begin URI.open(uri.to_s).read @@ -604,7 +604,7 @@ def custom_open(uri) end else begin - File.read(JSON::Util::URI2.unescape_path(uri)) + File.read(JSON::Util::URI.unescape_path(uri)) rescue SystemCallError => e raise JSON::Schema::JsonLoadError, e.message end diff --git a/lib/json-schema/validators/draft1.rb b/lib/json-schema/validators/draft1.rb index 682f455c..6d1c2a82 100644 --- a/lib/json-schema/validators/draft1.rb +++ b/lib/json-schema/validators/draft1.rb @@ -32,7 +32,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-01/schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-01/schema#') @names = ['draft1'] @metaschema_name = 'draft-01.json' end diff --git a/lib/json-schema/validators/draft2.rb b/lib/json-schema/validators/draft2.rb index 931abbc6..dd9d56f5 100644 --- a/lib/json-schema/validators/draft2.rb +++ b/lib/json-schema/validators/draft2.rb @@ -33,7 +33,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-02/schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-02/schema#') @names = ['draft2'] @metaschema_name = 'draft-02.json' end diff --git a/lib/json-schema/validators/draft3.rb b/lib/json-schema/validators/draft3.rb index 7c16b835..42acc736 100644 --- a/lib/json-schema/validators/draft3.rb +++ b/lib/json-schema/validators/draft3.rb @@ -37,7 +37,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-03/schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-03/schema#') @names = ['draft3', 'http://json-schema.org/draft-03/schema#'] @metaschema_name = 'draft-03.json' end diff --git a/lib/json-schema/validators/draft4.rb b/lib/json-schema/validators/draft4.rb index bc40ec67..a3418dd1 100644 --- a/lib/json-schema/validators/draft4.rb +++ b/lib/json-schema/validators/draft4.rb @@ -42,7 +42,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-04/schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-04/schema#') @names = ['draft4', 'http://json-schema.org/draft-04/schema#'] @metaschema_name = 'draft-04.json' end diff --git a/lib/json-schema/validators/draft6.rb b/lib/json-schema/validators/draft6.rb index a2faa206..66eb038e 100644 --- a/lib/json-schema/validators/draft6.rb +++ b/lib/json-schema/validators/draft6.rb @@ -44,7 +44,7 @@ def initialize 'uri' => UriFormat, } @formats = @default_formats.clone - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-06/schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-06/schema#') @names = ['draft6', 'http://json-schema.org/draft-06/schema#'] @metaschema_name = 'draft-06.json' end diff --git a/lib/json-schema/validators/hyper-draft1.rb b/lib/json-schema/validators/hyper-draft1.rb index 12902898..7e6a3189 100644 --- a/lib/json-schema/validators/hyper-draft1.rb +++ b/lib/json-schema/validators/hyper-draft1.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft1 < Draft1 def initialize super - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-01/hyper-schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-01/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft2.rb b/lib/json-schema/validators/hyper-draft2.rb index 6f6d7896..ad8b1e0b 100644 --- a/lib/json-schema/validators/hyper-draft2.rb +++ b/lib/json-schema/validators/hyper-draft2.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft2 < Draft2 def initialize super - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-02/hyper-schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-02/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft3.rb b/lib/json-schema/validators/hyper-draft3.rb index b41201cf..c00da997 100644 --- a/lib/json-schema/validators/hyper-draft3.rb +++ b/lib/json-schema/validators/hyper-draft3.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft3 < Draft3 def initialize super - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-03/hyper-schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-03/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft4.rb b/lib/json-schema/validators/hyper-draft4.rb index ad7441b9..7729d3fe 100644 --- a/lib/json-schema/validators/hyper-draft4.rb +++ b/lib/json-schema/validators/hyper-draft4.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft4 < Draft4 def initialize super - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-04/hyper-schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-04/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/lib/json-schema/validators/hyper-draft6.rb b/lib/json-schema/validators/hyper-draft6.rb index 1c8a2cf8..050245bf 100644 --- a/lib/json-schema/validators/hyper-draft6.rb +++ b/lib/json-schema/validators/hyper-draft6.rb @@ -3,7 +3,7 @@ class Schema class HyperDraft6 < Draft6 def initialize super - @uri = JSON::Util::URI2.parse('http://json-schema.org/draft-06/hyper-schema#') + @uri = JSON::Util::URI.parse('http://json-schema.org/draft-06/hyper-schema#') end JSON::Validator.register_validator(new) diff --git a/test/uri2_util_test.rb b/test/uri2_util_test.rb deleted file mode 100644 index a88ae63b..00000000 --- a/test/uri2_util_test.rb +++ /dev/null @@ -1,138 +0,0 @@ -require File.expand_path('../support/test_helper', __FILE__) - -class Uri2UtilTest < Minitest::Test - def test_normalized_uri - str = 'https://www.google.com/search' - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search',) - assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') - end - - def test_normalized_uri_with_empty_fragment - str = 'https://www.google.com/search#' - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search', - fragment: nil,) - assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') - end - - def test_normalized_uri_with_fragment - str = 'https://www.google.com/search#foo' - uri = Addressable::URI.new(scheme: 'https', - host: 'www.google.com', - path: 'search', - fragment: 'foo',) - assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') - end - - def test_normalized_uri_for_absolute_path - str = '/foo/bar.json' - uri = Addressable::URI.new(scheme: 'file', - host: '', - path: '/foo/bar.json',) - assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') - end - - def test_normalized_uri_for_relative_path - str = 'foo/bar.json' - uri = Addressable::URI.new(scheme: 'file', - host: '', - path: '/home/foo/bar.json',) - assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') - end - - def test_normalized_uri_for_file_path_with_host - str = 'file://localhost/foo/bar.json' - uri = Addressable::URI.new(scheme: 'file', - host: 'localhost', - path: '/foo/bar.json',) - assert_equal uri, JSON::Util::URI2.normalize_uri(str, '/home') - end - - def test_ref_fragment_path - uri = '#some-thing' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://www.example.com/foo/#some-thing'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/foo/#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_file_path - uri = '/some/thing' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_uri - uri = 'http://foo-bar.com' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_uri_with_path - uri = 'http://foo-bar.com/some/thing' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_uri_with_fragment - uri = 'http://foo-bar.com/some/thing#foo' - base = 'http://www.example.com/hello/#world' - - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_uri_with_fragment_and_base_with_no_fragment - uri = 'http://foo-bar.com/some/thing#foo' - base = 'http://www.example.com/hello' - - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_relative_path - uri = 'hello/world' - base = 'http://www.example.com/foo/#bar' - - assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_addressable_uri_with_host - uri = Addressable::URI.new(host: 'foo-bar.com') - base = 'http://www.example.com/hello/#world' - - assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/hello/#'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_addressable_uri_with_host_and_path - uri = Addressable::URI.new(host: 'foo-bar.com', path: '/hello/world') - base = 'http://www.example.com/a/#b' - - assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('http://www.example.com/hello/world'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_ref_addressable_uri_with_scheme_host_and_path - uri = Addressable::URI.new(scheme: 'https', host: 'foo-bar.com', path: '/hello/world') - base = 'http://www.example.com/a/#b' - - assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI2.normalize_ref(uri, base) - assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI2.absolutize_ref(uri, base) - end - - def test_normalize_ref_cache - assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI2.normalize_ref('#foo', 'http://www.example.com') - assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI2.normalize_ref('#foo', 'http://www.example.net') - end -end diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index b728d6f8..40dd031a 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -1,10 +1,54 @@ require File.expand_path('../support/test_helper', __FILE__) class UriUtilTest < Minitest::Test - def populate_cache_with(str, &blk) - cached_uri = Addressable::URI.parse(str) - Addressable::URI.stub(:parse, cached_uri, &blk) - cached_uri + def test_normalized_uri + str = 'https://www.google.com/search' + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search',) + assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + end + + def test_normalized_uri_with_empty_fragment + str = 'https://www.google.com/search#' + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search', + fragment: nil,) + assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + end + + def test_normalized_uri_with_fragment + str = 'https://www.google.com/search#foo' + uri = Addressable::URI.new(scheme: 'https', + host: 'www.google.com', + path: 'search', + fragment: 'foo',) + assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + end + + def test_normalized_uri_for_absolute_path + str = '/foo/bar.json' + uri = Addressable::URI.new(scheme: 'file', + host: '', + path: '/foo/bar.json',) + assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + end + + def test_normalized_uri_for_relative_path + str = 'foo/bar.json' + uri = Addressable::URI.new(scheme: 'file', + host: '', + path: '/home/foo/bar.json',) + assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + end + + def test_normalized_uri_for_file_path_with_host + str = 'file://localhost/foo/bar.json' + uri = Addressable::URI.new(scheme: 'file', + host: 'localhost', + path: '/foo/bar.json',) + assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') end def test_uri_parse @@ -12,13 +56,98 @@ def test_uri_parse uri = Addressable::URI.new(scheme: 'https', host: 'www.google.com', path: 'search',) - assert_equal uri, JSON::Util::URI2.parse(str) + assert_equal uri, JSON::Util::URI.parse(str) end def test_invalid_uri_parse uri = ':::::::' assert_raises(JSON::Schema::UriError) do - JSON::Util::URI2.parse(uri) + JSON::Util::URI.parse(uri) end end + + def test_ref_fragment_path + uri = '#some-thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/foo/#some-thing'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_file_path + uri = '/some/thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri + uri = 'http://foo-bar.com' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_path + uri = 'http://foo-bar.com/some/thing' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_fragment + uri = 'http://foo-bar.com/some/thing#foo' + base = 'http://www.example.com/hello/#world' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_uri_with_fragment_and_base_with_no_fragment + uri = 'http://foo-bar.com/some/thing#foo' + base = 'http://www.example.com/hello' + + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#foo'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://foo-bar.com/some/thing#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_relative_path + uri = 'hello/world' + base = 'http://www.example.com/foo/#bar' + + assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/foo/hello/world#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_host + uri = Addressable::URI.new(host: 'foo-bar.com') + base = 'http://www.example.com/hello/#world' + + assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/hello/#'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_host_and_path + uri = Addressable::URI.new(host: 'foo-bar.com', path: '/hello/world') + base = 'http://www.example.com/a/#b' + + assert_equal Addressable::URI.parse('http://www.example.com/foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('http://www.example.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_ref_addressable_uri_with_scheme_host_and_path + uri = Addressable::URI.new(scheme: 'https', host: 'foo-bar.com', path: '/hello/world') + base = 'http://www.example.com/a/#b' + + assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) + assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) + end + + def test_normalize_ref_cache + assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.com') + assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.net') + end end From a16e11bb1b8e5d70335e78277b1a8677aed9d9cc Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Sat, 6 Jul 2024 18:52:45 +0200 Subject: [PATCH 14/16] Rename methods back to reduce diff --- lib/json-schema/attributes/ref.rb | 2 +- lib/json-schema/schema/reader.rb | 2 +- lib/json-schema/util/uri.rb | 16 +++++++++------- lib/json-schema/validator.rb | 12 ++++++------ test/uri_util_test.rb | 12 ++++++------ 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/lib/json-schema/attributes/ref.rb b/lib/json-schema/attributes/ref.rb index 895c38ae..cfe52666 100644 --- a/lib/json-schema/attributes/ref.rb +++ b/lib/json-schema/attributes/ref.rb @@ -32,7 +32,7 @@ def self.get_referenced_uri_and_schema(s, current_schema, validator) if ref_schema # Perform fragment resolution to retrieve the appropriate level for the schema target_schema = ref_schema.schema - fragments = JSON::Util::URI.parse(JSON::Util::URI.unescape(temp_uri)).fragment.split('/') + fragments = JSON::Util::URI.parse(JSON::Util::URI.unescape_uri(temp_uri)).fragment.split('/') fragment_path = '' fragments.each do |fragment| if fragment && fragment != '' diff --git a/lib/json-schema/schema/reader.rb b/lib/json-schema/schema/reader.rb index 76e389a8..6bf6469e 100644 --- a/lib/json-schema/schema/reader.rb +++ b/lib/json-schema/schema/reader.rb @@ -130,7 +130,7 @@ def read_uri(uri) def read_file(pathname) if accept_file?(pathname) - File.read(JSON::Util::URI.unescape_path(pathname.to_s)) + File.read(JSON::Util::URI.unescaped_path(pathname.to_s)) else raise JSON::Schema::ReadRefused.new(pathname.to_s, :file) end diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index fad8e709..b6b3be3d 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -8,6 +8,8 @@ class URI < Addressable::URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero) class << self + alias unescape_uri unescape + # @param uri [String, Addressable::URI] # @return [Addressable::URI, nil] def parse(uri) @@ -24,8 +26,8 @@ def file_uri(uri) # @param uri [String, Addressable::URI # @return [String] - def unescape_path(uri) - parse(uri).unescape_path + def unescaped_path(uri) + parse(uri).unescaped_path end # Strips the fragment from the URI. @@ -37,8 +39,8 @@ def strip_fragment(uri) # @param uri [String, Addressable::URI] # @return [Addressable::URI] - def normalize_uri(uri, base_path = Dir.pwd) - parse(uri).normalize_uri(base_path) + def normalized_uri(uri, base_path = Dir.pwd) + parse(uri).normalized_uri(base_path) end # Normalizes the reference URI based on the provided base URI @@ -58,7 +60,7 @@ def absolutize_ref(ref, base) # Unencodes any percent encoded characters within a path component. # # @return [String] - def unescape_path + def unescaped_path self.class.unescape_component(path) end @@ -76,7 +78,7 @@ def strip_fragment # # @param base_path [String] the base path to use for relative URIs. Defaults to the current working directory. # @return [Addressable::URI] the normalized URI or nil - def normalize_uri(base_path = Dir.pwd) + def normalized_uri(base_path = Dir.pwd) if relative? if path[0, 1] == '/' self.class.file_uri(self) @@ -122,7 +124,7 @@ def absolutize_ref(base) if ref.absolute? ref else - self.class.strip_fragment(base).join(ref.path).normalize_uri + self.class.strip_fragment(base).join(ref.path).normalized_uri end end end diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index 63127df4..0a4d3b87 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -321,7 +321,7 @@ def schema_loaded?(schema_uri) end def schema_key_for(uri) - key = Util::URI.normalize_uri(uri).to_s + key = Util::URI.normalized_uri(uri).to_s key.end_with?('#') ? key : "#{key}#" end @@ -534,7 +534,7 @@ def initialize_schema(schema, default_validator) self.class.add_schema(schema) rescue JSON::Schema::JsonParseError # Build a uri for it - schema_uri = Util::URI.normalize_uri(schema) + schema_uri = Util::URI.normalized_uri(schema) if !self.class.schema_loaded?(schema_uri) schema = @options[:schema_reader].read(schema_uri) schema = JSON::Schema.stringify(schema) @@ -574,7 +574,7 @@ def initialize_data(data) if @options[:json] data = self.class.parse(data) elsif @options[:uri] - json_uri = Util::URI.normalize_uri(data) + json_uri = Util::URI.normalized_uri(data) data = self.class.parse(custom_open(json_uri)) elsif data.is_a?(String) begin @@ -583,7 +583,7 @@ def initialize_data(data) data = strict_convert ? data : self.class.parse(data) rescue JSON::Schema::JsonParseError begin - json_uri = Util::URI.normalize_uri(data) + json_uri = Util::URI.normalized_uri(data) data = self.class.parse(custom_open(json_uri)) rescue JSON::Schema::JsonLoadError, JSON::Schema::UriError # Silently discard the error - use the data as-is @@ -595,7 +595,7 @@ def initialize_data(data) end def custom_open(uri) - uri = Util::URI.normalize_uri(uri) if uri.is_a?(String) + uri = Util::URI.normalized_uri(uri) if uri.is_a?(String) if uri.absolute? && Util::URI::SUPPORTED_PROTOCOLS.include?(uri.scheme) begin URI.open(uri.to_s).read @@ -604,7 +604,7 @@ def custom_open(uri) end else begin - File.read(JSON::Util::URI.unescape_path(uri)) + File.read(JSON::Util::URI.unescaped_path(uri)) rescue SystemCallError => e raise JSON::Schema::JsonLoadError, e.message end diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index 40dd031a..2c66d10f 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -6,7 +6,7 @@ def test_normalized_uri uri = Addressable::URI.new(scheme: 'https', host: 'www.google.com', path: 'search',) - assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') end def test_normalized_uri_with_empty_fragment @@ -15,7 +15,7 @@ def test_normalized_uri_with_empty_fragment host: 'www.google.com', path: 'search', fragment: nil,) - assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') end def test_normalized_uri_with_fragment @@ -24,7 +24,7 @@ def test_normalized_uri_with_fragment host: 'www.google.com', path: 'search', fragment: 'foo',) - assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') end def test_normalized_uri_for_absolute_path @@ -32,7 +32,7 @@ def test_normalized_uri_for_absolute_path uri = Addressable::URI.new(scheme: 'file', host: '', path: '/foo/bar.json',) - assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') end def test_normalized_uri_for_relative_path @@ -40,7 +40,7 @@ def test_normalized_uri_for_relative_path uri = Addressable::URI.new(scheme: 'file', host: '', path: '/home/foo/bar.json',) - assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') end def test_normalized_uri_for_file_path_with_host @@ -48,7 +48,7 @@ def test_normalized_uri_for_file_path_with_host uri = Addressable::URI.new(scheme: 'file', host: 'localhost', path: '/foo/bar.json',) - assert_equal uri, JSON::Util::URI.normalize_uri(str, '/home') + assert_equal uri, JSON::Util::URI.normalized_uri(str, '/home') end def test_uri_parse From e476b07ddaa472c13f962a736a076b8a1f93bb5c Mon Sep 17 00:00:00 2001 From: Tema Bolshakov Date: Sat, 6 Jul 2024 19:26:36 +0200 Subject: [PATCH 15/16] Revert "Reproduce caching issue with the JSON::Util::URI.normalize_ref method" This reverts commit 401f707d383f1bbe2e834b6d035369e4c086cf70. --- test/uri_util_test.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/uri_util_test.rb b/test/uri_util_test.rb index 2c66d10f..d62482fa 100644 --- a/test/uri_util_test.rb +++ b/test/uri_util_test.rb @@ -145,9 +145,4 @@ def test_ref_addressable_uri_with_scheme_host_and_path assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world#'), JSON::Util::URI.normalize_ref(uri, base) assert_equal Addressable::URI.parse('https://foo-bar.com/hello/world'), JSON::Util::URI.absolutize_ref(uri, base) end - - def test_normalize_ref_cache - assert_equal Addressable::URI.parse('http://www.example.com/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.com') - assert_equal Addressable::URI.parse('http://www.example.net/#foo'), JSON::Util::URI.normalize_ref('#foo', 'http://www.example.net') - end end From 2ad9e19a6880be5f043b9de6e3fa077ec98ee849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ABma=20Bolshakov?= Date: Fri, 12 Jul 2024 13:31:23 +0200 Subject: [PATCH 16/16] Update lib/json-schema/util/uri.rb --- lib/json-schema/util/uri.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/json-schema/util/uri.rb b/lib/json-schema/util/uri.rb index b6b3be3d..9a8cbeb7 100644 --- a/lib/json-schema/util/uri.rb +++ b/lib/json-schema/util/uri.rb @@ -4,6 +4,7 @@ module JSON module Util + # @api private class URI < Addressable::URI SUPPORTED_PROTOCOLS = %w(http https ftp tftp sftp ssh svn+ssh telnet nntp gopher wais ldap prospero)