@@ -14,6 +14,10 @@ import (
1414 "time"
1515)
1616
17+ // DialContextFunc is a function type that matches the signature of net.Dialer.DialContext.
18+ // It can be used to provide custom connection establishment logic.
19+ type DialContextFunc func (ctx context.Context , network , address string ) (net.Conn , error )
20+
1721// DialerConfig holds the complete configuration for establishing network connections,
1822// including timeouts, keep-alive settings, TLS parameters, TCP options, custom DNS
1923// resolvers, and proxy settings.
@@ -34,6 +38,11 @@ type DialerConfig struct {
3438 // ProxyURL specifies the URL of an HTTP/HTTPS proxy server. If set, connections
3539 // will be established through the proxy using the CONNECT method.
3640 ProxyURL * url.URL
41+ // DialContext allows providing a custom dial function for establishing TCP connections.
42+ // If set, this function will be used instead of the default net.Dialer for direct connections.
43+ // Note: When ProxyURL is set, this function is used to connect to the proxy server.
44+ // The function should return a raw TCP connection; TLS wrapping is handled separately.
45+ DialContext DialContextFunc
3746}
3847
3948// Clone creates a deep copy of the DialerConfig, ensuring that mutable fields
@@ -54,6 +63,8 @@ func (c *DialerConfig) Clone() *DialerConfig {
5463 proxyURLCopy := * c .ProxyURL
5564 clone .ProxyURL = & proxyURLCopy
5665 }
66+ // DialContext is a function, safe to copy by value (shallow copy).
67+ // The caller is responsible for ensuring their custom DialContext is thread-safe.
5768 return & clone
5869}
5970
@@ -127,16 +138,23 @@ func DialTCPContext(ctx context.Context, network, address string, config *Dialer
127138
128139// dialDirect establishes a direct TCP connection to the target address.
129140func dialDirect (ctx context.Context , network , address string , config * DialerConfig ) (net.Conn , error ) {
130- dialer := & net.Dialer {
131- Timeout : config .Timeout ,
132- KeepAlive : config .KeepAlive ,
133- // Enable Happy Eyeballs (RFC 8305) for faster IPv4/IPv6 fallback.
134- FallbackDelay : 300 * time .Millisecond ,
135- Resolver : config .Resolver ,
136- }
141+ var rawConn net.Conn
142+ var err error
137143
138144 // Step 1: Establish the raw TCP connection.
139- rawConn , err := dialer .DialContext (ctx , network , address )
145+ // Use custom DialContext if provided, otherwise use the default net.Dialer.
146+ if config .DialContext != nil {
147+ rawConn , err = config .DialContext (ctx , network , address )
148+ } else {
149+ dialer := & net.Dialer {
150+ Timeout : config .Timeout ,
151+ KeepAlive : config .KeepAlive ,
152+ // Enable Happy Eyeballs (RFC 8305) for faster IPv4/IPv6 fallback.
153+ FallbackDelay : 300 * time .Millisecond ,
154+ Resolver : config .Resolver ,
155+ }
156+ rawConn , err = dialer .DialContext (ctx , network , address )
157+ }
140158 if err != nil {
141159 return nil , fmt .Errorf ("tcp dial failed: %w" , err )
142160 }
@@ -276,9 +294,9 @@ func (c *prefixedConn) Read(p []byte) (int, error) {
276294// both direct and proxied connections transparently.
277295//
278296// This function orchestrates the entire connection process:
279- // 1. Establishes a raw TCP connection (or a proxy tunnel) via DialTCPContext.
280- // 2. If `config.TLSConfig` is not nil, it performs a TLS handshake over the
281- // established connection to upgrade it to a secure `tls.Conn`.
297+ // 1. Establishes a raw TCP connection (or a proxy tunnel) via DialTCPContext.
298+ // 2. If `config.TLSConfig` is not nil, it performs a TLS handshake over the
299+ // established connection to upgrade it to a secure `tls.Conn`.
282300//
283301// It is suitable for creating connections for protocols that handle their own
284302// application layer logic, such as WebSockets.
0 commit comments