Skip to content

Commit

Permalink
Merge pull request #566 from inspec/zenspider/rebase/addressable_gem_…
Browse files Browse the repository at this point in the history
…instead_of_stdlib_uri

Use Addressable::URI, allowing IPv6 URLs.
  • Loading branch information
Ryan Davis authored Feb 3, 2020
2 parents 788b919 + e1aa37b commit 77fa475
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 29 deletions.
32 changes: 16 additions & 16 deletions lib/train.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
require_relative "train/plugins"
require_relative "train/errors"
require_relative "train/platforms"
require "uri"
require "addressable/uri"

module Train
# Create a new transport instance, with the plugin indicated by the
Expand Down Expand Up @@ -101,7 +101,7 @@ def self.unpack_target_from_uri(uri_string, opts = {}) # rubocop: disable Metric
creds[:path] ||= uri.path
creds[:password] ||=
if opts[:www_form_encoded_password] && !uri.password.nil?
URI.decode_www_form_component(uri.password)
Addressable::URI.unencode_component(uri.password)
else
uri.password
end
Expand All @@ -121,24 +121,24 @@ def self.unpack_target_from_uri(uri_string, opts = {}) # rubocop: disable Metric
# Parse a URI. Supports empty URI's with paths, e.g. `mock://`
#
# @param string [string] URI string, e.g. `schema://domain.com`
# @return [URI::Generic] parsed URI object
# @return [Addressable::URI] parsed URI object
def self.parse_uri(string)
URI.parse(string)
rescue URI::InvalidURIError => e
u = Addressable::URI.parse(string)
# A use-case we want to catch is parsing empty URIs with a schema
# e.g. mock://. To do this, we match it manually and fake the hostname
case string
when %r{^([a-z]+)://$}
string += "dummy"
when /^([a-z]+):$/
string += "//dummy"
else
raise Train::UserError, e
if u.scheme && (u.host.nil? || u.host.empty?) && u.path.empty?
case string
when %r{^([a-z]+)://$}
string += "dummy"
when /^([a-z]+):$/
string += "//dummy"
end
u = Addressable::URI.parse(string)
u.host = nil
end

uri = URI.parse(string)
uri.host = nil
uri
u
rescue Addressable::URI::InvalidURIError => e
raise Train::UserError, e
end
private_class_method :parse_uri

Expand Down
38 changes: 25 additions & 13 deletions test/unit/train_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
_(res[:target]).must_equal org[:target]
end

it "supports IPv6 URIs" do
it "supports IPv6 URIs (with brackets)" do
org = { target: "mock://[abc::def]:123" }
res = Train.target_config(org)
_(res[:backend]).must_equal "mock"
Expand All @@ -167,6 +167,18 @@
_(res[:target]).must_equal org[:target]
end

it "supports IPv6 URIs (without brackets)" do
org = { target: "mock://FEDC:BA98:7654:3210:FEDC:BA98:7654:3210:123" }
res = Train.target_config(org)
_(res[:backend]).must_equal "mock"
_(res[:host]).must_equal "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"
_(res[:user]).must_be_nil
_(res[:password]).must_be_nil
_(res[:port]).must_equal 123
_(res[:path]).must_be_nil
_(res[:target]).must_equal org[:target]
end

it "supports empty URIs with schema://" do
org = { target: "mock://" }
res = Train.target_config(org)
Expand All @@ -193,16 +205,16 @@

it "supports www-form encoded passwords when the option is set" do
raw_password = '+!@#$%^&*()_-\';:"\\|/?.>,<][}{=`~'
encoded_password = URI.encode_www_form_component(raw_password)
orig = { target: "mock://username:#{encoded_password}@1.2.3.4:100",
www_form_encoded_password: true }
result = Train.target_config(orig)
_(result[:backend]).must_equal "mock"
_(result[:host]).must_equal "1.2.3.4"
_(result[:user]).must_equal "username"
_(result[:password]).must_equal raw_password
_(result[:port]).must_equal 100
_(result[:target]).must_equal orig[:target]
encoded_password = Addressable::URI.normalize_component(raw_password, Addressable::URI::CharacterClasses::UNRESERVED)
org = { target: "mock://username:#{encoded_password}@1.2.3.4:100",
www_form_encoded_password: true }
res = Train.target_config(org)
_(res[:backend]).must_equal "mock"
_(res[:host]).must_equal "1.2.3.4"
_(res[:user]).must_equal "username"
_(res[:password]).must_equal raw_password
_(res[:port]).must_equal 100
_(res[:target]).must_equal org[:target]
end

it "ignores www-form-encoded password value when there is no password" do
Expand All @@ -217,8 +229,8 @@
_(res[:target]).must_equal org[:target]
end

it "it raises UserError on invalid URIs" do
org = { target: "mock world" }
it "it raises UserError on invalid URIs (invalid scheme)" do
org = { target: "123://invalid_scheme.example.com/" }
_ { Train.target_config(org) }.must_raise Train::UserError
end
end
Expand Down
1 change: 1 addition & 0 deletions train-core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Gem::Specification.new do |spec|

spec.require_paths = ["lib"]

spec.add_dependency "addressable", "~> 2.5"
spec.add_dependency "inifile", "~> 3.0"
spec.add_dependency "json", ">= 1.8", "< 3.0"
spec.add_dependency "mixlib-shellout", ">= 2.0", "< 4.0"
Expand Down

0 comments on commit 77fa475

Please sign in to comment.