Skip to content

Commit c8d920a

Browse files
committed
PYTHON-2795 Improve host parsing and error messages
1 parent 00ed232 commit c8d920a

File tree

4 files changed

+41
-5
lines changed

4 files changed

+41
-5
lines changed

pymongo/mongo_client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,10 @@ def __init__(
650650
opts = common._CaseInsensitiveDictionary()
651651
fqdn = None
652652
for entity in host:
653-
if "://" in entity:
653+
# A hostname can only include a-z, 0-9, '-' and '.'. If we find a '/'
654+
# it must be a URI,
655+
# https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
656+
if "/" in entity:
654657
# Determine connection timeout from kwargs.
655658
timeout = keyword_opts.get("connecttimeoutms")
656659
if timeout is not None:

pymongo/srv_resolver.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
"""Support for resolving hosts and options from mongodb+srv:// URIs."""
1616

17+
import ipaddress
18+
1719
try:
1820
from dns import resolver
1921
_HAVE_DNSPYTHON = True
@@ -40,20 +42,29 @@ def _resolve(*args, **kwargs):
4042
# dnspython 1.X
4143
return resolver.query(*args, **kwargs)
4244

45+
_INVALID_HOST_MSG = (
46+
"Invalid URI host: %s is not a valid hostname for 'mongodb+srv://'. "
47+
"Did you mean to use 'mongodb://'?")
4348

4449
class _SrvResolver(object):
4550
def __init__(self, fqdn, connect_timeout=None):
4651
self.__fqdn = fqdn
4752
self.__connect_timeout = connect_timeout or CONNECT_TIMEOUT
4853

4954
# Validate the fully qualified domain name.
55+
try:
56+
ipaddress.ip_address(fqdn)
57+
raise ConfigurationError(_INVALID_HOST_MSG % ("an IP address",))
58+
except ValueError:
59+
pass
60+
5061
try:
5162
self.__plist = self.__fqdn.split(".")[1:]
5263
except Exception:
53-
raise ConfigurationError("Invalid URI host: %s" % (fqdn,))
64+
raise ConfigurationError(_INVALID_HOST_MSG % (fqdn,))
5465
self.__slen = len(self.__plist)
5566
if self.__slen < 2:
56-
raise ConfigurationError("Invalid URI host: %s" % (fqdn,))
67+
raise ConfigurationError(_INVALID_HOST_MSG % (fqdn,))
5768

5869
def get_options(self):
5970
try:

test/test_client.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,20 @@ def test_max_pool_size_zero(self):
166166
with self.assertRaises(ValueError):
167167
MongoClient(maxPoolSize=0)
168168

169+
def test_uri_detection(self):
170+
self.assertRaises(
171+
ConfigurationError,
172+
MongoClient,
173+
"/foo")
174+
self.assertRaises(
175+
ConfigurationError,
176+
MongoClient,
177+
"://")
178+
self.assertRaises(
179+
ConfigurationError,
180+
MongoClient,
181+
"foo/")
182+
169183
def test_get_db(self):
170184
def make_db(base, name):
171185
return base[name]

test/test_dns.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,20 @@ class TestParsingErrors(unittest.TestCase):
142142
def test_invalid_host(self):
143143
self.assertRaisesRegex(
144144
ConfigurationError,
145-
"Invalid URI host: mongodb",
145+
"Invalid URI host: mongodb is not",
146146
MongoClient, "mongodb+srv://mongodb")
147147
self.assertRaisesRegex(
148148
ConfigurationError,
149-
"Invalid URI host: mongodb.com",
149+
"Invalid URI host: mongodb.com is not",
150150
MongoClient, "mongodb+srv://mongodb.com")
151+
self.assertRaisesRegex(
152+
ConfigurationError,
153+
"Invalid URI host: an IP address is not",
154+
MongoClient, "mongodb+srv://127.0.0.1")
155+
self.assertRaisesRegex(
156+
ConfigurationError,
157+
"Invalid URI host: an IP address is not",
158+
MongoClient, "mongodb+srv://[::1]")
151159

152160

153161
if __name__ == '__main__':

0 commit comments

Comments
 (0)