Skip to content

Commit 5aa62a3

Browse files
committed
Add basic IPv6 support PYTHON-237
commit a6ee56d8bbfdf9e0ca651f5efa6fb71556c650ed Author: behackett <bernie@10gen.com> Date: Tue Apr 12 10:16:08 2011 -0700 Make IPv4 explicit and add a few more tests. commit 793584a1c7032f2d2b7f5ae9ca0740ceec84ba98 Author: behackett <bernie@10gen.com> Date: Mon Apr 11 16:51:18 2011 -0700 Preliminary support for IPv6.
1 parent b324935 commit 5aa62a3

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

pymongo/connection.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@
5858
_CONNECT_TIMEOUT = 20.0
5959

6060

61+
def _partition_ipv6(source):
62+
if source.find(']') == -1:
63+
raise InvalidURI("an IPv6 address literal must be "
64+
"enclosed in '[' and ']' characters.")
65+
i = source.find(']:')
66+
if i == -1:
67+
return (source[1:-1], None)
68+
return (source[1: i], source[i + 2:])
69+
70+
6171
def _partition(source, sub):
6272
"""Our own string partitioning method.
6373
@@ -74,7 +84,14 @@ def _str_to_node(string, default_port=27017):
7484
7585
"localhost:27017" -> ("localhost", 27017)
7686
"""
77-
(host, port) = _partition(string, ":")
87+
# IPv6 literal
88+
if string[0] == '[':
89+
host, port = _partition_ipv6(string)
90+
elif string.count(':') > 1 or string.find(']') != -1:
91+
raise InvalidURI("an IPv6 address literal must be "
92+
"enclosed in '[' and ']' characters.")
93+
else:
94+
host, port = _partition(string, ":")
7895
if port:
7996
port = int(port)
8097
else:
@@ -240,9 +257,11 @@ def __init__(self, host=None, port=None, pool_size=None,
240257
username, and password present will be used.
241258
242259
:Parameters:
243-
- `host` (optional): hostname or IPv4 address of the
260+
- `host` (optional): hostname or IP address of the
244261
instance to connect to, or a mongodb URI, or a list of
245-
hostnames / mongodb URIs
262+
hostnames / mongodb URIs. If `host` is an IPv6 literal
263+
it must be enclosed in '[' and ']' characters following
264+
the RFC2732 URL syntax (e.g. '[::1]' for localhost)
246265
- `port` (optional): port number on which to connect
247266
- `pool_size` (optional): DEPRECATED
248267
- `auto_start_request` (optional): DEPRECATED
@@ -585,12 +604,23 @@ def __connect(self):
585604
host, port = self.__find_master()
586605

587606
try:
588-
sock = socket.socket()
589-
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
590-
sock.settimeout(self.__network_timeout or _CONNECT_TIMEOUT)
591-
sock.connect((host, port))
592-
sock.settimeout(self.__network_timeout)
593-
return sock
607+
try:
608+
# Prefer IPv4. If there is demand for an option
609+
# to specify one or the other we can add it later.
610+
sock = socket.socket(socket.AF_INET)
611+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
612+
sock.settimeout(self.__network_timeout or _CONNECT_TIMEOUT)
613+
sock.connect((host, port))
614+
sock.settimeout(self.__network_timeout)
615+
return sock
616+
except socket.gaierror:
617+
# If that fails try IPv6
618+
sock = socket.socket(socket.AF_INET6)
619+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
620+
sock.settimeout(self.__network_timeout or _CONNECT_TIMEOUT)
621+
sock.connect((host, port))
622+
sock.settimeout(self.__network_timeout)
623+
return sock
594624
except socket.error:
595625
self.disconnect()
596626
raise AutoReconnect("could not connect to %r" % list(self.__nodes))

test/test_connection.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,37 @@ def test_tz_aware(self):
463463
aware.pymongo_test.test.find_one()["x"].replace(tzinfo=None),
464464
naive.pymongo_test.test.find_one()["x"])
465465

466+
def test_ipv6(self):
467+
self.assertRaises(InvalidURI, _parse_uri, "::1", 27017)
468+
self.assertRaises(InvalidURI, _parse_uri, "[::1", 27017)
469+
self.assertRaises(InvalidURI, _parse_uri, "::1]:27017")
470+
self.assertRaises(InvalidURI, _parse_uri, "mongodb://::1", 27017)
471+
self.assert_(_parse_uri, "mongodb://[::1]:27017/?slaveOk=true")
472+
self.assert_(_parse_uri,
473+
"[::1]:27017,[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"
474+
":27018,192.168.0.212:27019,localhost:27020")
475+
self.assert_(_parse_uri,
476+
"mongodb://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"
477+
":27017/?slaveOk=true")
478+
try:
479+
connection = Connection("[::1]")
480+
except:
481+
# Either mongod was started without --ipv6
482+
# or the OS doesn't support it (or both).
483+
raise SkipTest()
484+
485+
# Try a few simple things
486+
connection = Connection("mongodb://[::1]:27017")
487+
connection = Connection("mongodb://[::1]:27017/?slaveOk=true")
488+
connection = Connection("[::1]:27017,localhost:27017")
489+
connection = Connection("localhost:27017,[::1]:27017")
490+
connection.pymongo_test.test.save({"dummy": u"object"})
491+
connection.pymongo_test_bernie.test.save({"dummy": u"object"})
492+
493+
dbs = connection.database_names()
494+
self.assert_("pymongo_test" in dbs)
495+
self.assert_("pymongo_test_bernie" in dbs)
496+
466497

467498
if __name__ == "__main__":
468499
unittest.main()

0 commit comments

Comments
 (0)