From 9eaf6b7b8328fcda9ea53350c7b6203e173e925f Mon Sep 17 00:00:00 2001 From: "David E. Chen" Date: Thu, 8 Sep 2011 15:31:33 -0700 Subject: [PATCH] Support the current MongoDB URI scheme with multiple hosts/ports separated by commas all using the same authentication and database. --- lib/mongo/util/uri_parser.rb | 48 +++++++++++++++++++++--------------- test/uri_test.rb | 8 +++--- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/lib/mongo/util/uri_parser.rb b/lib/mongo/util/uri_parser.rb index 6b4e69a..9336c93 100644 --- a/lib/mongo/util/uri_parser.rb +++ b/lib/mongo/util/uri_parser.rb @@ -20,7 +20,7 @@ module Mongo class URIParser DEFAULT_PORT = 27017 - MONGODB_URI_MATCHER = /(([-.\w]+):([^@]+)@)?([-.\w]+)(:([\w]+))?(\/([-\w]+))?/ + MONGODB_URI_MATCHER = /(([-.\w]+):([^@,]+)@)?((?:(?:[-.\w]+)(?::(?:[\w]+))?,?)+)(\/([-\w]+))?/ MONGODB_URI_SPEC = "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]" SPEC_ATTRS = [:nodes, :auths] OPT_ATTRS = [:connect, :replicaset, :slaveok, :safe, :w, :wtimeout, :fsync] @@ -108,35 +108,43 @@ module Mongo private - def parse_hosts(hosts) + def parse_hosts(uri_without_proto) @nodes = [] @auths = [] - specs = hosts.split(',') - specs.each do |spec| - matches = MONGODB_URI_MATCHER.match(spec) - if !matches - raise MongoArgumentError, "MongoDB URI must match this spec: #{MONGODB_URI_SPEC}" - end - uname = matches[2] - pwd = matches[3] - host = matches[4] - port = matches[6] || DEFAULT_PORT + matches = MONGODB_URI_MATCHER.match(uri_without_proto) + + if !matches + raise MongoArgumentError, "MongoDB URI must match this spec: #{MONGODB_URI_SPEC}" + end + + uname = matches[2] + pwd = matches[3] + hosturis = matches[4].split(',') + db = matches[6] + + hosturis.each do |hosturi| + # If port is present, use it, otherwise use default port + host, port = hosturi.split(':') + [DEFAULT_PORT] + if !(port.to_s =~ /^\d+$/) raise MongoArgumentError, "Invalid port #{port}; port must be specified as digits." end - port = port.to_i - db = matches[8] - if uname && pwd && db - auths << {'db_name' => db, 'username' => uname, 'password' => pwd} - elsif uname || pwd - raise MongoArgumentError, "MongoDB URI must include username, password, " + - "and db if username or password are specified." - end + port = port.to_i @nodes << [host, port] end + + if uname && pwd && db + auths << {'db_name' => db, 'username' => uname, 'password' => pwd} + elsif uname || pwd + raise MongoArgumentError, "MongoDB URI must include username, password, " + "and db if username and password are specified." + end + + # The auths are repeated for each host in a replica set + @auths *= hosturis.length end # This method uses the lambdas defined in OPT_VALID and OPT_CONV to validate diff --git a/test/uri_test.rb b/test/uri_test.rb index 84e1b1c..0d17d38 100644 --- a/test/uri_test.rb +++ b/test/uri_test.rb @@ -43,7 +43,7 @@ class TestThreading < Test::Unit::TestCase end def test_multiple_uris_with_auths - parser = Mongo::URIParser.new('mongodb://bob:secret@a.example.com:27018/test,joe:secret2@b.example.com/test2') + parser = Mongo::URIParser.new('mongodb://bob:secret@a.example.com:27018,b.example.com/test') assert_equal 2, parser.nodes.length assert_equal 'a.example.com', parser.nodes[0][0] assert_equal 27018, parser.nodes[0][1] @@ -53,9 +53,9 @@ class TestThreading < Test::Unit::TestCase assert_equal "bob", parser.auths[0]["username"] assert_equal "secret", parser.auths[0]["password"] assert_equal "test", parser.auths[0]["db_name"] - assert_equal "joe", parser.auths[1]["username"] - assert_equal "secret2", parser.auths[1]["password"] - assert_equal "test2", parser.auths[1]["db_name"] + assert_equal "bob", parser.auths[1]["username"] + assert_equal "secret", parser.auths[1]["password"] + assert_equal "test", parser.auths[1]["db_name"] end def test_opts_basic