PYTHON-6080 Introduce Custom Callback logic in Configurable DNS - #3051
sleepyStick wants to merge 44 commits into
Conversation
…was only here for the initial delivery of the project, removing now
…ver into PYTHON-5814
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…ver into PYTHON-5814
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Direct non-SRV URI parsing can silently ignore the validator, and polling behavior documentation needs clarification.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Introduces configurable SRV host validation and improves polling behavior for invalid DNS records.
Changes:
- Adds
srv_host_validatorto synchronous and asynchronous clients. - Skips invalid hosts during SRV polling and logs them.
- Updates suffix handling, tests, telemetry, and changelog documentation.
| File | Summary |
|---|---|
test/unified_format.py |
Updates synchronous test coverage and cleanup. |
test/test_srv_polling.py |
Tests synchronous SRV polling validation. |
test/test_dns.py |
Tests synchronous DNS validation and suffix behavior. |
test/asynchronous/unified_format.py |
Updates asynchronous test coverage and cleanup. |
test/asynchronous/test_srv_polling.py |
Tests asynchronous SRV polling validation. |
test/asynchronous/test_dns.py |
Tests callback and suffix behavior; prose test naming needs correction. |
pymongo/synchronous/uri_parser.py |
Synchronous URI parsing and validator propagation. |
pymongo/synchronous/srv_resolver.py |
Synchronous SRV validation and polling filtering. |
pymongo/synchronous/settings.py |
Stores synchronous validator configuration. |
pymongo/synchronous/monitor.py |
Applies validation during synchronous polling. |
pymongo/synchronous/mongo_client.py |
Exposes the synchronous validator option. |
pymongo/settings_shared.py |
Adds shared callback configuration. |
pymongo/public_suffix_list.dat |
Updates bundled suffix data. |
pymongo/common.py |
Validates callback options. |
pymongo/asynchronous/uri_parser.py |
Propagates the validator; non-SRV direct parsing must reject it. |
pymongo/asynchronous/srv_resolver.py |
Implements asynchronous validation and polling filtering. |
pymongo/asynchronous/settings.py |
Stores asynchronous validator configuration. |
pymongo/asynchronous/monitor.py |
Applies validation during asynchronous polling. |
pymongo/asynchronous/mongo_client.py |
Exposes and documents the asynchronous validator option. |
pymongo/_telemetry.py |
Logs skipped invalid SRV hosts. |
pymongo/_psl.py |
Adds special-use suffix handling. |
doc/changelog.rst |
Documents the new functionality. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| reject it. Rejecting a host raises | ||
| :exc:`~pymongo.errors.ConfigurationError`, as does an exception raised by | ||
| the callback itself. Use this when the set of acceptable hosts cannot be | ||
| expressed as a single suffix:: |
There was a problem hiding this comment.
I don't think users need to know the difference between initial discovery and polling so I believe this suggestion should be ignored.
There was a problem hiding this comment.
True but probably doesn't hurt to clarify "During initial discovery" …
| elif self.__srv_allowed_hosts_suffix is not None: | ||
| if not srv_host.endswith(self.__srv_allowed_hosts_suffix): | ||
| raise ConfigurationError(f"Invalid SRV host: {srv_host}") | ||
| else: |
There was a problem hiding this comment.
I think we miss this branch if the conditional on line 155 is True.
There was a problem hiding this comment.
sorry i'm not sure i understand what you mean by this comment -- it is possible to enter the else conditional and there is test coverage for all three branches. If we're referring to the identical hostname check, that's intentional per the spec. Can you clarify what you mean?
| connection string. See the | ||
| :class:`~pymongo.synchronous.mongo_client.MongoClient` and | ||
| :class:`~pymongo.asynchronous.mongo_client.AsyncMongoClient` documentation for | ||
| security considerations (`PYTHON-5814`_). |
There was a problem hiding this comment.
yes, but also current iris isn't sure why past iris put a ticket there so i'm going to just remove the ticket reference entirely.
| self.__srv_allowed_hosts_suffix | ||
| ): | ||
| self.__srv_host_validator = srv_host_validator | ||
| # parse_uri() can this constructor independently of AsyncMongoClient. |
There was a problem hiding this comment.
"can … this constructor"?
| self.__srv_allowed_hosts_suffix | ||
| ): | ||
| self.__srv_host_validator = srv_host_validator | ||
| # parse_uri() can this constructor independently of MongoClient. |
There was a problem hiding this comment.
"can … this constructor"?
There was a problem hiding this comment.
Does the caller reject it? Looks like a..b becomes .a..b
There was a problem hiding this comment.
sorry I think this was a case of bad comment -- realistically DNS won't return an SRV target with an empty label, so even if a user passes it in, it would fail verification. I've updated the comment appropriately in 07df266. Thanks!
| nodes = [] | ||
| for node in self.nodelist_callback(): | ||
| try: | ||
| resolver._validate_host(node[0].rstrip(".").lower()) |
There was a problem hiding this comment.
Since the mock calls _validate_host and drops rejected hosts here, tests 14 and 15 would still pass if _get_srv_response_and_hosts stopped filtering for some reason.
There was a problem hiding this comment.
ah good catch -- i've reworked this in 07df266 to only mock the DNS answer so the real filtering happens now!

PYTHON-6080
Changes in this PR
Test Plan
Checklist
Checklist for Author
Checklist for Reviewer