logstash-output-scalyr 0.1.9 → 0.1.14.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/Gemfile +5 -0
- data/README.md +2 -2
- data/lib/logstash/outputs/scalyr.rb +265 -142
- data/lib/scalyr/common/client.rb +80 -57
- data/lib/scalyr/common/util.rb +7 -0
- data/lib/scalyr/constants.rb +2 -0
- data/logstash-output-scalyr.gemspec +1 -1
- data/spec/logstash/outputs/scalyr_integration_spec.rb +187 -8
- data/spec/logstash/outputs/scalyr_spec.rb +12 -6
- data/vendor/bundle/jruby/2.5.0/cache/addressable-2.7.0.gem +0 -0
- data/vendor/bundle/jruby/2.5.0/cache/crack-0.4.5.gem +0 -0
- data/vendor/bundle/jruby/2.5.0/cache/hashdiff-1.0.1.gem +0 -0
- data/vendor/bundle/jruby/2.5.0/cache/public_suffix-4.0.6.gem +0 -0
- data/vendor/bundle/jruby/2.5.0/cache/rexml-3.2.5.gem +0 -0
- data/vendor/bundle/jruby/2.5.0/cache/webmock-3.13.0.gem +0 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/CHANGELOG.md +235 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/Gemfile +32 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/LICENSE.txt +202 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/README.md +121 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/Rakefile +34 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/data/unicode.data +0 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/lib/addressable.rb +4 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/lib/addressable/idna.rb +27 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/lib/addressable/idna/native.rb +61 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/lib/addressable/idna/pure.rb +676 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/lib/addressable/template.rb +1045 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/lib/addressable/uri.rb +2529 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/lib/addressable/version.rb +32 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/spec/addressable/idna_spec.rb +300 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/spec/addressable/net_http_compat_spec.rb +30 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/spec/addressable/rack_mount_compat_spec.rb +106 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/spec/addressable/security_spec.rb +59 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/spec/addressable/template_spec.rb +1451 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/spec/addressable/uri_spec.rb +6603 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/spec/spec_helper.rb +24 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/tasks/clobber.rake +4 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/tasks/gem.rake +93 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/tasks/git.rake +47 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/tasks/metrics.rake +24 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/tasks/rspec.rake +23 -0
- data/vendor/bundle/jruby/2.5.0/gems/addressable-2.7.0/tasks/yard.rake +29 -0
- data/vendor/bundle/jruby/2.5.0/gems/crack-0.4.5/lib/crack.rb +7 -0
- data/vendor/bundle/jruby/2.5.0/gems/crack-0.4.5/lib/crack/json.rb +98 -0
- data/vendor/bundle/jruby/2.5.0/gems/crack-0.4.5/lib/crack/util.rb +17 -0
- data/vendor/bundle/jruby/2.5.0/gems/crack-0.4.5/lib/crack/version.rb +3 -0
- data/vendor/bundle/jruby/2.5.0/gems/crack-0.4.5/lib/crack/xml.rb +238 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/Gemfile +8 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/LICENSE +19 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/README.md +276 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/Rakefile +18 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/changelog.md +100 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/hashdiff.gemspec +39 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff.rb +10 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/compare_hashes.rb +69 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/diff.rb +177 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/lcs.rb +66 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/lcs_compare_arrays.rb +32 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/linear_compare_array.rb +159 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/patch.rb +88 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/util.rb +155 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/lib/hashdiff/version.rb +5 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/best_diff_spec.rb +75 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/diff_array_spec.rb +60 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/diff_spec.rb +360 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/lcs_spec.rb +76 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/linear_compare_array_spec.rb +50 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/patch_spec.rb +185 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/readme_spec.rb +15 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/hashdiff/util_spec.rb +116 -0
- data/vendor/bundle/jruby/2.5.0/gems/hashdiff-1.0.1/spec/spec_helper.rb +15 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/2.0-Upgrade.md +52 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/CHANGELOG.md +406 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/Gemfile +15 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/LICENSE.txt +22 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/README.md +207 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/Rakefile +51 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/SECURITY.md +104 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/bin/console +15 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/codecov.yml +12 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/data/list.txt +13380 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/lib/public_suffix.rb +179 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/lib/public_suffix/domain.rb +235 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/lib/public_suffix/errors.rb +41 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/lib/public_suffix/list.rb +247 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/lib/public_suffix/rule.rb +350 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/lib/public_suffix/version.rb +13 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/public_suffix.gemspec +29 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/acceptance_test.rb +131 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/benchmarks/bm_find.rb +66 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/benchmarks/bm_find_all.rb +102 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/benchmarks/bm_names.rb +91 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/benchmarks/bm_select.rb +26 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/benchmarks/bm_select_incremental.rb +25 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/benchmarks/bm_valid.rb +101 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/profilers/domain_profiler.rb +12 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/profilers/find_profiler.rb +12 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/profilers/find_profiler_jp.rb +12 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/profilers/initialization_profiler.rb +11 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/profilers/list_profsize.rb +11 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/profilers/object_binsize.rb +57 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/psl_test.rb +52 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/test_helper.rb +18 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/tests.txt +98 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/unit/domain_test.rb +106 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/unit/errors_test.rb +25 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/unit/list_test.rb +241 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/unit/public_suffix_test.rb +188 -0
- data/vendor/bundle/jruby/2.5.0/gems/public_suffix-4.0.6/test/unit/rule_test.rb +222 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/LICENSE.txt +22 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/NEWS.md +178 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/README.md +48 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/context.rdoc +143 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/rdoc/child.rdoc +87 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/rdoc/document.rdoc +276 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/rdoc/element.rdoc +602 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/rdoc/node.rdoc +97 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/rdoc/parent.rdoc +267 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/tocs/child_toc.rdoc +12 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/tocs/document_toc.rdoc +30 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/tocs/element_toc.rdoc +55 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/tocs/master_toc.rdoc +135 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/tocs/node_toc.rdoc +16 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/doc/rexml/tasks/tocs/parent_toc.rdoc +25 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml.rb +3 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/attlistdecl.rb +63 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/attribute.rb +205 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/cdata.rb +68 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/child.rb +97 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/comment.rb +80 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/doctype.rb +311 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/document.rb +451 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/dtd/attlistdecl.rb +11 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/dtd/dtd.rb +47 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/dtd/elementdecl.rb +18 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/dtd/entitydecl.rb +57 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/dtd/notationdecl.rb +40 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/element.rb +2599 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/encoding.rb +51 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/entity.rb +171 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/formatters/default.rb +116 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/formatters/pretty.rb +142 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/formatters/transitive.rb +58 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/functions.rb +447 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/instruction.rb +79 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/light/node.rb +188 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/namespace.rb +59 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/node.rb +76 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/output.rb +30 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parent.rb +166 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parseexception.rb +52 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb +694 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/lightparser.rb +59 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/pullparser.rb +197 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/sax2parser.rb +273 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/streamparser.rb +61 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/treeparser.rb +101 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/ultralightparser.rb +57 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/parsers/xpathparser.rb +689 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/quickpath.rb +266 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/rexml.rb +37 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/sax2listener.rb +98 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/security.rb +28 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/source.rb +298 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/streamlistener.rb +93 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/text.rb +424 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/undefinednamespaceexception.rb +9 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/validation/relaxng.rb +539 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/validation/validation.rb +144 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/validation/validationexception.rb +10 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/xmldecl.rb +130 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/xmltokens.rb +85 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/xpath.rb +81 -0
- data/vendor/bundle/jruby/2.5.0/gems/rexml-3.2.5/lib/rexml/xpath_parser.rb +974 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/CHANGELOG.md +1894 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/Gemfile +9 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/LICENSE +20 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/README.md +1176 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/Rakefile +38 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock.rb +59 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/api.rb +109 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/assertion_failure.rb +11 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/callback_registry.rb +35 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/config.rb +18 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/cucumber.rb +10 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/deprecation.rb +9 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/errors.rb +17 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/curb_adapter.rb +351 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +231 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/excon_adapter.rb +165 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_lib_adapter.rb +7 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +19 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/httpclient_adapter.rb +259 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/net_http.rb +385 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/net_http_response.rb +34 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/patron_adapter.rb +130 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +174 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/matchers/hash_including_matcher.rb +17 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/minitest.rb +41 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/rack_response.rb +69 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/request_body_diff.rb +64 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/request_execution_verifier.rb +77 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/request_pattern.rb +405 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/request_registry.rb +35 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/request_signature.rb +54 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/request_signature_snippet.rb +61 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/request_stub.rb +100 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/response.rb +159 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/responses_sequence.rb +40 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/rspec.rb +42 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/rspec/matchers.rb +27 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/rspec/matchers/request_pattern_matcher.rb +78 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/rspec/matchers/webmock_matcher.rb +67 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/stub_registry.rb +82 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/stub_request_snippet.rb +38 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/test_unit.rb +20 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/hash_counter.rb +39 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/hash_keys_stringifier.rb +25 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/hash_validator.rb +17 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/headers.rb +64 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/json.rb +67 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/query_mapper.rb +281 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/uri.rb +111 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/values_stringifier.rb +20 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/util/version_checker.rb +111 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/version.rb +3 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/lib/webmock/webmock.rb +163 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/minitest/test_helper.rb +34 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/minitest/test_webmock.rb +9 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/minitest/webmock_spec.rb +60 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/curb/curb_spec.rb +499 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/curb/curb_spec_helper.rb +147 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/em_http_request/em_http_request_spec.rb +462 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +77 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/excon/excon_spec.rb +77 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/excon/excon_spec_helper.rb +52 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/httpclient/httpclient_spec.rb +217 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/httpclient/httpclient_spec_helper.rb +57 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/manticore/manticore_spec.rb +107 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/net_http/net_http_shared.rb +153 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/net_http/net_http_spec.rb +369 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/net_http/net_http_spec_helper.rb +64 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/net_http/real_net_http_spec.rb +20 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/patron/patron_spec.rb +125 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/patron/patron_spec_helper.rb +54 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +313 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/callbacks.rb +148 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/complex_cross_concern_behaviors.rb +36 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/enabling_and_disabling_webmock.rb +95 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/precedence_of_stubs.rb +15 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/request_expectations.rb +930 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/returning_declared_responses.rb +409 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/shared/stubbing_requests.rb +678 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +135 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +60 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/acceptance/webmock_shared.rb +41 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/fixtures/test.txt +1 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/quality_spec.rb +84 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/spec_helper.rb +48 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/support/example_curl_output.txt +22 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/support/failures.rb +9 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/support/my_rack_app.rb +53 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/support/network_connection.rb +19 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/support/webmock_server.rb +70 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/api_spec.rb +175 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/errors_spec.rb +129 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +17 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +12 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/rack_response_spec.rb +112 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/request_body_diff_spec.rb +90 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/request_execution_verifier_spec.rb +208 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/request_pattern_spec.rb +736 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/request_registry_spec.rb +95 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/request_signature_snippet_spec.rb +89 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/request_signature_spec.rb +155 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/request_stub_spec.rb +199 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/response_spec.rb +286 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/stub_registry_spec.rb +103 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/stub_request_snippet_spec.rb +115 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/util/hash_counter_spec.rb +39 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/util/hash_keys_stringifier_spec.rb +27 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/util/headers_spec.rb +28 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/util/json_spec.rb +33 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/util/query_mapper_spec.rb +157 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/util/uri_spec.rb +371 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/util/version_checker_spec.rb +65 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/spec/unit/webmock_spec.rb +60 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/test/http_request.rb +24 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/test/shared_test.rb +108 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/test/test_helper.rb +23 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/test/test_webmock.rb +12 -0
- data/vendor/bundle/jruby/2.5.0/gems/webmock-3.13.0/webmock.gemspec +54 -0
- data/vendor/bundle/jruby/2.5.0/specifications/addressable-2.7.0.gemspec +39 -0
- data/vendor/bundle/jruby/2.5.0/specifications/crack-0.4.5.gemspec +32 -0
- data/vendor/bundle/jruby/2.5.0/specifications/hashdiff-1.0.1.gemspec +46 -0
- data/vendor/bundle/jruby/2.5.0/specifications/public_suffix-4.0.6.gemspec +24 -0
- data/vendor/bundle/jruby/2.5.0/specifications/rexml-3.2.5.gemspec +42 -0
- data/vendor/bundle/jruby/2.5.0/specifications/webmock-3.13.0.gemspec +85 -0
- metadata +311 -4
@@ -0,0 +1,2529 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# encoding:utf-8
|
4
|
+
#--
|
5
|
+
# Copyright (C) Bob Aman
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#++
|
19
|
+
|
20
|
+
|
21
|
+
require "addressable/version"
|
22
|
+
require "addressable/idna"
|
23
|
+
require "public_suffix"
|
24
|
+
|
25
|
+
##
|
26
|
+
# Addressable is a library for processing links and URIs.
|
27
|
+
module Addressable
|
28
|
+
##
|
29
|
+
# This is an implementation of a URI parser based on
|
30
|
+
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
|
31
|
+
# <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
|
32
|
+
class URI
|
33
|
+
##
|
34
|
+
# Raised if something other than a uri is supplied.
|
35
|
+
class InvalidURIError < StandardError
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Container for the character classes specified in
|
40
|
+
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
|
41
|
+
module CharacterClasses
|
42
|
+
ALPHA = "a-zA-Z"
|
43
|
+
DIGIT = "0-9"
|
44
|
+
GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
|
45
|
+
SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
|
46
|
+
RESERVED = GEN_DELIMS + SUB_DELIMS
|
47
|
+
UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
|
48
|
+
PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
|
49
|
+
SCHEME = ALPHA + DIGIT + "\\-\\+\\."
|
50
|
+
HOST = UNRESERVED + SUB_DELIMS + "\\[\\:\\]"
|
51
|
+
AUTHORITY = PCHAR
|
52
|
+
PATH = PCHAR + "\\/"
|
53
|
+
QUERY = PCHAR + "\\/\\?"
|
54
|
+
FRAGMENT = PCHAR + "\\/\\?"
|
55
|
+
end
|
56
|
+
|
57
|
+
SLASH = '/'
|
58
|
+
EMPTY_STR = ''
|
59
|
+
|
60
|
+
URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
|
61
|
+
|
62
|
+
PORT_MAPPING = {
|
63
|
+
"http" => 80,
|
64
|
+
"https" => 443,
|
65
|
+
"ftp" => 21,
|
66
|
+
"tftp" => 69,
|
67
|
+
"sftp" => 22,
|
68
|
+
"ssh" => 22,
|
69
|
+
"svn+ssh" => 22,
|
70
|
+
"telnet" => 23,
|
71
|
+
"nntp" => 119,
|
72
|
+
"gopher" => 70,
|
73
|
+
"wais" => 210,
|
74
|
+
"ldap" => 389,
|
75
|
+
"prospero" => 1525
|
76
|
+
}
|
77
|
+
|
78
|
+
##
|
79
|
+
# Returns a URI object based on the parsed string.
|
80
|
+
#
|
81
|
+
# @param [String, Addressable::URI, #to_str] uri
|
82
|
+
# The URI string to parse.
|
83
|
+
# No parsing is performed if the object is already an
|
84
|
+
# <code>Addressable::URI</code>.
|
85
|
+
#
|
86
|
+
# @return [Addressable::URI] The parsed URI.
|
87
|
+
def self.parse(uri)
|
88
|
+
# If we were given nil, return nil.
|
89
|
+
return nil unless uri
|
90
|
+
# If a URI object is passed, just return itself.
|
91
|
+
return uri.dup if uri.kind_of?(self)
|
92
|
+
|
93
|
+
# If a URI object of the Ruby standard library variety is passed,
|
94
|
+
# convert it to a string, then parse the string.
|
95
|
+
# We do the check this way because we don't want to accidentally
|
96
|
+
# cause a missing constant exception to be thrown.
|
97
|
+
if uri.class.name =~ /^URI\b/
|
98
|
+
uri = uri.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
# Otherwise, convert to a String
|
102
|
+
begin
|
103
|
+
uri = uri.to_str
|
104
|
+
rescue TypeError, NoMethodError
|
105
|
+
raise TypeError, "Can't convert #{uri.class} into String."
|
106
|
+
end if not uri.is_a? String
|
107
|
+
|
108
|
+
# This Regexp supplied as an example in RFC 3986, and it works great.
|
109
|
+
scan = uri.scan(URIREGEX)
|
110
|
+
fragments = scan[0]
|
111
|
+
scheme = fragments[1]
|
112
|
+
authority = fragments[3]
|
113
|
+
path = fragments[4]
|
114
|
+
query = fragments[6]
|
115
|
+
fragment = fragments[8]
|
116
|
+
user = nil
|
117
|
+
password = nil
|
118
|
+
host = nil
|
119
|
+
port = nil
|
120
|
+
if authority != nil
|
121
|
+
# The Regexp above doesn't split apart the authority.
|
122
|
+
userinfo = authority[/^([^\[\]]*)@/, 1]
|
123
|
+
if userinfo != nil
|
124
|
+
user = userinfo.strip[/^([^:]*):?/, 1]
|
125
|
+
password = userinfo.strip[/:(.*)$/, 1]
|
126
|
+
end
|
127
|
+
host = authority.sub(
|
128
|
+
/^([^\[\]]*)@/, EMPTY_STR
|
129
|
+
).sub(
|
130
|
+
/:([^:@\[\]]*?)$/, EMPTY_STR
|
131
|
+
)
|
132
|
+
port = authority[/:([^:@\[\]]*?)$/, 1]
|
133
|
+
end
|
134
|
+
if port == EMPTY_STR
|
135
|
+
port = nil
|
136
|
+
end
|
137
|
+
|
138
|
+
return new(
|
139
|
+
:scheme => scheme,
|
140
|
+
:user => user,
|
141
|
+
:password => password,
|
142
|
+
:host => host,
|
143
|
+
:port => port,
|
144
|
+
:path => path,
|
145
|
+
:query => query,
|
146
|
+
:fragment => fragment
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Converts an input to a URI. The input does not have to be a valid
|
152
|
+
# URI — the method will use heuristics to guess what URI was intended.
|
153
|
+
# This is not standards-compliant, merely user-friendly.
|
154
|
+
#
|
155
|
+
# @param [String, Addressable::URI, #to_str] uri
|
156
|
+
# The URI string to parse.
|
157
|
+
# No parsing is performed if the object is already an
|
158
|
+
# <code>Addressable::URI</code>.
|
159
|
+
# @param [Hash] hints
|
160
|
+
# A <code>Hash</code> of hints to the heuristic parser.
|
161
|
+
# Defaults to <code>{:scheme => "http"}</code>.
|
162
|
+
#
|
163
|
+
# @return [Addressable::URI] The parsed URI.
|
164
|
+
def self.heuristic_parse(uri, hints={})
|
165
|
+
# If we were given nil, return nil.
|
166
|
+
return nil unless uri
|
167
|
+
# If a URI object is passed, just return itself.
|
168
|
+
return uri.dup if uri.kind_of?(self)
|
169
|
+
|
170
|
+
# If a URI object of the Ruby standard library variety is passed,
|
171
|
+
# convert it to a string, then parse the string.
|
172
|
+
# We do the check this way because we don't want to accidentally
|
173
|
+
# cause a missing constant exception to be thrown.
|
174
|
+
if uri.class.name =~ /^URI\b/
|
175
|
+
uri = uri.to_s
|
176
|
+
end
|
177
|
+
|
178
|
+
if !uri.respond_to?(:to_str)
|
179
|
+
raise TypeError, "Can't convert #{uri.class} into String."
|
180
|
+
end
|
181
|
+
# Otherwise, convert to a String
|
182
|
+
uri = uri.to_str.dup.strip
|
183
|
+
hints = {
|
184
|
+
:scheme => "http"
|
185
|
+
}.merge(hints)
|
186
|
+
case uri
|
187
|
+
when /^http:\//i
|
188
|
+
uri.sub!(/^http:\/+/i, "http://")
|
189
|
+
when /^https:\//i
|
190
|
+
uri.sub!(/^https:\/+/i, "https://")
|
191
|
+
when /^feed:\/+http:\//i
|
192
|
+
uri.sub!(/^feed:\/+http:\/+/i, "feed:http://")
|
193
|
+
when /^feed:\//i
|
194
|
+
uri.sub!(/^feed:\/+/i, "feed://")
|
195
|
+
when %r[^file:/{4}]i
|
196
|
+
uri.sub!(%r[^file:/+]i, "file:////")
|
197
|
+
when %r[^file://localhost/]i
|
198
|
+
uri.sub!(%r[^file://localhost/+]i, "file:///")
|
199
|
+
when %r[^file:/+]i
|
200
|
+
uri.sub!(%r[^file:/+]i, "file:///")
|
201
|
+
when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
|
202
|
+
uri.sub!(/^/, hints[:scheme] + "://")
|
203
|
+
when /\A\d+\..*:\d+\z/
|
204
|
+
uri = "#{hints[:scheme]}://#{uri}"
|
205
|
+
end
|
206
|
+
match = uri.match(URIREGEX)
|
207
|
+
fragments = match.captures
|
208
|
+
authority = fragments[3]
|
209
|
+
if authority && authority.length > 0
|
210
|
+
new_authority = authority.tr("\\", "/").gsub(" ", "%20")
|
211
|
+
# NOTE: We want offset 4, not 3!
|
212
|
+
offset = match.offset(4)
|
213
|
+
uri = uri.dup
|
214
|
+
uri[offset[0]...offset[1]] = new_authority
|
215
|
+
end
|
216
|
+
parsed = self.parse(uri)
|
217
|
+
if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
|
218
|
+
parsed = self.parse(hints[:scheme] + "://" + uri)
|
219
|
+
end
|
220
|
+
if parsed.path.include?(".")
|
221
|
+
if parsed.path[/\b@\b/]
|
222
|
+
parsed.scheme = "mailto" unless parsed.scheme
|
223
|
+
elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
|
224
|
+
parsed.defer_validation do
|
225
|
+
new_path = parsed.path.sub(
|
226
|
+
Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
|
227
|
+
parsed.host = new_host
|
228
|
+
parsed.path = new_path
|
229
|
+
parsed.scheme = hints[:scheme] unless parsed.scheme
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
return parsed
|
234
|
+
end
|
235
|
+
|
236
|
+
##
|
237
|
+
# Converts a path to a file scheme URI. If the path supplied is
|
238
|
+
# relative, it will be returned as a relative URI. If the path supplied
|
239
|
+
# is actually a non-file URI, it will parse the URI as if it had been
|
240
|
+
# parsed with <code>Addressable::URI.parse</code>. Handles all of the
|
241
|
+
# various Microsoft-specific formats for specifying paths.
|
242
|
+
#
|
243
|
+
# @param [String, Addressable::URI, #to_str] path
|
244
|
+
# Typically a <code>String</code> path to a file or directory, but
|
245
|
+
# will return a sensible return value if an absolute URI is supplied
|
246
|
+
# instead.
|
247
|
+
#
|
248
|
+
# @return [Addressable::URI]
|
249
|
+
# The parsed file scheme URI or the original URI if some other URI
|
250
|
+
# scheme was provided.
|
251
|
+
#
|
252
|
+
# @example
|
253
|
+
# base = Addressable::URI.convert_path("/absolute/path/")
|
254
|
+
# uri = Addressable::URI.convert_path("relative/path")
|
255
|
+
# (base + uri).to_s
|
256
|
+
# #=> "file:///absolute/path/relative/path"
|
257
|
+
#
|
258
|
+
# Addressable::URI.convert_path(
|
259
|
+
# "c:\\windows\\My Documents 100%20\\foo.txt"
|
260
|
+
# ).to_s
|
261
|
+
# #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
|
262
|
+
#
|
263
|
+
# Addressable::URI.convert_path("http://example.com/").to_s
|
264
|
+
# #=> "http://example.com/"
|
265
|
+
def self.convert_path(path)
|
266
|
+
# If we were given nil, return nil.
|
267
|
+
return nil unless path
|
268
|
+
# If a URI object is passed, just return itself.
|
269
|
+
return path if path.kind_of?(self)
|
270
|
+
if !path.respond_to?(:to_str)
|
271
|
+
raise TypeError, "Can't convert #{path.class} into String."
|
272
|
+
end
|
273
|
+
# Otherwise, convert to a String
|
274
|
+
path = path.to_str.strip
|
275
|
+
|
276
|
+
path.sub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
|
277
|
+
path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
|
278
|
+
uri = self.parse(path)
|
279
|
+
|
280
|
+
if uri.scheme == nil
|
281
|
+
# Adjust windows-style uris
|
282
|
+
uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
|
283
|
+
"/#{$1.downcase}:/"
|
284
|
+
end
|
285
|
+
uri.path.tr!("\\", SLASH)
|
286
|
+
if File.exist?(uri.path) &&
|
287
|
+
File.stat(uri.path).directory?
|
288
|
+
uri.path.chomp!(SLASH)
|
289
|
+
uri.path = uri.path + '/'
|
290
|
+
end
|
291
|
+
|
292
|
+
# If the path is absolute, set the scheme and host.
|
293
|
+
if uri.path.start_with?(SLASH)
|
294
|
+
uri.scheme = "file"
|
295
|
+
uri.host = EMPTY_STR
|
296
|
+
end
|
297
|
+
uri.normalize!
|
298
|
+
end
|
299
|
+
|
300
|
+
return uri
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# Joins several URIs together.
|
305
|
+
#
|
306
|
+
# @param [String, Addressable::URI, #to_str] *uris
|
307
|
+
# The URIs to join.
|
308
|
+
#
|
309
|
+
# @return [Addressable::URI] The joined URI.
|
310
|
+
#
|
311
|
+
# @example
|
312
|
+
# base = "http://example.com/"
|
313
|
+
# uri = Addressable::URI.parse("relative/path")
|
314
|
+
# Addressable::URI.join(base, uri)
|
315
|
+
# #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
|
316
|
+
def self.join(*uris)
|
317
|
+
uri_objects = uris.collect do |uri|
|
318
|
+
if !uri.respond_to?(:to_str)
|
319
|
+
raise TypeError, "Can't convert #{uri.class} into String."
|
320
|
+
end
|
321
|
+
uri.kind_of?(self) ? uri : self.parse(uri.to_str)
|
322
|
+
end
|
323
|
+
result = uri_objects.shift.dup
|
324
|
+
for uri in uri_objects
|
325
|
+
result.join!(uri)
|
326
|
+
end
|
327
|
+
return result
|
328
|
+
end
|
329
|
+
|
330
|
+
##
|
331
|
+
# Tables used to optimize encoding operations in `self.encode_component`
|
332
|
+
# and `self.normalize_component`
|
333
|
+
SEQUENCE_ENCODING_TABLE = Hash.new do |hash, sequence|
|
334
|
+
hash[sequence] = sequence.unpack("C*").map do |c|
|
335
|
+
format("%02x", c)
|
336
|
+
end.join
|
337
|
+
end
|
338
|
+
|
339
|
+
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = Hash.new do |hash, sequence|
|
340
|
+
hash[sequence] = sequence.unpack("C*").map do |c|
|
341
|
+
format("%%%02X", c)
|
342
|
+
end.join
|
343
|
+
end
|
344
|
+
|
345
|
+
##
|
346
|
+
# Percent encodes a URI component.
|
347
|
+
#
|
348
|
+
# @param [String, #to_str] component The URI component to encode.
|
349
|
+
#
|
350
|
+
# @param [String, Regexp] character_class
|
351
|
+
# The characters which are not percent encoded. If a <code>String</code>
|
352
|
+
# is passed, the <code>String</code> must be formatted as a regular
|
353
|
+
# expression character class. (Do not include the surrounding square
|
354
|
+
# brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
|
355
|
+
# everything but the letters 'b' through 'z' and the numbers '0' through
|
356
|
+
# '9' to be percent encoded. If a <code>Regexp</code> is passed, the
|
357
|
+
# value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
|
358
|
+
# useful <code>String</code> values may be found in the
|
359
|
+
# <code>Addressable::URI::CharacterClasses</code> module. The default
|
360
|
+
# value is the reserved plus unreserved character classes specified in
|
361
|
+
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
|
362
|
+
#
|
363
|
+
# @param [Regexp] upcase_encoded
|
364
|
+
# A string of characters that may already be percent encoded, and whose
|
365
|
+
# encodings should be upcased. This allows normalization of percent
|
366
|
+
# encodings for characters not included in the
|
367
|
+
# <code>character_class</code>.
|
368
|
+
#
|
369
|
+
# @return [String] The encoded component.
|
370
|
+
#
|
371
|
+
# @example
|
372
|
+
# Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
|
373
|
+
# => "simple%2Fex%61mple"
|
374
|
+
# Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
|
375
|
+
# => "simple%2Fex%61mple"
|
376
|
+
# Addressable::URI.encode_component(
|
377
|
+
# "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
|
378
|
+
# )
|
379
|
+
# => "simple%2Fexample"
|
380
|
+
def self.encode_component(component, character_class=
|
381
|
+
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
|
382
|
+
upcase_encoded='')
|
383
|
+
return nil if component.nil?
|
384
|
+
|
385
|
+
begin
|
386
|
+
if component.kind_of?(Symbol) ||
|
387
|
+
component.kind_of?(Numeric) ||
|
388
|
+
component.kind_of?(TrueClass) ||
|
389
|
+
component.kind_of?(FalseClass)
|
390
|
+
component = component.to_s
|
391
|
+
else
|
392
|
+
component = component.to_str
|
393
|
+
end
|
394
|
+
rescue TypeError, NoMethodError
|
395
|
+
raise TypeError, "Can't convert #{component.class} into String."
|
396
|
+
end if !component.is_a? String
|
397
|
+
|
398
|
+
if ![String, Regexp].include?(character_class.class)
|
399
|
+
raise TypeError,
|
400
|
+
"Expected String or Regexp, got #{character_class.inspect}"
|
401
|
+
end
|
402
|
+
if character_class.kind_of?(String)
|
403
|
+
character_class = /[^#{character_class}]/
|
404
|
+
end
|
405
|
+
# We can't perform regexps on invalid UTF sequences, but
|
406
|
+
# here we need to, so switch to ASCII.
|
407
|
+
component = component.dup
|
408
|
+
component.force_encoding(Encoding::ASCII_8BIT)
|
409
|
+
# Avoiding gsub! because there are edge cases with frozen strings
|
410
|
+
component = component.gsub(character_class) do |sequence|
|
411
|
+
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence]
|
412
|
+
end
|
413
|
+
if upcase_encoded.length > 0
|
414
|
+
upcase_encoded_chars = upcase_encoded.chars.map do |char|
|
415
|
+
SEQUENCE_ENCODING_TABLE[char]
|
416
|
+
end
|
417
|
+
component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
|
418
|
+
&:upcase)
|
419
|
+
end
|
420
|
+
return component
|
421
|
+
end
|
422
|
+
|
423
|
+
class << self
|
424
|
+
alias_method :encode_component, :encode_component
|
425
|
+
end
|
426
|
+
|
427
|
+
##
|
428
|
+
# Unencodes any percent encoded characters within a URI component.
|
429
|
+
# This method may be used for unencoding either components or full URIs,
|
430
|
+
# however, it is recommended to use the <code>unencode_component</code>
|
431
|
+
# alias when unencoding components.
|
432
|
+
#
|
433
|
+
# @param [String, Addressable::URI, #to_str] uri
|
434
|
+
# The URI or component to unencode.
|
435
|
+
#
|
436
|
+
# @param [Class] return_type
|
437
|
+
# The type of object to return.
|
438
|
+
# This value may only be set to <code>String</code> or
|
439
|
+
# <code>Addressable::URI</code>. All other values are invalid. Defaults
|
440
|
+
# to <code>String</code>.
|
441
|
+
#
|
442
|
+
# @param [String] leave_encoded
|
443
|
+
# A string of characters to leave encoded. If a percent encoded character
|
444
|
+
# in this list is encountered then it will remain percent encoded.
|
445
|
+
#
|
446
|
+
# @return [String, Addressable::URI]
|
447
|
+
# The unencoded component or URI.
|
448
|
+
# The return type is determined by the <code>return_type</code>
|
449
|
+
# parameter.
|
450
|
+
def self.unencode(uri, return_type=String, leave_encoded='')
|
451
|
+
return nil if uri.nil?
|
452
|
+
|
453
|
+
begin
|
454
|
+
uri = uri.to_str
|
455
|
+
rescue NoMethodError, TypeError
|
456
|
+
raise TypeError, "Can't convert #{uri.class} into String."
|
457
|
+
end if !uri.is_a? String
|
458
|
+
if ![String, ::Addressable::URI].include?(return_type)
|
459
|
+
raise TypeError,
|
460
|
+
"Expected Class (String or Addressable::URI), " +
|
461
|
+
"got #{return_type.inspect}"
|
462
|
+
end
|
463
|
+
uri = uri.dup
|
464
|
+
# Seriously, only use UTF-8. I'm really not kidding!
|
465
|
+
uri.force_encoding("utf-8")
|
466
|
+
leave_encoded = leave_encoded.dup.force_encoding("utf-8")
|
467
|
+
result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
|
468
|
+
c = sequence[1..3].to_i(16).chr
|
469
|
+
c.force_encoding("utf-8")
|
470
|
+
leave_encoded.include?(c) ? sequence : c
|
471
|
+
end
|
472
|
+
result.force_encoding("utf-8")
|
473
|
+
if return_type == String
|
474
|
+
return result
|
475
|
+
elsif return_type == ::Addressable::URI
|
476
|
+
return ::Addressable::URI.parse(result)
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
class << self
|
481
|
+
alias_method :unescape, :unencode
|
482
|
+
alias_method :unencode_component, :unencode
|
483
|
+
alias_method :unescape_component, :unencode
|
484
|
+
end
|
485
|
+
|
486
|
+
|
487
|
+
##
|
488
|
+
# Normalizes the encoding of a URI component.
|
489
|
+
#
|
490
|
+
# @param [String, #to_str] component The URI component to encode.
|
491
|
+
#
|
492
|
+
# @param [String, Regexp] character_class
|
493
|
+
# The characters which are not percent encoded. If a <code>String</code>
|
494
|
+
# is passed, the <code>String</code> must be formatted as a regular
|
495
|
+
# expression character class. (Do not include the surrounding square
|
496
|
+
# brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
|
497
|
+
# everything but the letters 'b' through 'z' and the numbers '0'
|
498
|
+
# through '9' to be percent encoded. If a <code>Regexp</code> is passed,
|
499
|
+
# the value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A
|
500
|
+
# set of useful <code>String</code> values may be found in the
|
501
|
+
# <code>Addressable::URI::CharacterClasses</code> module. The default
|
502
|
+
# value is the reserved plus unreserved character classes specified in
|
503
|
+
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
|
504
|
+
#
|
505
|
+
# @param [String] leave_encoded
|
506
|
+
# When <code>character_class</code> is a <code>String</code> then
|
507
|
+
# <code>leave_encoded</code> is a string of characters that should remain
|
508
|
+
# percent encoded while normalizing the component; if they appear percent
|
509
|
+
# encoded in the original component, then they will be upcased ("%2f"
|
510
|
+
# normalized to "%2F") but otherwise left alone.
|
511
|
+
#
|
512
|
+
# @return [String] The normalized component.
|
513
|
+
#
|
514
|
+
# @example
|
515
|
+
# Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
|
516
|
+
# => "simple%2Fex%61mple"
|
517
|
+
# Addressable::URI.normalize_component(
|
518
|
+
# "simpl%65/%65xampl%65", /[^b-zB-Z]/
|
519
|
+
# )
|
520
|
+
# => "simple%2Fex%61mple"
|
521
|
+
# Addressable::URI.normalize_component(
|
522
|
+
# "simpl%65/%65xampl%65",
|
523
|
+
# Addressable::URI::CharacterClasses::UNRESERVED
|
524
|
+
# )
|
525
|
+
# => "simple%2Fexample"
|
526
|
+
# Addressable::URI.normalize_component(
|
527
|
+
# "one%20two%2fthree%26four",
|
528
|
+
# "0-9a-zA-Z &/",
|
529
|
+
# "/"
|
530
|
+
# )
|
531
|
+
# => "one two%2Fthree&four"
|
532
|
+
def self.normalize_component(component, character_class=
|
533
|
+
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
|
534
|
+
leave_encoded='')
|
535
|
+
return nil if component.nil?
|
536
|
+
|
537
|
+
begin
|
538
|
+
component = component.to_str
|
539
|
+
rescue NoMethodError, TypeError
|
540
|
+
raise TypeError, "Can't convert #{component.class} into String."
|
541
|
+
end if !component.is_a? String
|
542
|
+
|
543
|
+
if ![String, Regexp].include?(character_class.class)
|
544
|
+
raise TypeError,
|
545
|
+
"Expected String or Regexp, got #{character_class.inspect}"
|
546
|
+
end
|
547
|
+
if character_class.kind_of?(String)
|
548
|
+
leave_re = if leave_encoded.length > 0
|
549
|
+
character_class = "#{character_class}%" unless character_class.include?('%')
|
550
|
+
|
551
|
+
"|%(?!#{leave_encoded.chars.map do |char|
|
552
|
+
seq = SEQUENCE_ENCODING_TABLE[char]
|
553
|
+
[seq.upcase, seq.downcase]
|
554
|
+
end.flatten.join('|')})"
|
555
|
+
end
|
556
|
+
|
557
|
+
character_class = /[^#{character_class}]#{leave_re}/
|
558
|
+
end
|
559
|
+
# We can't perform regexps on invalid UTF sequences, but
|
560
|
+
# here we need to, so switch to ASCII.
|
561
|
+
component = component.dup
|
562
|
+
component.force_encoding(Encoding::ASCII_8BIT)
|
563
|
+
unencoded = self.unencode_component(component, String, leave_encoded)
|
564
|
+
begin
|
565
|
+
encoded = self.encode_component(
|
566
|
+
Addressable::IDNA.unicode_normalize_kc(unencoded),
|
567
|
+
character_class,
|
568
|
+
leave_encoded
|
569
|
+
)
|
570
|
+
rescue ArgumentError
|
571
|
+
encoded = self.encode_component(unencoded)
|
572
|
+
end
|
573
|
+
encoded.force_encoding(Encoding::UTF_8)
|
574
|
+
return encoded
|
575
|
+
end
|
576
|
+
|
577
|
+
##
|
578
|
+
# Percent encodes any special characters in the URI.
|
579
|
+
#
|
580
|
+
# @param [String, Addressable::URI, #to_str] uri
|
581
|
+
# The URI to encode.
|
582
|
+
#
|
583
|
+
# @param [Class] return_type
|
584
|
+
# The type of object to return.
|
585
|
+
# This value may only be set to <code>String</code> or
|
586
|
+
# <code>Addressable::URI</code>. All other values are invalid. Defaults
|
587
|
+
# to <code>String</code>.
|
588
|
+
#
|
589
|
+
# @return [String, Addressable::URI]
|
590
|
+
# The encoded URI.
|
591
|
+
# The return type is determined by the <code>return_type</code>
|
592
|
+
# parameter.
|
593
|
+
def self.encode(uri, return_type=String)
|
594
|
+
return nil if uri.nil?
|
595
|
+
|
596
|
+
begin
|
597
|
+
uri = uri.to_str
|
598
|
+
rescue NoMethodError, TypeError
|
599
|
+
raise TypeError, "Can't convert #{uri.class} into String."
|
600
|
+
end if !uri.is_a? String
|
601
|
+
|
602
|
+
if ![String, ::Addressable::URI].include?(return_type)
|
603
|
+
raise TypeError,
|
604
|
+
"Expected Class (String or Addressable::URI), " +
|
605
|
+
"got #{return_type.inspect}"
|
606
|
+
end
|
607
|
+
uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
|
608
|
+
encoded_uri = Addressable::URI.new(
|
609
|
+
:scheme => self.encode_component(uri_object.scheme,
|
610
|
+
Addressable::URI::CharacterClasses::SCHEME),
|
611
|
+
:authority => self.encode_component(uri_object.authority,
|
612
|
+
Addressable::URI::CharacterClasses::AUTHORITY),
|
613
|
+
:path => self.encode_component(uri_object.path,
|
614
|
+
Addressable::URI::CharacterClasses::PATH),
|
615
|
+
:query => self.encode_component(uri_object.query,
|
616
|
+
Addressable::URI::CharacterClasses::QUERY),
|
617
|
+
:fragment => self.encode_component(uri_object.fragment,
|
618
|
+
Addressable::URI::CharacterClasses::FRAGMENT)
|
619
|
+
)
|
620
|
+
if return_type == String
|
621
|
+
return encoded_uri.to_s
|
622
|
+
elsif return_type == ::Addressable::URI
|
623
|
+
return encoded_uri
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
class << self
|
628
|
+
alias_method :escape, :encode
|
629
|
+
end
|
630
|
+
|
631
|
+
##
|
632
|
+
# Normalizes the encoding of a URI. Characters within a hostname are
|
633
|
+
# not percent encoded to allow for internationalized domain names.
|
634
|
+
#
|
635
|
+
# @param [String, Addressable::URI, #to_str] uri
|
636
|
+
# The URI to encode.
|
637
|
+
#
|
638
|
+
# @param [Class] return_type
|
639
|
+
# The type of object to return.
|
640
|
+
# This value may only be set to <code>String</code> or
|
641
|
+
# <code>Addressable::URI</code>. All other values are invalid. Defaults
|
642
|
+
# to <code>String</code>.
|
643
|
+
#
|
644
|
+
# @return [String, Addressable::URI]
|
645
|
+
# The encoded URI.
|
646
|
+
# The return type is determined by the <code>return_type</code>
|
647
|
+
# parameter.
|
648
|
+
def self.normalized_encode(uri, return_type=String)
|
649
|
+
begin
|
650
|
+
uri = uri.to_str
|
651
|
+
rescue NoMethodError, TypeError
|
652
|
+
raise TypeError, "Can't convert #{uri.class} into String."
|
653
|
+
end if !uri.is_a? String
|
654
|
+
|
655
|
+
if ![String, ::Addressable::URI].include?(return_type)
|
656
|
+
raise TypeError,
|
657
|
+
"Expected Class (String or Addressable::URI), " +
|
658
|
+
"got #{return_type.inspect}"
|
659
|
+
end
|
660
|
+
uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
|
661
|
+
components = {
|
662
|
+
:scheme => self.unencode_component(uri_object.scheme),
|
663
|
+
:user => self.unencode_component(uri_object.user),
|
664
|
+
:password => self.unencode_component(uri_object.password),
|
665
|
+
:host => self.unencode_component(uri_object.host),
|
666
|
+
:port => (uri_object.port.nil? ? nil : uri_object.port.to_s),
|
667
|
+
:path => self.unencode_component(uri_object.path),
|
668
|
+
:query => self.unencode_component(uri_object.query),
|
669
|
+
:fragment => self.unencode_component(uri_object.fragment)
|
670
|
+
}
|
671
|
+
components.each do |key, value|
|
672
|
+
if value != nil
|
673
|
+
begin
|
674
|
+
components[key] =
|
675
|
+
Addressable::IDNA.unicode_normalize_kc(value.to_str)
|
676
|
+
rescue ArgumentError
|
677
|
+
# Likely a malformed UTF-8 character, skip unicode normalization
|
678
|
+
components[key] = value.to_str
|
679
|
+
end
|
680
|
+
end
|
681
|
+
end
|
682
|
+
encoded_uri = Addressable::URI.new(
|
683
|
+
:scheme => self.encode_component(components[:scheme],
|
684
|
+
Addressable::URI::CharacterClasses::SCHEME),
|
685
|
+
:user => self.encode_component(components[:user],
|
686
|
+
Addressable::URI::CharacterClasses::UNRESERVED),
|
687
|
+
:password => self.encode_component(components[:password],
|
688
|
+
Addressable::URI::CharacterClasses::UNRESERVED),
|
689
|
+
:host => components[:host],
|
690
|
+
:port => components[:port],
|
691
|
+
:path => self.encode_component(components[:path],
|
692
|
+
Addressable::URI::CharacterClasses::PATH),
|
693
|
+
:query => self.encode_component(components[:query],
|
694
|
+
Addressable::URI::CharacterClasses::QUERY),
|
695
|
+
:fragment => self.encode_component(components[:fragment],
|
696
|
+
Addressable::URI::CharacterClasses::FRAGMENT)
|
697
|
+
)
|
698
|
+
if return_type == String
|
699
|
+
return encoded_uri.to_s
|
700
|
+
elsif return_type == ::Addressable::URI
|
701
|
+
return encoded_uri
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
##
|
706
|
+
# Encodes a set of key/value pairs according to the rules for the
|
707
|
+
# <code>application/x-www-form-urlencoded</code> MIME type.
|
708
|
+
#
|
709
|
+
# @param [#to_hash, #to_ary] form_values
|
710
|
+
# The form values to encode.
|
711
|
+
#
|
712
|
+
# @param [TrueClass, FalseClass] sort
|
713
|
+
# Sort the key/value pairs prior to encoding.
|
714
|
+
# Defaults to <code>false</code>.
|
715
|
+
#
|
716
|
+
# @return [String]
|
717
|
+
# The encoded value.
|
718
|
+
def self.form_encode(form_values, sort=false)
|
719
|
+
if form_values.respond_to?(:to_hash)
|
720
|
+
form_values = form_values.to_hash.to_a
|
721
|
+
elsif form_values.respond_to?(:to_ary)
|
722
|
+
form_values = form_values.to_ary
|
723
|
+
else
|
724
|
+
raise TypeError, "Can't convert #{form_values.class} into Array."
|
725
|
+
end
|
726
|
+
|
727
|
+
form_values = form_values.inject([]) do |accu, (key, value)|
|
728
|
+
if value.kind_of?(Array)
|
729
|
+
value.each do |v|
|
730
|
+
accu << [key.to_s, v.to_s]
|
731
|
+
end
|
732
|
+
else
|
733
|
+
accu << [key.to_s, value.to_s]
|
734
|
+
end
|
735
|
+
accu
|
736
|
+
end
|
737
|
+
|
738
|
+
if sort
|
739
|
+
# Useful for OAuth and optimizing caching systems
|
740
|
+
form_values = form_values.sort
|
741
|
+
end
|
742
|
+
escaped_form_values = form_values.map do |(key, value)|
|
743
|
+
# Line breaks are CRLF pairs
|
744
|
+
[
|
745
|
+
self.encode_component(
|
746
|
+
key.gsub(/(\r\n|\n|\r)/, "\r\n"),
|
747
|
+
CharacterClasses::UNRESERVED
|
748
|
+
).gsub("%20", "+"),
|
749
|
+
self.encode_component(
|
750
|
+
value.gsub(/(\r\n|\n|\r)/, "\r\n"),
|
751
|
+
CharacterClasses::UNRESERVED
|
752
|
+
).gsub("%20", "+")
|
753
|
+
]
|
754
|
+
end
|
755
|
+
return escaped_form_values.map do |(key, value)|
|
756
|
+
"#{key}=#{value}"
|
757
|
+
end.join("&")
|
758
|
+
end
|
759
|
+
|
760
|
+
##
|
761
|
+
# Decodes a <code>String</code> according to the rules for the
|
762
|
+
# <code>application/x-www-form-urlencoded</code> MIME type.
|
763
|
+
#
|
764
|
+
# @param [String, #to_str] encoded_value
|
765
|
+
# The form values to decode.
|
766
|
+
#
|
767
|
+
# @return [Array]
|
768
|
+
# The decoded values.
|
769
|
+
# This is not a <code>Hash</code> because of the possibility for
|
770
|
+
# duplicate keys.
|
771
|
+
def self.form_unencode(encoded_value)
|
772
|
+
if !encoded_value.respond_to?(:to_str)
|
773
|
+
raise TypeError, "Can't convert #{encoded_value.class} into String."
|
774
|
+
end
|
775
|
+
encoded_value = encoded_value.to_str
|
776
|
+
split_values = encoded_value.split("&").map do |pair|
|
777
|
+
pair.split("=", 2)
|
778
|
+
end
|
779
|
+
return split_values.map do |(key, value)|
|
780
|
+
[
|
781
|
+
key ? self.unencode_component(
|
782
|
+
key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
|
783
|
+
value ? (self.unencode_component(
|
784
|
+
value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
|
785
|
+
]
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
##
|
790
|
+
# Creates a new uri object from component parts.
|
791
|
+
#
|
792
|
+
# @option [String, #to_str] scheme The scheme component.
|
793
|
+
# @option [String, #to_str] user The user component.
|
794
|
+
# @option [String, #to_str] password The password component.
|
795
|
+
# @option [String, #to_str] userinfo
|
796
|
+
# The userinfo component. If this is supplied, the user and password
|
797
|
+
# components must be omitted.
|
798
|
+
# @option [String, #to_str] host The host component.
|
799
|
+
# @option [String, #to_str] port The port component.
|
800
|
+
# @option [String, #to_str] authority
|
801
|
+
# The authority component. If this is supplied, the user, password,
|
802
|
+
# userinfo, host, and port components must be omitted.
|
803
|
+
# @option [String, #to_str] path The path component.
|
804
|
+
# @option [String, #to_str] query The query component.
|
805
|
+
# @option [String, #to_str] fragment The fragment component.
|
806
|
+
#
|
807
|
+
# @return [Addressable::URI] The constructed URI object.
|
808
|
+
def initialize(options={})
|
809
|
+
if options.has_key?(:authority)
|
810
|
+
if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
|
811
|
+
raise ArgumentError,
|
812
|
+
"Cannot specify both an authority and any of the components " +
|
813
|
+
"within the authority."
|
814
|
+
end
|
815
|
+
end
|
816
|
+
if options.has_key?(:userinfo)
|
817
|
+
if (options.keys & [:user, :password]).any?
|
818
|
+
raise ArgumentError,
|
819
|
+
"Cannot specify both a userinfo and either the user or password."
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
823
|
+
self.defer_validation do
|
824
|
+
# Bunch of crazy logic required because of the composite components
|
825
|
+
# like userinfo and authority.
|
826
|
+
self.scheme = options[:scheme] if options[:scheme]
|
827
|
+
self.user = options[:user] if options[:user]
|
828
|
+
self.password = options[:password] if options[:password]
|
829
|
+
self.userinfo = options[:userinfo] if options[:userinfo]
|
830
|
+
self.host = options[:host] if options[:host]
|
831
|
+
self.port = options[:port] if options[:port]
|
832
|
+
self.authority = options[:authority] if options[:authority]
|
833
|
+
self.path = options[:path] if options[:path]
|
834
|
+
self.query = options[:query] if options[:query]
|
835
|
+
self.query_values = options[:query_values] if options[:query_values]
|
836
|
+
self.fragment = options[:fragment] if options[:fragment]
|
837
|
+
end
|
838
|
+
self.to_s
|
839
|
+
end
|
840
|
+
|
841
|
+
##
|
842
|
+
# Freeze URI, initializing instance variables.
|
843
|
+
#
|
844
|
+
# @return [Addressable::URI] The frozen URI object.
|
845
|
+
def freeze
|
846
|
+
self.normalized_scheme
|
847
|
+
self.normalized_user
|
848
|
+
self.normalized_password
|
849
|
+
self.normalized_userinfo
|
850
|
+
self.normalized_host
|
851
|
+
self.normalized_port
|
852
|
+
self.normalized_authority
|
853
|
+
self.normalized_site
|
854
|
+
self.normalized_path
|
855
|
+
self.normalized_query
|
856
|
+
self.normalized_fragment
|
857
|
+
self.hash
|
858
|
+
super
|
859
|
+
end
|
860
|
+
|
861
|
+
##
|
862
|
+
# The scheme component for this URI.
|
863
|
+
#
|
864
|
+
# @return [String] The scheme component.
|
865
|
+
def scheme
|
866
|
+
return defined?(@scheme) ? @scheme : nil
|
867
|
+
end
|
868
|
+
|
869
|
+
##
|
870
|
+
# The scheme component for this URI, normalized.
|
871
|
+
#
|
872
|
+
# @return [String] The scheme component, normalized.
|
873
|
+
def normalized_scheme
|
874
|
+
return nil unless self.scheme
|
875
|
+
@normalized_scheme ||= begin
|
876
|
+
if self.scheme =~ /^\s*ssh\+svn\s*$/i
|
877
|
+
"svn+ssh".dup
|
878
|
+
else
|
879
|
+
Addressable::URI.normalize_component(
|
880
|
+
self.scheme.strip.downcase,
|
881
|
+
Addressable::URI::CharacterClasses::SCHEME
|
882
|
+
)
|
883
|
+
end
|
884
|
+
end
|
885
|
+
# All normalized values should be UTF-8
|
886
|
+
@normalized_scheme.force_encoding(Encoding::UTF_8) if @normalized_scheme
|
887
|
+
@normalized_scheme
|
888
|
+
end
|
889
|
+
|
890
|
+
##
|
891
|
+
# Sets the scheme component for this URI.
|
892
|
+
#
|
893
|
+
# @param [String, #to_str] new_scheme The new scheme component.
|
894
|
+
def scheme=(new_scheme)
|
895
|
+
if new_scheme && !new_scheme.respond_to?(:to_str)
|
896
|
+
raise TypeError, "Can't convert #{new_scheme.class} into String."
|
897
|
+
elsif new_scheme
|
898
|
+
new_scheme = new_scheme.to_str
|
899
|
+
end
|
900
|
+
if new_scheme && new_scheme !~ /\A[a-z][a-z0-9\.\+\-]*\z/i
|
901
|
+
raise InvalidURIError, "Invalid scheme format: #{new_scheme}"
|
902
|
+
end
|
903
|
+
@scheme = new_scheme
|
904
|
+
@scheme = nil if @scheme.to_s.strip.empty?
|
905
|
+
|
906
|
+
# Reset dependent values
|
907
|
+
remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme)
|
908
|
+
remove_composite_values
|
909
|
+
|
910
|
+
# Ensure we haven't created an invalid URI
|
911
|
+
validate()
|
912
|
+
end
|
913
|
+
|
914
|
+
##
|
915
|
+
# The user component for this URI.
|
916
|
+
#
|
917
|
+
# @return [String] The user component.
|
918
|
+
def user
|
919
|
+
return defined?(@user) ? @user : nil
|
920
|
+
end
|
921
|
+
|
922
|
+
##
|
923
|
+
# The user component for this URI, normalized.
|
924
|
+
#
|
925
|
+
# @return [String] The user component, normalized.
|
926
|
+
def normalized_user
|
927
|
+
return nil unless self.user
|
928
|
+
return @normalized_user if defined?(@normalized_user)
|
929
|
+
@normalized_user ||= begin
|
930
|
+
if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
|
931
|
+
(!self.password || self.password.strip.empty?)
|
932
|
+
nil
|
933
|
+
else
|
934
|
+
Addressable::URI.normalize_component(
|
935
|
+
self.user.strip,
|
936
|
+
Addressable::URI::CharacterClasses::UNRESERVED
|
937
|
+
)
|
938
|
+
end
|
939
|
+
end
|
940
|
+
# All normalized values should be UTF-8
|
941
|
+
@normalized_user.force_encoding(Encoding::UTF_8) if @normalized_user
|
942
|
+
@normalized_user
|
943
|
+
end
|
944
|
+
|
945
|
+
##
|
946
|
+
# Sets the user component for this URI.
|
947
|
+
#
|
948
|
+
# @param [String, #to_str] new_user The new user component.
|
949
|
+
def user=(new_user)
|
950
|
+
if new_user && !new_user.respond_to?(:to_str)
|
951
|
+
raise TypeError, "Can't convert #{new_user.class} into String."
|
952
|
+
end
|
953
|
+
@user = new_user ? new_user.to_str : nil
|
954
|
+
|
955
|
+
# You can't have a nil user with a non-nil password
|
956
|
+
if password != nil
|
957
|
+
@user = EMPTY_STR if @user.nil?
|
958
|
+
end
|
959
|
+
|
960
|
+
# Reset dependent values
|
961
|
+
remove_instance_variable(:@userinfo) if defined?(@userinfo)
|
962
|
+
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
|
963
|
+
remove_instance_variable(:@authority) if defined?(@authority)
|
964
|
+
remove_instance_variable(:@normalized_user) if defined?(@normalized_user)
|
965
|
+
remove_composite_values
|
966
|
+
|
967
|
+
# Ensure we haven't created an invalid URI
|
968
|
+
validate()
|
969
|
+
end
|
970
|
+
|
971
|
+
##
|
972
|
+
# The password component for this URI.
|
973
|
+
#
|
974
|
+
# @return [String] The password component.
|
975
|
+
def password
|
976
|
+
return defined?(@password) ? @password : nil
|
977
|
+
end
|
978
|
+
|
979
|
+
##
|
980
|
+
# The password component for this URI, normalized.
|
981
|
+
#
|
982
|
+
# @return [String] The password component, normalized.
|
983
|
+
def normalized_password
|
984
|
+
return nil unless self.password
|
985
|
+
return @normalized_password if defined?(@normalized_password)
|
986
|
+
@normalized_password ||= begin
|
987
|
+
if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
|
988
|
+
(!self.user || self.user.strip.empty?)
|
989
|
+
nil
|
990
|
+
else
|
991
|
+
Addressable::URI.normalize_component(
|
992
|
+
self.password.strip,
|
993
|
+
Addressable::URI::CharacterClasses::UNRESERVED
|
994
|
+
)
|
995
|
+
end
|
996
|
+
end
|
997
|
+
# All normalized values should be UTF-8
|
998
|
+
if @normalized_password
|
999
|
+
@normalized_password.force_encoding(Encoding::UTF_8)
|
1000
|
+
end
|
1001
|
+
@normalized_password
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
##
|
1005
|
+
# Sets the password component for this URI.
|
1006
|
+
#
|
1007
|
+
# @param [String, #to_str] new_password The new password component.
|
1008
|
+
def password=(new_password)
|
1009
|
+
if new_password && !new_password.respond_to?(:to_str)
|
1010
|
+
raise TypeError, "Can't convert #{new_password.class} into String."
|
1011
|
+
end
|
1012
|
+
@password = new_password ? new_password.to_str : nil
|
1013
|
+
|
1014
|
+
# You can't have a nil user with a non-nil password
|
1015
|
+
@password ||= nil
|
1016
|
+
@user ||= nil
|
1017
|
+
if @password != nil
|
1018
|
+
@user = EMPTY_STR if @user.nil?
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
# Reset dependent values
|
1022
|
+
remove_instance_variable(:@userinfo) if defined?(@userinfo)
|
1023
|
+
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
|
1024
|
+
remove_instance_variable(:@authority) if defined?(@authority)
|
1025
|
+
remove_instance_variable(:@normalized_password) if defined?(@normalized_password)
|
1026
|
+
remove_composite_values
|
1027
|
+
|
1028
|
+
# Ensure we haven't created an invalid URI
|
1029
|
+
validate()
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
##
|
1033
|
+
# The userinfo component for this URI.
|
1034
|
+
# Combines the user and password components.
|
1035
|
+
#
|
1036
|
+
# @return [String] The userinfo component.
|
1037
|
+
def userinfo
|
1038
|
+
current_user = self.user
|
1039
|
+
current_password = self.password
|
1040
|
+
(current_user || current_password) && @userinfo ||= begin
|
1041
|
+
if current_user && current_password
|
1042
|
+
"#{current_user}:#{current_password}"
|
1043
|
+
elsif current_user && !current_password
|
1044
|
+
"#{current_user}"
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
##
|
1050
|
+
# The userinfo component for this URI, normalized.
|
1051
|
+
#
|
1052
|
+
# @return [String] The userinfo component, normalized.
|
1053
|
+
def normalized_userinfo
|
1054
|
+
return nil unless self.userinfo
|
1055
|
+
return @normalized_userinfo if defined?(@normalized_userinfo)
|
1056
|
+
@normalized_userinfo ||= begin
|
1057
|
+
current_user = self.normalized_user
|
1058
|
+
current_password = self.normalized_password
|
1059
|
+
if !current_user && !current_password
|
1060
|
+
nil
|
1061
|
+
elsif current_user && current_password
|
1062
|
+
"#{current_user}:#{current_password}".dup
|
1063
|
+
elsif current_user && !current_password
|
1064
|
+
"#{current_user}".dup
|
1065
|
+
end
|
1066
|
+
end
|
1067
|
+
# All normalized values should be UTF-8
|
1068
|
+
if @normalized_userinfo
|
1069
|
+
@normalized_userinfo.force_encoding(Encoding::UTF_8)
|
1070
|
+
end
|
1071
|
+
@normalized_userinfo
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
##
|
1075
|
+
# Sets the userinfo component for this URI.
|
1076
|
+
#
|
1077
|
+
# @param [String, #to_str] new_userinfo The new userinfo component.
|
1078
|
+
def userinfo=(new_userinfo)
|
1079
|
+
if new_userinfo && !new_userinfo.respond_to?(:to_str)
|
1080
|
+
raise TypeError, "Can't convert #{new_userinfo.class} into String."
|
1081
|
+
end
|
1082
|
+
new_user, new_password = if new_userinfo
|
1083
|
+
[
|
1084
|
+
new_userinfo.to_str.strip[/^(.*):/, 1],
|
1085
|
+
new_userinfo.to_str.strip[/:(.*)$/, 1]
|
1086
|
+
]
|
1087
|
+
else
|
1088
|
+
[nil, nil]
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
# Password assigned first to ensure validity in case of nil
|
1092
|
+
self.password = new_password
|
1093
|
+
self.user = new_user
|
1094
|
+
|
1095
|
+
# Reset dependent values
|
1096
|
+
remove_instance_variable(:@authority) if defined?(@authority)
|
1097
|
+
remove_composite_values
|
1098
|
+
|
1099
|
+
# Ensure we haven't created an invalid URI
|
1100
|
+
validate()
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
##
|
1104
|
+
# The host component for this URI.
|
1105
|
+
#
|
1106
|
+
# @return [String] The host component.
|
1107
|
+
def host
|
1108
|
+
return defined?(@host) ? @host : nil
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
##
|
1112
|
+
# The host component for this URI, normalized.
|
1113
|
+
#
|
1114
|
+
# @return [String] The host component, normalized.
|
1115
|
+
def normalized_host
|
1116
|
+
return nil unless self.host
|
1117
|
+
@normalized_host ||= begin
|
1118
|
+
if !self.host.strip.empty?
|
1119
|
+
result = ::Addressable::IDNA.to_ascii(
|
1120
|
+
URI.unencode_component(self.host.strip.downcase)
|
1121
|
+
)
|
1122
|
+
if result =~ /[^\.]\.$/
|
1123
|
+
# Single trailing dots are unnecessary.
|
1124
|
+
result = result[0...-1]
|
1125
|
+
end
|
1126
|
+
result = Addressable::URI.normalize_component(
|
1127
|
+
result,
|
1128
|
+
CharacterClasses::HOST)
|
1129
|
+
result
|
1130
|
+
else
|
1131
|
+
EMPTY_STR.dup
|
1132
|
+
end
|
1133
|
+
end
|
1134
|
+
# All normalized values should be UTF-8
|
1135
|
+
@normalized_host.force_encoding(Encoding::UTF_8) if @normalized_host
|
1136
|
+
@normalized_host
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
##
|
1140
|
+
# Sets the host component for this URI.
|
1141
|
+
#
|
1142
|
+
# @param [String, #to_str] new_host The new host component.
|
1143
|
+
def host=(new_host)
|
1144
|
+
if new_host && !new_host.respond_to?(:to_str)
|
1145
|
+
raise TypeError, "Can't convert #{new_host.class} into String."
|
1146
|
+
end
|
1147
|
+
@host = new_host ? new_host.to_str : nil
|
1148
|
+
|
1149
|
+
# Reset dependent values
|
1150
|
+
remove_instance_variable(:@authority) if defined?(@authority)
|
1151
|
+
remove_instance_variable(:@normalized_host) if defined?(@normalized_host)
|
1152
|
+
remove_composite_values
|
1153
|
+
|
1154
|
+
# Ensure we haven't created an invalid URI
|
1155
|
+
validate()
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
##
|
1159
|
+
# This method is same as URI::Generic#host except
|
1160
|
+
# brackets for IPv6 (and 'IPvFuture') addresses are removed.
|
1161
|
+
#
|
1162
|
+
# @see Addressable::URI#host
|
1163
|
+
#
|
1164
|
+
# @return [String] The hostname for this URI.
|
1165
|
+
def hostname
|
1166
|
+
v = self.host
|
1167
|
+
/\A\[(.*)\]\z/ =~ v ? $1 : v
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
##
|
1171
|
+
# This method is same as URI::Generic#host= except
|
1172
|
+
# the argument can be a bare IPv6 address (or 'IPvFuture').
|
1173
|
+
#
|
1174
|
+
# @see Addressable::URI#host=
|
1175
|
+
#
|
1176
|
+
# @param [String, #to_str] new_hostname The new hostname for this URI.
|
1177
|
+
def hostname=(new_hostname)
|
1178
|
+
if new_hostname &&
|
1179
|
+
(new_hostname.respond_to?(:ipv4?) || new_hostname.respond_to?(:ipv6?))
|
1180
|
+
new_hostname = new_hostname.to_s
|
1181
|
+
elsif new_hostname && !new_hostname.respond_to?(:to_str)
|
1182
|
+
raise TypeError, "Can't convert #{new_hostname.class} into String."
|
1183
|
+
end
|
1184
|
+
v = new_hostname ? new_hostname.to_str : nil
|
1185
|
+
v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
|
1186
|
+
self.host = v
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
##
|
1190
|
+
# Returns the top-level domain for this host.
|
1191
|
+
#
|
1192
|
+
# @example
|
1193
|
+
# Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk"
|
1194
|
+
def tld
|
1195
|
+
PublicSuffix.parse(self.host, ignore_private: true).tld
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
##
|
1199
|
+
# Sets the top-level domain for this URI.
|
1200
|
+
#
|
1201
|
+
# @param [String, #to_str] new_tld The new top-level domain.
|
1202
|
+
def tld=(new_tld)
|
1203
|
+
replaced_tld = host.sub(/#{tld}\z/, new_tld)
|
1204
|
+
self.host = PublicSuffix::Domain.new(replaced_tld).to_s
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
##
|
1208
|
+
# Returns the public suffix domain for this host.
|
1209
|
+
#
|
1210
|
+
# @example
|
1211
|
+
# Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk"
|
1212
|
+
def domain
|
1213
|
+
PublicSuffix.domain(self.host, ignore_private: true)
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
##
|
1217
|
+
# The authority component for this URI.
|
1218
|
+
# Combines the user, password, host, and port components.
|
1219
|
+
#
|
1220
|
+
# @return [String] The authority component.
|
1221
|
+
def authority
|
1222
|
+
self.host && @authority ||= begin
|
1223
|
+
authority = String.new
|
1224
|
+
if self.userinfo != nil
|
1225
|
+
authority << "#{self.userinfo}@"
|
1226
|
+
end
|
1227
|
+
authority << self.host
|
1228
|
+
if self.port != nil
|
1229
|
+
authority << ":#{self.port}"
|
1230
|
+
end
|
1231
|
+
authority
|
1232
|
+
end
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
##
|
1236
|
+
# The authority component for this URI, normalized.
|
1237
|
+
#
|
1238
|
+
# @return [String] The authority component, normalized.
|
1239
|
+
def normalized_authority
|
1240
|
+
return nil unless self.authority
|
1241
|
+
@normalized_authority ||= begin
|
1242
|
+
authority = String.new
|
1243
|
+
if self.normalized_userinfo != nil
|
1244
|
+
authority << "#{self.normalized_userinfo}@"
|
1245
|
+
end
|
1246
|
+
authority << self.normalized_host
|
1247
|
+
if self.normalized_port != nil
|
1248
|
+
authority << ":#{self.normalized_port}"
|
1249
|
+
end
|
1250
|
+
authority
|
1251
|
+
end
|
1252
|
+
# All normalized values should be UTF-8
|
1253
|
+
if @normalized_authority
|
1254
|
+
@normalized_authority.force_encoding(Encoding::UTF_8)
|
1255
|
+
end
|
1256
|
+
@normalized_authority
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
##
|
1260
|
+
# Sets the authority component for this URI.
|
1261
|
+
#
|
1262
|
+
# @param [String, #to_str] new_authority The new authority component.
|
1263
|
+
def authority=(new_authority)
|
1264
|
+
if new_authority
|
1265
|
+
if !new_authority.respond_to?(:to_str)
|
1266
|
+
raise TypeError, "Can't convert #{new_authority.class} into String."
|
1267
|
+
end
|
1268
|
+
new_authority = new_authority.to_str
|
1269
|
+
new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
|
1270
|
+
if new_userinfo
|
1271
|
+
new_user = new_userinfo.strip[/^([^:]*):?/, 1]
|
1272
|
+
new_password = new_userinfo.strip[/:(.*)$/, 1]
|
1273
|
+
end
|
1274
|
+
new_host = new_authority.sub(
|
1275
|
+
/^([^\[\]]*)@/, EMPTY_STR
|
1276
|
+
).sub(
|
1277
|
+
/:([^:@\[\]]*?)$/, EMPTY_STR
|
1278
|
+
)
|
1279
|
+
new_port =
|
1280
|
+
new_authority[/:([^:@\[\]]*?)$/, 1]
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
# Password assigned first to ensure validity in case of nil
|
1284
|
+
self.password = defined?(new_password) ? new_password : nil
|
1285
|
+
self.user = defined?(new_user) ? new_user : nil
|
1286
|
+
self.host = defined?(new_host) ? new_host : nil
|
1287
|
+
self.port = defined?(new_port) ? new_port : nil
|
1288
|
+
|
1289
|
+
# Reset dependent values
|
1290
|
+
remove_instance_variable(:@userinfo) if defined?(@userinfo)
|
1291
|
+
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
|
1292
|
+
remove_composite_values
|
1293
|
+
|
1294
|
+
# Ensure we haven't created an invalid URI
|
1295
|
+
validate()
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
##
|
1299
|
+
# The origin for this URI, serialized to ASCII, as per
|
1300
|
+
# RFC 6454, section 6.2.
|
1301
|
+
#
|
1302
|
+
# @return [String] The serialized origin.
|
1303
|
+
def origin
|
1304
|
+
if self.scheme && self.authority
|
1305
|
+
if self.normalized_port
|
1306
|
+
"#{self.normalized_scheme}://#{self.normalized_host}" +
|
1307
|
+
":#{self.normalized_port}"
|
1308
|
+
else
|
1309
|
+
"#{self.normalized_scheme}://#{self.normalized_host}"
|
1310
|
+
end
|
1311
|
+
else
|
1312
|
+
"null"
|
1313
|
+
end
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
##
|
1317
|
+
# Sets the origin for this URI, serialized to ASCII, as per
|
1318
|
+
# RFC 6454, section 6.2. This assignment will reset the `userinfo`
|
1319
|
+
# component.
|
1320
|
+
#
|
1321
|
+
# @param [String, #to_str] new_origin The new origin component.
|
1322
|
+
def origin=(new_origin)
|
1323
|
+
if new_origin
|
1324
|
+
if !new_origin.respond_to?(:to_str)
|
1325
|
+
raise TypeError, "Can't convert #{new_origin.class} into String."
|
1326
|
+
end
|
1327
|
+
new_origin = new_origin.to_str
|
1328
|
+
new_scheme = new_origin[/^([^:\/?#]+):\/\//, 1]
|
1329
|
+
unless new_scheme
|
1330
|
+
raise InvalidURIError, 'An origin cannot omit the scheme.'
|
1331
|
+
end
|
1332
|
+
new_host = new_origin[/:\/\/([^\/?#:]+)/, 1]
|
1333
|
+
unless new_host
|
1334
|
+
raise InvalidURIError, 'An origin cannot omit the host.'
|
1335
|
+
end
|
1336
|
+
new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
self.scheme = defined?(new_scheme) ? new_scheme : nil
|
1340
|
+
self.host = defined?(new_host) ? new_host : nil
|
1341
|
+
self.port = defined?(new_port) ? new_port : nil
|
1342
|
+
self.userinfo = nil
|
1343
|
+
|
1344
|
+
# Reset dependent values
|
1345
|
+
remove_instance_variable(:@userinfo) if defined?(@userinfo)
|
1346
|
+
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
|
1347
|
+
remove_instance_variable(:@authority) if defined?(@authority)
|
1348
|
+
remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority)
|
1349
|
+
remove_composite_values
|
1350
|
+
|
1351
|
+
# Ensure we haven't created an invalid URI
|
1352
|
+
validate()
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
# Returns an array of known ip-based schemes. These schemes typically
|
1356
|
+
# use a similar URI form:
|
1357
|
+
# <code>//<user>:<password>@<host>:<port>/<url-path></code>
|
1358
|
+
def self.ip_based_schemes
|
1359
|
+
return self.port_mapping.keys
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
# Returns a hash of common IP-based schemes and their default port
|
1363
|
+
# numbers. Adding new schemes to this hash, as necessary, will allow
|
1364
|
+
# for better URI normalization.
|
1365
|
+
def self.port_mapping
|
1366
|
+
PORT_MAPPING
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
##
|
1370
|
+
# The port component for this URI.
|
1371
|
+
# This is the port number actually given in the URI. This does not
|
1372
|
+
# infer port numbers from default values.
|
1373
|
+
#
|
1374
|
+
# @return [Integer] The port component.
|
1375
|
+
def port
|
1376
|
+
return defined?(@port) ? @port : nil
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
##
|
1380
|
+
# The port component for this URI, normalized.
|
1381
|
+
#
|
1382
|
+
# @return [Integer] The port component, normalized.
|
1383
|
+
def normalized_port
|
1384
|
+
return nil unless self.port
|
1385
|
+
return @normalized_port if defined?(@normalized_port)
|
1386
|
+
@normalized_port ||= begin
|
1387
|
+
if URI.port_mapping[self.normalized_scheme] == self.port
|
1388
|
+
nil
|
1389
|
+
else
|
1390
|
+
self.port
|
1391
|
+
end
|
1392
|
+
end
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
##
|
1396
|
+
# Sets the port component for this URI.
|
1397
|
+
#
|
1398
|
+
# @param [String, Integer, #to_s] new_port The new port component.
|
1399
|
+
def port=(new_port)
|
1400
|
+
if new_port != nil && new_port.respond_to?(:to_str)
|
1401
|
+
new_port = Addressable::URI.unencode_component(new_port.to_str)
|
1402
|
+
end
|
1403
|
+
|
1404
|
+
if new_port.respond_to?(:valid_encoding?) && !new_port.valid_encoding?
|
1405
|
+
raise InvalidURIError, "Invalid encoding in port"
|
1406
|
+
end
|
1407
|
+
|
1408
|
+
if new_port != nil && !(new_port.to_s =~ /^\d+$/)
|
1409
|
+
raise InvalidURIError,
|
1410
|
+
"Invalid port number: #{new_port.inspect}"
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
@port = new_port.to_s.to_i
|
1414
|
+
@port = nil if @port == 0
|
1415
|
+
|
1416
|
+
# Reset dependent values
|
1417
|
+
remove_instance_variable(:@authority) if defined?(@authority)
|
1418
|
+
remove_instance_variable(:@normalized_port) if defined?(@normalized_port)
|
1419
|
+
remove_composite_values
|
1420
|
+
|
1421
|
+
# Ensure we haven't created an invalid URI
|
1422
|
+
validate()
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
##
|
1426
|
+
# The inferred port component for this URI.
|
1427
|
+
# This method will normalize to the default port for the URI's scheme if
|
1428
|
+
# the port isn't explicitly specified in the URI.
|
1429
|
+
#
|
1430
|
+
# @return [Integer] The inferred port component.
|
1431
|
+
def inferred_port
|
1432
|
+
if self.port.to_i == 0
|
1433
|
+
self.default_port
|
1434
|
+
else
|
1435
|
+
self.port.to_i
|
1436
|
+
end
|
1437
|
+
end
|
1438
|
+
|
1439
|
+
##
|
1440
|
+
# The default port for this URI's scheme.
|
1441
|
+
# This method will always returns the default port for the URI's scheme
|
1442
|
+
# regardless of the presence of an explicit port in the URI.
|
1443
|
+
#
|
1444
|
+
# @return [Integer] The default port.
|
1445
|
+
def default_port
|
1446
|
+
URI.port_mapping[self.scheme.strip.downcase] if self.scheme
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
##
|
1450
|
+
# The combination of components that represent a site.
|
1451
|
+
# Combines the scheme, user, password, host, and port components.
|
1452
|
+
# Primarily useful for HTTP and HTTPS.
|
1453
|
+
#
|
1454
|
+
# For example, <code>"http://example.com/path?query"</code> would have a
|
1455
|
+
# <code>site</code> value of <code>"http://example.com"</code>.
|
1456
|
+
#
|
1457
|
+
# @return [String] The components that identify a site.
|
1458
|
+
def site
|
1459
|
+
(self.scheme || self.authority) && @site ||= begin
|
1460
|
+
site_string = "".dup
|
1461
|
+
site_string << "#{self.scheme}:" if self.scheme != nil
|
1462
|
+
site_string << "//#{self.authority}" if self.authority != nil
|
1463
|
+
site_string
|
1464
|
+
end
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
##
|
1468
|
+
# The normalized combination of components that represent a site.
|
1469
|
+
# Combines the scheme, user, password, host, and port components.
|
1470
|
+
# Primarily useful for HTTP and HTTPS.
|
1471
|
+
#
|
1472
|
+
# For example, <code>"http://example.com/path?query"</code> would have a
|
1473
|
+
# <code>site</code> value of <code>"http://example.com"</code>.
|
1474
|
+
#
|
1475
|
+
# @return [String] The normalized components that identify a site.
|
1476
|
+
def normalized_site
|
1477
|
+
return nil unless self.site
|
1478
|
+
@normalized_site ||= begin
|
1479
|
+
site_string = "".dup
|
1480
|
+
if self.normalized_scheme != nil
|
1481
|
+
site_string << "#{self.normalized_scheme}:"
|
1482
|
+
end
|
1483
|
+
if self.normalized_authority != nil
|
1484
|
+
site_string << "//#{self.normalized_authority}"
|
1485
|
+
end
|
1486
|
+
site_string
|
1487
|
+
end
|
1488
|
+
# All normalized values should be UTF-8
|
1489
|
+
@normalized_site.force_encoding(Encoding::UTF_8) if @normalized_site
|
1490
|
+
@normalized_site
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
##
|
1494
|
+
# Sets the site value for this URI.
|
1495
|
+
#
|
1496
|
+
# @param [String, #to_str] new_site The new site value.
|
1497
|
+
def site=(new_site)
|
1498
|
+
if new_site
|
1499
|
+
if !new_site.respond_to?(:to_str)
|
1500
|
+
raise TypeError, "Can't convert #{new_site.class} into String."
|
1501
|
+
end
|
1502
|
+
new_site = new_site.to_str
|
1503
|
+
# These two regular expressions derived from the primary parsing
|
1504
|
+
# expression
|
1505
|
+
self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
|
1506
|
+
self.authority = new_site[
|
1507
|
+
/^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
|
1508
|
+
]
|
1509
|
+
else
|
1510
|
+
self.scheme = nil
|
1511
|
+
self.authority = nil
|
1512
|
+
end
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
##
|
1516
|
+
# The path component for this URI.
|
1517
|
+
#
|
1518
|
+
# @return [String] The path component.
|
1519
|
+
def path
|
1520
|
+
return defined?(@path) ? @path : EMPTY_STR
|
1521
|
+
end
|
1522
|
+
|
1523
|
+
NORMPATH = /^(?!\/)[^\/:]*:.*$/
|
1524
|
+
##
|
1525
|
+
# The path component for this URI, normalized.
|
1526
|
+
#
|
1527
|
+
# @return [String] The path component, normalized.
|
1528
|
+
def normalized_path
|
1529
|
+
@normalized_path ||= begin
|
1530
|
+
path = self.path.to_s
|
1531
|
+
if self.scheme == nil && path =~ NORMPATH
|
1532
|
+
# Relative paths with colons in the first segment are ambiguous.
|
1533
|
+
path = path.sub(":", "%2F")
|
1534
|
+
end
|
1535
|
+
# String#split(delimeter, -1) uses the more strict splitting behavior
|
1536
|
+
# found by default in Python.
|
1537
|
+
result = path.strip.split(SLASH, -1).map do |segment|
|
1538
|
+
Addressable::URI.normalize_component(
|
1539
|
+
segment,
|
1540
|
+
Addressable::URI::CharacterClasses::PCHAR
|
1541
|
+
)
|
1542
|
+
end.join(SLASH)
|
1543
|
+
|
1544
|
+
result = URI.normalize_path(result)
|
1545
|
+
if result.empty? &&
|
1546
|
+
["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
|
1547
|
+
result = SLASH.dup
|
1548
|
+
end
|
1549
|
+
result
|
1550
|
+
end
|
1551
|
+
# All normalized values should be UTF-8
|
1552
|
+
@normalized_path.force_encoding(Encoding::UTF_8) if @normalized_path
|
1553
|
+
@normalized_path
|
1554
|
+
end
|
1555
|
+
|
1556
|
+
##
|
1557
|
+
# Sets the path component for this URI.
|
1558
|
+
#
|
1559
|
+
# @param [String, #to_str] new_path The new path component.
|
1560
|
+
def path=(new_path)
|
1561
|
+
if new_path && !new_path.respond_to?(:to_str)
|
1562
|
+
raise TypeError, "Can't convert #{new_path.class} into String."
|
1563
|
+
end
|
1564
|
+
@path = (new_path || EMPTY_STR).to_str
|
1565
|
+
if !@path.empty? && @path[0..0] != SLASH && host != nil
|
1566
|
+
@path = "/#{@path}"
|
1567
|
+
end
|
1568
|
+
|
1569
|
+
# Reset dependent values
|
1570
|
+
remove_instance_variable(:@normalized_path) if defined?(@normalized_path)
|
1571
|
+
remove_composite_values
|
1572
|
+
|
1573
|
+
# Ensure we haven't created an invalid URI
|
1574
|
+
validate()
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
##
|
1578
|
+
# The basename, if any, of the file in the path component.
|
1579
|
+
#
|
1580
|
+
# @return [String] The path's basename.
|
1581
|
+
def basename
|
1582
|
+
# Path cannot be nil
|
1583
|
+
return File.basename(self.path).sub(/;[^\/]*$/, EMPTY_STR)
|
1584
|
+
end
|
1585
|
+
|
1586
|
+
##
|
1587
|
+
# The extname, if any, of the file in the path component.
|
1588
|
+
# Empty string if there is no extension.
|
1589
|
+
#
|
1590
|
+
# @return [String] The path's extname.
|
1591
|
+
def extname
|
1592
|
+
return nil unless self.path
|
1593
|
+
return File.extname(self.basename)
|
1594
|
+
end
|
1595
|
+
|
1596
|
+
##
|
1597
|
+
# The query component for this URI.
|
1598
|
+
#
|
1599
|
+
# @return [String] The query component.
|
1600
|
+
def query
|
1601
|
+
return defined?(@query) ? @query : nil
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
##
|
1605
|
+
# The query component for this URI, normalized.
|
1606
|
+
#
|
1607
|
+
# @return [String] The query component, normalized.
|
1608
|
+
def normalized_query(*flags)
|
1609
|
+
return nil unless self.query
|
1610
|
+
return @normalized_query if defined?(@normalized_query)
|
1611
|
+
@normalized_query ||= begin
|
1612
|
+
modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
|
1613
|
+
# Make sure possible key-value pair delimiters are escaped.
|
1614
|
+
modified_query_class.sub!("\\&", "").sub!("\\;", "")
|
1615
|
+
pairs = (self.query || "").split("&", -1)
|
1616
|
+
pairs.delete_if(&:empty?) if flags.include?(:compacted)
|
1617
|
+
pairs.sort! if flags.include?(:sorted)
|
1618
|
+
component = pairs.map do |pair|
|
1619
|
+
Addressable::URI.normalize_component(pair, modified_query_class, "+")
|
1620
|
+
end.join("&")
|
1621
|
+
component == "" ? nil : component
|
1622
|
+
end
|
1623
|
+
# All normalized values should be UTF-8
|
1624
|
+
@normalized_query.force_encoding(Encoding::UTF_8) if @normalized_query
|
1625
|
+
@normalized_query
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
##
|
1629
|
+
# Sets the query component for this URI.
|
1630
|
+
#
|
1631
|
+
# @param [String, #to_str] new_query The new query component.
|
1632
|
+
def query=(new_query)
|
1633
|
+
if new_query && !new_query.respond_to?(:to_str)
|
1634
|
+
raise TypeError, "Can't convert #{new_query.class} into String."
|
1635
|
+
end
|
1636
|
+
@query = new_query ? new_query.to_str : nil
|
1637
|
+
|
1638
|
+
# Reset dependent values
|
1639
|
+
remove_instance_variable(:@normalized_query) if defined?(@normalized_query)
|
1640
|
+
remove_composite_values
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
##
|
1644
|
+
# Converts the query component to a Hash value.
|
1645
|
+
#
|
1646
|
+
# @param [Class] return_type The return type desired. Value must be either
|
1647
|
+
# `Hash` or `Array`.
|
1648
|
+
#
|
1649
|
+
# @return [Hash, Array, nil] The query string parsed as a Hash or Array
|
1650
|
+
# or nil if the query string is blank.
|
1651
|
+
#
|
1652
|
+
# @example
|
1653
|
+
# Addressable::URI.parse("?one=1&two=2&three=3").query_values
|
1654
|
+
# #=> {"one" => "1", "two" => "2", "three" => "3"}
|
1655
|
+
# Addressable::URI.parse("?one=two&one=three").query_values(Array)
|
1656
|
+
# #=> [["one", "two"], ["one", "three"]]
|
1657
|
+
# Addressable::URI.parse("?one=two&one=three").query_values(Hash)
|
1658
|
+
# #=> {"one" => "three"}
|
1659
|
+
# Addressable::URI.parse("?").query_values
|
1660
|
+
# #=> {}
|
1661
|
+
# Addressable::URI.parse("").query_values
|
1662
|
+
# #=> nil
|
1663
|
+
def query_values(return_type=Hash)
|
1664
|
+
empty_accumulator = Array == return_type ? [] : {}
|
1665
|
+
if return_type != Hash && return_type != Array
|
1666
|
+
raise ArgumentError, "Invalid return type. Must be Hash or Array."
|
1667
|
+
end
|
1668
|
+
return nil if self.query == nil
|
1669
|
+
split_query = self.query.split("&").map do |pair|
|
1670
|
+
pair.split("=", 2) if pair && !pair.empty?
|
1671
|
+
end.compact
|
1672
|
+
return split_query.inject(empty_accumulator.dup) do |accu, pair|
|
1673
|
+
# I'd rather use key/value identifiers instead of array lookups,
|
1674
|
+
# but in this case I really want to maintain the exact pair structure,
|
1675
|
+
# so it's best to make all changes in-place.
|
1676
|
+
pair[0] = URI.unencode_component(pair[0])
|
1677
|
+
if pair[1].respond_to?(:to_str)
|
1678
|
+
# I loathe the fact that I have to do this. Stupid HTML 4.01.
|
1679
|
+
# Treating '+' as a space was just an unbelievably bad idea.
|
1680
|
+
# There was nothing wrong with '%20'!
|
1681
|
+
# If it ain't broke, don't fix it!
|
1682
|
+
pair[1] = URI.unencode_component(pair[1].to_str.tr("+", " "))
|
1683
|
+
end
|
1684
|
+
if return_type == Hash
|
1685
|
+
accu[pair[0]] = pair[1]
|
1686
|
+
else
|
1687
|
+
accu << pair
|
1688
|
+
end
|
1689
|
+
accu
|
1690
|
+
end
|
1691
|
+
end
|
1692
|
+
|
1693
|
+
##
|
1694
|
+
# Sets the query component for this URI from a Hash object.
|
1695
|
+
# An empty Hash or Array will result in an empty query string.
|
1696
|
+
#
|
1697
|
+
# @param [Hash, #to_hash, Array] new_query_values The new query values.
|
1698
|
+
#
|
1699
|
+
# @example
|
1700
|
+
# uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
|
1701
|
+
# uri.query
|
1702
|
+
# # => "a=a&b=c&b=d&b=e"
|
1703
|
+
# uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']]
|
1704
|
+
# uri.query
|
1705
|
+
# # => "a=a&b=c&b=d&b=e"
|
1706
|
+
# uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]]
|
1707
|
+
# uri.query
|
1708
|
+
# # => "a=a&b=c&b=d&b=e"
|
1709
|
+
# uri.query_values = [['flag'], ['key', 'value']]
|
1710
|
+
# uri.query
|
1711
|
+
# # => "flag&key=value"
|
1712
|
+
def query_values=(new_query_values)
|
1713
|
+
if new_query_values == nil
|
1714
|
+
self.query = nil
|
1715
|
+
return nil
|
1716
|
+
end
|
1717
|
+
|
1718
|
+
if !new_query_values.is_a?(Array)
|
1719
|
+
if !new_query_values.respond_to?(:to_hash)
|
1720
|
+
raise TypeError,
|
1721
|
+
"Can't convert #{new_query_values.class} into Hash."
|
1722
|
+
end
|
1723
|
+
new_query_values = new_query_values.to_hash
|
1724
|
+
new_query_values = new_query_values.map do |key, value|
|
1725
|
+
key = key.to_s if key.kind_of?(Symbol)
|
1726
|
+
[key, value]
|
1727
|
+
end
|
1728
|
+
# Useful default for OAuth and caching.
|
1729
|
+
# Only to be used for non-Array inputs. Arrays should preserve order.
|
1730
|
+
new_query_values.sort!
|
1731
|
+
end
|
1732
|
+
|
1733
|
+
# new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
|
1734
|
+
buffer = "".dup
|
1735
|
+
new_query_values.each do |key, value|
|
1736
|
+
encoded_key = URI.encode_component(
|
1737
|
+
key, CharacterClasses::UNRESERVED
|
1738
|
+
)
|
1739
|
+
if value == nil
|
1740
|
+
buffer << "#{encoded_key}&"
|
1741
|
+
elsif value.kind_of?(Array)
|
1742
|
+
value.each do |sub_value|
|
1743
|
+
encoded_value = URI.encode_component(
|
1744
|
+
sub_value, CharacterClasses::UNRESERVED
|
1745
|
+
)
|
1746
|
+
buffer << "#{encoded_key}=#{encoded_value}&"
|
1747
|
+
end
|
1748
|
+
else
|
1749
|
+
encoded_value = URI.encode_component(
|
1750
|
+
value, CharacterClasses::UNRESERVED
|
1751
|
+
)
|
1752
|
+
buffer << "#{encoded_key}=#{encoded_value}&"
|
1753
|
+
end
|
1754
|
+
end
|
1755
|
+
self.query = buffer.chop
|
1756
|
+
end
|
1757
|
+
|
1758
|
+
##
|
1759
|
+
# The HTTP request URI for this URI. This is the path and the
|
1760
|
+
# query string.
|
1761
|
+
#
|
1762
|
+
# @return [String] The request URI required for an HTTP request.
|
1763
|
+
def request_uri
|
1764
|
+
return nil if self.absolute? && self.scheme !~ /^https?$/i
|
1765
|
+
return (
|
1766
|
+
(!self.path.empty? ? self.path : SLASH) +
|
1767
|
+
(self.query ? "?#{self.query}" : EMPTY_STR)
|
1768
|
+
)
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
##
|
1772
|
+
# Sets the HTTP request URI for this URI.
|
1773
|
+
#
|
1774
|
+
# @param [String, #to_str] new_request_uri The new HTTP request URI.
|
1775
|
+
def request_uri=(new_request_uri)
|
1776
|
+
if !new_request_uri.respond_to?(:to_str)
|
1777
|
+
raise TypeError, "Can't convert #{new_request_uri.class} into String."
|
1778
|
+
end
|
1779
|
+
if self.absolute? && self.scheme !~ /^https?$/i
|
1780
|
+
raise InvalidURIError,
|
1781
|
+
"Cannot set an HTTP request URI for a non-HTTP URI."
|
1782
|
+
end
|
1783
|
+
new_request_uri = new_request_uri.to_str
|
1784
|
+
path_component = new_request_uri[/^([^\?]*)\??(?:.*)$/, 1]
|
1785
|
+
query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
|
1786
|
+
path_component = path_component.to_s
|
1787
|
+
path_component = (!path_component.empty? ? path_component : SLASH)
|
1788
|
+
self.path = path_component
|
1789
|
+
self.query = query_component
|
1790
|
+
|
1791
|
+
# Reset dependent values
|
1792
|
+
remove_composite_values
|
1793
|
+
end
|
1794
|
+
|
1795
|
+
##
|
1796
|
+
# The fragment component for this URI.
|
1797
|
+
#
|
1798
|
+
# @return [String] The fragment component.
|
1799
|
+
def fragment
|
1800
|
+
return defined?(@fragment) ? @fragment : nil
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
##
|
1804
|
+
# The fragment component for this URI, normalized.
|
1805
|
+
#
|
1806
|
+
# @return [String] The fragment component, normalized.
|
1807
|
+
def normalized_fragment
|
1808
|
+
return nil unless self.fragment
|
1809
|
+
return @normalized_fragment if defined?(@normalized_fragment)
|
1810
|
+
@normalized_fragment ||= begin
|
1811
|
+
component = Addressable::URI.normalize_component(
|
1812
|
+
self.fragment,
|
1813
|
+
Addressable::URI::CharacterClasses::FRAGMENT
|
1814
|
+
)
|
1815
|
+
component == "" ? nil : component
|
1816
|
+
end
|
1817
|
+
# All normalized values should be UTF-8
|
1818
|
+
if @normalized_fragment
|
1819
|
+
@normalized_fragment.force_encoding(Encoding::UTF_8)
|
1820
|
+
end
|
1821
|
+
@normalized_fragment
|
1822
|
+
end
|
1823
|
+
|
1824
|
+
##
|
1825
|
+
# Sets the fragment component for this URI.
|
1826
|
+
#
|
1827
|
+
# @param [String, #to_str] new_fragment The new fragment component.
|
1828
|
+
def fragment=(new_fragment)
|
1829
|
+
if new_fragment && !new_fragment.respond_to?(:to_str)
|
1830
|
+
raise TypeError, "Can't convert #{new_fragment.class} into String."
|
1831
|
+
end
|
1832
|
+
@fragment = new_fragment ? new_fragment.to_str : nil
|
1833
|
+
|
1834
|
+
# Reset dependent values
|
1835
|
+
remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment)
|
1836
|
+
remove_composite_values
|
1837
|
+
|
1838
|
+
# Ensure we haven't created an invalid URI
|
1839
|
+
validate()
|
1840
|
+
end
|
1841
|
+
|
1842
|
+
##
|
1843
|
+
# Determines if the scheme indicates an IP-based protocol.
|
1844
|
+
#
|
1845
|
+
# @return [TrueClass, FalseClass]
|
1846
|
+
# <code>true</code> if the scheme indicates an IP-based protocol.
|
1847
|
+
# <code>false</code> otherwise.
|
1848
|
+
def ip_based?
|
1849
|
+
if self.scheme
|
1850
|
+
return URI.ip_based_schemes.include?(
|
1851
|
+
self.scheme.strip.downcase)
|
1852
|
+
end
|
1853
|
+
return false
|
1854
|
+
end
|
1855
|
+
|
1856
|
+
##
|
1857
|
+
# Determines if the URI is relative.
|
1858
|
+
#
|
1859
|
+
# @return [TrueClass, FalseClass]
|
1860
|
+
# <code>true</code> if the URI is relative. <code>false</code>
|
1861
|
+
# otherwise.
|
1862
|
+
def relative?
|
1863
|
+
return self.scheme.nil?
|
1864
|
+
end
|
1865
|
+
|
1866
|
+
##
|
1867
|
+
# Determines if the URI is absolute.
|
1868
|
+
#
|
1869
|
+
# @return [TrueClass, FalseClass]
|
1870
|
+
# <code>true</code> if the URI is absolute. <code>false</code>
|
1871
|
+
# otherwise.
|
1872
|
+
def absolute?
|
1873
|
+
return !relative?
|
1874
|
+
end
|
1875
|
+
|
1876
|
+
##
|
1877
|
+
# Joins two URIs together.
|
1878
|
+
#
|
1879
|
+
# @param [String, Addressable::URI, #to_str] The URI to join with.
|
1880
|
+
#
|
1881
|
+
# @return [Addressable::URI] The joined URI.
|
1882
|
+
def join(uri)
|
1883
|
+
if !uri.respond_to?(:to_str)
|
1884
|
+
raise TypeError, "Can't convert #{uri.class} into String."
|
1885
|
+
end
|
1886
|
+
if !uri.kind_of?(URI)
|
1887
|
+
# Otherwise, convert to a String, then parse.
|
1888
|
+
uri = URI.parse(uri.to_str)
|
1889
|
+
end
|
1890
|
+
if uri.to_s.empty?
|
1891
|
+
return self.dup
|
1892
|
+
end
|
1893
|
+
|
1894
|
+
joined_scheme = nil
|
1895
|
+
joined_user = nil
|
1896
|
+
joined_password = nil
|
1897
|
+
joined_host = nil
|
1898
|
+
joined_port = nil
|
1899
|
+
joined_path = nil
|
1900
|
+
joined_query = nil
|
1901
|
+
joined_fragment = nil
|
1902
|
+
|
1903
|
+
# Section 5.2.2 of RFC 3986
|
1904
|
+
if uri.scheme != nil
|
1905
|
+
joined_scheme = uri.scheme
|
1906
|
+
joined_user = uri.user
|
1907
|
+
joined_password = uri.password
|
1908
|
+
joined_host = uri.host
|
1909
|
+
joined_port = uri.port
|
1910
|
+
joined_path = URI.normalize_path(uri.path)
|
1911
|
+
joined_query = uri.query
|
1912
|
+
else
|
1913
|
+
if uri.authority != nil
|
1914
|
+
joined_user = uri.user
|
1915
|
+
joined_password = uri.password
|
1916
|
+
joined_host = uri.host
|
1917
|
+
joined_port = uri.port
|
1918
|
+
joined_path = URI.normalize_path(uri.path)
|
1919
|
+
joined_query = uri.query
|
1920
|
+
else
|
1921
|
+
if uri.path == nil || uri.path.empty?
|
1922
|
+
joined_path = self.path
|
1923
|
+
if uri.query != nil
|
1924
|
+
joined_query = uri.query
|
1925
|
+
else
|
1926
|
+
joined_query = self.query
|
1927
|
+
end
|
1928
|
+
else
|
1929
|
+
if uri.path[0..0] == SLASH
|
1930
|
+
joined_path = URI.normalize_path(uri.path)
|
1931
|
+
else
|
1932
|
+
base_path = self.path.dup
|
1933
|
+
base_path = EMPTY_STR if base_path == nil
|
1934
|
+
base_path = URI.normalize_path(base_path)
|
1935
|
+
|
1936
|
+
# Section 5.2.3 of RFC 3986
|
1937
|
+
#
|
1938
|
+
# Removes the right-most path segment from the base path.
|
1939
|
+
if base_path.include?(SLASH)
|
1940
|
+
base_path.sub!(/\/[^\/]+$/, SLASH)
|
1941
|
+
else
|
1942
|
+
base_path = EMPTY_STR
|
1943
|
+
end
|
1944
|
+
|
1945
|
+
# If the base path is empty and an authority segment has been
|
1946
|
+
# defined, use a base path of SLASH
|
1947
|
+
if base_path.empty? && self.authority != nil
|
1948
|
+
base_path = SLASH
|
1949
|
+
end
|
1950
|
+
|
1951
|
+
joined_path = URI.normalize_path(base_path + uri.path)
|
1952
|
+
end
|
1953
|
+
joined_query = uri.query
|
1954
|
+
end
|
1955
|
+
joined_user = self.user
|
1956
|
+
joined_password = self.password
|
1957
|
+
joined_host = self.host
|
1958
|
+
joined_port = self.port
|
1959
|
+
end
|
1960
|
+
joined_scheme = self.scheme
|
1961
|
+
end
|
1962
|
+
joined_fragment = uri.fragment
|
1963
|
+
|
1964
|
+
return self.class.new(
|
1965
|
+
:scheme => joined_scheme,
|
1966
|
+
:user => joined_user,
|
1967
|
+
:password => joined_password,
|
1968
|
+
:host => joined_host,
|
1969
|
+
:port => joined_port,
|
1970
|
+
:path => joined_path,
|
1971
|
+
:query => joined_query,
|
1972
|
+
:fragment => joined_fragment
|
1973
|
+
)
|
1974
|
+
end
|
1975
|
+
alias_method :+, :join
|
1976
|
+
|
1977
|
+
##
|
1978
|
+
# Destructive form of <code>join</code>.
|
1979
|
+
#
|
1980
|
+
# @param [String, Addressable::URI, #to_str] The URI to join with.
|
1981
|
+
#
|
1982
|
+
# @return [Addressable::URI] The joined URI.
|
1983
|
+
#
|
1984
|
+
# @see Addressable::URI#join
|
1985
|
+
def join!(uri)
|
1986
|
+
replace_self(self.join(uri))
|
1987
|
+
end
|
1988
|
+
|
1989
|
+
##
|
1990
|
+
# Merges a URI with a <code>Hash</code> of components.
|
1991
|
+
# This method has different behavior from <code>join</code>. Any
|
1992
|
+
# components present in the <code>hash</code> parameter will override the
|
1993
|
+
# original components. The path component is not treated specially.
|
1994
|
+
#
|
1995
|
+
# @param [Hash, Addressable::URI, #to_hash] The components to merge with.
|
1996
|
+
#
|
1997
|
+
# @return [Addressable::URI] The merged URI.
|
1998
|
+
#
|
1999
|
+
# @see Hash#merge
|
2000
|
+
def merge(hash)
|
2001
|
+
if !hash.respond_to?(:to_hash)
|
2002
|
+
raise TypeError, "Can't convert #{hash.class} into Hash."
|
2003
|
+
end
|
2004
|
+
hash = hash.to_hash
|
2005
|
+
|
2006
|
+
if hash.has_key?(:authority)
|
2007
|
+
if (hash.keys & [:userinfo, :user, :password, :host, :port]).any?
|
2008
|
+
raise ArgumentError,
|
2009
|
+
"Cannot specify both an authority and any of the components " +
|
2010
|
+
"within the authority."
|
2011
|
+
end
|
2012
|
+
end
|
2013
|
+
if hash.has_key?(:userinfo)
|
2014
|
+
if (hash.keys & [:user, :password]).any?
|
2015
|
+
raise ArgumentError,
|
2016
|
+
"Cannot specify both a userinfo and either the user or password."
|
2017
|
+
end
|
2018
|
+
end
|
2019
|
+
|
2020
|
+
uri = self.class.new
|
2021
|
+
uri.defer_validation do
|
2022
|
+
# Bunch of crazy logic required because of the composite components
|
2023
|
+
# like userinfo and authority.
|
2024
|
+
uri.scheme =
|
2025
|
+
hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
|
2026
|
+
if hash.has_key?(:authority)
|
2027
|
+
uri.authority =
|
2028
|
+
hash.has_key?(:authority) ? hash[:authority] : self.authority
|
2029
|
+
end
|
2030
|
+
if hash.has_key?(:userinfo)
|
2031
|
+
uri.userinfo =
|
2032
|
+
hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
|
2033
|
+
end
|
2034
|
+
if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
|
2035
|
+
uri.user =
|
2036
|
+
hash.has_key?(:user) ? hash[:user] : self.user
|
2037
|
+
uri.password =
|
2038
|
+
hash.has_key?(:password) ? hash[:password] : self.password
|
2039
|
+
end
|
2040
|
+
if !hash.has_key?(:authority)
|
2041
|
+
uri.host =
|
2042
|
+
hash.has_key?(:host) ? hash[:host] : self.host
|
2043
|
+
uri.port =
|
2044
|
+
hash.has_key?(:port) ? hash[:port] : self.port
|
2045
|
+
end
|
2046
|
+
uri.path =
|
2047
|
+
hash.has_key?(:path) ? hash[:path] : self.path
|
2048
|
+
uri.query =
|
2049
|
+
hash.has_key?(:query) ? hash[:query] : self.query
|
2050
|
+
uri.fragment =
|
2051
|
+
hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
|
2052
|
+
end
|
2053
|
+
|
2054
|
+
return uri
|
2055
|
+
end
|
2056
|
+
|
2057
|
+
##
|
2058
|
+
# Destructive form of <code>merge</code>.
|
2059
|
+
#
|
2060
|
+
# @param [Hash, Addressable::URI, #to_hash] The components to merge with.
|
2061
|
+
#
|
2062
|
+
# @return [Addressable::URI] The merged URI.
|
2063
|
+
#
|
2064
|
+
# @see Addressable::URI#merge
|
2065
|
+
def merge!(uri)
|
2066
|
+
replace_self(self.merge(uri))
|
2067
|
+
end
|
2068
|
+
|
2069
|
+
##
|
2070
|
+
# Returns the shortest normalized relative form of this URI that uses the
|
2071
|
+
# supplied URI as a base for resolution. Returns an absolute URI if
|
2072
|
+
# necessary. This is effectively the opposite of <code>route_to</code>.
|
2073
|
+
#
|
2074
|
+
# @param [String, Addressable::URI, #to_str] uri The URI to route from.
|
2075
|
+
#
|
2076
|
+
# @return [Addressable::URI]
|
2077
|
+
# The normalized relative URI that is equivalent to the original URI.
|
2078
|
+
def route_from(uri)
|
2079
|
+
uri = URI.parse(uri).normalize
|
2080
|
+
normalized_self = self.normalize
|
2081
|
+
if normalized_self.relative?
|
2082
|
+
raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
|
2083
|
+
end
|
2084
|
+
if uri.relative?
|
2085
|
+
raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}"
|
2086
|
+
end
|
2087
|
+
if normalized_self == uri
|
2088
|
+
return Addressable::URI.parse("##{normalized_self.fragment}")
|
2089
|
+
end
|
2090
|
+
components = normalized_self.to_hash
|
2091
|
+
if normalized_self.scheme == uri.scheme
|
2092
|
+
components[:scheme] = nil
|
2093
|
+
if normalized_self.authority == uri.authority
|
2094
|
+
components[:user] = nil
|
2095
|
+
components[:password] = nil
|
2096
|
+
components[:host] = nil
|
2097
|
+
components[:port] = nil
|
2098
|
+
if normalized_self.path == uri.path
|
2099
|
+
components[:path] = nil
|
2100
|
+
if normalized_self.query == uri.query
|
2101
|
+
components[:query] = nil
|
2102
|
+
end
|
2103
|
+
else
|
2104
|
+
if uri.path != SLASH and components[:path]
|
2105
|
+
self_splitted_path = split_path(components[:path])
|
2106
|
+
uri_splitted_path = split_path(uri.path)
|
2107
|
+
self_dir = self_splitted_path.shift
|
2108
|
+
uri_dir = uri_splitted_path.shift
|
2109
|
+
while !self_splitted_path.empty? && !uri_splitted_path.empty? and self_dir == uri_dir
|
2110
|
+
self_dir = self_splitted_path.shift
|
2111
|
+
uri_dir = uri_splitted_path.shift
|
2112
|
+
end
|
2113
|
+
components[:path] = (uri_splitted_path.fill('..') + [self_dir] + self_splitted_path).join(SLASH)
|
2114
|
+
end
|
2115
|
+
end
|
2116
|
+
end
|
2117
|
+
end
|
2118
|
+
# Avoid network-path references.
|
2119
|
+
if components[:host] != nil
|
2120
|
+
components[:scheme] = normalized_self.scheme
|
2121
|
+
end
|
2122
|
+
return Addressable::URI.new(
|
2123
|
+
:scheme => components[:scheme],
|
2124
|
+
:user => components[:user],
|
2125
|
+
:password => components[:password],
|
2126
|
+
:host => components[:host],
|
2127
|
+
:port => components[:port],
|
2128
|
+
:path => components[:path],
|
2129
|
+
:query => components[:query],
|
2130
|
+
:fragment => components[:fragment]
|
2131
|
+
)
|
2132
|
+
end
|
2133
|
+
|
2134
|
+
##
|
2135
|
+
# Returns the shortest normalized relative form of the supplied URI that
|
2136
|
+
# uses this URI as a base for resolution. Returns an absolute URI if
|
2137
|
+
# necessary. This is effectively the opposite of <code>route_from</code>.
|
2138
|
+
#
|
2139
|
+
# @param [String, Addressable::URI, #to_str] uri The URI to route to.
|
2140
|
+
#
|
2141
|
+
# @return [Addressable::URI]
|
2142
|
+
# The normalized relative URI that is equivalent to the supplied URI.
|
2143
|
+
def route_to(uri)
|
2144
|
+
return URI.parse(uri).route_from(self)
|
2145
|
+
end
|
2146
|
+
|
2147
|
+
##
|
2148
|
+
# Returns a normalized URI object.
|
2149
|
+
#
|
2150
|
+
# NOTE: This method does not attempt to fully conform to specifications.
|
2151
|
+
# It exists largely to correct other people's failures to read the
|
2152
|
+
# specifications, and also to deal with caching issues since several
|
2153
|
+
# different URIs may represent the same resource and should not be
|
2154
|
+
# cached multiple times.
|
2155
|
+
#
|
2156
|
+
# @return [Addressable::URI] The normalized URI.
|
2157
|
+
def normalize
|
2158
|
+
# This is a special exception for the frequently misused feed
|
2159
|
+
# URI scheme.
|
2160
|
+
if normalized_scheme == "feed"
|
2161
|
+
if self.to_s =~ /^feed:\/*http:\/*/
|
2162
|
+
return URI.parse(
|
2163
|
+
self.to_s[/^feed:\/*(http:\/*.*)/, 1]
|
2164
|
+
).normalize
|
2165
|
+
end
|
2166
|
+
end
|
2167
|
+
|
2168
|
+
return self.class.new(
|
2169
|
+
:scheme => normalized_scheme,
|
2170
|
+
:authority => normalized_authority,
|
2171
|
+
:path => normalized_path,
|
2172
|
+
:query => normalized_query,
|
2173
|
+
:fragment => normalized_fragment
|
2174
|
+
)
|
2175
|
+
end
|
2176
|
+
|
2177
|
+
##
|
2178
|
+
# Destructively normalizes this URI object.
|
2179
|
+
#
|
2180
|
+
# @return [Addressable::URI] The normalized URI.
|
2181
|
+
#
|
2182
|
+
# @see Addressable::URI#normalize
|
2183
|
+
def normalize!
|
2184
|
+
replace_self(self.normalize)
|
2185
|
+
end
|
2186
|
+
|
2187
|
+
##
|
2188
|
+
# Creates a URI suitable for display to users. If semantic attacks are
|
2189
|
+
# likely, the application should try to detect these and warn the user.
|
2190
|
+
# See <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
|
2191
|
+
# section 7.6 for more information.
|
2192
|
+
#
|
2193
|
+
# @return [Addressable::URI] A URI suitable for display purposes.
|
2194
|
+
def display_uri
|
2195
|
+
display_uri = self.normalize
|
2196
|
+
display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host)
|
2197
|
+
return display_uri
|
2198
|
+
end
|
2199
|
+
|
2200
|
+
##
|
2201
|
+
# Returns <code>true</code> if the URI objects are equal. This method
|
2202
|
+
# normalizes both URIs before doing the comparison, and allows comparison
|
2203
|
+
# against <code>Strings</code>.
|
2204
|
+
#
|
2205
|
+
# @param [Object] uri The URI to compare.
|
2206
|
+
#
|
2207
|
+
# @return [TrueClass, FalseClass]
|
2208
|
+
# <code>true</code> if the URIs are equivalent, <code>false</code>
|
2209
|
+
# otherwise.
|
2210
|
+
def ===(uri)
|
2211
|
+
if uri.respond_to?(:normalize)
|
2212
|
+
uri_string = uri.normalize.to_s
|
2213
|
+
else
|
2214
|
+
begin
|
2215
|
+
uri_string = ::Addressable::URI.parse(uri).normalize.to_s
|
2216
|
+
rescue InvalidURIError, TypeError
|
2217
|
+
return false
|
2218
|
+
end
|
2219
|
+
end
|
2220
|
+
return self.normalize.to_s == uri_string
|
2221
|
+
end
|
2222
|
+
|
2223
|
+
##
|
2224
|
+
# Returns <code>true</code> if the URI objects are equal. This method
|
2225
|
+
# normalizes both URIs before doing the comparison.
|
2226
|
+
#
|
2227
|
+
# @param [Object] uri The URI to compare.
|
2228
|
+
#
|
2229
|
+
# @return [TrueClass, FalseClass]
|
2230
|
+
# <code>true</code> if the URIs are equivalent, <code>false</code>
|
2231
|
+
# otherwise.
|
2232
|
+
def ==(uri)
|
2233
|
+
return false unless uri.kind_of?(URI)
|
2234
|
+
return self.normalize.to_s == uri.normalize.to_s
|
2235
|
+
end
|
2236
|
+
|
2237
|
+
##
|
2238
|
+
# Returns <code>true</code> if the URI objects are equal. This method
|
2239
|
+
# does NOT normalize either URI before doing the comparison.
|
2240
|
+
#
|
2241
|
+
# @param [Object] uri The URI to compare.
|
2242
|
+
#
|
2243
|
+
# @return [TrueClass, FalseClass]
|
2244
|
+
# <code>true</code> if the URIs are equivalent, <code>false</code>
|
2245
|
+
# otherwise.
|
2246
|
+
def eql?(uri)
|
2247
|
+
return false unless uri.kind_of?(URI)
|
2248
|
+
return self.to_s == uri.to_s
|
2249
|
+
end
|
2250
|
+
|
2251
|
+
##
|
2252
|
+
# A hash value that will make a URI equivalent to its normalized
|
2253
|
+
# form.
|
2254
|
+
#
|
2255
|
+
# @return [Integer] A hash of the URI.
|
2256
|
+
def hash
|
2257
|
+
@hash ||= self.to_s.hash * -1
|
2258
|
+
end
|
2259
|
+
|
2260
|
+
##
|
2261
|
+
# Clones the URI object.
|
2262
|
+
#
|
2263
|
+
# @return [Addressable::URI] The cloned URI.
|
2264
|
+
def dup
|
2265
|
+
duplicated_uri = self.class.new(
|
2266
|
+
:scheme => self.scheme ? self.scheme.dup : nil,
|
2267
|
+
:user => self.user ? self.user.dup : nil,
|
2268
|
+
:password => self.password ? self.password.dup : nil,
|
2269
|
+
:host => self.host ? self.host.dup : nil,
|
2270
|
+
:port => self.port,
|
2271
|
+
:path => self.path ? self.path.dup : nil,
|
2272
|
+
:query => self.query ? self.query.dup : nil,
|
2273
|
+
:fragment => self.fragment ? self.fragment.dup : nil
|
2274
|
+
)
|
2275
|
+
return duplicated_uri
|
2276
|
+
end
|
2277
|
+
|
2278
|
+
##
|
2279
|
+
# Omits components from a URI.
|
2280
|
+
#
|
2281
|
+
# @param [Symbol] *components The components to be omitted.
|
2282
|
+
#
|
2283
|
+
# @return [Addressable::URI] The URI with components omitted.
|
2284
|
+
#
|
2285
|
+
# @example
|
2286
|
+
# uri = Addressable::URI.parse("http://example.com/path?query")
|
2287
|
+
# #=> #<Addressable::URI:0xcc5e7a URI:http://example.com/path?query>
|
2288
|
+
# uri.omit(:scheme, :authority)
|
2289
|
+
# #=> #<Addressable::URI:0xcc4d86 URI:/path?query>
|
2290
|
+
def omit(*components)
|
2291
|
+
invalid_components = components - [
|
2292
|
+
:scheme, :user, :password, :userinfo, :host, :port, :authority,
|
2293
|
+
:path, :query, :fragment
|
2294
|
+
]
|
2295
|
+
unless invalid_components.empty?
|
2296
|
+
raise ArgumentError,
|
2297
|
+
"Invalid component names: #{invalid_components.inspect}."
|
2298
|
+
end
|
2299
|
+
duplicated_uri = self.dup
|
2300
|
+
duplicated_uri.defer_validation do
|
2301
|
+
components.each do |component|
|
2302
|
+
duplicated_uri.send((component.to_s + "=").to_sym, nil)
|
2303
|
+
end
|
2304
|
+
duplicated_uri.user = duplicated_uri.normalized_user
|
2305
|
+
end
|
2306
|
+
duplicated_uri
|
2307
|
+
end
|
2308
|
+
|
2309
|
+
##
|
2310
|
+
# Destructive form of omit.
|
2311
|
+
#
|
2312
|
+
# @param [Symbol] *components The components to be omitted.
|
2313
|
+
#
|
2314
|
+
# @return [Addressable::URI] The URI with components omitted.
|
2315
|
+
#
|
2316
|
+
# @see Addressable::URI#omit
|
2317
|
+
def omit!(*components)
|
2318
|
+
replace_self(self.omit(*components))
|
2319
|
+
end
|
2320
|
+
|
2321
|
+
##
|
2322
|
+
# Determines if the URI is an empty string.
|
2323
|
+
#
|
2324
|
+
# @return [TrueClass, FalseClass]
|
2325
|
+
# Returns <code>true</code> if empty, <code>false</code> otherwise.
|
2326
|
+
def empty?
|
2327
|
+
return self.to_s.empty?
|
2328
|
+
end
|
2329
|
+
|
2330
|
+
##
|
2331
|
+
# Converts the URI to a <code>String</code>.
|
2332
|
+
#
|
2333
|
+
# @return [String] The URI's <code>String</code> representation.
|
2334
|
+
def to_s
|
2335
|
+
if self.scheme == nil && self.path != nil && !self.path.empty? &&
|
2336
|
+
self.path =~ NORMPATH
|
2337
|
+
raise InvalidURIError,
|
2338
|
+
"Cannot assemble URI string with ambiguous path: '#{self.path}'"
|
2339
|
+
end
|
2340
|
+
@uri_string ||= begin
|
2341
|
+
uri_string = String.new
|
2342
|
+
uri_string << "#{self.scheme}:" if self.scheme != nil
|
2343
|
+
uri_string << "//#{self.authority}" if self.authority != nil
|
2344
|
+
uri_string << self.path.to_s
|
2345
|
+
uri_string << "?#{self.query}" if self.query != nil
|
2346
|
+
uri_string << "##{self.fragment}" if self.fragment != nil
|
2347
|
+
uri_string.force_encoding(Encoding::UTF_8)
|
2348
|
+
uri_string
|
2349
|
+
end
|
2350
|
+
end
|
2351
|
+
|
2352
|
+
##
|
2353
|
+
# URI's are glorified <code>Strings</code>. Allow implicit conversion.
|
2354
|
+
alias_method :to_str, :to_s
|
2355
|
+
|
2356
|
+
##
|
2357
|
+
# Returns a Hash of the URI components.
|
2358
|
+
#
|
2359
|
+
# @return [Hash] The URI as a <code>Hash</code> of components.
|
2360
|
+
def to_hash
|
2361
|
+
return {
|
2362
|
+
:scheme => self.scheme,
|
2363
|
+
:user => self.user,
|
2364
|
+
:password => self.password,
|
2365
|
+
:host => self.host,
|
2366
|
+
:port => self.port,
|
2367
|
+
:path => self.path,
|
2368
|
+
:query => self.query,
|
2369
|
+
:fragment => self.fragment
|
2370
|
+
}
|
2371
|
+
end
|
2372
|
+
|
2373
|
+
##
|
2374
|
+
# Returns a <code>String</code> representation of the URI object's state.
|
2375
|
+
#
|
2376
|
+
# @return [String] The URI object's state, as a <code>String</code>.
|
2377
|
+
def inspect
|
2378
|
+
sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s)
|
2379
|
+
end
|
2380
|
+
|
2381
|
+
##
|
2382
|
+
# This method allows you to make several changes to a URI simultaneously,
|
2383
|
+
# which separately would cause validation errors, but in conjunction,
|
2384
|
+
# are valid. The URI will be revalidated as soon as the entire block has
|
2385
|
+
# been executed.
|
2386
|
+
#
|
2387
|
+
# @param [Proc] block
|
2388
|
+
# A set of operations to perform on a given URI.
|
2389
|
+
def defer_validation
|
2390
|
+
raise LocalJumpError, "No block given." unless block_given?
|
2391
|
+
@validation_deferred = true
|
2392
|
+
yield
|
2393
|
+
@validation_deferred = false
|
2394
|
+
validate
|
2395
|
+
return nil
|
2396
|
+
end
|
2397
|
+
|
2398
|
+
protected
|
2399
|
+
SELF_REF = '.'
|
2400
|
+
PARENT = '..'
|
2401
|
+
|
2402
|
+
RULE_2A = /\/\.\/|\/\.$/
|
2403
|
+
RULE_2B_2C = /\/([^\/]*)\/\.\.\/|\/([^\/]*)\/\.\.$/
|
2404
|
+
RULE_2D = /^\.\.?\/?/
|
2405
|
+
RULE_PREFIXED_PARENT = /^\/\.\.?\/|^(\/\.\.?)+\/?$/
|
2406
|
+
|
2407
|
+
##
|
2408
|
+
# Resolves paths to their simplest form.
|
2409
|
+
#
|
2410
|
+
# @param [String] path The path to normalize.
|
2411
|
+
#
|
2412
|
+
# @return [String] The normalized path.
|
2413
|
+
def self.normalize_path(path)
|
2414
|
+
# Section 5.2.4 of RFC 3986
|
2415
|
+
|
2416
|
+
return nil if path.nil?
|
2417
|
+
normalized_path = path.dup
|
2418
|
+
begin
|
2419
|
+
mod = nil
|
2420
|
+
mod ||= normalized_path.gsub!(RULE_2A, SLASH)
|
2421
|
+
|
2422
|
+
pair = normalized_path.match(RULE_2B_2C)
|
2423
|
+
parent, current = pair[1], pair[2] if pair
|
2424
|
+
if pair && ((parent != SELF_REF && parent != PARENT) ||
|
2425
|
+
(current != SELF_REF && current != PARENT))
|
2426
|
+
mod ||= normalized_path.gsub!(
|
2427
|
+
Regexp.new(
|
2428
|
+
"/#{Regexp.escape(parent.to_s)}/\\.\\./|" +
|
2429
|
+
"(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
|
2430
|
+
), SLASH
|
2431
|
+
)
|
2432
|
+
end
|
2433
|
+
|
2434
|
+
mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR)
|
2435
|
+
# Non-standard, removes prefixed dotted segments from path.
|
2436
|
+
mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH)
|
2437
|
+
end until mod.nil?
|
2438
|
+
|
2439
|
+
return normalized_path
|
2440
|
+
end
|
2441
|
+
|
2442
|
+
##
|
2443
|
+
# Ensures that the URI is valid.
|
2444
|
+
def validate
|
2445
|
+
return if !!@validation_deferred
|
2446
|
+
if self.scheme != nil && self.ip_based? &&
|
2447
|
+
(self.host == nil || self.host.empty?) &&
|
2448
|
+
(self.path == nil || self.path.empty?)
|
2449
|
+
raise InvalidURIError,
|
2450
|
+
"Absolute URI missing hierarchical segment: '#{self.to_s}'"
|
2451
|
+
end
|
2452
|
+
if self.host == nil
|
2453
|
+
if self.port != nil ||
|
2454
|
+
self.user != nil ||
|
2455
|
+
self.password != nil
|
2456
|
+
raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
|
2457
|
+
end
|
2458
|
+
end
|
2459
|
+
if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH &&
|
2460
|
+
self.authority != nil
|
2461
|
+
raise InvalidURIError,
|
2462
|
+
"Cannot have a relative path with an authority set: '#{self.to_s}'"
|
2463
|
+
end
|
2464
|
+
if self.path != nil && !self.path.empty? &&
|
2465
|
+
self.path[0..1] == SLASH + SLASH && self.authority == nil
|
2466
|
+
raise InvalidURIError,
|
2467
|
+
"Cannot have a path with two leading slashes " +
|
2468
|
+
"without an authority set: '#{self.to_s}'"
|
2469
|
+
end
|
2470
|
+
unreserved = CharacterClasses::UNRESERVED
|
2471
|
+
sub_delims = CharacterClasses::SUB_DELIMS
|
2472
|
+
if !self.host.nil? && (self.host =~ /[<>{}\/\\\?\#\@"[[:space:]]]/ ||
|
2473
|
+
(self.host[/^\[(.*)\]$/, 1] != nil && self.host[/^\[(.*)\]$/, 1] !~
|
2474
|
+
Regexp.new("^[#{unreserved}#{sub_delims}:]*$")))
|
2475
|
+
raise InvalidURIError, "Invalid character in host: '#{self.host.to_s}'"
|
2476
|
+
end
|
2477
|
+
return nil
|
2478
|
+
end
|
2479
|
+
|
2480
|
+
##
|
2481
|
+
# Replaces the internal state of self with the specified URI's state.
|
2482
|
+
# Used in destructive operations to avoid massive code repetition.
|
2483
|
+
#
|
2484
|
+
# @param [Addressable::URI] uri The URI to replace <code>self</code> with.
|
2485
|
+
#
|
2486
|
+
# @return [Addressable::URI] <code>self</code>.
|
2487
|
+
def replace_self(uri)
|
2488
|
+
# Reset dependent values
|
2489
|
+
instance_variables.each do |var|
|
2490
|
+
if instance_variable_defined?(var) && var != :@validation_deferred
|
2491
|
+
remove_instance_variable(var)
|
2492
|
+
end
|
2493
|
+
end
|
2494
|
+
|
2495
|
+
@scheme = uri.scheme
|
2496
|
+
@user = uri.user
|
2497
|
+
@password = uri.password
|
2498
|
+
@host = uri.host
|
2499
|
+
@port = uri.port
|
2500
|
+
@path = uri.path
|
2501
|
+
@query = uri.query
|
2502
|
+
@fragment = uri.fragment
|
2503
|
+
return self
|
2504
|
+
end
|
2505
|
+
|
2506
|
+
##
|
2507
|
+
# Splits path string with "/" (slash).
|
2508
|
+
# It is considered that there is empty string after last slash when
|
2509
|
+
# path ends with slash.
|
2510
|
+
#
|
2511
|
+
# @param [String] path The path to split.
|
2512
|
+
#
|
2513
|
+
# @return [Array<String>] An array of parts of path.
|
2514
|
+
def split_path(path)
|
2515
|
+
splitted = path.split(SLASH)
|
2516
|
+
splitted << EMPTY_STR if path.end_with? SLASH
|
2517
|
+
splitted
|
2518
|
+
end
|
2519
|
+
|
2520
|
+
##
|
2521
|
+
# Resets composite values for the entire URI
|
2522
|
+
#
|
2523
|
+
# @api private
|
2524
|
+
def remove_composite_values
|
2525
|
+
remove_instance_variable(:@uri_string) if defined?(@uri_string)
|
2526
|
+
remove_instance_variable(:@hash) if defined?(@hash)
|
2527
|
+
end
|
2528
|
+
end
|
2529
|
+
end
|