[Connection] Add an optional Dialer to ClientOptions - #1532
Open
SimonWoolf wants to merge 1 commit into
Open
Conversation
There is currently no way for a caller to influence how the client establishes TCP connections to brokers: connection.connect() calls net.Dial/net.DialTimeout/tls.DialWithDialer directly, and ClientOptions exposes no hook. That makes several things impossible from outside the library (in our case, we want to blocklist IPs in a way that isn't trivially avoidable by having a bad actor change DNS resolution for an address they supply after the pulsar producer is constructed). This adds an optional ClientOptions.Dialer. connect() is restructured so the TCP dial and the TLS handshake are separate steps. tls.DialWithDialer does both at once and cannot take an injected dialer, so it becomes a dial followed by tls.Client(...).HandshakeContext(). The dialer therefore returns a plain net.Conn and the library still owns TLS, leaving getTLSConfig() and certificate verification unchanged. Two behaviours of tls.DialWithDialer are preserved explicitly, since tls.Client does not provide them: - ServerName is inferred from the address being dialed when the config leaves it blank. tls.Client would otherwise verify against an empty name and fail. - ConnectionTimeout bounds the dial and the handshake together, not just the dial. The context is therefore built once in connect() and passed to both, mirroring what crypto/tls does internally. Bounding only the dial would let a peer that accepts TCP and then goes silent hang the connection attempt indefinitely. HandshakeContext is used rather than setting a deadline on the raw conn, so no deadline lingers on the long-lived broker connection afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SimonWoolf
force-pushed
the
ably-dialer
branch
from
August 26, 2026 19:33
9b0127e to
6eeb6d6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
There is currently no way for a caller to influence how the client establishes TCP connections to brokers: connection.connect() calls net.Dial/net.DialTimeout/tls.DialWithDialer directly, and ClientOptions exposes no hook. That makes several things impossible from outside the library (in our case, we want to blocklist IPs in a way that isn't trivially avoidable by having a bad actor change DNS resolution for an address they supply after the pulsar producer is constructed).
Modifications
This adds an optional ClientOptions.Dialer.
connect() is restructured so the TCP dial and the TLS handshake are separate steps. tls.DialWithDialer does both at once and cannot take an injected dialer, so it becomes a dial followed by tls.Client(...).Handshake(). The dialer therefore returns a plain net.Conn and the library still owns TLS, leaving getTLSConfig() and certificate verification unchanged.
(NB: tls.DialWithDialer infers ServerName from the address being dialed when the config leaves it blank, whereas tls.Client does not and would verify against an empty name. This preserves that behaviour).
Verifying this change
This change added tests and can be verified as follows:
go test ./pulsar/internal/ -run TestConnectionThe existing TLS integration tests cover the restructured connection.connect().
Does this pull request potentially affect one of the following parts:
Documentation