@@ -180,7 +180,7 @@ func open(
180
180
if clock == nil {
181
181
return nil , errors .NotValidf ("nil clock" )
182
182
}
183
- conn , tlsConfig , err := connectWebsocket (info , opts )
183
+ conn , tlsConfig , err := dialAPI (info , opts )
184
184
if err != nil {
185
185
return nil , errors .Trace (err )
186
186
}
@@ -275,70 +275,6 @@ func (t *hostSwitchingTransport) RoundTrip(req *http.Request) (*http.Response, e
275
275
return t .fallback .RoundTrip (req )
276
276
}
277
277
278
- // connectWebsocket establishes a websocket connection to the RPC
279
- // API websocket on the API server using Info. If multiple API addresses
280
- // are provided in Info they will be tried concurrently - the first successful
281
- // connection wins.
282
- //
283
- // It also returns the TLS configuration that it has derived from the Info.
284
- func connectWebsocket (info * Info , opts DialOpts ) (* websocket.Conn , * tls.Config , error ) {
285
- if len (info .Addrs ) == 0 {
286
- return nil , nil , errors .New ("no API addresses to connect to" )
287
- }
288
- tlsConfig := utils .SecureTLSConfig ()
289
- tlsConfig .InsecureSkipVerify = opts .InsecureSkipVerify
290
-
291
- if info .CACert != "" && ! tlsConfig .InsecureSkipVerify {
292
- // We want to be specific here (rather than just using "anything".
293
- // See commit 7fc118f015d8480dfad7831788e4b8c0432205e8 (PR 899).
294
- tlsConfig .ServerName = "juju-apiserver"
295
- certPool , err := CreateCertPool (info .CACert )
296
- if err != nil {
297
- return nil , nil , errors .Annotate (err , "cert pool creation failed" )
298
- }
299
- tlsConfig .RootCAs = certPool
300
- }
301
- path , err := apiPath (info .ModelTag , "/api" )
302
- if err != nil {
303
- return nil , nil , errors .Trace (err )
304
- }
305
- conn , err := dialWebSocket (info .Addrs , path , tlsConfig , opts )
306
- if err != nil {
307
- return nil , nil , errors .Trace (err )
308
- }
309
- logger .Infof ("connection established to %q" , conn .RemoteAddr ())
310
- return conn , tlsConfig , nil
311
- }
312
-
313
- // dialWebSocket dials a websocket with one of the provided addresses, the
314
- // specified URL path, TLS configuration, and dial options. Each of the
315
- // specified addresses will be attempted concurrently, and the first
316
- // successful connection will be returned.
317
- func dialWebSocket (addrs []string , path string , tlsConfig * tls.Config , opts DialOpts ) (* websocket.Conn , error ) {
318
- // Dial all addresses at reasonable intervals.
319
- try := parallel .NewTry (0 , nil )
320
- defer try .Kill ()
321
- for _ , addr := range addrs {
322
- err := dialWebsocket (addr , path , opts , tlsConfig , try )
323
- if err == parallel .ErrStopped {
324
- break
325
- }
326
- if err != nil {
327
- return nil , errors .Trace (err )
328
- }
329
- select {
330
- case <- time .After (opts .DialAddressInterval ):
331
- case <- try .Dead ():
332
- }
333
- }
334
- try .Close ()
335
- result , err := try .Result ()
336
- if err != nil {
337
- return nil , errors .Trace (err )
338
- }
339
- return result .(* websocket.Conn ), nil
340
- }
341
-
342
278
// ConnectStream implements StreamConnector.ConnectStream.
343
279
func (st * state ) ConnectStream (path string , attrs url.Values ) (base.Stream , error ) {
344
280
if ! st .isLoggedIn () {
@@ -486,7 +422,83 @@ func tagToString(tag names.Tag) string {
486
422
return tag .String ()
487
423
}
488
424
489
- func dialWebsocket (addr , path string , opts DialOpts , tlsConfig * tls.Config , try * parallel.Try ) error {
425
+ // dialAPI establishes a websocket connection to the RPC
426
+ // API websocket on the API server using Info. If multiple API addresses
427
+ // are provided in Info they will be tried concurrently - the first successful
428
+ // connection wins.
429
+ //
430
+ // It also returns the TLS configuration that it has derived from the Info.
431
+ func dialAPI (info * Info , opts DialOpts ) (* websocket.Conn , * tls.Config , error ) {
432
+ // Set opts.DialWebsocket here rather than in open because
433
+ // some tests call dialAPI directly.
434
+ if opts .DialWebsocket == nil {
435
+ opts .DialWebsocket = websocket .DialConfig
436
+ }
437
+ if len (info .Addrs ) == 0 {
438
+ return nil , nil , errors .New ("no API addresses to connect to" )
439
+ }
440
+ tlsConfig := utils .SecureTLSConfig ()
441
+ tlsConfig .InsecureSkipVerify = opts .InsecureSkipVerify
442
+
443
+ if info .CACert != "" {
444
+ // We want to be specific here (rather than just using "anything".
445
+ // See commit 7fc118f015d8480dfad7831788e4b8c0432205e8 (PR 899).
446
+ tlsConfig .ServerName = "juju-apiserver"
447
+ certPool , err := CreateCertPool (info .CACert )
448
+ if err != nil {
449
+ return nil , nil , errors .Annotate (err , "cert pool creation failed" )
450
+ }
451
+ tlsConfig .RootCAs = certPool
452
+ } else {
453
+ // No CA certificate so use the SNI host name for all
454
+ // connections (if SNIHostName is empty, the host
455
+ // name in the address will be used as usual).
456
+ tlsConfig .ServerName = info .SNIHostName
457
+ }
458
+ path , err := apiPath (info .ModelTag , "/api" )
459
+ if err != nil {
460
+ return nil , nil , errors .Trace (err )
461
+ }
462
+ conn , err := dialWebsocketMulti (info .Addrs , path , tlsConfig , opts )
463
+ if err != nil {
464
+ return nil , nil , errors .Trace (err )
465
+ }
466
+ logger .Infof ("connection established to %q" , conn .RemoteAddr ())
467
+ return conn , tlsConfig , nil
468
+ }
469
+
470
+ // dialWebsocketMulti dials a websocket with one of the provided addresses, the
471
+ // specified URL path, TLS configuration, and dial options. Each of the
472
+ // specified addresses will be attempted concurrently, and the first
473
+ // successful connection will be returned.
474
+ func dialWebsocketMulti (addrs []string , path string , tlsConfig * tls.Config , opts DialOpts ) (* websocket.Conn , error ) {
475
+ // Dial all addresses at reasonable intervals.
476
+ try := parallel .NewTry (0 , nil )
477
+ defer try .Kill ()
478
+ for _ , addr := range addrs {
479
+ err := startDialWebsocket (try , addr , path , opts , tlsConfig )
480
+ if err == parallel .ErrStopped {
481
+ break
482
+ }
483
+ if err != nil {
484
+ return nil , errors .Trace (err )
485
+ }
486
+ select {
487
+ case <- time .After (opts .DialAddressInterval ):
488
+ case <- try .Dead ():
489
+ }
490
+ }
491
+ try .Close ()
492
+ result , err := try .Result ()
493
+ if err != nil {
494
+ return nil , errors .Trace (err )
495
+ }
496
+ return result .(* websocket.Conn ), nil
497
+ }
498
+
499
+ // startDialWebsocket starts websocket connection to a single address
500
+ // on the given try instance.
501
+ func startDialWebsocket (try * parallel.Try , addr , path string , opts DialOpts , tlsConfig * tls.Config ) error {
490
502
// origin is required by the WebSocket API, used for "origin policy"
491
503
// in websockets. We pass localhost to satisfy the API; it is
492
504
// inconsequential to us.
@@ -499,11 +511,10 @@ func dialWebsocket(addr, path string, opts DialOpts, tlsConfig *tls.Config, try
499
511
return try .Start (newWebsocketDialer (cfg , opts ))
500
512
}
501
513
502
- // newWebsocketDialer returns a function that
503
- // can be passed to utils/parallel.Try.Start.
504
- var newWebsocketDialer = createWebsocketDialer
505
-
506
- func createWebsocketDialer (cfg * websocket.Config , opts DialOpts ) func (<- chan struct {}) (io.Closer , error ) {
514
+ // newWebsocketDialer0 returns a function that dials the websocket represented
515
+ // by the given configuration with the given dial options, suitable for passing
516
+ // to utils/parallel.Try.Start.
517
+ func newWebsocketDialer (cfg * websocket.Config , opts DialOpts ) func (<- chan struct {}) (io.Closer , error ) {
507
518
// TODO(katco): 2016-08-09: lp:1611427
508
519
openAttempt := utils.AttemptStrategy {
509
520
Total : opts .Timeout ,
@@ -517,7 +528,7 @@ func createWebsocketDialer(cfg *websocket.Config, opts DialOpts) func(<-chan str
517
528
default :
518
529
}
519
530
logger .Infof ("dialing %q" , cfg .Location )
520
- conn , err := websocket . DialConfig (cfg )
531
+ conn , err := opts . DialWebsocket (cfg )
521
532
if err == nil {
522
533
return conn , nil
523
534
}
0 commit comments