@@ -13,6 +13,7 @@ package tls
13
13
14
14
import (
15
15
"bytes"
16
+ "context"
16
17
"crypto"
17
18
"crypto/ecdsa"
18
19
"crypto/ed25519"
@@ -111,29 +112,35 @@ func (timeoutError) Temporary() bool { return true }
111
112
// DialWithDialer interprets a nil configuration as equivalent to the zero
112
113
// configuration; see the documentation of Config for the defaults.
113
114
func DialWithDialer (dialer * net.Dialer , network , addr string , config * Config ) (* Conn , error ) {
115
+ return dial (context .Background (), dialer , network , addr , config )
116
+ }
117
+
118
+ func dial (ctx context.Context , netDialer * net.Dialer , network , addr string , config * Config ) (* Conn , error ) {
114
119
// We want the Timeout and Deadline values from dialer to cover the
115
120
// whole process: TCP connection and TLS handshake. This means that we
116
121
// also need to start our own timers now.
117
- timeout := dialer .Timeout
122
+ timeout := netDialer .Timeout
118
123
119
- if ! dialer .Deadline .IsZero () {
120
- deadlineTimeout := time .Until (dialer .Deadline )
124
+ if ! netDialer .Deadline .IsZero () {
125
+ deadlineTimeout := time .Until (netDialer .Deadline )
121
126
if timeout == 0 || deadlineTimeout < timeout {
122
127
timeout = deadlineTimeout
123
128
}
124
129
}
125
130
126
- var errChannel chan error
127
-
131
+ // hsErrCh is non-nil if we might not wait for Handshake to complete.
132
+ var hsErrCh chan error
133
+ if timeout != 0 || ctx .Done () != nil {
134
+ hsErrCh = make (chan error , 2 )
135
+ }
128
136
if timeout != 0 {
129
- errChannel = make (chan error , 2 )
130
137
timer := time .AfterFunc (timeout , func () {
131
- errChannel <- timeoutError {}
138
+ hsErrCh <- timeoutError {}
132
139
})
133
140
defer timer .Stop ()
134
141
}
135
142
136
- rawConn , err := dialer . Dial ( network , addr )
143
+ rawConn , err := netDialer . DialContext ( ctx , network , addr )
137
144
if err != nil {
138
145
return nil , err
139
146
}
@@ -158,14 +165,26 @@ func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*
158
165
159
166
conn := Client (rawConn , config )
160
167
161
- if timeout == 0 {
168
+ if hsErrCh == nil {
162
169
err = conn .Handshake ()
163
170
} else {
164
171
go func () {
165
- errChannel <- conn .Handshake ()
172
+ hsErrCh <- conn .Handshake ()
166
173
}()
167
174
168
- err = <- errChannel
175
+ select {
176
+ case <- ctx .Done ():
177
+ err = ctx .Err ()
178
+ case err = <- hsErrCh :
179
+ if err != nil {
180
+ // If the error was due to the context
181
+ // closing, prefer the context's error, rather
182
+ // than some random network teardown error.
183
+ if e := ctx .Err (); e != nil {
184
+ err = e
185
+ }
186
+ }
187
+ }
169
188
}
170
189
171
190
if err != nil {
@@ -186,6 +205,54 @@ func Dial(network, addr string, config *Config) (*Conn, error) {
186
205
return DialWithDialer (new (net.Dialer ), network , addr , config )
187
206
}
188
207
208
+ // Dialer dials TLS connections given a configuration and a Dialer for the
209
+ // underlying connection.
210
+ type Dialer struct {
211
+ // NetDialer is the optional dialer to use for the TLS connections'
212
+ // underlying TCP connections.
213
+ // A nil NetDialer is equivalent to the net.Dialer zero value.
214
+ NetDialer * net.Dialer
215
+
216
+ // Config is the TLS configuration to use for new connections.
217
+ // A nil configuration is equivalent to the zero
218
+ // configuration; see the documentation of Config for the
219
+ // defaults.
220
+ Config * Config
221
+ }
222
+
223
+ // Dial connects to the given network address and initiates a TLS
224
+ // handshake, returning the resulting TLS connection.
225
+ //
226
+ // The returned Conn, if any, will always be of type *Conn.
227
+ func (d * Dialer ) Dial (network , addr string ) (net.Conn , error ) {
228
+ return d .DialContext (context .Background (), network , addr )
229
+ }
230
+
231
+ func (d * Dialer ) netDialer () * net.Dialer {
232
+ if d .NetDialer != nil {
233
+ return d .NetDialer
234
+ }
235
+ return new (net.Dialer )
236
+ }
237
+
238
+ // Dial connects to the given network address and initiates a TLS
239
+ // handshake, returning the resulting TLS connection.
240
+ //
241
+ // The provided Context must be non-nil. If the context expires before
242
+ // the connection is complete, an error is returned. Once successfully
243
+ // connected, any expiration of the context will not affect the
244
+ // connection.
245
+ //
246
+ // The returned Conn, if any, will always be of type *Conn.
247
+ func (d * Dialer ) DialContext (ctx context.Context , network , addr string ) (net.Conn , error ) {
248
+ c , err := dial (ctx , d .netDialer (), network , addr , d .Config )
249
+ if err != nil {
250
+ // Don't return c (a typed nil) in an interface.
251
+ return nil , err
252
+ }
253
+ return c , nil
254
+ }
255
+
189
256
// LoadX509KeyPair reads and parses a public/private key pair from a pair
190
257
// of files. The files must contain PEM encoded data. The certificate file
191
258
// may contain intermediate certificates following the leaf certificate to
0 commit comments